1 /** 2 * multipleUpload dialog 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add("editor/plugin/multipleUpload/dialog", function (S, Editor, ProgressBar, Overlay4E, FlashBridge, localStorage, undefined) { 6 7 var UA = S.UA, 8 DOM = S.DOM, 9 $ = S.all, 10 JSON = S.JSON, 11 PIC_NUM_LIMIT = 15, 12 PIC_NUM_LIMIT_WARNING = "系统将只保留 n 张", 13 PIC_SIZE_LIMIT = 1000, 14 PIC_SIZE_LIMIT_WARNING = "图片太大,请压缩至 n M以下", 15 Dialog = Overlay4E.Dialog, 16 KEY = "Multiple-Upload-Save", 17 movie = Editor.Utils.debugUrl("plugin/uploader/uploader.longzang.swf"), 18 name = "ks-editor-multipleUpload", 19 FLASH_VERSION_REQUIRED = "10.0.0"; 20 21 function MultiUploadDialog(editor, config) { 22 this.editor = editor; 23 this.progressBars = {}; 24 this.config = config; 25 Editor.Utils.lazyRun(this, "_prepareShow", "_realShow"); 26 } 27 28 29 //定义通用的函数交换两个结点的位置 30 function swapNode(node1, node2) { 31 //获取父结点 32 var _parent = node1.parentNode; 33 //获取两个结点的相对位置 34 var _t1 = node1.nextSibling; 35 var _t2 = node2.nextSibling; 36 //将node2插入到原来node1的位置 37 _parent.insertBefore(node2, _t1); 38 //将node1插入到原来node2的位置 39 _parent.insertBefore(node1, _t2); 40 } 41 42 43 S.augment(MultiUploadDialog, { 44 addRes:Editor.Utils.addRes, 45 destroy:Editor.Utils.destroyRes, 46 _prepareShow:function () { 47 var self = this, 48 editor = self.editor, 49 uploadCfg = self.config; 50 51 self.addRes(function () { 52 var progressBars = self.progressBars; 53 for (var p in progressBars) { 54 if (progressBars.hasOwnProperty(p)) { 55 progressBars[p].destroy(); 56 } 57 } 58 }); 59 60 self.dialog = new Dialog({ 61 headerContent:"批量上传", 62 mask:false, 63 constrain:false, 64 autoRender:true, 65 focus4e:false, 66 width:"600px" 67 }); 68 var d = self.dialog; 69 70 // ie 6,7,8 upload swf 的容器如果设计了 visibility hidden 又 visible , 71 // 那个 swf 似乎就出事了 72 // 所以不设置 visibility ,漂移大法 73 d.on("beforeVisibleChange", function (ev) { 74 if (!ev.newVal) { 75 d.set("xy", [-9999, -9999]); 76 return false; 77 } 78 }); 79 80 self.addRes(d); 81 82 var bangpaiUploaderHolder = d.get("body"), 83 btnHolder = $( 84 "<div class='ks-editor-upload-btn-wrap'>" + 85 "<span " + 86 "style='" + 87 "margin:0 15px 0 0px;" + 88 "color:#969696;" + 89 "display:inline-block;" + 90 "vertical-align:middle;" + 91 "width:450px;" + 92 "'></span>" + 93 "</div>").appendTo(bangpaiUploaderHolder, undefined), 94 listWrap = $("<div style='display:none'>") 95 .appendTo(bangpaiUploaderHolder, undefined), 96 btn = $("<a class='ks-editor-button ks-inline-block'>浏 览</a>") 97 .appendTo(btnHolder, undefined), 98 99 listTableWrap = $("<div>" + 100 "<table class='ks-editor-upload-list'>" + 101 "<thead>" + 102 "<tr>" + 103 "<th style='width:30px;'>" + 104 "序号" + 105 "</th>" + 106 "<th>" + 107 "图片" + 108 "</th>" + 109 "<th>" + 110 "大小" + 111 "</th>" + 112 "<th style='width:30%'>" + 113 "上传进度" + 114 "</th>" + 115 "<th>" + 116 "图片操作" + 117 "</th>" + 118 "</tr>" + 119 "</thead>" + 120 "<tbody>" + 121 "</tbody>" + 122 "</table>" + 123 "</div>").appendTo(listWrap, undefined), 124 list = listTableWrap.one("tbody"), 125 upHolder = $("<p " + 126 "style='" + 127 "margin:15px 15px 30px 6px;" + 128 "'>" + 129 "<a class='ks-editor-bangpaiupload-delall'" + 130 " style='" + 131 "margin-right:20px;" + 132 "cursor:pointer;" + 133 "margin-left:40px;" + 134 "'>清空列表</a>" + 135 "<a class='ks-editor-button ks-editor-bangpaiupload-ok ks-inline-block'>确定上传</a>" + 136 "<a class='ks-editor-button ks-editor-bangpaiupload-insertall ks-inline-block'" + 137 " style='margin-left:20px;'>全部插入</a>" + 138 "</p>") 139 .appendTo(listWrap, undefined), 140 up = upHolder.one(".ks-editor-bangpaiupload-ok"), 141 insertAll = upHolder.one(".ks-editor-bangpaiupload-insertall"), 142 delAll = upHolder.one(".ks-editor-bangpaiupload-delall"), 143 fid = S.guid(name), 144 statusText = $("<span>") 145 .prependTo(upHolder, undefined); 146 147 148 self._sizeLimit = uploadCfg['sizeLimit'] || PIC_SIZE_LIMIT; 149 self._numberLimit = uploadCfg['numberLimit'] || PIC_NUM_LIMIT; 150 151 var TIP = "允许用户同时上传" + 152 self._numberLimit 153 + "张图片,单张图片容量不超过" + 154 self._sizeLimit / 1000 155 + "M"; 156 157 if (!UA.fpvGEQ(FLASH_VERSION_REQUIRED)) { 158 TIP = "您的flash插件版本过低,该功能不可用," + 159 "请<a href='http://get.adobe.com/cn/flashplayer/'" + 160 " target='_blank'>点此升级</a>"; 161 } 162 163 btn.addClass("ks-editor-button-disabled", undefined); 164 self.tipSpan = btnHolder.one("span"); 165 self.tipSpan.html(TIP); 166 if (!UA.fpvGEQ(FLASH_VERSION_REQUIRED)) { 167 return; 168 } 169 if (uploadCfg['extraHtml']) { 170 listTableWrap.append(uploadCfg['extraHtml']); 171 } 172 173 self._list = list; 174 self['_listTable'] = list.parent("table"); 175 self._listWrap = listWrap; 176 self._ds = uploadCfg['serverUrl']; 177 self._dsp = uploadCfg['serverParams'] || {}; 178 self._fileInput = uploadCfg['fileInput'] || "Filedata"; 179 self.statusText = statusText; 180 self.btn = btn; 181 self.up = up; 182 183 184 var bel = btn, 185 boffset = bel.offset(), 186 fwidth = bel.width() * 2, 187 fheight = bel.height() * 1.5, 188 flashPos = $("<div style='" + 189 ("position:absolute;" + 190 "width:" + fwidth + "px;" + 191 "height:" + fheight + "px;" + 192 "z-index:" + Editor.baseZIndex(9999) + ";") 193 + "'>").appendTo(btnHolder, undefined); 194 195 //swfready 要求可见 196 flashPos.offset(boffset); 197 self.flashPos = flashPos; 198 var uploader = new FlashBridge({ 199 movie:movie, 200 ajbridge:true, 201 methods:[ 202 "getReady", 203 //"cancel", 204 "removeFile", 205 "lock", 206 "unlock", 207 "setAllowMultipleFiles", 208 "setFileFilters", 209 "uploadAll"], 210 holder:flashPos, 211 attrs:{ 212 width:fwidth, 213 height:fheight 214 }, 215 params:{ 216 wmode:"transparent" 217 }, 218 flashVars:{ 219 allowedDomain:location.hostname, 220 btn:true, 221 hand:true 222 } 223 }); 224 225 self.uploader = uploader; 226 227 uploader.on("mouseOver", function () { 228 bel.addClass("ks-editor-button-hover", undefined); 229 }); 230 uploader.on("mouseOut", function () { 231 bel.removeClass("ks-editor-button-hover", undefined); 232 }); 233 self.addRes(uploader); 234 235 var editorDoc = editor.get("document")[0]; 236 237 insertAll.on("click", function (ev) { 238 var trs = list.all("tr"); 239 for (var i = 0; i < trs.length; i++) { 240 var tr = $(trs[i]), 241 url = tr.attr("url"); 242 if (url) { 243 // chrome refer empty in empty src iframe 244 new Image().src = url; 245 editor.insertElement($("<p> <img src='" + 246 url + "'/> </p>", editorDoc)); 247 self._removeTrFile(tr); 248 } 249 } 250 if (url) { 251 listWrap.hide(); 252 d.hide(); 253 } 254 ev.halt(); 255 }); 256 self.addRes(insertAll); 257 258 delAll.on("click", function (ev) { 259 var trs = list.all("tr"); 260 for (var i = 0; i < trs.length; i++) { 261 var tr = $(trs[i]); 262 self._removeTrFile(tr); 263 } 264 listWrap.hide(); 265 ev.halt(); 266 }); 267 self.addRes(delAll); 268 269 list.on("click", function (ev) { 270 var target = $(ev.target), tr; 271 ev.halt(); 272 if (target.hasClass("ks-editor-upload-insert", undefined)) { 273 tr = target.parent("tr"); 274 var url = tr.attr("url"); 275 new Image().src = url; 276 editor.insertElement($("<img src='" + 277 url + "'/>", null, editor.get("document")[0])); 278 } 279 if ( 280 target.hasClass("ks-editor-upload-delete", undefined) 281 || 282 target.hasClass("ks-editor-upload-insert", undefined) 283 ) { 284 tr = target.parent("tr"); 285 self._removeTrFile(tr); 286 } 287 288 /** 289 * 支持排序 290 */ 291 if (target.hasClass("ks-editor-upload-moveup", undefined)) { 292 tr = target.parent("tr"); 293 tr.css("backgroundColor", "#eef4f9"); 294 tr['animate']({ 295 backgroundColor:"#FBFBFB" 296 }, 1, null, function () { 297 tr.css("backgroundColor", ""); 298 }); 299 300 var pre = tr.prev(undefined, undefined); 301 if (pre) { 302 swapNode(tr[0], pre[0]); 303 self._syncStatus(); 304 } 305 306 } else if (target.hasClass("ks-editor-upload-movedown", undefined)) { 307 tr = target.parent("tr"); 308 tr.css("backgroundColor", "#eef4f9"); 309 tr['animate']({ 310 backgroundColor:"#FBFBFB" 311 }, 1, null, function () { 312 tr.css("backgroundColor", ""); 313 }); 314 var next = tr.next(); 315 if (next) { 316 swapNode(tr[0], next[0]); 317 self._syncStatus(); 318 } 319 } 320 ev.halt(); 321 }); 322 323 self.addRes(list); 324 325 uploader.on("fileSelect", self._onSelect, self); 326 uploader.on("uploadStart", self._onUploadStart, self); 327 uploader.on("uploadProgress", self._onProgress, self); 328 uploader.on("uploadCompleteData", self._onUploadCompleteData, self); 329 uploader.on("contentReady", self._ready, self); 330 uploader.on("uploadError", self._uploadError, self); 331 332 //从本地恢复已上传记录 333 if (localStorage.ready) { 334 localStorage.ready(function () { 335 self._restore(); 336 }); 337 } else { 338 self._restore(); 339 } 340 341 //上传后:缩略图预览 342 var previewWidth = uploadCfg['previewWidth']; 343 var previewSuffix = uploadCfg['previewSuffix']; 344 if (previewWidth) { 345 346 var previewWin = new (S.require("overlay"))({ 347 mask:false, 348 prefixCls:'ks-editor-', 349 autoRender:true, 350 width:previewWidth, 351 render:listWrap 352 }); 353 self.addRes(previewWin); 354 var preview = previewWin.get("contentEl"); 355 preview.css("border", "none"); 356 357 var currentFid = 0; 358 listWrap.on("mouseover", function (ev) { 359 var t = $(ev.target), 360 td = t.parent(".ks-editor-upload-filename"); 361 if (td) { 362 var tr = td.parent("tr"); 363 if (tr.hasClass("ks-editor-upload-complete", undefined)) { 364 var url = tr.attr("url"), 365 fid = tr.attr("fid"); 366 if (!url) return; 367 if (fid == currentFid) { 368 } else { 369 currentFid = fid; 370 if (previewSuffix) { 371 url = url.replace(/(\.\w+$)/, previewSuffix); 372 } 373 preview.html("<img " + 374 "style='display:block;' " + 375 "src='" + 376 url + "' />") 377 } 378 var offset = DOM.offset(td); 379 offset.left += td[0].offsetWidth; 380 previewWin.set("xy", [offset.left, offset.top]); 381 previewWin.show(); 382 } 383 } else { 384 previewWin.hide(); 385 } 386 }); 387 self.addRes(listWrap); 388 } 389 390 //webkit 一旦整个可被选择就会导致点击事件没有 391 //因为拖放要求mousedown被阻止,ie9 也是奇怪了! 392 if (!UA['webkit'] && Editor.Utils.ieEngine != 9) { 393 d.set("handlers", [d.get("el")]); 394 } 395 }, 396 _removeTrFile:function (tr) { 397 var self = this, 398 progressBars = self.progressBars, 399 fid = tr.attr("fid"), 400 uploader = self.uploader; 401 if (fid) { 402 try { 403 uploader['removeFile'](fid); 404 } catch (e) { 405 } 406 } 407 if (progressBars[fid]) { 408 progressBars[fid].destroy(); 409 delete progressBars[fid]; 410 } 411 tr.remove(); 412 self.denable(); 413 self._syncStatus(); 414 }, 415 416 _realShow:function () { 417 this.dialog.center(); 418 this.dialog.show(); 419 }, 420 show:function () { 421 this._prepareShow(); 422 }, 423 _uploadError:function (ev) { 424 var self = this, 425 progressBars = self.progressBars, 426 uploader = self.uploader, 427 id = ev.id || (ev['file'] && ev['file'].id); 428 if (!id) { 429 S.log(ev); 430 return; 431 } 432 var tr = self._getFileTr(id), 433 bar = progressBars[id], 434 status = ev.status; 435 436 uploader['removeFile'](id); 437 if (!ev._custom) { 438 S.log(status); 439 status = "服务器出错或格式不正确"; 440 } 441 if (tr) { 442 bar && bar.destroy(); 443 delete progressBars[id]; 444 tr.one(".ks-editor-upload-progress").html("<div " + 445 "style='color:red;'>" + 446 status + 447 "</div>"); 448 } 449 }, 450 _getFileTr:function (id) { 451 var self = this, 452 list = self._list, 453 trs = list.all("tr"); 454 for (var i = 0; i < trs.length; i++) { 455 var tr = $(trs[i]); 456 if (tr.attr("fid") == id) { 457 return tr; 458 } 459 } 460 }, 461 _onUploadStart:function (ev) { 462 var id = ev.id || (ev['file'] && ev['file'].id); 463 var tr = this._getFileTr(id); 464 tr[0].className = "ks-editor-upload-uploading"; 465 }, 466 _onUploadCompleteData:function (ev) { 467 var self = this, 468 uploader = self.uploader, 469 data = S.trim(ev.data).replace(/\r|\n/g, ""), 470 id = ev['file'].id; 471 //S.log(data); 472 473 //成功后不会自动清除列表,自己清除 474 if (id) { 475 try { 476 uploader['removeFile'](id); 477 } catch (e) { 478 } 479 } 480 481 if (!data) return; 482 try { 483 data = JSON.parse(data); 484 } catch (ex) { 485 S.log("multiUpload _onUploadCompleteData error :"); 486 S.log(ex); 487 throw ex; 488 } 489 if (data.error) { 490 self._uploadError({ 491 id:id, 492 _custom:1, 493 status:data.error 494 }); 495 return; 496 } 497 var tr = self._getFileTr(id); 498 if (tr) { 499 tr.one(".ks-editor-upload-insert").show(); 500 self._tagComplete(tr, data['imgUrl']); 501 } 502 503 self._syncStatus(); 504 505 }, 506 _onProgress:function (ev) { 507 var fid = ev['file'].id, 508 progressBars = this.progressBars, 509 progess = Math.floor(ev['bytesLoaded'] * 100 / ev['bytesTotal']), 510 bar = progressBars[fid]; 511 bar && bar.set("progress", progess); 512 513 }, 514 ddisable:function () { 515 var self = this; 516 self.uploader['lock'](); 517 self.btn.addClass("ks-editor-button-disabled", undefined); 518 self.flashPos.offset({ 519 left:-9999, 520 top:-9999 521 }); 522 }, 523 denable:function () { 524 var self = this; 525 self.uploader['unlock'](); 526 self.btn.removeClass("ks-editor-button-disabled", undefined); 527 self.flashPos.offset(self.btn.offset()); 528 }, 529 _syncStatus:function () { 530 var self = this, 531 list = self._list, 532 seq = 1, 533 trs = list.all("tr"); 534 if (trs.length == 0) { 535 self._listWrap.hide(); 536 } else { 537 list.all(".ks-editor-upload-seq").each(function (n) { 538 n.html(seq++); 539 }); 540 var wait = 0; 541 for (var i = 0; i < trs.length; i++) { 542 var tr = $(trs[i]); 543 if (!tr.attr("url")) wait++; 544 } 545 self.statusText.html("队列中剩余" + wait + "张图片" 546 + ",点击确定上传,开始上传。 " 547 ); 548 } 549 //当前已上传的文件同步到本地 550 self._save(); 551 }, 552 //当前已上传的图片保存下来 553 _restore:function () { 554 var self = this, 555 data = localStorage.getItem(KEY), 556 tbl = self._list[0]; 557 if (!data) return; 558 data = JSON.parse(decodeURIComponent(data)); 559 for (var i = 0; i < data.length; i++) { 560 var d = data[i]; 561 d.complete = 1; 562 d.fid = "restore_" + i; 563 var r = self._createFileTr(tbl, d); 564 self._tagComplete(r, d.url); 565 } 566 if (d) { 567 self._listWrap.show(); 568 self._syncStatus(); 569 } 570 }, 571 _tagComplete:function (tr, url) { 572 tr.attr("url", url); 573 tr[0].className = "ks-editor-upload-complete"; 574 }, 575 _save:function () { 576 var self = this, 577 list = self._list, 578 trs = list.all("tr"), 579 data = []; 580 for (var i = 0; i < trs.length; i++) { 581 var tr = $(trs[i]), 582 url = tr.attr("url"); 583 if (url) { 584 var size = tr.one(".ks-editor-upload-filesize").html(), 585 name = tr.one(".ks-editor-upload-filename").text(); 586 data.push({ 587 name:name, 588 size:size, 589 url:url 590 }); 591 } 592 } 593 594 localStorage.setItem(KEY, encodeURIComponent(JSON.stringify(data))); 595 596 }, 597 _getFilesSize:function (files) { 598 var n = 0; 599 for (var i in files) { 600 if (files.hasOwnProperty(i)) 601 n++; 602 } 603 return n; 604 }, 605 _createFileTr:function (tbl, f) { 606 607 /* 608 chrome not work ! 609 kissy bug? 610 var row = $("<tr fid='" + id + "'>" 611 + "<td class='ks-editor-upload-seq'>" 612 + "</td>" 613 + "<td>" 614 + f.name 615 + "</td>" 616 + "<td>" 617 + size 618 + "k</td>" + 619 "<td class='ks-editor-upload-progress'>" + 620 "</td>" + 621 "<td>" + 622 "<a href='#' " + 623 "class='ks-editor-upload-insert' " + 624 "style='display:none'>" + 625 "[插入]</a> " + 626 "<a href='#' class='ks-editor-upload-delete'>[删除]</a> " 627 + 628 "</td>" 629 + "</tr>").appendTo(list); 630 */ 631 632 633 var self = this, 634 progressBars = self.progressBars, 635 id = f.fid, 636 row = tbl.insertRow(-1); 637 DOM.attr(row, "fid", id); 638 var cell = row.insertCell(-1); 639 DOM.attr(cell, "class", 'ks-editor-upload-seq'); 640 cell = row.insertCell(-1); 641 if (f.name.length > 18) { 642 f.name = f.name.substring(0, 18) + "..."; 643 } 644 DOM.html(cell, "<div style='width:160px;overflow:hidden;'><div style='width:9999px;text-align:left;'>" + f.name + "</div></div>"); 645 DOM.attr(cell, "class", 'ks-editor-upload-filename'); 646 cell = row.insertCell(-1); 647 DOM.html(cell, f.size); 648 DOM.attr(cell, "class", 'ks-editor-upload-filesize'); 649 cell = row.insertCell(-1); 650 DOM.attr(cell, "class", 'ks-editor-upload-progress'); 651 cell = row.insertCell(-1); 652 DOM.html(cell, "" + 653 "<a class='ks-editor-upload-moveup' href='#'>[上移]</a> " + 654 "<a class='ks-editor-upload-movedown' href='#'>[下移]</a> " + 655 "<a href='#' class='ks-editor-upload-insert' style='" + 656 (f.complete ? "" : "display:none;") + 657 "' " + 658 659 ">" + 660 "[插入]</a> " + 661 "<a href='#' class='ks-editor-upload-delete'>" + 662 "[删除]" + 663 "</a> "); 664 665 var rowNode = $(row); 666 667 if (parseInt(f.size) > self._sizeLimit) { 668 self._uploadError({ 669 id:id, 670 _custom:1, 671 status:PIC_SIZE_LIMIT_WARNING 672 .replace(/n/, self._sizeLimit / 1000) 673 }); 674 self.uploader['removeFile'](id); 675 676 } else { 677 progressBars[id] = new ProgressBar({ 678 container:rowNode.one(".ks-editor-upload-progress"), 679 width:"100px", 680 height:"15px" 681 }); 682 if (f.complete) { 683 progressBars[id].set("progress", 100); 684 } 685 } 686 687 return rowNode; 688 }, 689 _onSelect:function (ev) { 690 var self = this, 691 uploader = self.uploader, 692 list = self._list, 693 curNum = 0, 694 //当前队列的所有文件,连续选择的话累计!!! 695 files = ev['fileList'], 696 available = self._numberLimit, i; 697 698 if (files) { 699 //去除已经 ui 显示出来的 700 var trs = list.children("tr"); 701 for (i = 0; i < trs.length; i++) { 702 var tr = trs[i], fid = DOM.attr(tr, "fid"); 703 fid && files[fid] && (delete files[fid]); 704 } 705 //限额-目前ui的 706 available = self._numberLimit - trs.length; 707 708 var l = self._getFilesSize(files); 709 710 if (l > available) { 711 alert(PIC_NUM_LIMIT_WARNING.replace(/n/, self._numberLimit)); 712 } 713 714 if (l >= available) { 715 self.ddisable(); 716 } 717 718 self._listWrap.show(); 719 var tbl = self._list[0]; 720 //files 是这次新选择的啦! 721 //新选择的随即删除一些 722 for (i in files) { 723 if (!files.hasOwnProperty(i)) continue; 724 curNum++; 725 var f = files[i], 726 size = Math.floor(f.size / 1000), 727 id = f.id; 728 729 if (curNum > available) { 730 731 uploader['removeFile'](id); 732 continue; 733 } 734 self._createFileTr(tbl, { 735 size:size + "k", 736 fid:id, 737 name:f.name 738 }); 739 } 740 self._syncStatus(); 741 } 742 }, 743 744 _ready:function () { 745 var self = this, 746 uploader = self.uploader, 747 up = self.up, 748 btn = self.btn, 749 flashPos = self.flashPos, 750 normParams = Editor.Utils.normParams; 751 if ("ready" != uploader['getReady']()) { 752 self.tipSpan.html("您的浏览器不支持该功能," + 753 "请升级当前浏览器," + 754 "并同时 <a href='http://get.adobe.com/cn/flashplayer/'" + 755 " target='_blank'>点此升级</a> flash 插件"); 756 flashPos.offset({ 757 left:-9999, 758 top:-9999 759 }); 760 return; 761 } 762 btn.removeClass("ks-editor-button-disabled", undefined); 763 flashPos.offset(btn.offset()); 764 uploader['setAllowMultipleFiles'](true); 765 uploader['setFileFilters']([ 766 { 767 ext:"*.jpeg;*.jpg;*.png;*.gif", 768 desc:"图片文件( png,jpg,jpeg,gif )" 769 } 770 ]); 771 up.detach(); 772 up.on("click", function (ev) { 773 uploader['uploadAll'](self._ds, 774 "POST", 775 normParams(self._dsp), 776 self._fileInput); 777 ev.halt(); 778 }); 779 self.addRes(up); 780 } 781 }); 782 783 return MultiUploadDialog; 784 }, { 785 requires:['editor', 786 '../progressbar/', 787 '../overlay/', 788 '../flashBridge/', 789 '../localStorage/'] 790 });