<%@ Page Title="Server-Sent Events Progress" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" %>
<%@ Register Assembly="AjaxUploader" Namespace="AjaxUploader.Controls" TagPrefix="au" %> 
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitleContent" runat="server">Server-Sent Events Progress - AjaxUploader v5.2 Demos</asp:Content>
<asp:Content ID="HeaderContent" ContentPlaceHolderID="HeaderTitle" runat="server">Server-Sent Events Progress</asp:Content> 
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server"> 
<div class="demo-card"> <div class="demo-card-header">Server-Sent Events Progress</div> <div class="demo-card-body"> <p class="demo-description"> This conceptual demo shows how server-sent events (SSE) can provide real-time progress
 updates from the server during chunked uploads. The server pushes processing status
 as chunks are received and assembled.
 </p> <div id="uploader"></div> <div class="mt-3"> <h6>Server Events Stream</h6> <div id="sseLog" class="p-2 bg-dark text-success border rounded font-monospace" role="log" aria-live="polite"
 style="max-height:200px;overflow-y:auto;font-size:.8rem;"> <div class="text-muted">Waiting for upload to start...</div> </div> </div> <div class="code-panel"> <pre><code>// Client: start SSE listener after upload begins
var uploader = AjaxUploader.create('#uploader', {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 chunkUrl: '<%= ResolveUrl("~/ajaxupload.axd/chunk") %>',
 chunked: true,
 chunkSize: 2 * 1024 * 1024,
 onTaskStart: function(task) {
 // Connect to SSE endpoint for server-side progress
 var sse = new EventSource('/api/upload/progress/' + task.id);
 sse.onmessage = function(e) {
 var data = JSON.parse(e.data);
 logSSE('Server: chunk ' + data.chunk + ' received');
 };
 }
});

// Server: implement SSE endpoint via Generic Handler (.ashx)
// or WebAPI controller</code></pre> </div> </div>
</div> 
</asp:Content> 
<asp:Content ID="ScriptsContent" ContentPlaceHolderID="ScriptsContent" runat="server">
<script> var sseLog = document.getElementById('sseLog');
 var chunkCount = 0;
 function clampPercent(value) {
 var pct = Number(value);
 if (!isFinite(pct)) return 0;
 return Math.max(0, Math.min(100, Math.round(pct)));
 }

 function logSSE(msg) {
 if (!sseLog) return;
 var div = document.createElement('div');
 div.textContent = '> ' + msg;
 sseLog.appendChild(div);
 sseLog.scrollTop = sseLog.scrollHeight;
 }

 var uploaderHost = document.getElementById('uploader');
 if (window.AjaxUploader && typeof AjaxUploader.create === 'function' && uploaderHost) {
 AjaxUploader.create(uploaderHost, {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 chunkUrl: '<%= ResolveUrl("~/ajaxupload.axd/chunk") %>',
 chunked: true,
 chunkSize: 2 * 1024 * 1024,
 onTaskStart: function(task) {
 if (sseLog) {
 sseLog.textContent = '';
 }
 chunkCount = 0;
 logSSE('Connection established for: ' + task.fileName);
 },
 onTaskProgress: function(task) {
 chunkCount++;
 logSSE('Chunk #' + chunkCount + ' received - ' + clampPercent(task.percent) + '% complete');
 },
 onTaskComplete: function(task) {
 logSSE('Assembly complete: ' + task.fileName);
 logSSE('All ' + chunkCount + ' chunks merged successfully.');
 logSSE('Stream closed.');
 },
 onTaskError: function(task, err) {
 logSSE('ERROR: ' + err);
 }
 });
 }
</script>
</asp:Content>
