BUpload.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /**
  2. * HTML5上传插件
  3. * @site https://git.oschina.net/blackfox/ajaxUpload
  4. * @author yangjian<yangjian102621@gmail.com>
  5. * @version 1.0.1
  6. */
  7. (function($) {
  8. //判断浏览器是否支持html5
  9. if ( typeof FormData == "undefined" )
  10. throw new Error("您当前的浏览器不支持HTML5,请先升级浏览器才能使用该上传插件!");
  11. //image crop
  12. $.fn.imageCrop = function(__width, __height) {
  13. $(this).on("load", function () {
  14. var width, height, left, top;
  15. var orgRate = this.width/this.height;
  16. var cropRate = __width/__height;
  17. if ( orgRate >= cropRate ) {
  18. height = __height;
  19. width = __width * orgRate;
  20. top = 0;
  21. left = (width - __width)/2;
  22. } else {
  23. width = __width;
  24. height = __height / orgRate;
  25. left = 0;
  26. //top = (height - __height)/2;
  27. top = 0;
  28. }
  29. $(this).css({
  30. "position" : "absolute",
  31. top : -top + "px",
  32. left : -left + "px",
  33. width : width + "px",
  34. height : height + "px"
  35. });
  36. });
  37. }
  38. //make element draggable
  39. $.fn.draggable = function(options) {
  40. var defaults = {
  41. handler : null
  42. }
  43. options = $.extend(defaults, options);
  44. var __self = this;
  45. $(options.handler).mousedown(function(e) {
  46. var offsetLeft = e.pageX - $(__self).position().left;
  47. var offsetTop = e.pageY - $(__self).position().top;
  48. $(document).mousemove(function(e) {
  49. //清除拖动鼠标的时候选择文本
  50. window.getSelection ? window.getSelection().removeAllRanges():document.selection.empty();
  51. $(__self).css({
  52. 'top' : e.pageY-offsetTop + 'px',
  53. 'left' : e.pageX-offsetLeft + 'px'
  54. });
  55. });
  56. }).mouseup(function() {
  57. $(document).unbind('mousemove');
  58. });
  59. }
  60. if ( Array.prototype.remove == undefined ) {
  61. Array.prototype.remove = function(item) {
  62. for ( var i = 0; i < this.length; i++ ) {
  63. if ( this[i] == item ) {
  64. this.splice(i, 1);
  65. break;
  66. }
  67. }
  68. }
  69. }
  70. if ( Array.prototype.uinque == undefined ) {
  71. Array.prototype.uinque = function() {
  72. var result = [], hash = {};
  73. for ( var i = 0, item; (item = this[i]) != null; i++ ) {
  74. if ( !hash[item] ) {
  75. result.push(item);
  76. hash[item] = true;
  77. }
  78. }
  79. return result;
  80. }
  81. }
  82. window.BUpload = function(options) {
  83. options = $.extend({
  84. src : "src",
  85. upload_url : null,
  86. list_url : null,
  87. data_type : "json",
  88. top : 20,
  89. fileType : "image", //文件类型,默认是图片,可选flash,media,file
  90. max_filesize : 2048, //unit:KB
  91. max_filenum : 20,
  92. no_data_text : "(⊙o⊙)亲,没有多数据了。",
  93. ext_allow : "jpg|png|gif|jpeg",
  94. ext_refuse : "exe|txt",
  95. extra_params : {},
  96. errorHandler : function(messsage, type) {
  97. alert(messsage);
  98. },
  99. callback : function(data) {
  100. console.log(data);
  101. }
  102. }, options);
  103. //错误代码和提示消息
  104. var codeMessageMap = {
  105. '000' : '文件上传成功',
  106. '001' : '文件上传失败',
  107. '003' : '文件大小超出限制',
  108. '004' : '非法文件名后缀'
  109. };
  110. var mimeType = {
  111. "3gpp":"audio/3gpp, video/3gpp",
  112. "ac3":"audio/ac3",
  113. "asf":"allpication/vnd.ms-asf",
  114. "au":"audio/basic",
  115. "css":"text/css",
  116. "csv":"text/csv",
  117. "doc":"application/msword",
  118. "dot":"application/msword",
  119. "dtd":"application/xml-dtd",
  120. "dwg":"image/vnd.dwg",
  121. "dxf":"image/vnd.dxf",
  122. "gif":"image/gif",
  123. "htm":"text/html",
  124. "html":"text/html",
  125. "jp2":"image/jp2",
  126. "jpe":"image/jpeg",
  127. "jpeg":"image/jpeg",
  128. "jpg":"image/jpeg",
  129. "js":"text/javascript, application/javascript",
  130. "json":"application/json",
  131. "mp2":"audio/mpeg, video/mpeg",
  132. "mp3":"audio/mpeg",
  133. "mp4":"audio/mp4, video/mp4",
  134. "mpeg":"video/mpeg",
  135. "mpg":"video/mpeg",
  136. "mpp":"application/vnd.ms-project",
  137. "ogg":"application/ogg, audio/ogg",
  138. "pdf":"application/pdf",
  139. "png":"image/png",
  140. "pot":"application/vnd.ms-powerpoint",
  141. "pps":"application/vnd.ms-powerpoint",
  142. "ppt":"application/vnd.ms-powerpoint",
  143. "rtf":"application/rtf, text/rtf",
  144. "svf":"image/vnd.svf",
  145. "tif":"image/tiff",
  146. "tiff":"image/tiff",
  147. "txt":"text/plain",
  148. "wdb":"application/vnd.ms-works",
  149. "wps":"application/vnd.ms-works",
  150. "xhtml":"application/xhtml+xml",
  151. "xlc":"application/vnd.ms-excel",
  152. "xlm":"application/vnd.ms-excel",
  153. "xls":"application/vnd.ms-excel",
  154. "xlt":"application/vnd.ms-excel",
  155. "xlw":"application/vnd.ms-excel",
  156. "xml":"text/xml, application/xml",
  157. "zip":"aplication/zip",
  158. "xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  159. }
  160. var o = {};
  161. o.dialog = null;
  162. o.todoList = new Array(); //the file queue to be uploaded
  163. o.uploadSuccessNum = 0; //已经上传成功的图片数量
  164. o.selectedList = new Array(); //the file queue upload successfully
  165. o.addedFileNumber = 0; //the numbers of files that has added
  166. o.totalFilesize = 0; //total file size
  167. o.uploadLock = false; //upload thread lock
  168. o.page = 1; //服务器图片列表页码
  169. o.marker = null, //七牛云上传的分页标识
  170. o.noRecord = false;
  171. var dialogSCode = Math.ceil(Math.random() * 1000000000000); //对话框的令牌,如果创建多个BUpload上传对象用来保持唯一性
  172. //close the dialog
  173. o.close = function () {
  174. o.dialog.remove();
  175. if (typeof options.close == 'function') {
  176. options.close();
  177. }
  178. }
  179. //create dialog
  180. function createDialog() {
  181. var builder = new StringBuilder();
  182. builder.append('<div class="uedbody ke-animated"><div class="ued_title">');
  183. builder.append('<div class="uedbar"><span>'+options.lang.title+'</span></div><div class="close_btn icon"' +
  184. ' title="'+options.lang.closeText+'"></div>');
  185. builder.append('</div><div class="wrapper"><div id="wra_head" class="wra_head"><span class="tab' +
  186. ' tab-upload focus" tab="upload-panel">'+options.lang.localUpload+'</span>');
  187. if ( options.list_url != null ) {
  188. builder.append('<span class="tab tab-online" tab="online">'+options.lang.fileServer+'</span>');
  189. }
  190. builder.append('</div><div class="wra_body"><div class="tab-panel upload-panel"><div class="wra_pla"><div class="upload-image-placeholder">');
  191. builder.append('<div class="btn btn-primary image-select">'+options.lang.selectFile+'</div><input type="file" name="'+options.src+'" class="webuploader-element-invisible"' +
  192. ' multiple="multiple" accept="'+getAccept()+'">');
  193. builder.append('</div></div><div class="image-list-box" style="display: none;"><div class="wra_bar"><div class="info fl"></div>');
  194. builder.append('<div class="fr"><span class="btn btn-default btn-continue-add">'+options.lang.continueAdd+'</span><span class="btn btn-primary btn-start-upload">'+options.lang.startUpload+'</span></div></div>');
  195. builder.append('<ul class="filelist"></ul></div></div><div class="tab-panel online"><div class="imagelist"><ul class="list clearfix"></ul><div class="no-data"></div></div></div>');
  196. builder.append('<div class="tab-panel searchbox"><div class="search-bar"><input class="searTxt"' +
  197. ' type="text" placeholder="'+options.lang.searchPlaceholder+'" />');
  198. builder.append('<input value="'+options.lang.searchBtn+'" class="btn btn-primary btn-search" type="button" /><input value="'+options.lang.searchClear+'" class="btn btn-default btn-reset" type="button" />');
  199. builder.append('</div><div class="search-imagelist-box"><ul class="search-list"></ul><div class="no-data"></div></div>');
  200. builder.append('</div><div class="loading-icon"></div></div><!-- end of wrapper --></div><div class="wra-btn-group"><span class="btn btn-primary btn-confirm">'+options.lang.confirmBtnText+'</span>');
  201. builder.append('<span class="btn btn-default btn-cancel">'+options.lang.cancelBtnText+'</span></div></div>');
  202. o.dialog = $(builder.toString());
  203. $("body").append(o.dialog);
  204. if (options.top == 0) {
  205. options.top = ($(window).height() - o.dialog.height())/2;
  206. }
  207. o.dialog.css({
  208. left : ($(window).width() - o.dialog.width())/2 + "px",
  209. top : options.top + "px"
  210. });
  211. //给对话框添加拖拽事件
  212. o.dialog.draggable({handler : o.dialog.find(".ued_title")})
  213. }
  214. //绑定元素事件
  215. function bindEvent() {
  216. //选项卡事件
  217. G(".tab").on("click", function() {
  218. var tab = $(this).attr("tab");
  219. G(".tab-panel").hide();
  220. G("."+tab).show();
  221. G(".tab").removeClass("focus");
  222. $(this).addClass("focus");
  223. });
  224. //关闭对话框
  225. G(".close_btn").on("click", function() {
  226. o.close();
  227. });
  228. //选择文件事件
  229. G(".webuploader-element-invisible").on("change", function() {
  230. addFiles(this);
  231. });
  232. //弹出上传文件选择框
  233. G(".image-select").on("click", function() {
  234. G(".webuploader-element-invisible").trigger("click");
  235. });
  236. G(".btn-continue-add").on("click", function() {
  237. G(".webuploader-element-invisible").trigger("click");
  238. });
  239. //开始上传按钮事件
  240. G(".btn-start-upload").on("click", function() {
  241. if ( o.uploadLock ) return;
  242. if ( o.todoList.length == 0 ) {
  243. options.errorHandler(options.lang.noFileAdded, "error");
  244. return false;
  245. }
  246. $(this).addClass("disabled").text(options.lang.uploading);
  247. uploadFile(o.todoList.shift());
  248. });
  249. //点击确认|取消按钮事件
  250. G(".btn-confirm").on("click", function() {
  251. if ( o.todoList.length > 0 ) {
  252. options.errorHandler(options.lang.fileNotUpload, "error");
  253. return false;
  254. }
  255. if (o.selectedList.length == 0) {
  256. options.errorHandler(options.lang.noFileSelected, "error");
  257. return false;
  258. }
  259. options.callback(o.selectedList);
  260. o.close();
  261. });
  262. G(".btn-cancel").on("click", function() {
  263. o.close();
  264. });
  265. //从服务器加载文件
  266. G(".tab-online").on("click", function() {
  267. if ( G(".imagelist .list").children().length == 0 ) {
  268. loadFilesFromServer()
  269. }
  270. });
  271. //当滚动条滚到底部时自动去加载图片
  272. G(".imagelist").on("scroll", function() {
  273. if ( this.scrollTop + this.clientHeight >= this.scrollHeight ) {
  274. loadFilesFromServer();
  275. }
  276. });
  277. }
  278. //add file to upload list
  279. function addFiles(input) {
  280. var files = input.files;
  281. var totalFileNum = o.todoList.length + o.uploadSuccessNum + files.length; //本次上传文件总数
  282. for ( var i = o.addedFileNumber; i < o.addedFileNumber+files.length; i++ ) {
  283. if ( totalFileNum > options.max_filenum ) {
  284. options.errorHandler(KindEditor.tmpl(options.lang.uploadLimit, {uploadLimit: options.max_filenum}), "error");
  285. return;
  286. }
  287. var builder = new StringBuilder();
  288. var tempFile = files[i- o.addedFileNumber];
  289. builder.append('<li id="img-comtainer-'+dialogSCode+i+'"><div class="imgWrap">');
  290. //如果上传的不是图片,则通过判断文件后缀来显示不同的图标
  291. var extension = getFileExt(tempFile.name);
  292. if ( extension == '' ) extension = "default";
  293. extension = extension.toLowerCase();
  294. if ( "jpg|jpeg|gif|png|bmp".indexOf(extension) == -1 ) {
  295. builder.append('<span class="icon-placeholder icon-default icon-'+extension+'"></span>');
  296. } else {
  297. builder.append('<img src="'+window.URL.createObjectURL(tempFile)+'" border="0" />');
  298. }
  299. builder.append('</div><div class="file-opt-box clearfix"><span class="remove" index="'+i+'">'+options.lang.remove+'</span><span class="rotateRight">'+options.lang.rotateRight+'</span>');
  300. builder.append('<span class="rotateLeft">'+options.lang.rotateLeft+'</span></div><div class="success"></div><div class="error"></div>');
  301. builder.append('<div class="progress"><span style="display: none; width: 0px;"></span></div></li>');
  302. var $image = $(builder.toString());
  303. //bind onelele event
  304. $image.find(".remove").on("click", function() {
  305. $(this).parents("li").remove(); //remove element
  306. //remove file from todoList
  307. var index = $(this).attr("index");
  308. for ( var i = 0; i < o.todoList.length; i++ ) {
  309. if ( o.todoList[i].index == index ) {
  310. o.totalFilesize -= o.todoList[i].file.size;
  311. updateInfoText(o.uploadSuccessNum + o.todoList.length-1, o.totalFilesize);
  312. o.todoList.splice(i, 1);
  313. break;
  314. }
  315. }
  316. if (G(".filelist li").length == 0) {
  317. G(".image-list-box").hide();
  318. G(".wra_pla").show();
  319. }
  320. });
  321. $image.on("mouseover", function() {
  322. $(this).find(".file-opt-box").show();
  323. }).on("mouseout", function() {
  324. $(this).find(".file-opt-box").hide();
  325. });
  326. G(".wra_pla").hide();
  327. G(".image-list-box").show();
  328. G(".filelist").append($image);
  329. o.todoList.push({index:i, file:tempFile});
  330. o.totalFilesize += tempFile.size;
  331. //console.log(tempFile);
  332. }
  333. o.addedFileNumber += files.length;
  334. updateInfoText(o.uploadSuccessNum + o.todoList.length, o.totalFilesize);
  335. //缩放并裁剪图片
  336. $(".imgWrap img").imageCrop(113,113);
  337. }
  338. /**
  339. * upload file function(文件上传主函数)
  340. * @param node 数据节点
  341. */
  342. function uploadFile(node) {
  343. if ( !fileCheckHandler(node) ) {
  344. uploadNextFile(); //skip the file and upload the next file
  345. return;
  346. }
  347. // prepare XMLHttpRequest
  348. var xhr = new XMLHttpRequest();
  349. xhr.open('POST', options.upload_url);
  350. //upload successfully
  351. xhr.addEventListener('load',function(e) {
  352. if ( options.data_type == "json" ) {
  353. //console.log(e);
  354. var data = $.parseJSON(e.target.responseText);
  355. if ( data.code == "000" ) {
  356. o.selectedList.push(data.data.url); //添加文件到上传文件列表
  357. o.uploadSuccessNum++;
  358. $("#img-comtainer-"+dialogSCode+ node.index).find(".file-opt-box").remove();
  359. $("#img-comtainer-"+dialogSCode+ node.index).find(".progress").remove();
  360. $("#img-comtainer-"+dialogSCode+ node.index).find(".success").show();
  361. } else {
  362. __error__(codeMessageMap[data.code], node);
  363. }
  364. }
  365. }, false);
  366. // file upload complete
  367. xhr.addEventListener('loadend', function () {
  368. uploadNextFile(); //upload the next file
  369. }, false);
  370. //上传失败
  371. xhr.addEventListener('error', function() {
  372. __error__(options.lang.uploadFail, node);
  373. }, false);
  374. xhr.upload.addEventListener('progress', function(e) {
  375. updateProgress(e, node);
  376. }, false);
  377. // prepare FormData
  378. var formData = new FormData();
  379. formData.append(options.src, node.file);
  380. // add extra params
  381. for(var k in options.extra_params) {
  382. formData.append(k, options.extra_params[k]);
  383. }
  384. xhr.send(formData);
  385. }
  386. //upload next file(上传下一个文件)
  387. function uploadNextFile() {
  388. if ( o.todoList.length ) {
  389. var nextFile = o.todoList.shift();
  390. uploadFile(nextFile);
  391. } else {
  392. o.uploadLock = false; //release the upload lock
  393. G(".btn-start-upload").removeClass("disabled").text(options.lang.startUpload);
  394. //console.log(o.selectedList);
  395. }
  396. }
  397. // progress handler(文件上传进度控制)
  398. function updateProgress(e, node) {
  399. if ( e.lengthComputable ) {
  400. $("#img-comtainer-"+dialogSCode+ node.index).find(".progress span").css({"width" : (e.loaded/e.total)*100+'%', "display":"block"});
  401. }
  402. }
  403. //update file info text
  404. function updateInfoText(filenum, filesize) {
  405. var text = KindEditor.tmpl(options.lang.uploadDesc, {numSelect:filenum, totalSize:formatFileSize(filesize), numLeft:(options.max_filenum - filenum)});
  406. G(".info").text(text);
  407. }
  408. //format file size(格式化文件大小)
  409. function formatFileSize(size) {
  410. if ( size/1048576 > 1 ) {
  411. return (size/1048576).toFixed(2)+"MB";
  412. } else {
  413. return (size/1024).toFixed(2)+"KB";
  414. }
  415. }
  416. //file check handler(文件检测处理函数)
  417. function fileCheckHandler(node) {
  418. //检查文件大小
  419. var maxsize = options.max_filesize * 1024;
  420. if ( maxsize > 0 && node.file.size > maxsize ) {
  421. __error__(KindEditor.tmpl(options.lang.sizeLimit, {sizeLimit:options.max_filesize}), node);
  422. return false;
  423. }
  424. //检查文件后缀名
  425. var ext = getFileExt(node.file.name);
  426. if ( ext && options.ext_allow.indexOf(ext) != -1
  427. && options.ext_refuse.indexOf(ext) == -1 ) {
  428. return true;
  429. } else {
  430. __error__(KindEditor.tmpl(options.lang.invalidExt, {invalidExt:ext}), node);
  431. return false;
  432. }
  433. }
  434. //获取文件后缀名
  435. function getFileExt(filename) {
  436. if ( !filename ) return false;
  437. var position = filename.lastIndexOf('.')
  438. if ( position != -1 ) {
  439. return filename.substr(position+1).toLowerCase();
  440. }
  441. return false;
  442. }
  443. //获取可接受的文件后缀
  444. function getAccept() {
  445. var extensions = options.ext_allow.split("|");
  446. var accept = [];
  447. $.each(extensions, function(idx, item) {
  448. accept.push(mimeType[item]);
  449. });
  450. if ( accept.length > 1 ) {
  451. return accept.uinque().join(",");
  452. }
  453. return "*";
  454. }
  455. //显示上传错误信息
  456. function __error__(message, node) {
  457. G("#img-comtainer-"+dialogSCode+ node.index).find(".error").show().text(message);
  458. }
  459. //query
  460. function G(query) {
  461. return o.dialog.find(query);
  462. }
  463. //从服务器上获取图片地址
  464. function loadFilesFromServer() {
  465. if ( !options.list_url ) {
  466. G(".online .no-data").html('<span class="error">'+options.lang.noListUrl+'</span>').show();
  467. return false;
  468. }
  469. if ( o.noRecord ) return false;
  470. G(".loading-icon").show(); //显示加载图标
  471. $.get(options.list_url, {
  472. page : o.page,
  473. marker : o.marker,
  474. fileType : options.fileType,
  475. }, function(res) {
  476. G(".loading-icon").hide(); //隐藏加载图标
  477. if ( res.code == "000" ) {
  478. if (!res.data[0]) { //没有加载到数据
  479. G(".online .no-data").text(options.lang.noDataText).show();
  480. return;
  481. }
  482. o.marker = res.extra; //存储marker
  483. o.page++;
  484. appendFiles(res.data, "online");
  485. } else {
  486. G(".online .no-data").text(options.lang.noDataText).show();
  487. o.noRecord = true;
  488. }
  489. }, "json");
  490. }
  491. //追加元素到图片列表
  492. function appendFiles(data, module) {
  493. $.each(data, function(idx, item) {
  494. var builder = new StringBuilder();
  495. builder.append('<li>');
  496. var extension = getFileExt(item.thumbURL);
  497. if ( extension == '' ) extension = "default";
  498. extension = extension.toLowerCase();
  499. //如果不是图片,则根据文件的后缀名去加载对应的缩略图
  500. var imgSize = item.width+'x'+item.height; //图片尺寸
  501. if ( "jpg|jpeg|gif|png|bmp".indexOf(extension) == -1 ) {
  502. imgSize = formatFileSize(item.filesize); //如果是文件则显示文件大小
  503. builder.append('<span class="icon-placeholder icon-'+extension+'" data-src="'+item.oriURL+'"></span>');
  504. } else {
  505. builder.append('<img src="'+item.thumbURL+'" data-src="'+item.oriURL+'" border="0">');
  506. }
  507. builder.append('<span class="ic" data-module="'+module+'"><em class="img-size">'+imgSize+'</em></span></li>');
  508. var $image = $(builder.toString());
  509. //绑定选择图片事件
  510. $image.find(".ic").on("click", function() {
  511. var src = $(this).prev().data("src");
  512. var module = $(this).data("module");
  513. if ( $(this).hasClass("selected") ) {
  514. $(this).removeClass("selected");
  515. } else {
  516. $(this).addClass("selected");
  517. o.selectedList.push(src);
  518. }
  519. //console.log(o.selectedList);
  520. });
  521. //裁剪显示图片
  522. $image.find("img").imageCrop(113, 113);
  523. if ( module == "online" ) {
  524. G(".imagelist .list").append($image);
  525. } else if ( module == "search" ) {
  526. G(".search-imagelist-box .search-list").append($image);
  527. }
  528. });
  529. }
  530. //initialize dialog
  531. createDialog();
  532. bindEvent();
  533. return o;
  534. }; //end of JUpload
  535. //string builder
  536. var StringBuilder = function() {
  537. var buffer = new Array();
  538. StringBuilder.prototype.append = function(str) {
  539. buffer.push(str);
  540. }
  541. StringBuilder.prototype.toString = function () {
  542. return buffer.join("");
  543. }
  544. }
  545. })(jQuery);