Index: django/contrib/admin/media/js/UploadProgress.js
===================================================================
--- django/contrib/admin/media/js/UploadProgress.js	(revision 0)
+++ django/contrib/admin/media/js/UploadProgress.js	(revision 0)
@@ -0,0 +1,528 @@
+function getxy(){
+    var x,y;
+    if (self.innerHeight) // all except Explorer
+        {
+        x = self.innerWidth;
+        y = self.innerHeight;
+        }
+    else if (document.documentElement && document.documentElement.clientHeight)
+        // Explorer 6 Strict Mode
+        {
+        x = document.documentElement.clientWidth;
+        y = document.documentElement.clientHeight;
+        }
+    else if (document.body) // other Explorers
+        {
+        x = document.body.clientWidth;
+        y = document.body.clientHeight;
+        }
+    return {'x':x,'y':y}
+    }
+
+var humanvalue = ['B','KB','MB','GB']
+function humanize(bytes) {
+    curbytes = bytes
+    iterations = 0
+    while (curbytes>1024) {
+        iterations++
+        curbytes=curbytes/1024
+        }
+    return curbytes.toFixed(1) + ' ' + humanvalue[iterations]
+    }
+
+interval = null;
+function fetch(uuid) {
+    req = xmlhttp
+    req.open("GET", "/admin/upload_progress/", 1);
+    req.setRequestHeader("X-Progress-Id", uuid);
+    req.onreadystatechange = function () {
+    if (req.readyState == 4) {
+        if (req.status == 200) {
+
+            var upload = eval( '(' + req.responseText + ')' );
+
+            if (upload.state == 'done' || upload.state == 'uploading') 
+                bar = document.getElementById('progress_bar');
+                progress_wrap = document.getElementById('progress_wrap');
+                if (upload.state == 'done') {
+                    window.clearTimeout(interval);
+	            progress_wrap.style.visibility = 'hidden';
+                    return;   
+                }
+                bar_txt = document.getElementById('progress_text')
+                bar_txt.innerHTML = ((upload.received / upload.size) * 100).toFixed(1) + '% - ' +
+                    humanize(upload.received) + ' of ' + humanize(upload.size) 
+                w = 400 * upload.received / upload.size;
+                bar.style.width = w + 'px';
+
+                }
+            }
+        }
+    req.send(null); 
+
+    }
+
+function closeprogress() {
+
+
+
+}
+
+function openprogress(e) {
+
+    uuid = "";
+    for (i = 0; i < 32; i++) {
+        uuid += Math.floor(Math.random() * 16).toString(16);
+        }
+    frm = e.target||e.srcElement;
+
+    if (frm.action.indexOf('?') == -1) {
+       frm.action=frm.action+"?progress_id=" + uuid;
+    } else {
+       frm.action=frm.action+"&progress_id=" + uuid;
+    }
+
+    if (document.getElementById('progress_wrap')) {
+        document.getElementById('progress_wrap').style.visibility = 'visible';
+        document.getElementById('progress_bar').style.width = '0';
+        document.getElementById('progress_text').innerHTML = '0%';
+
+        interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+        return;
+    }
+
+    pos = getxy()
+    posx = parseInt((pos.x/2)-(420/2), 10)
+    posy = parseInt((pos.y/2)-(50/2), 10)
+
+    progress_wrap = quickElement('div', document.body, '', 'style', 
+        'position: absolute; top: '+posy+'px; left: '+posx+'px; height: 50px; ' +
+        'padding: 10px; width: 420px; background: #ffffff; ' +
+        'border: solid 1px #dddddd;', 'id', 'progress_wrap')
+
+    progress_label = quickElement('h1', progress_wrap, 'Upload progress')
+
+    progress = quickElement('div', progress_wrap, '', 'style', 
+        'top: 0; left: 0; width: 0px; ', 'id', 'progress_bar', 'class', 'submit-row')
+
+    progress_text = quickElement('div', progress_wrap, '0%', 'style',
+        'color: #000000; ', 'id', 'progress_text')
+ 
+    interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+    }
+
+addEvent(window, 'load', function() {
+        frms = document.getElementsByTagName('form');
+        for (var i=0; i<frms.length; i++) {
+           if (frms[i].encoding.toLowerCase() == 'multipart/form-data') {
+              addEvent(frms[i], 'submit',  openprogress);
+              return;
+           }
+        }
+    });
+function getxy(){
+    var x,y;
+    if (self.innerHeight) // all except Explorer
+        {
+        x = self.innerWidth;
+        y = self.innerHeight;
+        }
+    else if (document.documentElement && document.documentElement.clientHeight)
+        // Explorer 6 Strict Mode
+        {
+        x = document.documentElement.clientWidth;
+        y = document.documentElement.clientHeight;
+        }
+    else if (document.body) // other Explorers
+        {
+        x = document.body.clientWidth;
+        y = document.body.clientHeight;
+        }
+    return {'x':x,'y':y}
+    }
+
+var humanvalue = ['B','KB','MB','GB']
+function humanize(bytes) {
+    curbytes = bytes
+    iterations = 0
+    while (curbytes>1024) {
+        iterations++
+        curbytes=curbytes/1024
+        }
+    return curbytes.toFixed(1) + ' ' + humanvalue[iterations]
+    }
+
+interval = null;
+function fetch(uuid) {
+    req = xmlhttp
+    req.open("GET", "/admin/upload_progress/", 1);
+    req.setRequestHeader("X-Progress-Id", uuid);
+    req.onreadystatechange = function () {
+    if (req.readyState == 4) {
+        if (req.status == 200) {
+
+            var upload = eval( '(' + req.responseText + ')' );
+
+            if (upload.state == 'done' || upload.state == 'uploading') 
+                bar = document.getElementById('progress_bar');
+                progress_wrap = document.getElementById('progress_wrap');
+                if (upload.state == 'done') {
+                    window.clearTimeout(interval);
+	            progress_wrap.style.visibility = 'hidden';
+                    return;   
+                }
+                bar_txt = document.getElementById('progress_text')
+                bar_txt.innerHTML = ((upload.received / upload.size) * 100).toFixed(1) + '% - ' +
+                    humanize(upload.received) + ' of ' + humanize(upload.size) 
+                w = 400 * upload.received / upload.size;
+                bar.style.width = w + 'px';
+
+                }
+            }
+        }
+    req.send(null); 
+
+    }
+
+function closeprogress() {
+
+
+
+}
+
+function openprogress(e) {
+
+    uuid = "";
+    for (i = 0; i < 32; i++) {
+        uuid += Math.floor(Math.random() * 16).toString(16);
+        }
+    frm = e.target||e.srcElement;
+
+    if (frm.action.indexOf('?') == -1) {
+       frm.action=frm.action+"?progress_id=" + uuid;
+    } else {
+       frm.action=frm.action+"&progress_id=" + uuid;
+    }
+
+    if (document.getElementById('progress_wrap')) {
+        document.getElementById('progress_wrap').style.visibility = 'visible';
+        document.getElementById('progress_bar').style.width = '0';
+        document.getElementById('progress_text').innerHTML = '0%';
+
+        interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+        return;
+    }
+
+    pos = getxy()
+    posx = parseInt((pos.x/2)-(420/2), 10)
+    posy = parseInt((pos.y/2)-(50/2), 10)
+
+    progress_wrap = quickElement('div', document.body, '', 'style', 
+        'position: absolute; top: '+posy+'px; left: '+posx+'px; height: 50px; ' +
+        'padding: 10px; width: 420px; background: #ffffff; ' +
+        'border: solid 1px #dddddd;', 'id', 'progress_wrap')
+
+    progress_label = quickElement('h1', progress_wrap, 'Upload progress')
+
+    progress = quickElement('div', progress_wrap, '', 'style', 
+        'top: 0; left: 0; width: 0px; ', 'id', 'progress_bar', 'class', 'submit-row')
+
+    progress_text = quickElement('div', progress_wrap, '0%', 'style',
+        'color: #000000; ', 'id', 'progress_text')
+ 
+    interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+    }
+
+addEvent(window, 'load', function() {
+        frms = document.getElementsByTagName('form');
+        for (var i=0; i<frms.length; i++) {
+           if (frms[i].encoding.toLowerCase() == 'multipart/form-data') {
+              addEvent(frms[i], 'submit',  openprogress);
+              return;
+           }
+        }
+    });
+function getxy(){
+    var x,y;
+    if (self.innerHeight) // all except Explorer
+        {
+        x = self.innerWidth;
+        y = self.innerHeight;
+        }
+    else if (document.documentElement && document.documentElement.clientHeight)
+        // Explorer 6 Strict Mode
+        {
+        x = document.documentElement.clientWidth;
+        y = document.documentElement.clientHeight;
+        }
+    else if (document.body) // other Explorers
+        {
+        x = document.body.clientWidth;
+        y = document.body.clientHeight;
+        }
+    return {'x':x,'y':y}
+    }
+
+var humanvalue = ['B','KB','MB','GB']
+function humanize(bytes) {
+    curbytes = bytes
+    iterations = 0
+    while (curbytes>1024) {
+        iterations++
+        curbytes=curbytes/1024
+        }
+    return curbytes.toFixed(1) + ' ' + humanvalue[iterations]
+    }
+
+interval = null;
+function fetch(uuid) {
+    req = xmlhttp
+    req.open("GET", "/admin/upload_progress/", 1);
+    req.setRequestHeader("X-Progress-Id", uuid);
+    req.onreadystatechange = function () {
+    if (req.readyState == 4) {
+        if (req.status == 200) {
+
+            var upload = eval( '(' + req.responseText + ')' );
+
+            if (upload.state == 'done' || upload.state == 'uploading') 
+                bar = document.getElementById('progress_bar');
+                progress_wrap = document.getElementById('progress_wrap');
+                if (upload.state == 'done') {
+                    window.clearTimeout(interval);
+	            progress_wrap.style.visibility = 'hidden';
+                    return;   
+                }
+                bar_txt = document.getElementById('progress_text')
+                bar_txt.innerHTML = ((upload.received / upload.size) * 100).toFixed(1) + '% - ' +
+                    humanize(upload.received) + ' of ' + humanize(upload.size) 
+                w = 400 * upload.received / upload.size;
+                bar.style.width = w + 'px';
+
+                }
+            }
+        }
+    req.send(null); 
+
+    }
+
+function closeprogress() {
+
+
+
+}
+
+function openprogress(e) {
+
+    uuid = "";
+    for (i = 0; i < 32; i++) {
+        uuid += Math.floor(Math.random() * 16).toString(16);
+        }
+    frm = e.target||e.srcElement;
+
+    if (frm.action.indexOf('?') == -1) {
+       frm.action=frm.action+"?progress_id=" + uuid;
+    } else {
+       frm.action=frm.action+"&progress_id=" + uuid;
+    }
+
+    if (document.getElementById('progress_wrap')) {
+        document.getElementById('progress_wrap').style.visibility = 'visible';
+        document.getElementById('progress_bar').style.width = '0';
+        document.getElementById('progress_text').innerHTML = '0%';
+
+        interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+        return;
+    }
+
+    pos = getxy()
+    posx = parseInt((pos.x/2)-(420/2), 10)
+    posy = parseInt((pos.y/2)-(50/2), 10)
+
+    progress_wrap = quickElement('div', document.body, '', 'style', 
+        'position: absolute; top: '+posy+'px; left: '+posx+'px; height: 50px; ' +
+        'padding: 10px; width: 420px; background: #ffffff; ' +
+        'border: solid 1px #dddddd;', 'id', 'progress_wrap')
+
+    progress_label = quickElement('h1', progress_wrap, 'Upload progress')
+
+    progress = quickElement('div', progress_wrap, '', 'style', 
+        'top: 0; left: 0; width: 0px; ', 'id', 'progress_bar', 'class', 'submit-row')
+
+    progress_text = quickElement('div', progress_wrap, '0%', 'style',
+        'color: #000000; ', 'id', 'progress_text')
+ 
+    interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+    }
+
+addEvent(window, 'load', function() {
+        frms = document.getElementsByTagName('form');
+        for (var i=0; i<frms.length; i++) {
+           if (frms[i].encoding.toLowerCase() == 'multipart/form-data') {
+              addEvent(frms[i], 'submit',  openprogress);
+              return;
+           }
+        }
+    });
+function getxy(){
+    var x,y;
+    if (self.innerHeight) // all except Explorer
+        {
+        x = self.innerWidth;
+        y = self.innerHeight;
+        }
+    else if (document.documentElement && document.documentElement.clientHeight)
+        // Explorer 6 Strict Mode
+        {
+        x = document.documentElement.clientWidth;
+        y = document.documentElement.clientHeight;
+        }
+    else if (document.body) // other Explorers
+        {
+        x = document.body.clientWidth;
+        y = document.body.clientHeight;
+        }
+    return {'x':x,'y':y}
+    }
+
+var humanvalue = ['B','KB','MB','GB']
+function humanize(bytes) {
+    curbytes = bytes
+    iterations = 0
+    while (curbytes>1024) {
+        iterations++
+        curbytes=curbytes/1024
+        }
+    return curbytes.toFixed(1) + ' ' + humanvalue[iterations]
+    }
+
+interval = null;
+function fetch(uuid) {
+    req = xmlhttp
+    req.open("GET", "/admin/upload_progress/", 1);
+    req.setRequestHeader("X-Progress-Id", uuid);
+    req.onreadystatechange = function () {
+    if (req.readyState == 4) {
+        if (req.status == 200) {
+
+            var upload = eval( '(' + req.responseText + ')' );
+
+            if (upload.state == 'done' || upload.state == 'uploading') 
+                bar = document.getElementById('progress_bar');
+                progress_wrap = document.getElementById('progress_wrap');
+                if (upload.state == 'done') {
+                    window.clearTimeout(interval);
+	            progress_wrap.style.visibility = 'hidden';
+                    return;   
+                }
+                bar_txt = document.getElementById('progress_text')
+                bar_txt.innerHTML = ((upload.received / upload.size) * 100).toFixed(1) + '% - ' +
+                    humanize(upload.received) + ' of ' + humanize(upload.size) 
+                w = 400 * upload.received / upload.size;
+                bar.style.width = w + 'px';
+
+                }
+            }
+        }
+    req.send(null); 
+
+    }
+
+function closeprogress() {
+
+
+
+}
+
+function openprogress(e) {
+
+    uuid = "";
+    for (i = 0; i < 32; i++) {
+        uuid += Math.floor(Math.random() * 16).toString(16);
+        }
+    frm = e.target||e.srcElement;
+
+    if (frm.action.indexOf('?') == -1) {
+       frm.action=frm.action+"?progress_id=" + uuid;
+    } else {
+       frm.action=frm.action+"&progress_id=" + uuid;
+    }
+
+    if (document.getElementById('progress_wrap')) {
+        document.getElementById('progress_wrap').style.visibility = 'visible';
+        document.getElementById('progress_bar').style.width = '0';
+        document.getElementById('progress_text').innerHTML = '0%';
+
+        interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+        return;
+    }
+
+    pos = getxy()
+    posx = parseInt((pos.x/2)-(420/2), 10)
+    posy = parseInt((pos.y/2)-(50/2), 10)
+
+    progress_wrap = quickElement('div', document.body, '', 'style', 
+        'position: absolute; top: '+posy+'px; left: '+posx+'px; height: 50px; ' +
+        'padding: 10px; width: 420px; background: #ffffff; ' +
+        'border: solid 1px #dddddd;', 'id', 'progress_wrap')
+
+    progress_label = quickElement('h1', progress_wrap, 'Upload progress')
+
+    progress = quickElement('div', progress_wrap, '', 'style', 
+        'top: 0; left: 0; width: 0px; ', 'id', 'progress_bar', 'class', 'submit-row')
+
+    progress_text = quickElement('div', progress_wrap, '0%', 'style',
+        'color: #000000; ', 'id', 'progress_text')
+ 
+    interval = window.setInterval(
+        function () {
+            fetch(uuid);
+            },
+        1000
+        );
+    }
+
+addEvent(window, 'load', function() {
+        frms = document.getElementsByTagName('form');
+        for (var i=0; i<frms.length; i++) {
+           if (frms[i].encoding.toLowerCase() == 'multipart/form-data') {
+              addEvent(frms[i], 'submit',  openprogress);
+              return;
+           }
+        }
+    });
\ No newline at end of file
Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 5116)
+++ django/contrib/admin/options.py	(working copy)
@@ -154,6 +154,8 @@
         js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
         if self.prepopulated_fields:
             js.append('js/urlify.js')
