PageSourceSearch

https://www.stocorp.com/wp-content/plugins/sto-file-share/assets/sfs.js?ver=0.2.6

js stocorp.com collected 2026-09-25 21:06:12 UTC 17,883 bytes, 181 lines download raw bytes

1(function($){
2  function request(action,data,fd){
3    if(fd){ fd.append('action',action); fd.append('nonce',SFS.nonce); return $.ajax({url:SFS.ajax,method:'POST',data:fd,processData:false,contentType:false}); }
4    return $.post(SFS.ajax,Object.assign({action:action,nonce:SFS.nonce},data||{}));
5  }
6  function esc(v){ return $('<div>').text(v==null?'':v).html(); }
7  function modal(html){ $('[data-modal-body]').html(html); $('[data-modal]').prop('hidden',false); }
8  $(document).on('click','[data-close-modal]',()=> $('[data-modal]').prop('hidden',true));
9
10  function renderExplorer(app,data,isPublic){
11    app.attr('data-current-folder',data.current.id);
12    var crumbs=(data.breadcrumbs||[]).map((c,i)=>'<button data-go-folder="'+c.id+'"'+(i===data.breadcrumbs.length-1?' disabled':'')+'>'+esc(c.name)+'</button>').join('<span>›</span>');
13    app.find('[data-breadcrumbs]').html(crumbs||'<span>My Files</span>');
14    if(!isPublic){ app.find('[data-share-current]').prop('hidden',!data.can_share); app.find('[data-storage-stat]').text(data.storage||''); }
15    var currentZip=(data.current&&data.current.zip_download)||'';app.find('[data-zip-current]').prop('hidden',!currentZip).attr('href',currentZip||'#');
16    var rows='';
17    (data.rows||[]).forEach(function(r){
18      if(r.type==='folder'){
19        rows+='<tr class="sfs-row sfs-folder-row" data-item-type="folder" data-item-id="'+r.id+'"><td><button class="sfs-name-button" data-go-folder="'+r.id+'"><span class="sfs-icon">📁</span><span>'+esc(r.name)+'</span></button></td><td>—</td><td>'+esc(r.modified)+'</td><td class="sfs-expires-in" title="Expires '+esc(r.expires||'')+'">'+esc(r.expires_in||'—')+'</td><td class="sfs-row-actions">';
20        if(!isPublic) rows+='<button class="sfs-kebab" data-item-menu aria-label="Folder actions">⋯</button><div class="sfs-menu"><button data-row-share>Share</button><a class="sfs-menu-link" href="'+esc(r.zip_download||'#')+'">ZIP &amp; Download All</a><button data-row-rename>Rename</button><button data-row-keep>'+((r.keep_forever)?'Resume Expiration':'Keep Forever')+'</button><button class="is-danger" data-row-delete>Delete</button></div>';
21        else rows+='<a class="sfs-download" href="'+esc(r.zip_download||'#')+'">Download ZIP</a>';
22        rows+='</td></tr>';
23      } else {
24        rows+='<tr class="sfs-row" data-item-type="file" data-item-id="'+r.id+'"><td><span class="sfs-file-name"><span class="sfs-icon">📄</span><span>'+esc(r.name)+'</span></span></td><td>'+esc(r.size)+'</td><td>'+esc(r.modified)+'</td><td class="sfs-expires-in" title="Expires '+esc(r.expires||'')+'">'+esc(r.expires_in||'—')+'</td><td class="sfs-row-actions">';
25        if(isPublic) rows+='<a class="sfs-download" href="'+esc(r.download)+'">Download</a>';
26        else rows+='<button class="sfs-kebab" data-item-menu aria-label="File actions">⋯</button><div class="sfs-menu"><button data-row-rename>Rename</button><button class="is-danger" data-row-delete>Delete</button></div>';
27        rows+='</td></tr>';
28      }
29    });
30    if(!rows) rows='<tr><td colspan="5"><div class="sfs-empty">This folder is empty.</div></td></tr>';
31    app.find('[data-explorer-body]').html(rows);
32  }
33
34  function loadManager(app,folder){
35    request('sfs_explorer_list',{folder_id:folder}).done(function(r){ if(!r.success){alert(r.data&&r.data.message||'Could not load folder');return;} renderExplorer(app,r.data,false); });
36  }
37  function loadPublic(app,folder){
38    request('sfs_public_list',{folder_id:folder,token:app.data('token')}).done(function(r){ if(!r.success){app.find('[data-explorer-body]').html('<tr><td colspan="5"><div class="sfs-notice">'+esc(r.data&&r.data.message||'Could not load folder')+'</div></td></tr>');return;} renderExplorer(app,r.data,true); });
39  }
40  $('[data-sfs-manager]').each(function(){loadManager($(this),0);});
41  $('[data-sfs-public]').each(function(){loadPublic($(this),Number($(this).data('root-folder')));});
42
43  $(document).on('click','[data-go-folder]',function(){
44    var app=$(this).closest('[data-sfs-manager],[data-sfs-public]'),id=Number($(this).data('go-folder')); if(app.is('[data-sfs-public]'))loadPublic(app,id);else loadManager(app,id);
45  });
46  $(document).on('click','[data-item-menu]',function(e){ e.stopPropagation(); var menu=$(this).siblings('.sfs-menu'); $('.sfs-menu').not(menu).removeClass('is-open'); menu.toggleClass('is-open'); });
47  $(document).on('click',function(){ $('.sfs-menu').removeClass('is-open'); });
48
49  $(document).on('click','[data-create-folder]',function(){
50    var app=$(this).closest('[data-sfs-manager]'); modal('<h3>New Folder</h3><input class="sfs-input" data-new-folder placeholder="Folder name" autofocus><div class="sfs-modal-actions"><button class="sfs-btn" data-save-folder>Create Folder</button></div>'); $('[data-save-folder]').data('app',app);
51  });
52  $(document).on('click','[data-save-folder]',function(){
53    var app=$('[data-sfs-manager]'),parent=Number(app.attr('data-current-folder')); request('sfs_create_folder',{name:$('[data-new-folder]').val(),parent_id:parent}).done(function(r){if(r.success){$('[data-modal]').prop('hidden',true);loadManager(app,parent);}else alert(r.data.message);});
54  });
55
56  function renderShareList(folderId){
57    var box=$('[data-share-list]'); box.html('<div class="sfs-share-loading">Loading shared recipients…</div>');
58    request('sfs_list_shares',{folder_id:folderId}).done(function(r){
59      if(!r.success){box.html('<div class="sfs-modal-message">'+esc(r.data&&r.data.message||'Could not load recipients.')+'</div>');return;}
60      var shares=r.data.shares||[];
61      if(!shares.length){box.html('<div class="sfs-share-empty">This folder has not been shared with anyone yet.</div>');return;}
62      var html='';
63      shares.forEach(function(x){
64        html+='<div class="sfs-share-person'+(x.active?'':' is-revoked')+'"><div class="sfs-share-person-main"><strong>'+esc(x.email)+'</strong><span class="sfs-share-status">'+esc(x.status)+' · '+esc(x.invited)+'</span></div><div class="sfs-share-person-actions">';
65        if(x.active) html+='<button type="button" class="sfs-share-action" data-resend-share="'+x.id+'" data-folder="'+folderId+'">Resend</button><button type="button" class="sfs-share-action is-danger" data-revoke-share="'+x.id+'" data-folder="'+folderId+'">Revoke</button>';
66        html+='</div></div>';
67      });
68      box.html(html);
69    });
70  }
71  function shareFolder(app,id,name){
72    modal('<h3>Share '+esc(name||'Folder')+'</h3><p>The recipient will receive a secure link and verification code. Acce
72ss includes this folder and all subfolders beneath it.</p><div class="sfs-share-add"><input class="sfs-input" type="email" data-share-email placeholder="[email protected]"><button class="sfs-btn" data-send-share data-folder="'+id+'">Send Share Link</button></div><div class="sfs-modal-message" data-share-msg></div><div class="sfs-share-admin"><div class="sfs-share-admin-title">Shared With</div><div data-share-list></div></div>');
73    renderShareList(id);
74  }
75  $(document).on('click','[data-share-current]',function(){var app=$(this).closest('[data-sfs-manager]');shareFolder(app,Number(app.attr('data-current-folder')),'this folder');});
76  $(document).on('click','[data-row-share]',function(e){e.stopPropagation();var row=$(this).closest('[data-item-id]');shareFolder(row.closest('[data-sfs-manager]'),row.data('item-id'),row.find('.sfs-name-button span:last').text());});
77  $(document).on('click','[data-send-share]',function(){var b=$(this),folder=b.data('folder'),email=$('[data-share-email]').val();b.prop('disabled',true);request('sfs_share_folder',{folder_id:folder,email:email}).done(function(r){$('[data-share-msg]').text(r.data&&r.data.message||'');if(r.success){$('[data-share-email]').val('');renderShareList(folder);}}).always(function(){b.prop('disabled',false);});});
78  $(document).on('click','[data-revoke-share]',function(){var b=$(this),folder=b.data('folder');if(!confirm('Revoke this recipient’s access to the shared folder?'))return;b.prop('disabled',true);request('sfs_revoke_share',{share_id:b.data('revoke-share')}).done(function(r){$('[data-share-msg]').text(r.data&&r.data.message||'');if(r.success)renderShareList(folder);}).always(function(){b.prop('disabled',false);});});
79  $(document).on('click','[data-resend-share]',function(){var b=$(this),folder=b.data('folder');b.prop('disabled',true);request('sfs_resend_share',{share_id:b.data('resend-share')}).done(function(r){$('[data-share-msg]').text(r.data&&r.data.message||'');if(r.success)renderShareList(folder);}).always(function(){b.prop('disabled',false);});});
80
81
82  $(document).on('click','[data-row-keep]',function(e){
83    e.stopPropagation();
84    var row=$(this).closest('[data-item-id]'),app=row.closest('[data-sfs-manager]'),id=Number(row.data('item-id')),name=row.find('.sfs-name-button span:last').text(),isKeep=$(this).text().indexOf('Resume')===0;
85    var msg=isKeep
86      ? 'Resume automatic expiration for "'+name+'"? A fresh 30-day timer will begin for this folder.'
87      : 'Keep "'+name+'" forever? Only this folder will be kept. Files and subfolders inside it will continue to expire normally.';
88    if(!confirm(msg))return;
89    request('sfs_toggle_keep_forever',{folder_id:id}).done(function(r){
90      if(r.success)loadManager(app,Number(app.attr('data-current-folder')));
91      else alert(r.data&&r.data.message||'Could not change expiration setting.');
92    });
93  });
94
95  $(document).on('click','[data-row-delete]',function(e){
96    e.stopPropagation();var row=$(this).closest('[data-item-id]'),app=row.closest('[data-sfs-manager]'),type=row.data('item-type'),id=row.data('item-id'),name=row.find('td:first').text().trim();
97    if(!confirm(type==='folder'?'Delete "'+name+'" and everything inside it?':'Delete "'+name+'"?'))return;
98    request(type==='folder'?'sfs_delete_folder':'sfs_delete_file',type==='folder'?{folder_id:id}:{file_id:id}).done(function(r){if(r.success)loadManager(app,Number(app.attr('data-current-folder')));else alert(r.data.message);});
99  });
100  $(document).on('click','[data-row-rename]',function(e){
101    e.stopPropagation();var row=$(this).closest('[data-item-id]'),type=row.data('item-type'),id=row.data('item-id'),name=row.find('td:first span:last').text();modal('<h3>Rename '+(type==='folder'?'Folder':'File')+'</h3><input class="sfs-input" data-rename-value value="'+esc(name)+'"><div class="sfs-modal-actions"><button class="sfs-btn" data-save-rename data-type="'+type+'" data-id="'+id+'">Save</button></div>');
102  });
103  $(document).on('click','[data-save-rename]',function(){var app=$('[data-sfs-manager]'),b=$(this),type=b.data('type');request(type==='folder'?'sfs_rename_folder':'sfs_rename_file',type==='folder'?{folder_id:b.data('id'),name:$('[data-rename-value]').val()}:{file_id:b.data('id'),name:$('[data-rename-value]').val()}).done(function(r){if(r.success){$('[data-modal]').prop('hidden',true);loadManager(app,Number(app.attr('data-current-folder')));}else alert(r.data.message);});});
104
105  $(document).on('click','[data-upload-files]',function(){var app=$(this).closest('[data-sfs-manager]');if(Number(app.attr('data-current-folder'))===0){alert('Open or create a folder before uploading individual files. Use Upload Folder to upload a complete desktop folder at My Files.');return;}app.find('[data-file-input]').trigger('click');});
106  $(document).on('click','[data-upload-folder]',function(){$(this).closest('[data-sfs-manager]').find('[data-folder-input]').trigger('click');});
107  $(document).on('change','[data-file-input]',function(){queueFiles($(this).closest('[data-sfs-manager]'),Array.from(this.files),false);this.value='';});
108  $(document).on('change','[data-folder-input]',function(){queueFiles($(this).closest('[data-sfs-manager]'),Array.from(this.files),true);this.value='';});
109
110  function formatBytes(bytes){
111    bytes=Number(bytes)||0;if(bytes<1024)return bytes+' B';
112    var units=['KB','MB','GB','TB'],v=bytes/1024,i=0;
113    while(v>=1024&&i<units.length-1){v/=1024;i++;}
114    return (v>=100?v.toFixed(0):v>=10?v.toFixed(1):v.toFixed(2))+' '+units[i];
115  }
116  function ajaxErrorMessage(err,chunk,total){
117    var status=err&&err.status?('HTTP '+err.status):'Network error',body='';
118    if(err&&err.responseJSON&&err.responseJSON.data&&err.responseJSON.data.message)body=err.responseJSON.data.message;
119    else if(err&&err.responseText){try{var j=JSON.parse(err.responseText);body=j&&j.data&&j.data.message?j.data.message:'';}catch(e){}}
120    return (chunk!=null?'Chunk '+(chunk+1)+' of '+total+' failed. ':'')+status+(body?' — '+body:'');
121  }
122  async function queueFiles(app,files,preserve){
123    if(!files.length)return;
124    var max=Number(SFS.maxFileSize)||1073741824;
125    var q=app.find('[data-upload-queue]').prop('hidden',false).empty();
126    q.append('<div class="sfs-queue-title">Uploading '+files.length+' file'+(files.length===1?'':'s')+'</div><div class="sfs-overall"><div></div></div><div class="sfs-upload-status" data-queue-status></div>');
127    var folder=Number(app.attr('data-current-folder')),completed=0,failed=0;
128    for(const file of files){
129      var rel=preserve?(file.webkitRelativePath||file._relativePath||file.name):'';
130      if(file.size>max){failed++;q.append('<div class="sfs-upload-error"><strong>'+esc(file.name)+'</strong><br>File is '+esc(formatBytes(file.size))+'. Maximum allowed size is '+esc(formatBytes(max))+'.</div>');continue;}
131      try{
132        await uploadFile(file,folder,rel,function(info){
133          var pct=info.totalBytes?Math.round(info.sentBytes/info.totalBytes*100):100;
134          q.find('.sfs-overall>div').css('width',(((completed+pct/100)/files.length)*100)+'%');
135          q.find('[data-queue-status]').html('<strong>'+esc(rel||file.name)+'</strong><br>'+esc(formatBytes(info.sentBytes))+' / '+esc(formatBytes(info.totalBytes))+' &nbsp;·&nbsp; Chunk '+info.chunk+' of '+info.totalChunks+' &nbsp;·&nbsp; '+pct+'%');
136        });
137        completed++;
138      }catch(err){failed++;q.append('<div class="sfs-upload-error"><strong>'+esc(file.name)+'</strong><br>'+esc(err.message||String(err))+'</div>');}
139      q.find('.sfs-overall>div').css('width',((completed+failed)/files.length*100)+'%');
140    }
141    q.find('[data-queue-status]').html(failed?'<strong>Upload finished with errors.</strong> '+completed+' succeeded, '+failed+' failed.':'<strong>Upload complete.</strong> '+completed+' file'+(completed===1?'':'s')+' uploaded.');
142    loadManager(app,Number(app.attr('data-current-folder')));
143  }
144  async function uploadFile(file,folder,relative,onProgress){
145    var chunkSize=Math.max(1048576,Number(SFS.chunkSize)||4194304);
146    var totalChunks=Math.max(1,Math.ceil(file.size/chunkSize));
147    var init;
148    try{init=await request('sfs_upload_init',{folder_id:folder,name:file.name,relative_path:relative,size:file.size,total_chunks:totalChunks});}
149    catch(e){throw new Error(ajaxErrorMessage(e,null,totalChunks));}
150    if(!init||!init.success)throw new Error((init&&init.data&&init.data.message)||'Could not start upload.');
151    var id=init.data.upload_id,retries=Math.max(0,Number(SFS.chunkRetries)||3),sent=0;
152    for(let chunkIndex=0;chunkIndex<totalChunks;chunkIndex++){
153      let start=chunkIndex*chunkSize,end=Math.min(file.size,start+chunkSize),blob=file.slice(start,end);
154      if(file.size===0)blob=new Blob(['']);
155      let attempt=0;
156      while(true){
157        let fd=new FormData();
158        fd.append('upload_id',id);fd.append('chunk_index',String(chunkIndex));fd.append('chunk',blob,file.name+'.part');
159        try{let r=await request('sfs_upload_chunk',{},fd);if(!r||!r.success)throw {status:200,responseJSON:r};break;}
160        catch(e){attempt++;if(attempt>retries)throw new Error(ajaxErrorMessage(e,chunkIndex,totalChunks));await new Promise(resolve=>setTimeout(resolve,750*attempt));}
161      }
162      sent=end;if(file.size===0)sent=0;
163      if(onProgress)onProgress({sentBytes:sent,totalBytes:file.size,chunk:chunkIndex+1,totalChunks:totalChunks});
164    }
165    var complete;
166    try{complete=await request('sfs_upload_complete',{upload_id:id});}
167    catch(e){throw new Error('Finalization failed. '+ajaxErrorMessage(e,null,totalChunks));}
168    if(!complete||!complete.success)throw new Error('Finalization failed. '+((complete&&complete.data&&complete.data.message)||'Unknown server error.'));
169    return complete.data;
170  }
171
172  // Folder/file drag-and-drop. Chromium/Edge can expose recursive directory entries here.
173  async function entryFiles(entry,path){
174    if(entry.isFile)return new Promise(resolve=>entry.file(f=>{f._relativePath=path+f.name;resolve([f]);},()=>resolve([])));
175    if(!entry.isDirectory)return[];var reader=entry.createReader(),entries=[];while(true){var batch=await new Promise(resolve=>reader.readEntries(resolve,()=>resolve([])));if(!batch.length)break;entries=entries.concat(batch);}var out=[];for(const child of entries)out=out.concat(await entryFiles(child,path+entry.name+'/'));return out;
176  }
177  $(document).on('dragover','[data-dropzone]',function(e){e.preventDefault();$(this).addClass('is-dragging');}).on('dragleave','[data-dropzone]',function(){$(this).removeClass('is-dragging');});
178  $(document).on('drop','[data-dropzone]',async function(e){e.preventDefault();$(this).removeClass('is-dragging');var app=$(this).closest('[data-sfs-manager]'),dt=e.originalEvent.dataTransfer,files=[];if(dt.items&&dt.items.length&&dt.items[0].webkitGetAsEntry){for(const item of dt.items){var ent=item.webkitGetAsEntry();if(ent)files=files.concat(await entryFiles(ent,''));}await queueFiles(app,files,true);}else{await queueFiles(app,Array.from(dt.files||[]),false);}});
179
180  $(document).on('click','[data-verify]',function(){var box=$(this).closest('[data-sfs-verify]');request('sfs_verify_code',{token:box.data('token'),code:box.find('[data-code]').val()}).done(r=>{box.find('[data-verify-msg]').text(r.success?'Verified. Opening files…':r.data.message);if(r.success)location.reload();});});
181})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.