BUpload.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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 ( !window.applicationCache )
  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. errorHandler : function(messsage, type) {
  96. alert(messsage);
  97. },
  98. callback : function(data) {
  99. console.log(data);
  100. }
  101. }, options);
  102. //错误代码和提示消息
  103. var codeMessageMap = {
  104. '000' : '文件上传成功',
  105. '001' : '文件上传失败',
  106. '003' : '文件大小超出限制',
  107. '004' : '非法文件名后缀'
  108. };
  109. var mimeType = {
  110. "3gpp":"audio/3gpp, video/3gpp",
  111. "ac3":"audio/ac3",
  112. "asf":"allpication/vnd.ms-asf",
  113. "au":"audio/basic",
  114. "css":"text/css",
  115. "csv":"text/csv",
  116. "doc":"application/msword",
  117. "dot":"application/msword",
  118. "dtd":"application/xml-dtd",
  119. "dwg":"image/vnd.dwg",
  120. "dxf":"image/vnd.dxf",
  121. "gif":"image/gif",
  122. "htm":"text/html",
  123. "html":"text/html",
  124. "jp2":"image/jp2",
  125. "jpe":"image/jpeg",
  126. "jpeg":"image/jpeg",
  127. "jpg":"image/jpeg",
  128. "js":"text/javascript, application/javascript",
  129. "json":"application/json",
  130. "mp2":"audio/mpeg, video/mpeg",
  131. "mp3":"audio/mpeg",
  132. "mp4":"audio/mp4, video/mp4",
  133. "mpeg":"video/mpeg",
  134. "mpg":"video/mpeg",
  135. "mpp":"application/vnd.ms-project",
  136. "ogg":"application/ogg, audio/ogg",
  137. "pdf":"application/pdf",
  138. "png":"image/png",
  139. "pot":"application/vnd.ms-powerpoint",
  140. "pps":"application/vnd.ms-powerpoint",
  141. "ppt":"application/vnd.ms-powerpoint",
  142. "rtf":"application/rtf, text/rtf",
  143. "svf":"image/vnd.svf",
  144. "tif":"image/tiff",
  145. "tiff":"image/tiff",
  146. "txt":"text/plain",
  147. "wdb":"application/vnd.ms-works",
  148. "wps":"application/vnd.ms-works",
  149. "xhtml":"application/xhtml+xml",
  150. "xlc":"application/vnd.ms-excel",
  151. "xlm":"application/vnd.ms-excel",
  152. "xls":"application/vnd.ms-excel",
  153. "xlt":"application/vnd.ms-excel",
  154. "xlw":"application/vnd.ms-excel",
  155. "xml":"text/xml, application/xml",
  156. "zip":"aplication/zip",
  157. "xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  158. }
  159. var o = {};
  160. o.dialog = null;
  161. o.todoList = new Array(); //the file queue to be uploaded
  162. o.uploadSuccessNum = 0; //已经上传成功的图片数量
  163. o.selectedList = new Array(); //the file queue upload successfully
  164. o.addedFileNumber = 0; //the numbers of files that has added
  165. o.totalFilesize = 0; //total file size
  166. o.uploadLock = false; //upload thread lock
  167. o.page = 1; //服务器图片列表页码
  168. o.marker = null, //七牛云上传的分页标识
  169. o.noRecord = false;
  170. var dialogSCode = Math.ceil(Math.random() * 1000000000000); //对话框的令牌,如果创建多个BUpload上传对象用来保持唯一性
  171. //close the dialog
  172. o.close = function () {
  173. o.dialog.remove();
  174. if (typeof options.close == 'function') {
  175. options.close();
  176. }
  177. }
  178. //create dialog
  179. function createDialog() {
  180. var builder = new StringBuilder();
  181. builder.append('<div class="uedbody ke-animated"><div class="ued_title">');
  182. builder.append('<div class="uedbar"><span>'+options.lang.title+'</span></div><div class="close_btn icon"' +
  183. ' title="'+options.lang.closeText+'"></div>');
  184. builder.append('</div><div class="wrapper"><div id="wra_head" class="wra_head"><span class="tab' +
  185. ' tab-upload focus" tab="upload-panel">'+options.lang.localUpload+'</span>');
  186. if ( options.list_url != null ) {
  187. builder.append('<span class="tab tab-online" tab="online">'+options.lang.fileServer+'</span>');
  188. }
  189. builder.append('</div><div class="wra_body"><div class="tab-panel upload-panel"><div class="wra_pla"><div class="upload-image-placeholder">');
  190. builder.append('<div class="btn btn-primary image-select">'+options.lang.selectFile+'</div><input type="file" name="'+options.src+'" class="webuploader-element-invisible"' +
  191. ' multiple="multiple" accept="'+getAccept()+'">');
  192. builder.append('</div></div><div class="image-list-box" style="display: none;"><div class="wra_bar"><div class="info fl"></div>');
  193. 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>');
  194. 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>');
  195. builder.append('<div class="tab-panel searchbox"><div class="search-bar"><input class="searTxt"' +
  196. ' type="text" placeholder="'+options.lang.searchPlaceholder+'" />');
  197. 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" />');
  198. builder.append('</div><div class="search-imagelist-box"><ul class="search-list"></ul><div class="no-data"></div></div>');
  199. 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>');
  200. builder.append('<span class="btn btn-default btn-cancel">'+options.lang.cancelBtnText+'</span></div></div>');
  201. o.dialog = $(builder.toString());
  202. $("body").append(o.dialog);
  203. if (options.top == 0) {
  204. options.top = (Math.max(0,$(window).height() - o.dialog.height()))/2;
  205. }
  206. o.dialog.css({
  207. left : ($(window).width() - o.dialog.width())/2 + "px",
  208. top : options.top + "px"
  209. });
  210. $(".wrapper", o.dialog).css("max-height", ($(window).height() - 100)+"px");
  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|svg|webp".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. xhr.send(formData);
  381. }
  382. //upload next file(上传下一个文件)
  383. function uploadNextFile() {
  384. if ( o.todoList.length ) {
  385. var nextFile = o.todoList.shift();
  386. uploadFile(nextFile);
  387. } else {
  388. o.uploadLock = false; //release the upload lock
  389. G(".btn-start-upload").removeClass("disabled").text(options.lang.startUpload);
  390. //console.log(o.selectedList);
  391. }
  392. }
  393. // progress handler(文件上传进度控制)
  394. function updateProgress(e, node) {
  395. if ( e.lengthComputable ) {
  396. $("#img-comtainer-"+dialogSCode+ node.index).find(".progress span").css({"width" : (e.loaded/e.total)*100+'%', "display":"block"});
  397. }
  398. }
  399. //update file info text
  400. function updateInfoText(filenum, filesize) {
  401. var text = KindEditor.tmpl(options.lang.uploadDesc, {numSelect:filenum, totalSize:formatFileSize(filesize), numLeft:(options.max_filenum - filenum)});
  402. G(".info").text(text);
  403. }
  404. //format file size(格式化文件大小)
  405. function formatFileSize(size) {
  406. if ( size/1048576 > 1 ) {
  407. return (size/1048576).toFixed(2)+"MB";
  408. } else {
  409. return (size/1024).toFixed(2)+"KB";
  410. }
  411. }
  412. //file check handler(文件检测处理函数)
  413. function fileCheckHandler(node) {
  414. //检查文件大小
  415. var maxsize = options.max_filesize * 1024;
  416. if ( maxsize > 0 && node.file.size > maxsize ) {
  417. __error__(KindEditor.tmpl(options.lang.sizeLimit, {sizeLimit:options.max_filesize}), node);
  418. return false;
  419. }
  420. //检查文件后缀名
  421. var ext = getFileExt(node.file.name);
  422. if ( ext && options.ext_allow.indexOf(ext) != -1
  423. && options.ext_refuse.indexOf(ext) == -1 ) {
  424. return true;
  425. } else {
  426. __error__(KindEditor.tmpl(options.lang.invalidExt, {invalidExt:ext}), node);
  427. return false;
  428. }
  429. }
  430. //获取文件后缀名
  431. function getFileExt(filename) {
  432. if ( !filename ) return false;
  433. var position = filename.lastIndexOf('.')
  434. if ( position != -1 ) {
  435. return filename.substr(position+1).toLowerCase();
  436. }
  437. return false;
  438. }
  439. //获取可接受的文件后缀
  440. function getAccept() {
  441. var extensions = options.ext_allow.split("|");
  442. var accept = [];
  443. $.each(extensions, function(idx, item) {
  444. accept.push(mimeType[item]);
  445. });
  446. if ( accept.length > 1 ) {
  447. return accept.uinque().join(",");
  448. }
  449. return "*";
  450. }
  451. //显示上传错误信息
  452. function __error__(message, node) {
  453. G("#img-comtainer-"+dialogSCode+ node.index).find(".error").show().text(message);
  454. }
  455. //query
  456. function G(query) {
  457. return o.dialog.find(query);
  458. }
  459. //从服务器上获取图片地址
  460. function loadFilesFromServer() {
  461. if ( !options.list_url ) {
  462. G(".online .no-data").html('<span class="error">'+options.lang.noListUrl+'</span>').show();
  463. return false;
  464. }
  465. if ( o.noRecord ) return false;
  466. G(".loading-icon").show(); //显示加载图标
  467. $.get(options.list_url, {
  468. page : o.page,
  469. marker : o.marker,
  470. fileType : options.fileType,
  471. }, function(res) {
  472. G(".loading-icon").hide(); //隐藏加载图标
  473. if ( res.code == "000" ) {
  474. if (!res.data[0]) { //没有加载到数据
  475. G(".online .no-data").text(options.lang.noDataText).show();
  476. return;
  477. }
  478. o.marker = res.extra; //存储marker
  479. o.page++;
  480. appendFiles(res.data, "online");
  481. } else {
  482. G(".online .no-data").text(options.lang.noDataText).show();
  483. o.noRecord = true;
  484. }
  485. }, "json");
  486. }
  487. //追加元素到图片列表
  488. function appendFiles(data, module) {
  489. $.each(data, function(idx, item) {
  490. var builder = new StringBuilder();
  491. builder.append('<li>');
  492. var extension = getFileExt(item.thumbURL);
  493. if ( extension == '' ) extension = "default";
  494. extension = extension.toLowerCase();
  495. //如果不是图片,则根据文件的后缀名去加载对应的缩略图
  496. var imgSize = item.width+'x'+item.height; //图片尺寸
  497. if ( "jpg|jpeg|gif|png|bmp".indexOf(extension) == -1 ) {
  498. imgSize = formatFileSize(item.filesize); //如果是文件则显示文件大小
  499. builder.append('<span class="icon-placeholder icon-'+extension+'" data-src="'+item.oriURL+'"></span>');
  500. } else {
  501. builder.append('<img src="'+item.thumbURL+'" data-src="'+item.oriURL+'" border="0">');
  502. }
  503. builder.append('<span class="ic" data-module="'+module+'"><em class="img-size">'+imgSize+'</em></span></li>');
  504. var $image = $(builder.toString());
  505. //绑定选择图片事件
  506. $image.find(".ic").on("click", function() {
  507. var src = $(this).prev().data("src");
  508. var module = $(this).data("module");
  509. if ( $(this).hasClass("selected") ) {
  510. $(this).removeClass("selected");
  511. } else {
  512. $(this).addClass("selected");
  513. o.selectedList.push(src);
  514. }
  515. //console.log(o.selectedList);
  516. });
  517. //裁剪显示图片
  518. $image.find("img").imageCrop(113, 113);
  519. if ( module == "online" ) {
  520. G(".imagelist .list").append($image);
  521. } else if ( module == "search" ) {
  522. G(".search-imagelist-box .search-list").append($image);
  523. }
  524. });
  525. }
  526. //initialize dialog
  527. createDialog();
  528. bindEvent();
  529. return o;
  530. }; //end of JUpload
  531. //string builder
  532. var StringBuilder = function() {
  533. var buffer = new Array();
  534. StringBuilder.prototype.append = function(str) {
  535. buffer.push(str);
  536. }
  537. StringBuilder.prototype.toString = function () {
  538. return buffer.join("");
  539. }
  540. }
  541. })(jQuery);