[zeam.form.silva][Sylvain Viollon] Beta new upload widget.

[email protected]
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Thu Jul 25 15:23:54 2013 +0200
revision:  408:404f9e87e1ff in zeam.form.silva
branch:    2.4
details:   https://hg.infrae.com/zeam.form.silva?cmd=changeset;node=404f9e87e1ff
modified:  src/zeam/form/silva/form/static/public.min.css src/zeam/form/silva/widgets/file.py src/zeam/form/silva/widgets/file_templates/filewidgetinput.cpt src/zeam/form/silva/widgets/static/upload.js src/zeam/form/silva/widgets/static/upload.min.js
added:     
removed:   
log:       Beta new upload widget.


diffstat:

 src/zeam/form/silva/form/static/public.min.css                 |    2 +-
 src/zeam/form/silva/widgets/file.py                            |   88 +-
 src/zeam/form/silva/widgets/file_templates/filewidgetinput.cpt |   45 +-
 src/zeam/form/silva/widgets/static/upload.js                   |  451 +++++----
 src/zeam/form/silva/widgets/static/upload.min.js               |    2 +-
 5 files changed, 324 insertions(+), 264 deletions(-)

diffs (735 lines):

diff -r c495987a48d9 -r 404f9e87e1ff src/zeam/form/silva/form/static/public.min.css
--- a/src/zeam/form/silva/form/static/public.min.css	Mon Jul 15 15:56:35 2013 +0200
+++ b/src/zeam/form/silva/form/static/public.min.css	Thu Jul 25 15:23:54 2013 +0200
@@ -1,1 +1,1 @@
-.public-form,.login-form{width:35em}.form{margin:1em 0;padding:.2em .5em .3em;border:1px solid #808080}.form-title{height:1.6em;font-size:15px;font-weight:700}.form-label{float:left;width:7em;height:2em}.form-label label{display:block;margin:.2em 0 0;font-weight:400}.form-field{height:2.2em}.form-field input[type="text"],.form-field input[type="password"],.form-field input[type="email"]{float:right;width:22em}.form-actions .submit{float:right;padding:.1em 1.5em;font-weight:700;margin-left:.4em}.form-actions .cancel{padding:.1em 1.5em}.form-error,.field-error{color:red;font-weight:700}.field-captcha-wrapper{width:22em;float:right}
\ No newline at end of file
+.public-form,.login-form{width:35em}.form{margin:1em 0;padding:.5em .5em 0;border:1px solid #808080}.form-element{clear:both}.form-title{height:1.6em;font-size:15px;font-weight:700}.form-label{float:left;width:7em;height:2em}.form-label label{display:block;margin:.2em 0 0;font-weight:400}.form-field{height:2.2em}.form-field input[type="text"],.form-field input[type="password"],.form-field input[type="email"],.form-field textarea{float:right;width:22em;padding:2px}.form-field textarea{height:3.5em}.form-actions .submit{float:right;padding:.1em 1.5em;font-weight:700;margin-left:.4em}.form-actions .cancel{padding:.1em 1.5em}.form-error,.field-error{color:red;font-weight:700}.field-captcha-wrapper{width:22em;float:right}
\ No newline at end of file
diff -r c495987a48d9 -r 404f9e87e1ff src/zeam/form/silva/widgets/file.py
--- a/src/zeam/form/silva/widgets/file.py	Mon Jul 15 15:56:35 2013 +0200
+++ b/src/zeam/form/silva/widgets/file.py	Thu Jul 25 15:23:54 2013 +0200
@@ -5,18 +5,18 @@
 import os.path
 import logging
 import json
+import uuid
 
 from five import grok
 from zope.interface import Interface
 from zope.traversing.browser import absoluteURL
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 
-from App.config import getConfiguration
-
 from js.jqueryui import jqueryui
 
 from infrae import rest
 from silva.core import conf as silvaconf
+from silva.core import interfaces
 from silva.core.conf import schema as silvaschema
 from silva.core.interfaces.adapters import IIconResolver
 from silva.fanstatic import need
@@ -25,6 +25,7 @@
 from zeam.form.base.errors import Error
 from zeam.form.base.markers import NO_VALUE, NO_CHANGE
 from zeam.form.base.widgets import WidgetExtractor
+from zeam.form.composed.interfaces import ISubForm
 from zeam.form.ztk.fields import SchemaField, SchemaFieldWidget
 from zeam.form.ztk.fields import registerSchemaField
 from zeam.form.silva.interfaces import ISMIForm
@@ -105,9 +106,16 @@
         need(IUploadResources)
         super(FileWidgetInput, self).update()
 
+    def uploadIdentifier(self):
+        return str(uuid.uuid1())
+
     def uploadURL(self):
-        return absoluteURL(self.form.context, self.request) + \
-            '/++rest++zeam.form.silva.upload'
+        if ISubForm.providedBy(self.form):
+            form = self.form.getComposedForm()
+        else:
+            form = self.form
+        return absoluteURL(form, self.request) + \
+            '/upload'
 
     def prepareContentValue(self, value):
         formatted_value = u''
@@ -117,17 +125,25 @@
 
     def displayValue(self):
         value = self.inputValue()
+        status = None
         label = None
+        icon = IIconResolver(self.request).get_tag(None)
         if value:
             label = self.component.fileSetLabel
             if value != u'__NO_CHANGE__':
-                return {'icon': None,
-                        'message': None,
-                        'filename': unicode(os.path.basename(value))}
+                manager = self.request.environ.get('infrae.fileupload.manager')
+                if manager is None:
+                    raise interfaces.Error(
+                        'The upload component is not available.')
+                bucket = manager.access_upload_bucket(value)
+                if bucket is not None:
+                    status = json.dumps(bucket.get_status())
         else:
             label = self.component.fileNotSetLabel
-        icon = IIconResolver(self.request).get_tag(None)
-        return {'icon': icon, 'message': label, 'filename': None}
+        return {'icon': icon,
+                'message': label,
+                'status': status,
+                'empty': status is None}
 
     def valueToUnicode(self, value):
         if value is NO_CHANGE:
@@ -135,52 +151,58 @@
         return unicode(value)
 
 
+class UploadedFile(object):
+
+    def __init__(self, bucket):
+        metadata = bucket.get_status()
+        self.name = metadata.get('filename') # Python filename
+        self.filename = metadata.get('filename') # Zope filename
+        self.__descriptor = open(bucket.get_filename(), 'r')
+
+    def __getattr__(self, name):
+        return getattr(self.__descriptor, name)
+
+
 class FileWidgetExtractor(WidgetExtractor):
     grok.adapts(FileSchemaField, ISMIForm, Interface)
 
-    @classmethod
-    def upload_dir(cls):
-        if hasattr(cls, '_upload_dir_cache'):
-            return cls._upload_dir_cache
-        zconf = getattr(getConfiguration(), 'product_config', {})
-        config = zconf.get('zeam.form.silva', {})
-        upload_dir = config.get('upload-directory', None)
-        if upload_dir is None:
-            raise RuntimeError(
-                '(zeam.form.silva) upload directory no configured')
-        cls._upload_dir_cache = upload_dir
-        return upload_dir
-
     def extract(self):
         value = self.request.form.get(self.identifier, u'')
         if value == "__NO_CHANGE__":
             return NO_CHANGE, None
-        if value:
-            return open(os.path.join(self.upload_dir(), value), 'r'), None
-        return NO_VALUE, None
+        if not value:
+            return NO_VALUE, None
+        manager = self.request.environ.get('infrae.fileupload.manager')
+        if manager is None:
+            raise interfaces.Error('The upload component is not available')
+        bucket = manager.access_upload_bucket(value)
+        if bucket is not None:
+            if not bucket.is_complete():
+                return NO_VALUE, _(u"Upload is incomplete.")
+            return UploadedFile(bucket), None
+        return NO_VALUE, _(u"Upload failed.")
 
 
 class Upload(rest.REST):
     """ Check security and return information about gp.fileupload upload
     """
-    grok.context(Interface)
+    grok.adapts(ISMIForm, Interface)
     grok.require('silva.ChangeSilvaContent')
-    grok.name('zeam.form.silva.upload')
+    grok.name('upload')
 
     def POST(self):
         """ get information about file upload
         """
-        upload_id = long(self.request.get('gp.fileupload.id'))
-        paths = self.request.environ.get('HTTP_STORED_PATHS', '').split(':')
-        path = paths[0]
-        logger.info("upload paths: %r" % paths)
+        info = self.request.environ['infrae.fileupload.current'].get_status()
+        info['message'] = 'File uploaded'
         return """
             <html>
                 <body>
                     <script>
                         var $ = window.parent.jQuery;
-                        $(window.parent.document).trigger('done-%d-upload', %s);
+                        $(window.parent.document).trigger(
+                            'done-%s-upload', %s);
                     </script>
                 </body>
             </html>
-        """ % (upload_id, json.dumps({'path': path, 'upload-id': upload_id}))
+        """ % (info['identifier'], json.dumps(info))
diff -r c495987a48d9 -r 404f9e87e1ff src/zeam/form/silva/widgets/file_templates/filewidgetinput.cpt
--- a/src/zeam/form/silva/widgets/file_templates/filewidgetinput.cpt	Mon Jul 15 15:56:35 2013 +0200
+++ b/src/zeam/form/silva/widgets/file_templates/filewidgetinput.cpt	Thu Jul 25 15:23:54 2013 +0200
@@ -1,39 +1,50 @@
-<div class="upload-file" i18n:domain="silva">
-  <div class="upload-popup" style="width: 100px; height:10px;">
-    <p></p>
-  </div>
+<div class="upload-file"
+     data-upload-url="${widget.uploadURL()}"
+     data-upload-identifier="${widget.uploadIdentifier()}"
+     i18n:domain="silva">
   <div class="upload-display"
-     tal:define="display widget.displayValue()">
-    <p tal:condition="display.message">
+       data-upload-status="${display.status}"
+       tal:define="display widget.displayValue()">
+    <p class="upload-display-empty">
       <img tal:condition="display.icon"
            tal:replace="structure display.icon" />
       <i tal:content="display.message" />
     </p>
-    <span tal:content="display.filename"
-          tal:condition="display.filename" />
+    <p class="upload-display-uploading"
+       style="display: none;">
+      <ins class="icon form_warning" />
+      <i i18n:translate="">Uploading {filename}...</i>
+    </p>
+    <p class="upload-display-complete"
+       style="display: none;">
+      <ins class="icon form_check" />
+      <i i18n:translate="">File {filename} uploaded.</i>
+    </p>
   </div>
   <div style="display:none"
        class="upload-progress">
   </div>
-  <iframe width="0"
-          height="0"
-          style="display:none"
-          src="about:blank"
-          class="upload-iframe"
-          name="${widget.htmlId()}-upload-iframe">
-  </iframe>
-  <a href="${widget.uploadURL()}" onclick="return false"
+  <a onclick="return false"
      title="Upload a new file in Silva"
      class="form-button upload-trigger ui-state-default"
      i18n:attributes="title">
     <ins class="ui-icon ui-icon-document"></ins>
     <tal:msg i18n:translate="">Upload</tal:msg>
   </a>
-  <a tal:condition="not widget.required" href="#" onclick="return false"
+  <a tal:condition="not widget.required"
+     onclick="return false"
      class="upload-clear form-button ui-state-default">
     <ins class="ui-icon ui-icon-circle-close"></ins>
     <tal:msg i18n:translate="">Clear</tal:msg>
   </a>
+  <a onclick="return false"
+     style="display: none;"
+     class="upload-cancel form-button ui-state-default">
+    <ins class="ui-icon ui-icon-circle-close"></ins>
+    <tal:msg i18n:translate="">Cancel</tal:msg>
+  </a>
+  <input type="file" name="${widget.htmlId()}.upload"
+         class="upload-file-input" />
   <input type="hidden"
          id="${widget.htmlId()}"
          name="${widget.identifier}"
diff -r c495987a48d9 -r 404f9e87e1ff src/zeam/form/silva/widgets/static/upload.js
--- a/src/zeam/form/silva/widgets/static/upload.js	Mon Jul 15 15:56:35 2013 +0200
+++ b/src/zeam/form/silva/widgets/static/upload.js	Thu Jul 25 15:23:54 2013 +0200
@@ -1,240 +1,267 @@
+(function($, jsontemplate) {
+    var POLL_RETRIES = 5,
+        POLL_DELAY = 500,
+        NEED_POPUP = (function () {
+            // Test if we need a Javascript POPUP to display the file input or not.
+            var $test = $('<div style="display:none;"><form id="testform1">' +
+                      '<input type="file" form="testform2" /></form><form id="testform2">' +
+                      '</form></div>'),
+            supported = false;
+        $('body').append($test);
+        supported = ($test.find('#testform1')[0].length === 0 &&
+                     $test.find('#testform2')[0].length === 1);
+        $test.remove();
+        return supported;
+    })();
 
-(function($) {
+    var UploadForm = function($field, identifier, url) {
+        // Upload a file input using the given identifier at the given URL.
+        // It will trigger callbacks uring various steps of the progress.
+        var $upload = $('<div style="display:none">' +
+                        '<iframe width="0" height="0" style="display:none"' +
+                        'src="about:blank" class="upload-iframe" name="upload-' +
+                        identifier + '-iframe"></iframe>' +
+                        '<form class="upload-form" enctype="multipart/form-data"' +
+                        'method="POST" action="' + url + '?identifier=' + identifier +
+                        '" target="upload-' + identifier +
+                        '-iframe" id="upload-' + identifier + '-form">' +
+                        '<input type="reset" class="upload-reset" /></form></div>'),
+            poll_retries = POLL_RETRIES,
+            poll_timeout = null,
+            result = $.Deferred().fail(function() {
+                $upload.find('.upload-reset').click();
+                $upload.find('.upload-iframe').attr('src', 'javascript:false;');
+            }).always(function() {
+                if (poll_timeout !== null) {
+                    clearTimeout(poll_timeout);
+                    poll_timeout = null;
+                };
+                $upload.remove();
+                $field.removeAttr('form');
+            }),
+            start = $.Deferred().done(function() {
+                poll_timeout = setTimeout(poll, POLL_DELAY);
+            });
 
-    function basename(path) {
-        return path.replace(/^.*[\/\\](.*)/, '$1');
-    };
-
-    var default_options = {
-        stat_url: '/gp.fileupload.stat/',
-        stat_timeout: 5000,
-        stat_delay: 500,
-        max_retries: 3,
-        errors: {
-            too_many_retries: 'unknown error while uploading file.',
-            file_too_big: 'upload failed file is too big.'
-        }
-    };
-
-    var FileUploader = function($progress, options){
-        var deferred = $.Deferred();
-        var progress = 0;
-        var retries = 0;
-
-        var upload_id = Math.random() * 10000000000000000000;
-        var timeout = null;
-
-        var form = null;
-
-        options = $.extend({}, default_options, options);
-
-        var clear_status = function() {
-            if (timeout !== null) {
-                clearTimeout(timeout);
-                timeout = null;
-            };
-        };
-
-        var set_progress = function(value) {
-            if (progress != value) {
-                progress = value;
-                $progress.progressbar('option', 'value', value);
-            }
-        };
-
-        var abort_upload = function(message) {
-            clear_status();
-            deferred.reject({filename: null, message: message});
-        };
-
-        var poll_success = function(data) {
-            switch (data.state) {
-            case -1:
-                abort_upload(options.errors.file_too_big);
-                break;
-            case 0:
-                // not started
-                retries += 1;
-                if (retries > options.max_retries) {
-                    abort_upload(options.errors.too_many_retries);
-                    return;
-                };
-                $progress.show();
-                poll_status();
-                break;
-            case 1:
-                // dl in progress
-                retries = 0;
-                $progress.show();
-                if (data.percent > 0) {
-                    set_progress(data.percent);
-                };
-                if (data.percent < 100) {
-                    poll_status();
-                };
-                break;
-            case 2:
-                // not using the progressbar
-                $progress.hide();
-                clear_status();
-                break;
-            };
-        };
-
-        var poll_error = function(jqXHR, textStatus, errorThrown) {
-            retries += 1;
-            if (retries > options.max_retries) {
-                abort_upload(options.errors.too_many_retries);
-                return;
-            }
-            poll_status();
-        };
-
-        var _poll_status = function() {
-            var url = options.stat_url + upload_id;
+        var poll = function() {
+            // Poll status from the server.
             $.ajax({
                 type: 'GET',
                 dataType: 'json',
-                timeout: options.stat_timeout,
-                url: url,
-                success: poll_success,
-                error: poll_error
+                url: url + '?status&identifier=' + identifier
+            }).then(function(info){
+                result.notify(info);
+                if (!info['upload-finished'] && result.state() == "pending") {
+                    // Unless the file is completely uploaded or
+                    // cancel request status again.
+                    poll_retries = POLL_RETRIES;
+                    poll_timeout = setTimeout(poll, POLL_DELAY);
+                };
+            }, function() {
+                if (result.state() == "pending") {
+                    if (poll_retries) {
+                        poll_retries -= 1;
+                        poll_timeout = setTimeout(poll, POLL_DELAY);
+                    } else {
+                        result.reject({message: 'Status check failed: cannot upload file.'});
+                    }
+                }
             });
         };
-
-        var poll_status = function() {
-            clear_status();
-            timeout = setTimeout(_poll_status, options.stat_delay);
-        };
-
         return {
-            get_form: function(target, action) {
-                var sep = '?';
-
-                form = $('<form class="upload-form"' +
-                         'enctype="multipart/form-data"' +
-                         'method="POST"' +
-                         'action=""' +
-                         'target="' + target + '">' +
-                         '<input type="file" name="file" />' +
-                         '</form>');
-                if (action.match(/\?/))
-                    sep = "&";
-                form.attr('action', action + sep + 'gp.fileupload.id=' + upload_id);
-                return form;
+            start: function() {
+                $(document).one('done-' + identifier + '-upload', function(event, data) {
+                    result.resolve(data);
+                });
+                $('body').append($upload);
+                $field.attr('form', 'upload-' + identifier + '-form');
+                $upload.find('form').submit();
+                start.resolve({
+                    'uploaded-length': 0, 'content-type': 'n/a', 'request-length': 0, 'filename': ''});
             },
-            send: function() {
-                if (form === null) {
-                    return null;
+            cancel: function() {
+                result.reject({});
+            },
+            register: function(plugin) {
+                if (plugin.start !== undefined) {
+                    start.done(plugin.start);
                 };
-                // listen to event triggered by the iframe, that will
-                // mark the end of a successful upload
-                $(document).one('done-' + String(upload_id) + '-upload', function(event, data) {
-                    deferred.resolve({filename: data.path});
-                });
-                form.submit();
-                poll_status();
-                return deferred.promise();
+                for (var event in {progress: 1, done: 1, fail: 1, always: 1}) {
+                    if (plugin[event] !== undefined) {
+                        result[event](plugin[event]);
+                    };
+                };
             }
         };
     };
 
-    var create_upload_field = function() {
-        var $field = $(this);
+    var UploadStatusMessage = function($display) {
+        // Manage the status message of the upload widget.
+        var $empty = $display.children('.upload-display-empty'),
+            $uploading = $display.children('.upload-display-uploading'),
+            $done = $display.children('.upload-display-complete');
+        var empty = $empty.children('i').text(),
+            uploading = jsontemplate.Template($uploading.children('i').text(), {}),
+            done = jsontemplate.Template($done.children('i').text(), {}),
+            state = 'empty',
+            original = $display.data('upload-status');
 
-        var $upload_button = $field.find('a.upload-trigger');
-        var $clear_button = $field.find('a.upload-clear');
+        var api = {
+            start: function(data) {
+                if (state != 'starting') {
+                    $uploading.children('i').text(uploading.expand(data));
+                    $empty.hide();
+                    $done.hide();
+                    $uploading.show();
+                    state = 'starting';
+                }
+            },
+            progress: function(data) {
+                if (state != 'uploading') {
+                    // Refresh information on the first notification of progress.
+                    $uploading.children('i').text(uploading.expand(data));
+                    state = 'uploading';
+                };
+            },
+            done: function(data) {
+                if (state != 'done') {
+                    $done.children('i').text(done.expand(data));
+                    $empty.hide();
+                    $uploading.hide();
+                    $done.show();
+                    state = 'done';
+                };
+            },
+            fail: function(data) {
+                if (state != 'empty' || data.message) {
+                    $empty.children('i').text(data.message || empty);
+                    if (state != 'empty') {
+                        $uploading.hide();
+                        $done.hide();
+                        $empty.show();
+                        state = 'empty';
+                    };
+                };
+            }
+        };
+        if (original) {
+            api.done(original);
+        };
+        return api;
+    };
 
-        var $progress = $field.find('.upload-progress');
-        var $input = $field.find('.upload-input');
-        var $status = $field.find('.upload-display');
+    var UploadProgress = function($progress) {
+        // Manage the progress bar of the upload widget.
+        var progress = 0;
 
-        var previous_value = $input.val();
+        return {
+            start: function() {
+                $progress.progressbar({value: 0}).show();
+            },
+            progress: function(data) {
+                var updated = 0;
 
-        var set_input_value = function(data) {
-            if (data.filename) {
-                $status.text(basename(data.filename));
-            } else if (data.message) {
-                $status.text(data.message);
-            } else {
-                $status.text('not set.');
+                if (!data['missing']) {
+                    updated = data['uploaded-length'] * 100 / data['request-length'];
+                };
+                if (updated != progress) {
+                    $progress.progressbar({value: updated});
+                    progress = updated;
+                };
+            },
+            always: function(data) {
+                $progress.hide();
+                progress = 0;
+            }
+        };
+    };
+
+    var UploadField = function ($field, set_objection) {
+        var $input = $field.children('.upload-input'),
+            $trigger = $field.children('.upload-trigger'),
+            $cancel = $field.children('.upload-cancel'),
+            $clear = $field.children('.upload-clear'),
+            $file = $field.children('.upload-file-input');
+        var upload = null,
+            objection = null,
+            progress = UploadProgress($field.children('.upload-progress')),
+            status = UploadStatusMessage($field.children('.upload-display')),
+            ui = {
+                start: function() {
+                    if (set_objection !== undefined) {
+                        objection = $.Deferred();
+                        set_objection(function () {
+                            if (objection.state() == 'pending') {
+                                var $dialog = $('<div title="Uploading file">Please wait until the upload is finished ...</div>');
+
+                                $dialog.dialog({
+                                    modal: true,
+                                    closeOnEscape: false,
+                                    resizable: false,
+                                    draggable: false,
+                                    beforeClose: function() {
+                                        return false;
+                                }});
+                                return objection.always(function() {
+                                    $dialog.dialog('close').remove();
+                                    objection = null;
+                                });
+                            };
+                            return null;
+                        });
+                    };
+                    $file.hide();
+                    $cancel.show();
+                },
+                done: function(data) {
+                    $input.attr('value', $field.data('upload-identifier'));
+                },
+                always: function(data) {
+                    if (objection !== null) {
+                        objection.resolve();
+                    };
+                    $file.show();
+                    $cancel.hide();
+                    upload = null;
+                }
             };
-            $input.val(data.filename);
-            $input.change();
+
+        var get_upload = function(create) {
+            if (upload == null && create) {
+                upload = UploadForm($file, $field.data('upload-identifier'), $field.data('upload-url'));
+                upload.register(status);
+                upload.register(progress);
+                upload.register(ui);
+            }
+            return upload;
         };
 
-        var disable_upload_button = function() {
-            $progress.progressbar({value: 0});
-            //$progress.show();
-            $upload_button.button('disable');
-            $upload_button.hide();
-        };
-
-        var enable_upload_button = function() {
-            $progress.hide();
-            $upload_button.button('enable');
-            $upload_button.show();
-        };
-
-        // Support for clear button
-        $clear_button.bind('click', function(){
-            set_input_value('');
+        $trigger.remove();
+        $cancel.on('click', function() {
+            var upload = get_upload();
+            if (upload !== null) {
+                upload.cancel();
+            };
         });
-
-        // Support for upload button
-        $upload_button.bind('click', function(){
-            var $iframe = $field.find('iframe');
-            var $popup = $field.find('.upload-popup').clone();
-            var uploader = FileUploader($progress);
-            var promise = null;
-
-            $popup.dialog({
-                title: $upload_button.text(),
-                modal: true,
-                create: function() {
-                    $popup.find('p').append(
-                        uploader.get_form(
-                            $iframe.attr('name'), $upload_button.attr('href')));
-                },
-                autoOpen: false,
-                zIndex: 12500,
-                buttons: {
-                    Send: function(event){
-                        disable_upload_button();
-                        promise = uploader.send().fail(
-                            function (data) {
-                                // failure
-                                $field.trigger(
-                                    'notify-feedback-smi',
-                                    {message: data.message, category: 'error'});
-                            }).always(function (data) {
-                                // in any case
-                                set_input_value(data);
-                                enable_upload_button();
-                                $popup.remove();
-                            });
-                        $popup.dialog('close');
-                    }
-                }
-            });
-            $popup.bind('dialogclose', function() {
-                // the user click on close.
-                if (promise === null) {
-                    $popup.remove();
-                }
-            });
-            $popup.dialog('open');
+        $clear.on('click', function() {
+            var upload = get_upload(true);
+            upload.cancel();
+            $input.attr('value', '');
         });
-
+        $file.on('change', function() {
+            upload = UploadForm($file, $field.data('upload-identifier'), $field.data('upload-url'));
+            upload.register(status);
+            upload.register(progress);
+            upload.register(ui);
+            upload.start();
+        });
     };
 
-    $(document).on('loadwidget-smiform', '.form-fields-container', function(event) {
-        $(this).find('.upload-file').each(create_upload_field);
+    $(document).on('loadwidget-smiform', '.form-fields-container', function(event, data) {
+        $(this).find('.upload-file').each(function(){
+            UploadField($(this), data.set_objection);
+        });
         event.stopPropagation();
     });
 
-    $(document).ready(function() {
-        $('.upload-file').each(create_upload_field);
-    });
-
-})(jQuery);
+})(jQuery, jsontemplate);
diff -r c495987a48d9 -r 404f9e87e1ff src/zeam/form/silva/widgets/static/upload.min.js
--- a/src/zeam/form/silva/widgets/static/upload.min.js	Mon Jul 15 15:56:35 2013 +0200
+++ b/src/zeam/form/silva/widgets/static/upload.min.js	Thu Jul 25 15:23:54 2013 +0200
@@ -1,1 +1,1 @@
-(function($){function basename(path){return path.replace(/^.*[\/\\](.*)/,"$1")}var default_options={stat_url:"/gp.fileupload.stat/",stat_timeout:5e3,stat_delay:500,max_retries:3,errors:{too_many_retries:"unknown error while uploading file.",file_too_big:"upload failed file is too big."}},FileUploader=function($progress,options){var deferred=$.Deferred(),progress=0,retries=0,upload_id=1e19*Math.random(),timeout=null,form=null;options=$.extend({},default_options,options);var clear_status=function(){null!==timeout&&(clearTimeout(timeout),timeout=null)},set_progress=function(value){progress!=value&&(progress=value,$progress.progressbar("option","value",value))},abort_upload=function(message){clear_status(),deferred.reject({filename:null,message:message})},poll_success=function(data){switch(da
 ta.state){case-1:abort_upload(options.errors.file_too_big);break;case 0:if(retries+=1,retries>options.max_retries)return abort_upload(options.errors.too_many_retries),void 0;$progress.show()
 ,poll_status();break;case 1:retries=0,$progress.show(),data.percent>0&&set_progress(data.percent),100>data.percent&&poll_status();break;case 2:$progress.hide(),clear_status()}},poll_error=function(){return retries+=1,retries>options.max_retries?(abort_upload(options.errors.too_many_retries),void 0):(poll_status(),void 0)},_poll_status=function(){var url=options.stat_url+upload_id;$.ajax({type:"GET",dataType:"json",timeout:options.stat_timeout,url:url,success:poll_success,error:poll_error})},poll_status=function(){clear_status(),timeout=setTimeout(_poll_status,options.stat_delay)};return{get_form:function(target,action){var sep="?";return form=$('<form class="upload-form"enctype="multipart/form-data"method="POST"action=""target="'+target+'">'+'<input type="file" name="file" />'+"</form>"),
 action.match(/\?/)&&(sep="&"),form.attr("action",action+sep+"gp.fileupload.id="+upload_id),form},send:function(){return null===form?null:($(document).one("done-"+(upload_id+"")+"-upload",fun
 ction(event,data){deferred.resolve({filename:data.path})}),form.submit(),poll_status(),deferred.promise())}}},create_upload_field=function(){var $field=$(this),$upload_button=$field.find("a.upload-trigger"),$clear_button=$field.find("a.upload-clear"),$progress=$field.find(".upload-progress"),$input=$field.find(".upload-input"),$status=$field.find(".upload-display");$input.val();var set_input_value=function(data){data.filename?$status.text(basename(data.filename)):data.message?$status.text(data.message):$status.text("not set."),$input.val(data.filename),$input.change()},disable_upload_button=function(){$progress.progressbar({value:0}),$upload_button.button("disable"),$upload_button.hide()},enable_upload_button=function(){$progress.hide(),$upload_button.button("enable"),$upload_button.show(
 )};$clear_button.bind("click",function(){set_input_value("")}),$upload_button.bind("click",function(){var $iframe=$field.find("iframe"),$popup=$field.find(".upload-popup").clone(),uploader=F
 ileUploader($progress),promise=null;$popup.dialog({title:$upload_button.text(),modal:!0,create:function(){$popup.find("p").append(uploader.get_form($iframe.attr("name"),$upload_button.attr("href")))},autoOpen:!1,zIndex:12500,buttons:{Send:function(){disable_upload_button(),promise=uploader.send().fail(function(data){$field.trigger("notify-feedback-smi",{message:data.message,category:"error"})}).always(function(data){set_input_value(data),enable_upload_button(),$popup.remove()}),$popup.dialog("close")}}}),$popup.bind("dialogclose",function(){null===promise&&$popup.remove()}),$popup.dialog("open")})};$(document).on("loadwidget-smiform",".form-fields-container",function(event){$(this).find(".upload-file").each(create_upload_field),event.stopPropagation()}),$(document).ready(function(){$(".up
 load-file").each(create_upload_field)})})(jQuery);
\ No newline at end of file
+(function($,jsontemplate){var POLL_RETRIES=5,POLL_DELAY=500;(function(){var $test=$('<div style="display:none;"><form id="testform1"><input type="file" form="testform2" /></form><form id="testform2"></form></div>'),supported=!1;return $("body").append($test),supported=0===$test.find("#testform1")[0].length&&1===$test.find("#testform2")[0].length,$test.remove(),supported})();var UploadForm=function($field,identifier,url){var $upload=$('<div style="display:none"><iframe width="0" height="0" style="display:none"src="about:blank" class="upload-iframe" name="upload-'+identifier+'-iframe"></iframe>'+'<form class="upload-form" enctype="multipart/form-data"'+'method="POST" action="'+url+"?identifier="+identifier+'" target="upload-'+identifier+'-iframe" id="upload-'+identifier+'-form">'+'<input ty
 pe="reset" class="upload-reset" /></form></div>'),poll_retries=POLL_RETRIES,poll_timeout=null,result=$.Deferred().fail(function(){$upload.find(".upload-reset").click(),$upload.find(".upload-
 iframe").attr("src","javascript:false;")}).always(function(){null!==poll_timeout&&(clearTimeout(poll_timeout),poll_timeout=null),$upload.remove(),$field.removeAttr("form")}),start=$.Deferred().done(function(){poll_timeout=setTimeout(poll,POLL_DELAY)}),poll=function(){$.ajax({type:"GET",dataType:"json",url:url+"?status&identifier="+identifier}).then(function(info){result.notify(info),info["upload-finished"]||"pending"!=result.state()||(poll_retries=POLL_RETRIES,poll_timeout=setTimeout(poll,POLL_DELAY))},function(){"pending"==result.state()&&(poll_retries?(poll_retries-=1,poll_timeout=setTimeout(poll,POLL_DELAY)):result.reject({message:"Status check failed: cannot upload file."}))})};return{start:function(){$(document).one("done-"+identifier+"-upload",function(event,data){result.resolve(dat
 a)}),$("body").append($upload),$field.attr("form","upload-"+identifier+"-form"),$upload.find("form").submit(),start.resolve({"uploaded-length":0,"content-type":"n/a","request-length":0,filen
 ame:""})},cancel:function(){result.reject({})},register:function(plugin){void 0!==plugin.start&&start.done(plugin.start);for(var event in{progress:1,done:1,fail:1,always:1})void 0!==plugin[event]&&result[event](plugin[event])}}},UploadStatusMessage=function($display){var $empty=$display.children(".upload-display-empty"),$uploading=$display.children(".upload-display-uploading"),$done=$display.children(".upload-display-complete"),empty=$empty.children("i").text(),uploading=jsontemplate.Template($uploading.children("i").text(),{}),done=jsontemplate.Template($done.children("i").text(),{}),state="empty",original=$display.data("upload-status"),api={start:function(data){"starting"!=state&&($uploading.children("i").text(uploading.expand(data)),$empty.hide(),$done.hide(),$uploading.show(),state="s
 tarting")},progress:function(data){"uploading"!=state&&($uploading.children("i").text(uploading.expand(data)),state="uploading")},done:function(data){"done"!=state&&($done.children("i").text
 (done.expand(data)),$empty.hide(),$uploading.hide(),$done.show(),state="done")},fail:function(data){("empty"!=state||data.message)&&($empty.children("i").text(data.message||empty),"empty"!=state&&($uploading.hide(),$done.hide(),$empty.show(),state="empty"))}};return original&&api.done(original),api},UploadProgress=function($progress){var progress=0;return{start:function(){$progress.progressbar({value:0}).show()},progress:function(data){var updated=0;data.missing||(updated=100*data["uploaded-length"]/data["request-length"]),updated!=progress&&($progress.progressbar({value:updated}),progress=updated)},always:function(){$progress.hide(),progress=0}}},UploadField=function($field,set_objection){var $input=$field.children(".upload-input"),$trigger=$field.children(".upload-trigger"),$cancel=$fie
 ld.children(".upload-cancel"),$clear=$field.children(".upload-clear"),$file=$field.children(".upload-file-input"),upload=null,objection=null,progress=UploadProgress($field.children(".upload-
 progress")),status=UploadStatusMessage($field.children(".upload-display")),ui={start:function(){void 0!==set_objection&&(objection=$.Deferred(),set_objection(function(){if("pending"==objection.state()){var $dialog=$('<div title="Uploading file">Please wait until the upload is finished ...</div>');return $dialog.dialog({modal:!0,closeOnEscape:!1,resizable:!1,draggable:!1,beforeClose:function(){return!1}}),objection.always(function(){$dialog.dialog("close").remove(),objection=null})}return null})),$file.hide(),$cancel.show()},done:function(){$input.attr("value",$field.data("upload-identifier"))},always:function(){null!==objection&&objection.resolve(),$file.show(),$cancel.hide(),upload=null}},get_upload=function(create){return null==upload&&create&&(upload=UploadForm($file,$field.data("uploa
 d-identifier"),$field.data("upload-url")),upload.register(status),upload.register(progress),upload.register(ui)),upload};$trigger.remove(),$cancel.on("click",function(){var upload=get_upload
 ();null!==upload&&upload.cancel()}),$clear.on("click",function(){var upload=get_upload(!0);upload.cancel(),$input.attr("value","")}),$file.on("change",function(){upload=UploadForm($file,$field.data("upload-identifier"),$field.data("upload-url")),upload.register(status),upload.register(progress),upload.register(ui),upload.start()})};$(document).on("loadwidget-smiform",".form-fields-container",function(event,data){$(this).find(".upload-file").each(function(){UploadField($(this),data.set_objection)}),event.stopPropagation()})})(jQuery,jsontemplate);
\ No newline at end of file
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.