+        if self.opts.has_field_type(models.FileField):
+            js.append('js/UploadProgress.js')
         if self.opts.has_field_type(models.DateTimeField) or self.opts.has_field_type(models.TimeField) or self.opts.has_field_type(models.DateField):
             js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
         if self.opts.get_ordered_objects():
Index: django/contrib/admin/urls.py
===================================================================
--- django/contrib/admin/urls.py	(revision 5116)
+++ django/contrib/admin/urls.py	(working copy)
@@ -9,6 +9,8 @@
     #('^password_change/done/$', 'django.contrib.auth.views.password_change_done'),
     ('^template_validator/$', 'django.contrib.admin.views.template.template_validator'),
 
+    # Upload progress bar support
+    #('^upload_progress/$', 'django.contrib.admin.views.main.upload_progress'),
     # "Add user" -- a special-case view
     ('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
     # "Change user password" -- another special-case view
Index: django/contrib/admin/views/main.py
===================================================================
--- django/contrib/admin/views/main.py	(revision 5116)
+++ django/contrib/admin/views/main.py	(working copy)
@@ -48,6 +48,20 @@
             res[i] = '_%02X' % ord(c)
     return ''.join(res)
 
+def upload_progress(request):
+    """
+    Given this request, returns a JSON
+    object that has information on a file upload progress.
+    If there is no file upload in progress, returns an
+    empty dictionary, '{}'.
+    """
+
+    from django.utils import simplejson
+
+    content = simplejson.dumps(request.file_progress)
+
+    return HttpResponse(content=content, mimetype='text/plain')
+
 def model_admin_view(request, app_label, model_name, rest_of_url):
     model = models.get_model(app_label, model_name)
     if model is None:
