BUpload.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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 = ($(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. //给对话框添加拖拽事件
  211. o.dialog.draggable({handler : o.dialog.find(".ued_title")})
  212. }
  213. //绑定元素事件
  214. function bindEvent() {
  215. //选项卡事件
  216. G(".tab").on("click", function() {
  217. var tab = $(this).attr("tab");
  218. G(".tab-panel").hide();
  219. G("."+tab).show();
  220. G(".tab").removeClass("focus");
  221. $(this).addClass("focus");
  222. });
  223. //关闭对话框
  224. G(".close_btn").on("click", function() {
  225. o.close();
  226. });
  227. //选择文件事件
  228. G(".webuploader-element-invisible").on("change", function() {
  229. addFiles(this);
  230. });
  231. //弹出上传文件选择框
  232. G(".image-select").on("click", function() {
  233. G(".webuploader-element-invisible").trigger("click");
  234. });
  235. G(".btn-continue-add").on("click", function() {
  236. G(".webuploader-element-invisible").trigger("click");
  237. });
  238. //开始上传按钮事件
  239. G(".btn-start-upload").on("click", function() {
  240. if ( o.uploadLock ) return;
  241. if ( o.todoList.length == 0 ) {
  242. options.errorHandler(options.lang.noFileAdded, "error");
  243. return false;
  244. }
  245. $(this).addClass("disabled").text(options.lang.uploading);
  246. uploadFile(o.todoList.shift());
  247. });
  248. //点击确认|取消按钮事件
  249. G(".btn-confirm").on("click", function() {
  250. if ( o.todoList.length > 0 ) {
  251. options.errorHandler(options.lang.fileNotUpload, "error");
  252. return false;
  253. }
  254. if (o.selectedList.length == 0) {
  255. options.errorHandler(options.lang.noFileSelected, "error");
  256. return false;
  257. }
  258. options.callback(o.selectedList);
  259. o.close();
  260. });
  261. G(".btn-cancel").on("click", function() {
  262. o.close();
  263. });
  264. //从服务器加载文件
  265. G(".tab-online").on("click", function() {
  266. if ( G(".imagelist .list").children().length == 0 ) {
  267. loadFilesFromServer()
  268. }
  269. });
  270. //当滚动条滚到底部时自动去加载图片
  271. G(".imagelist").on("scroll", function() {
  272. if ( this.scrollTop + this.clientHeight >= this.scrollHeight ) {
  273. loadFilesFromServer();
  274. }
  275. });
  276. }
  277. //add file to upload list
  278. function addFiles(input) {
  279. var files = input.files;
  280. var totalFileNum = o.todoList.length + o.uploadSuccessNum + files.length; //本次上传文件总数
  281. for ( var i = o.addedFileNumber; i < o.addedFileNumber+files.length; i++ ) {
  282. if ( totalFileNum > options.max_filenum ) {
  283. options.errorHandler(KindEditor.tmpl(options.lang.uploadLimit, {uploadLimit: options.max_filenum}), "error");
  284. return;
  285. }
  286. var builder = new StringBuilder();
  287. var tempFile = files[i- o.addedFileNumber];
  288. builder.append('<li id="img-comtainer-'+dialogSCode+i+'"><div class="imgWrap">');
  289. //如果上传的不是图片,则通过判断文件后缀来显示不同的图标
  290. var extension = getFileExt(tempFile.name);
  291. if ( extension == '' ) extension = "default";
  292. extension = extension.toLowerCase();
  293. if ( "jpg|jpeg|gif|png|bmp".indexOf(extension) == -1 ) {
  294. builder.append('<span class="icon-placeholder icon-default icon-'+extension+'"></span>');
  295. } else {
  296. builder.append('<img src="'+window.URL.createObjectURL(tempFile)+'" border="0" />');
  297. }
  298. 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>');
  299. builder.append('<span class="rotateLeft">'+options.lang.rotateLeft+'</span></div><div class="success"></div><div class="error"></div>');
  300. builder.append('<div class="progress"><span style="display: none; width: 0px;"></span></div></li>');
  301. var $image = $(builder.toString());
  302. //bind onelele event
  303. $image.find(".remove").on("click", function() {
  304. $(this).parents("li").remove(); //remove element
  305. //remove file from todoList
  306. var index = $(this).attr("index");
  307. for ( var i = 0; i < o.todoList.length; i++ ) {
  308. if ( o.todoList[i].index == index ) {
  309. o.totalFilesize -= o.todoList[i].file.size;
  310. updateInfoText(o.uploadSuccessNum + o.todoList.length-1, o.totalFilesize);
  311. o.todoList.splice(i, 1);
  312. break;
  313. }
  314. }
  315. if (G(".filelist li").length == 0) {
  316. G(".image-list-box").hide();
  317. G(".wra_pla").show();
  318. }
  319. });
  320. $image.on("mouseover", function() {
  321. $(this).find(".file-opt-box").show();
  322. }).on("mouseout", function() {
  323. $(this).find(".file-opt-box").hide();
  324. });
  325. G(".wra_pla").hide();
  326. G(".image-list-box").show();
  327. G(".filelist").append($image);
  328. o.todoList.push({index:i, file:tempFile});
  329. o.totalFilesize += tempFile.size;
  330. //console.log(tempFile);
  331. }
  332. o.addedFileNumber += files.length;
  333. updateInfoText(o.uploadSuccessNum + o.todoList.length, o.totalFilesize);
  334. //缩放并裁剪图片
  335. $(".imgWrap img").imageCrop(113,113);
  336. }
  337. /**
  338. * upload file function(文件上传主函数)
  339. * @param node 数据节点
  340. */
  341. function uploadFile(node) {
  342. if ( !fileCheckHandler(node) ) {
  343. uploadNextFile(); //skip the file and upload the next file
  344. return;
  345. }
  346. // prepare XMLHttpRequest
  347. var xhr = new XMLHttpRequest();
  348. xhr.open('POST', options.upload_url);
  349. //upload successfully
  350. xhr.addEventListener('load',function(e) {
  351. if ( options.data_type == "json" ) {
  352. //console.log(e);
  353. var data = $.parseJSON(e.target.responseText);
  354. if ( data.code == "000" ) {
  355. o.selectedList.push(data.data.url); //添加文件到上传文件列表
  356. o.uploadSuccessNum++;
  357. $("#img-comtainer-"+dialogSCode+ node.index).find(".file-opt-box").remove();
  358. $("#img-comtainer-"+dialogSCode+ node.index).find(".progress").remove();
  359. $("#img-comtainer-"+dialogSCode+ node.index).find(".success").show();
  360. } else {
  361. __error__(codeMessageMap[data.code], node);
  362. }
  363. }
  364. }, false);
  365. // file upload complete
  366. xhr.addEventListener('loadend', function () {
  367. uploadNextFile(); //upload the next file
  368. }, false);
  369. //上传失败
  370. xhr.addEventListener('error', function() {
  371. __error__(options.lang.uploadFail, node);
  372. }, false);
  373. xhr.upload.addEventListener('progress', function(e) {
  374. updateProgress(e, node);
  375. }, false);
  376. // prepare FormData
  377. var formData = new FormData();
  378. formData.append(options.src, node.file);
  379. xhr.send(formData);
  380. }
  381. //upload next file(上传下一个文件)
  382. function uploadNextFile() {
  383. if ( o.todoList.length ) {
  384. var nextFile = o.todoList.shift();
  385. uploadFile(nextFile);
  386. } else {
  387. o.uploadLock = false; //release the upload lock
  388. G(".btn-start-upload").removeClass("disabled").text(options.lang.startUpload);
  389. //console.log(o.selectedList);
  390. }
  391. }
  392. // progress handler(文件上传进度控制)
  393. function updateProgress(e, node) {
  394. if ( e.lengthComputable ) {
  395. $("#img-comtainer-"+dialogSCode+ node.index).find(".progress span").css({"width" : (e.loaded/e.total)*100+'%', "display":"block"});
  396. }
  397. }
  398. //update file info text
  399. function updateInfoText(filenum, filesize) {
  400. var text = KindEditor.tmpl(options.lang.uploadDesc, {numSelect:filenum, totalSize:formatFileSize(filesize), numLeft:(options.max_filenum - filenum)});
  401. G(".info").text(text);
  402. }
  403. //format file size(格式化文件大小)
  404. function formatFileSize(size) {
  405. if ( size/1048576 > 1 ) {
  406. return (size/1048576).toFixed(2)+"MB";
  407. } else {
  408. return (size/1024).toFixed(2)+"KB";
  409. }
  410. }
  411. //file check handler(文件检测处理函数)
  412. function fileCheckHandler(node) {
  413. //检查文件大小
  414. var maxsize = options.max_filesize * 1024;
  415. if ( maxsize > 0 && node.file.size > maxsize ) {
  416. __error__(KindEditor.tmpl(options.lang.sizeLimit, {sizeLimit:options.max_filesize}), node);
  417. return false;
  418. }
  419. //检查文件后缀名
  420. var ext = getFileExt(node.file.name);
  421. if ( ext && options.ext_allow.indexOf(ext) != -1
  422. && options.ext_refuse.indexOf(ext) == -1 ) {
  423. return true;
  424. } else {
  425. __error__(KindEditor.tmpl(options.lang.invalidExt, {invalidExt:ext}), node);
  426. return false;
  427. }
  428. }
  429. //获取文件后缀名
  430. function getFileExt(filename) {
  431. if ( !filename ) return false;
  432. var position = filename.lastIndexOf('.')
  433. if ( position != -1 ) {
  434. return filename.substr(position+1).toLowerCase();
  435. }
  436. return false;
  437. }
  438. //获取可接受的文件后缀
  439. function getAccept() {
  440. var extensions = options.ext_allow.split("|");
  441. var accept = [];
  442. $.each(extensions, function(idx, item) {
  443. accept.push(mimeType[item]);
  444. });
  445. if ( accept.length > 1 ) {
  446. return accept.uinque().join(",");
  447. }
  448. return "*";
  449. }
  450. //显示上传错误信息
  451. function __error__(message, node) {
  452. G("#img-comtainer-"+dialogSCode+ node.index).find(".error").show().text(message);
  453. }
  454. //query
  455. function G(query) {
  456. return o.dialog.find(query);
  457. }
  458. //从服务器上获取图片地址
  459. function loadFilesFromServer() {
  460. if ( !options.list_url ) {
  461. G(".online .no-data").html('<span class="error">'+options.lang.noListUrl+'</span>').show();
  462. return false;
  463. }
  464. if ( o.noRecord ) return false;
  465. G(".loading-icon").show(); //显示加载图标
  466. $.get(options.list_url, {
  467. page : o.page,
  468. marker : o.marker,
  469. fileType : options.fileType,
  470. }, function(res) {
  471. G(".loading-icon").hide(); //隐藏加载图标
  472. if ( res.code == "000" ) {
  473. if (!res.data[0]) { //没有加载到数据
  474. G(".online .no-data").text(options.lang.noDataText).show();
  475. return;
  476. }
  477. o.marker = res.extra; //存储marker
  478. o.page++;
  479. appendFiles(res.data, "online");
  480. } else {
  481. G(".online .no-data").text(options.lang.noDataText).show();
  482. o.noRecord = true;
  483. }
  484. }, "json");
  485. }
  486. //追加元素到图片列表
  487. function appendFiles(data, module) {
  488. $.each(data, function(idx, item) {
  489. var builder = new StringBuilder();
  490. builder.append('<li>');
  491. var extension = getFileExt(item.thumbURL);
  492. if ( extension == '' ) extension = "default";
  493. extension = extension.toLowerCase();
  494. //如果不是图片,则根据文件的后缀名去加载对应的缩略图
  495. var imgSize = item.width+'x'+item.height; //图片尺寸
  496. if ( "jpg|jpeg|gif|png|bmp".indexOf(extension) == -1 ) {
  497. imgSize = formatFileSize(item.filesize); //如果是文件则显示文件大小
  498. builder.append('<span class="icon-placeholder icon-'+extension+'" data-src="'+item.oriURL+'"></span>');
  499. } else {
  500. builder.append('<img src="'+item.thumbURL+'" data-src="'+item.oriURL+'" border="0">');
  501. }
  502. builder.append('<span class="ic" data-module="'+module+'"><em class="img-size">'+imgSize+'</em></span></li>');
  503. var $image = $(builder.toString());
  504. //绑定选择图片事件
  505. $image.find(".ic").on("click", function() {
  506. var src = $(this).prev().data("src");
  507. var module = $(this).data("module");
  508. if ( $(this).hasClass("selected") ) {
  509. $(this).removeClass("selected");
  510. } else {
  511. $(this).addClass("selected");
  512. o.selectedList.push(src);
  513. }
  514. //console.log(o.selectedList);
  515. });
  516. //裁剪显示图片
  517. $image.find("img").imageCrop(113, 113);
  518. if ( module == "online" ) {
  519. G(".imagelist .list").append($image);
  520. } else if ( module == "search" ) {
  521. G(".search-imagelist-box .search-list").append($image);
  522. }
  523. });
  524. }
  525. //initialize dialog
  526. createDialog();
  527. bindEvent();
  528. return o;
  529. }; //end of JUpload
  530. //string builder
  531. var StringBuilder = function() {
  532. var buffer = new Array();
  533. StringBuilder.prototype.append = function(str) {
  534. buffer.push(str);
  535. }
  536. StringBuilder.prototype.toString = function () {
  537. return buffer.join("");
  538. }
  539. }
  540. })(jQuery);