From 8e8ec1523f039231d5ceab9289dd7159610602d8 Mon Sep 17 00:00:00 2001 From: Erlend Dalen Date: Mon, 27 May 2013 12:49:25 +0200 Subject: [PATCH] Issue #25: Filter on the current site. Making sure that the image plugin use the site filter so that only images that are added to the current site can be used. --- media_tree/admin/filenode_admin.py | 6 +- media_tree/contrib/cms_plugins/cms_app.py | 9 + .../media_tree_image/cms_plugins.py | 7 + .../cms_plugins/media_tree_image/models.py | 8 + media_tree/contrib/views/detail/__init__.py | 2 + media_tree/fields.py | 2 +- media_tree/manage.py | 9 + media_tree/middleware.py | 7 + media_tree/models.py | 4 +- .../lib/swfupload/core changelog.txt | 315 ++++ .../lib/swfupload/documentation/index.html | 1332 +++++++++++++ .../swfupload/plugins/SWFObject License.txt | 4 + .../swfupload/plugins/swfupload.cookies.js | 53 + .../lib/swfupload/plugins/swfupload.proxy.js | 96 + .../lib/swfupload/plugins/swfupload.queue.js | 98 + .../lib/swfupload/plugins/swfupload.speed.js | 346 ++++ .../lib/swfupload/swfupload license.txt | 12 + .../swfupload_fp10/AsyncJPEGEncoder.as | 761 ++++++++ .../swfupload_fp10/EncodeCompleteEvent.as | 28 + .../swfupload/swfupload_fp10/ExternalCall.as | 144 ++ .../lib/swfupload/swfupload_fp10/FileItem.as | 126 ++ .../swfupload/swfupload_fp10/ImageResizer.as | 203 ++ .../swfupload_fp10/ImageResizerEvent.as | 20 + .../swfupload_fp10/MultipartURLLoader.as | 352 ++++ .../swfupload/swfupload_fp10/PNGEncoder.as | 295 +++ .../swfupload_fp10/SWFUpload FP10.as3proj | 81 + .../lib/swfupload/swfupload_fp10/SWFUpload.as | 1679 +++++++++++++++++ .../lib/swfupload/swfupload_fp10/deploy.bat | 4 + .../swfupload/swfupload_fp10/jpegencoder.swc | Bin 0 -> 167837 bytes .../lib/swfupload/swfupload_fp10/swfupload.js | 1132 +++++++++++ .../swfupload/swfupload_fp10/swfupload.swf | Bin 0 -> 186661 bytes .../swfupload/swfupload_fp9/ExternalCall.as | 140 ++ .../lib/swfupload/swfupload_fp9/FileItem.as | 123 ++ .../swfupload_fp9/SWFUpload FP9.as3proj | 81 + .../lib/swfupload/swfupload_fp9/SWFUpload.as | 1572 +++++++++++++++ .../lib/swfupload/swfupload_fp9/deploy.bat | 4 + .../swfupload/swfupload_fp9/swfupload_fp9.swf | Bin 0 -> 13333 bytes .../change_list_results_thumbnails.html | 41 + .../templates/media_tree/filenode/image.html | 13 + media_tree/urls.py | 19 + media_tree/views.py | 17 + 41 files changed, 9141 insertions(+), 4 deletions(-) create mode 100644 media_tree/contrib/cms_plugins/cms_app.py create mode 100644 media_tree/manage.py create mode 100644 media_tree/middleware.py create mode 100644 media_tree/static/media_tree/lib/swfupload/core changelog.txt create mode 100644 media_tree/static/media_tree/lib/swfupload/documentation/index.html create mode 100644 media_tree/static/media_tree/lib/swfupload/plugins/SWFObject License.txt create mode 100644 media_tree/static/media_tree/lib/swfupload/plugins/swfupload.cookies.js create mode 100644 media_tree/static/media_tree/lib/swfupload/plugins/swfupload.proxy.js create mode 100644 media_tree/static/media_tree/lib/swfupload/plugins/swfupload.queue.js create mode 100644 media_tree/static/media_tree/lib/swfupload/plugins/swfupload.speed.js create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload license.txt create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/AsyncJPEGEncoder.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/EncodeCompleteEvent.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ExternalCall.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/FileItem.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizer.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizerEvent.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/MultipartURLLoader.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/PNGEncoder.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload FP10.as3proj create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/deploy.bat create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/jpegencoder.swc create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/swfupload.js create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp10/swfupload.swf create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp9/ExternalCall.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp9/FileItem.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload FP9.as3proj create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload.as create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp9/deploy.bat create mode 100644 media_tree/static/media_tree/lib/swfupload/swfupload_fp9/swfupload_fp9.swf create mode 100644 media_tree/templates/admin/media_tree/filenode/change_list_results_thumbnails.html create mode 100644 media_tree/templates/media_tree/filenode/image.html create mode 100644 media_tree/urls.py create mode 100644 media_tree/views.py diff --git a/media_tree/admin/filenode_admin.py b/media_tree/admin/filenode_admin.py index 6493f45..acd0980 100644 --- a/media_tree/admin/filenode_admin.py +++ b/media_tree/admin/filenode_admin.py @@ -11,7 +11,7 @@ # TODO: Ordering of tree by column (within parent) should be possible # TODO: Refactor SWFUpload stuff as extension. This would require signals calls # to be called in the FileNodeAdmin view methods. - +from django.contrib.sites.models import Site from media_tree.fields import FileNodeChoiceField from media_tree.models import FileNode @@ -132,6 +132,10 @@ def __init__(self, *args, **kwargs): # http://stackoverflow.com/questions/1618728/disable-link-to-edit-object-in-djangos-admin-display-list-only self.list_display_links = (None, ) + def queryset(self, request): + qs = super(FileNodeAdmin, self).queryset(request) + return qs.filter(site=Site.objects.get_current()) + def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name == 'parent' and issubclass(db_field.rel.to, FileNode): # overriding formfield_for_dbfield, thus bypassign both Django's and mptt's diff --git a/media_tree/contrib/cms_plugins/cms_app.py b/media_tree/contrib/cms_plugins/cms_app.py new file mode 100644 index 0000000..1a36eb4 --- /dev/null +++ b/media_tree/contrib/cms_plugins/cms_app.py @@ -0,0 +1,9 @@ +from cms.app_base import CMSApp +from cms.apphook_pool import apphook_pool +from django.utils.translation import ugettext_lazy as _ + +class MediaTreeApphook(CMSApp): + name = _("Media Tree") + urls = ["media_tree.urls"] + +apphook_pool.register(MediaTreeApphook) diff --git a/media_tree/contrib/cms_plugins/media_tree_image/cms_plugins.py b/media_tree/contrib/cms_plugins/media_tree_image/cms_plugins.py index 5a0efa7..e64dffe 100644 --- a/media_tree/contrib/cms_plugins/media_tree_image/cms_plugins.py +++ b/media_tree/contrib/cms_plugins/media_tree_image/cms_plugins.py @@ -1,3 +1,4 @@ +from django.contrib.sites.models import Site from media_tree.contrib.cms_plugins.media_tree_image.models import MediaTreeImage from media_tree.contrib.cms_plugins.forms import MediaTreePluginFormBase from media_tree.contrib.views.detail.image import ImageNodeDetailMixin @@ -9,9 +10,15 @@ from django.utils.translation import ugettext_lazy as _ # TODO: Solve image_detail with get_absolute_url()? +from media_tree.models import FileNode class MediaTreeImagePluginForm(MediaTreePluginFormBase): + + def __init__(self, *args, **kwargs): + super(MediaTreeImagePluginForm, self).__init__(*args, **kwargs) + self.fields['node'].queryset = FileNode.objects.filter(site=Site.objects.get_current()) + class Meta: model = MediaTreeImage diff --git a/media_tree/contrib/cms_plugins/media_tree_image/models.py b/media_tree/contrib/cms_plugins/media_tree_image/models.py index 9da1cd9..c41bd2c 100644 --- a/media_tree/contrib/cms_plugins/media_tree_image/models.py +++ b/media_tree/contrib/cms_plugins/media_tree_image/models.py @@ -1,3 +1,4 @@ +from django.contrib.sites.models import Site from media_tree import media_types from media_tree.contrib.cms_plugins import settings as plugins_settings from media_tree.fields import FileNodeForeignKey, DimensionField @@ -6,6 +7,11 @@ from django.utils.translation import ugettext_lazy as _ +class MediaTreeImageSiteManager(models.Manager): + def get_query_set(self): + return super(MediaTreeImageSiteManager, self).get_query_set().filter(node__site=Site.objects.get_current()) + + class MediaTreeImage(CMSPlugin): node = FileNodeForeignKey(allowed_media_types=(media_types.SUPPORTED_IMAGE,), verbose_name=_('file')) link_type = models.CharField(_('link type'), max_length=1, blank=True, null=True, default=plugins_settings.MEDIA_TREE_CMS_PLUGIN_LINK_TYPE_DEFAULT, choices=plugins_settings.MEDIA_TREE_CMS_PLUGIN_LINK_TYPE_CHOICES, help_text=_('Makes the image a clickable link.')) @@ -16,5 +22,7 @@ class MediaTreeImage(CMSPlugin): height = DimensionField(_('max. height'), null=True, blank=True, help_text=_('You can leave this empty to use an automatically determined image height.')) render_template = models.CharField(_("template"), max_length=100, choices=None, blank=True, null=True, help_text=_('Template used to render the image.')) + objects = MediaTreeImageSiteManager() + def __unicode__(self): return self.node.__unicode__() diff --git a/media_tree/contrib/views/detail/__init__.py b/media_tree/contrib/views/detail/__init__.py index 2a83cb0..99bbc68 100644 --- a/media_tree/contrib/views/detail/__init__.py +++ b/media_tree/contrib/views/detail/__init__.py @@ -1,3 +1,4 @@ +from django.contrib.sites.models import Site from media_tree.models import FileNode from media_tree.contrib.views.mixin_base import PluginMixin from django.views.generic.detail import DetailView @@ -75,6 +76,7 @@ def get_object(self, queryset=None): def get_queryset(self, *args, **kwargs): queryset = super(FileNodeDetailView, self).get_queryset(*args, **kwargs) + queryset = queryset.filter(node__site=Site.objects.get_current()) kwargs = {} if self.filter_node_types: kwargs['node_type__in'] = self.filter_node_types diff --git a/media_tree/fields.py b/media_tree/fields.py index 774882f..1acbc4e 100644 --- a/media_tree/fields.py +++ b/media_tree/fields.py @@ -31,7 +31,7 @@ def __init__(self, allowed_node_types=None, allowed_media_types=None, allowed_ex self.allowed_node_types = allowed_node_types self.allowed_media_types = allowed_media_types self.allowed_extensions = allowed_extensions - kwargs['level_indicator'] = level_indicator; + kwargs['level_indicator'] = level_indicator if not kwargs.has_key('widget'): kwargs['widget'] = self.widget diff --git a/media_tree/manage.py b/media_tree/manage.py new file mode 100644 index 0000000..feefca0 --- /dev/null +++ b/media_tree/manage.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +import os, sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "datakortet.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/media_tree/middleware.py b/media_tree/middleware.py new file mode 100644 index 0000000..d767165 --- /dev/null +++ b/media_tree/middleware.py @@ -0,0 +1,7 @@ +from django.conf import settings + +class SessionPostMiddleware(object): + + def process_request(self, request): + if not request.COOKIES.has_key(settings.SESSION_COOKIE_NAME) and request.POST.has_key(settings.SESSION_COOKIE_NAME): + request.COOKIES[settings.SESSION_COOKIE_NAME] = request.POST[settings.SESSION_COOKIE_NAME] \ No newline at end of file diff --git a/media_tree/models.py b/media_tree/models.py index 70ee009..0b4af86 100644 --- a/media_tree/models.py +++ b/media_tree/models.py @@ -87,7 +87,7 @@ def filter(self, *args, **kwargs): """ if 'path' in kwargs: kwargs = self.get_filter_args_with_path(False, **kwargs) - return super(FileNodeManager, self).filter(*args, **kwargs) + return super(FileNodeManager, self).filter(site=Site.objects.get_current()).filter(*args, **kwargs) def exclude(self, *args, **kwargs): """ @@ -98,7 +98,7 @@ def exclude(self, *args, **kwargs): """ if 'path' in kwargs: kwargs = self.get_filter_args_with_path(False, **kwargs) - return super(FileNodeManager, self).exclude(*args, **kwargs) + return super(FileNodeManager, self).filter(site=Site.objects.get_current()).exclude(*args, **kwargs) def get(self, *args, **kwargs): """ diff --git a/media_tree/static/media_tree/lib/swfupload/core changelog.txt b/media_tree/static/media_tree/lib/swfupload/core changelog.txt new file mode 100644 index 0000000..15695f9 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/core changelog.txt @@ -0,0 +1,315 @@ + * SWFUpload Core, January 2009, www.swfupload.org, swfupload.googlecode.com + + * --------- Version 2.5.0----------- + * + Added Client Side thumbnail generation and uploading and related settings + * -- Thanks to matzes for getting standard HTTP uploading to work + * + Added Alchemy based JPEG Encoder (which is much much faster) + * = Fixed double encoding of settings issue + * = Improved movieElement grabbing + * - Removed settings clone as it is unreliable + * = Fixed speed plugin so it checks for a file before extending it + * + Progress event manually fired for 100% if not fired by Flash + * + mouseClick, mouseOver, mouseOut event and handler settings added + * - Removed BUTTON_ACTION.JAVASCRIPT constant and button_action_handler. These are replaced by the mouseClick event + * + Added proxy plugin + * - Removed swfobject plugin + * + Integrated swfobject with core code + * + Added swfupload_preload_handler event/setting + * + Added swfupload_load_failed_handler event/setting + * + Added flash9_url to support SWF split to support Flash Player 9 and Flash Player 10 + * = Fixed param encoding issue + * = Fixed button cursor when no button image was loaded + + + * --------- Version 2.2.1----------- + * + Added requeueUpload and updated documentation + * + Added button_action type of JAVASCRIPT and a buttonAction event to trigger a JavaScript function when the Flash Button is clicked + * = Updated destroy method to make sure the testExternalInterface timer is cleared + * = Also fixed destroy so the element is removed in non-IE browsers + * = Also fixed destroy method to call cleanUp method rather than duplicating the code + * = Fixed scope issue in cleanUp method. + * = Fixed bug in Queue Plugin that prevented the queueComplete event from firing + * + The place holder element is now replaced with the same type of flow element (div for block, span for inline) + + + * --------- Version 2.2.0.1----------- + * - Removed requeueUpload due to bugs/poor testing + * = Fixed namespace conflict that broke stopUpload + + * --------- Version 2.2.0----------- + * + Added button_placeholder setting that accepts a DOM element. + * + Added ability to requeue any file (including some improvement to internal queue state tracking) + * + UploadErrors caused by a missing upload_url now causes the file to be requeued automatically + * + Added preserve_relative_urls setting + * + SWFUpload now converts relative URLs to absolute URLs to avoid issues with Flash Player interpreting it differently on some clients + * + Added assume_success_timeout setting which allows uploadSuccess to be called after a timeout if, for some reason Flash ignores the server's response + * An additional parameter has been added to the uploadSuccess event to indicate whether a response was received or success was assumed. + + * --------- Version 2.2.0 Beta 2 ~ 5----------- + * = Fixed a Queue Limit bug + * + Improved internal event handling code so uploadSuccess fires even when not content is returned from the server (*woot*, except for Macs) + * = Fixed issues in Destroy + * = Fixed issues with Queue Plugin + * + Added periodic checks of the ExternalInterface + * + Improved IE memory leak prevention code + * + Added Speed Plugin + * = Updated Queue Plugin for better multi-plugin compatibility + + * --------- Version 2.2.0 Beta 1----------- + * + Added Flash Player 10 Support + * = Added setting for defining a button image + * = Added setting for defining button text + * = Added setting for defining button width, height and padding + * = Added setting for defining what element the Flash Movie should replace + * = Added setting for defining flash wmode + * = Added setting for defining the mouse cursor + * + Added prevent_swf_caching setting as a work-around for issues in Avant Browser (and other IE based browser) + * + Added setting for accepting HTTP Status codes other than 200 as successful + * + Added parameter to cancelUpload that allows the uploadError event for cancelled uploads to be supressed + * + Added pro-active memory leak fix for IE and fixed problems with the destroy function (credits to steffen for patches and testing) + * + Replaced callFlash with CallFunction (using the internal function that Flash uses). Based on code from MooTools. + * = Fixed bug in the Queue plugin that breaks startUpload when passing a file id. + * + Updated Queue plugin to stop the queue if false is returned from the uploadStart handler. + * = Fixed small issues in SWFObject plugin + * = Fixed issue with ExternalInterface string escaping + * - Dropped Graceful Degradation Plugin + * - Dropped v1.0.2 Plugin + * - Dropped Flash Player 8 support + + * --------- Version 2.1.0 ----------- + * = Fixed GET values so they are escaped properly + * + Added destroy function + * = Added exception handling around browse() calls + * = Minor code cleanup + * + Split Core and Demos + + * --------- Version 2.1.0 Beta 2----------- + * = Fixed bug in XHTML fix where it wasn't split correctly by Flash + * = Fixed file params "undefined" in debug output + * + Added requeue_on_error settings so HTTP/IO/Security errors requeue the file instead of discarding it. + This will affect the queue plugin (if an error is occurring the file will be reuploaded and reuploaded). + * = Fixed HTTP/IO error behavior. We'll see how this goes Flash 9 is supposed to call HTTPError followed by an IO error but I suspect they come out of orde sometimes. + * = Fixed invalid characters in file param names. Worked around flash bug by escaping the names. Should be transparent to devs. + * = Fixed missing upload URL logic so it fires consistently + * = Fixed file params not being sent when useQueryString is true + * + Added SWFObject plugin and demo. + * + Added CookieBug demo to demonstrate what they bug really is all about. + * + Added VB.Net version of the Application Demo + + + * --------- Version 2.1.0 Beta 1----------- + * + Added allowScriptAccess="always" to the embed/object elements so the SWF can be served from different domains. + * = Fixed a type-o in the debug output that prevented the instance id (movieName) from displaying. - Thx Joel + * + Rewrote SWFUpload.js for better code reuse based on sample code from batiste.bieler (thanks!!!) + * + Added queueComplete event to the Queue Plugin + * + Added Simple Upload demo + * = JSLinted all the JavaScript code + * + Added use_query_string setting (and setUseQueryString function) that forces post_param and file_param values to be sent on the query_string instead of the post (for Flash 9 version) + * = Fixed file.type and date properties so a default value is provided (rather than null) when no value is provided by flash. + * = Fixed misc bugs in the demos + * = Fixed ExternalInterface calls being made available for Flash 9 versions <9.0.28 which aren't supported + * + Fixed use of & producing invalid XHTML in the and tags. + * - Removed the use of the embed tag (using the sample from the Flash Satay method) + * = Updated plugins to work with code rewrite changes. + * = Extracted FileProgress object in to its own file. + * + Added addPostParam and removePostParam functions + + * --------- Version 2.0.2 ----------- + * = Fixed a bug where post params could not be added to the current file (because it is removed from the queue when it becomes current) + * = Fixed a conversion error when converting kilobytes to bytes in the file size check + * = Fixed a problem in the documentation that said the file_size_limit was bytes when it is actually kilobytes + * + Added formatting to the documentation, a table of contents, and details for each setting . + * + Added units for file_size_limit setting. The setting understands B, KB, MB, GB. Default is KB. + * + Added a check for the ExternalInterface functions in flashReady so SWFUpload will not fire the loaded event if those are not available. + + * --------- Version 2.0.1 ----------- + * = Fixed a bug where zero-byte files would stop file the queuing process. + * = Finished updating Features Demo + * + Added GetFileByIndex(i) that gets a JavaScript File Object. The index never changes. Finished files continue to be available. + * The JavaScript GetFile function will accept a file_id or an index. + * + Added CheckFileSize constants to replace the magic numbers + * + Added some code in an attempt to fix "Script is running slowly" error messages + * = Better cleanup of FileReference objects + + * --------- Version 2.0 ----------- + * + Re-created SWFUpload in Actionscript v2 for Flash 8 compatibility. Flash 8 loses POST and Server Data features. The two versions are otherwise fully compatible. + * Flash 8 uses URL to pass post_params/file_params. + * = Changed uploadStart event so it's part of the setTimeout/eventQueue workaround. This allows Flash functions to be called from uploadStart. + * = Renamed uploadComplete to uploadSuccess and fileComplete to uploadComplete. All started uploads call uploadComplete (even if cancelled or stopped). + * = Changed startUpload validation failure behavior. Rather than cancelling the upload the file is now requeued. Devs can cancel + * or do whatever the need to in uploadError to handle the problem. + * = Fixed fileQueueLimit/fileUploadLimit logic so it works correctly. + * = Moved the upload request building to a later point so that the post params and file params can be updated in uploadStart. + * - Removed the last of the UI stuff (ui_container, degraded_container). + * + Started development on Plug-ins. Graceful Degradation, v1.0.2, Cookies, Queue Handling + * = Fixed missing file_status field in FileItem. + * + Added modificationDate to FileItem (file object) + * + Added setStats function that lets you change the file upload count, etc. This will give more power over the queue limits. Not well tested. + * = Renamed compeleted_uploads to successful_uploads in getStats object + * + Added in_progress to getStats object + + * --------- Revision 7.0 beta 3 ----------- + * + Added an "event queue". Events are added to an array and executeEvent is called on a setTimeout. This prevents out of order issues that occur + * in the Safari browser. + * + Added a check for the UPLOAD_COMPLETE_DATA event constant which only became available in Flash Player 9.0.28. This + * fixes the Flash Version detection (Flash Object Detection) which was accepting Flash Player 9 versions before 9.0.28. + * - Removed old code block that was missed when moving from a Flash Timer to the JavaScript timeout (caused certain cancel events to be called twice) + * = Change ShowUI to the swfUploadLoaded event which will hopefully make it more clear that this is an overrideable event + * = Changed flashReady to behave like the other events (uses setTimeout and the Event Queue). + + * --------- Revision 7.0 beta 2 ----------- + * = Changed ERROR_CODE_FILE_NOT_FOUND to ERROR_CODE_FILE_ID_NOT_FOUND + * + Grouped the error code constants in objects for queue errors and upload errors. + * + Added an UPLOAD_STOPPED error code. + * = Changed Event calling method (using Timer) in Flash. Timer is no longer called + * instead setTimeout is called in JavaScript. This includes a change to the + * JavaSCript design so the Event methods are not directly overridden but stored + * internally and called if defined (with a setTimeout). This is an effort + * be more compatible with the current Flash Player on Linux + * = Changed the parameter order for the fileQueueError and uploadError events so the fileObj is first, like other events. + * + Added an empty JavaScript object (customSettings) where users can store settings associated with the instance. + * + Worked around an escaping bug in the ExternalInterface library by escaping all backslashes in out-going strings. + * = Updated all the demos. + + * --------- Revision 7.0 beta 1 ----------- + * = Redesigned the Event Chain + * - Removed much of the queue concepts + * - Removed the fileValidation events. This can be done in the new uploadStart event + * - Removed beginUploadOnQueue feature. This can be done in the new dialogComplete event. + * - Removed use_server_data. This is now always on. + * + Added functions for retrieving queue stats (number of files uploaded, queued, errors, etc) + * + Added a file status property to the FileObject. This indicates, uploaded, error, waiting. + * + Added a single file browser (user cannot select multiple files) + * + Fixed bug (hopefully) caused if Flash call to JavaScript and in the callback JavaSCript calls to Flash + * This only place this does not apply is to uploadStart. If you call in to Flash from uploadStart use a setTimeout to do it. + + * --------- Revision 6.2 ----------- + * + Added API calls for changing all the changeable settings dynamically + * = Fixed a bug in FileComplete event handler (in the SWF) that caused an error in Debug Players + * and prevent the event from being called + * + Added a setting (use_server_data_event) to indicate whether FileComplete or ServerData should be called. + * The SWFUpload architecture requires that only one file upload success event can be called. + * = Updated all the Demos, especially the Features Demo and the Forms Demo + + + * --------- Revision 6 ----------- + * - Removed the call to setUploadSettings in flashReady. This was a left over call that is unnecessary. + * + Finished the parsing of post params during the init stage. This ommision was hidden by the call to setUploadSettings. + * - Removed the flash_target_id setting. The Flash file should only ever be added to the body tag. + * + Fixed (hopefully for good) another SWF race condition. IE executes the SWF very very early. The this.movieElement value should never be referenced. + * The movie Element should always be retrieved using this.getMovieElement(). + + * --------- Revision 6 ----------- + * + Ported to ActionScript 3. Revision 6 requires Flash Player 9. + * = Fixed bug caused when cancelling single files. Would break any function that searched for a file_id. + * - Removed the automatic cookie sending setting. Devs should just pass the value they want to send in the post_params + * - Removed the query params settings (global and file specific). All params should be sent in the post_params + * + Added post_params which adds post values to the file upload post. + * + Added validate_files setting flag which causes the fileValidation event to be called before each file is uploaded. + * + Added fileValidation event. Return false if validation fails and true if validation is successful. + * + Added server_data parameter to the fileComplete event which returns any text that the upload script returns. + * = Updated all demos to work with Revision 6 + * + Added in-code file extension validation. Before a file is queued the extension is checked against the valid extensions. + * Files the have invalid extensions cause the error event to be raised. + * + Added 'file_post_name' setting that allows the post variable name containing the file data to be named something other than 'Filedata' + * = Fixed a race condition in loadFlash where a cached flash movie would execute before this.movieElement could be assigned and loading would fail. + + * --------- Revision 5.2 ----------- + * = A little more code cleaning and variable renaming + * + Changed from an array queue to a FIFO queue. This eliminates the "current_index" and + * should reduce some memory usage. + * + Added out of order file uploading. Call StartUpload(/file_id/). + * + Added custom query_string parameters in addition to the cookies + * + Added the ability to modify the URL, cookies, params and send to flash + * + Added per file query_string params + * + Added files queued limit. Sometimes you may want the user to only queue one file at a time (or something) + * + Fixed limits so a zero(0) value means unlimited. + + * --------- Revision 5 ------------- + * = More code cleaning. Ported SWF to FlashDevelop. (Since my Flash Studio trial expired) + * The port to FlashDevelop is a big deal. It significantly changes the code structure + * and could introduce bugs. Also there have been reported issues with the FlashDevelop + * version from swfupload.mammon.se: Doesn't start when reloading in IE. Doesn't start + * in Firefox if the SWF file is visible because of a page scroll. + * + I fixed the Firefox issue by removing the wmode attribute from the embed object. + * + I cannot reproduce the IE issue on my local machine (although I can reproduce it at swfupload.mammon.se) + * + Event Handlers are now attached to the SWFUpload javascript object. The SWF file + * now calls the handlers in the context of the SWFUpload object which means the "this" + * object inside the handler refers to the proper SWFUpload instance. + * + Tested and Fixed upload target cookie attachment + * = Cleaned up / renamed everything for clarity and consistancy + * + File queuing is now subject to the upload limit. If the user attempts to queue more files + * than allowed an error is returned and the files are not queued. + * + Fixed misc bugs and text encodings. + * + Added more debug info for the SWF file. + * + SWF file now obeys the debug setting. + * + Added SetUploadTargetURL function that allows you to "dynamically" change the upload target + * + Added error code for zero byte file uploads which always return an IO error. The files are now rejected + * instead of being uploaded. + + * --------- Revision 4 ------------- + * = Cleaned up code. Added comments. Reorganized. Added more try..catches. Removed old unused methods. + * - Removed the 'create_ui' setting. The UI is now completely up to the developer. + * + Added upload_backend_cookies setting. Can set a string, or array of cookie names. These values will be + * passed as part of the upload_backend url + * + * = Changed QueueComplete event to only fire if at least one file has been successfully uploaded. + * + Added "Stop Upload" feature. + * = Revised the FLA file to clean things up, better handle errors, etc. + * = Fixed a bug where cancelling the first upload would cause the remaining uploads to fire before calling + * "startUpload". This change is in the FLA. + * + * + Fixed a bug in the upload.swf that prevented further file processing after an error is returned. + * + Added uploadLimit variable. Only complete uploads are counted. Once the limit is reached the flash + * movie will not upload any more files. (The ability to select or queue many files is not affected + * by the upload limit) + * + Added cancelQueue and cancelUpload methods. + * + Added ID property to the FileObj in the upload.swf + * + Added Upload and Queue settings + * + Added methods for generating the flash HTML and inserting it into the DOM. + * - Removed SWFObject + * + Updated the upload.swf and added the "flashReady" event. This will only call back + * for Flash 8 and above. With this we don't need a flash version detect script. + * The script initializes the Flash then waits for the Callback to init the UI. + * + Added seperate ui_target, degraded_target, create_ui settings. This allows fine control + * over what parts of the GUI the script displays and hides + * + * + Changed from a Static Class to an Instance (changed code/class structure) + * + Added "flash_version" setting. When set to zero the version check is skipped + * + Added Debug Console. The Instance class can't do document.write. + * = De-obfuscated SWFObject a bit + * - Removed standalone mode. + * + Added "ui_target" setting. When non-blank the link is added. + * + Added "flash_target" setting. When blank the flash is appended to the tag + * = This fixes ASP.Net not allowing the flash to be added to the Form + * + Added error checking to the callSWF method + * + + + * -------- -------- -------- -------- -------- -------- -------- -------- + * SWFUpload 0.7: Flash upload dialog - http://profandesign.se/swfupload/ + * SWFUpload is (c) 2006 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * -------- -------- -------- -------- -------- -------- -------- -------- + + * SWFUpload 0.7: Flash upload dialog - http://profandesign.se/swfupload/ + * + * VERSION HISTORY + * 0.5 - First release + * + * 0.6 - 2006-11-24 + * - Got rid of flash overlay + * - SWF size reduced to 840b + * - CSS-only styling of button + * - Add upload to links etc. + * + * 0.7 - 2006-11-27 + * - Added filesize param and check in SWF + * + * 0.7.1 - 2006-12-01 + * - Added link_mode param for standalone links + * if set to "standalone", createElement("a") won't run. + * - Added link_text param if css isn't needed. + * - Renamed cssClass to css_class for consistency + * + */ diff --git a/media_tree/static/media_tree/lib/swfupload/documentation/index.html b/media_tree/static/media_tree/lib/swfupload/documentation/index.html new file mode 100644 index 0000000..d9d4457 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/documentation/index.html @@ -0,0 +1,1332 @@ + + + + SWFUpload v2 Documentation + + + + + +

SWFUpload v2.5.0 Documentation

+

Table of Contents

+
    +
  1. SWFUpload
  2. +
  3. SWFUpload Version 2
  4. +
  5. Overview
  6. +
  7. Getting Started
  8. +
  9. SWFUpload JavaScript Object +
      +
    1. Constructor
    2. +
    3. Globals and Constants +
        +
      1. instances
      2. +
      3. movieCount
      4. +
      5. QUEUE_ERROR
      6. +
      7. UPLOAD_ERROR
      8. +
      9. FILE_STATUS
      10. +
      11. UPLOAD_TYPE
      12. +
      13. BUTTON_ACTION
      14. +
      15. CURSOR
      16. +
      17. BUTTON_WINDOW_MODE
      18. +
      19. RESIZE_ENCODING
      20. +
      21. onload
      22. +
      +
    4. +
    5. Properties +
        +
      1. customSettings
      2. +
      3. movieName
      4. +
      +
    6. +
    7. Methods +
        +
      1. addSetting (deprecated)
      2. +
      3. getSetting (deprecated)
      4. +
      5. retrieveSetting (removed in v2.1.0)
      6. +
      7. destroy (added in v2.1.0)
      8. +
      9. displayDebugInfo
      10. +
      11. selectFile
      12. +
      13. selectFiles
      14. +
      15. startUpload
      16. +
      17. startResizedUpload (added in v2.5.0)
      18. +
      19. cancelUpload
      20. +
      21. stopUpload
      22. +
      23. requeueUpload
      24. +
      25. getStats
      26. +
      27. setStats
      28. +
      29. getFile
      30. +
      31. getQueueFile (added in v2.5.0)
      32. +
      33. addPostParam
      34. +
      35. removePostParam
      36. +
      37. addFileParam
      38. +
      39. removeFileParam
      40. +
      41. setUploadURL
      42. +
      43. setPostParams
      44. +
      45. setFileTypes
      46. +
      47. setFileSizeLimit
      48. +
      49. setFileUploadLimit
      50. +
      51. setFileQueueLimit
      52. +
      53. setFilePostName
      54. +
      55. setUseQueryString
      56. +
      57. setDebugEnabled
      58. +
      59. setButtonImageURL (added in v2.2.0)
      60. +
      61. setButtonDimensions (added in v2.2.0)
      62. +
      63. setButtonText (added in v2.2.0)
      64. +
      65. setButtonTextStyle (added in v2.2.0)
      66. +
      67. setButtonTextPadding (added in v2.2.0)
      68. +
      69. setButtonDisabled (added in v2.2.0)
      70. +
      71. setButtonAction (added in v2.2.0)
      72. +
      73. setButtonCursor (added in v2.2.0)
      74. +
      +
    8. +
    9. Events +
        +
      1. flashReady
      2. +
      3. swfUploadPreload
      4. +
      5. swfUploadLoadFailed
      6. +
      7. swfUploadLoaded
      8. +
      9. buttonAction
      10. +
      11. fileDialogStart
      12. +
      13. fileQueued
      14. +
      15. fileQueueError
      16. +
      17. fileDialogComplete
      18. +
      19. uploadResizeStart
      20. +
      21. uploadStart
      22. +
      23. uploadProgress
      24. +
      25. uploadError
      26. +
      27. uploadSuccess
      28. +
      29. uploadComplete
      30. +
      31. debug
      32. +
      +
    10. +
    11. SWFUpload Utility Objects +
        +
      1. Settings Object
      2. +
      3. Settings Description
      4. +
      5. Support Object
      6. +
      7. File Object
      8. +
      9. Stats Object
      10. +
      +
    12. +
    +
  10. +
  11. SWFUpload Plug-ins
  12. +
  13. Known Issues
  14. +
+ + +

SWFUpload

+

+ SWFUpload is a client-side file upload tool originally developed by Vinterwebb.se. It uses a combination of Flash and JavaScript to + provide file upload functionality beyond what the basic browser provides with the <input type="file" /> tag. +

+

The main features that SWFUpload provides are:

+

+

    +
  • The ability to select multiple files in the file browser dialog.
  • +
  • AJAX-style uploading without a page refresh.
  • +
  • Upload progress events.
  • +
  • Client-side image resizing
  • +
  • Namespaced classes compatible with other JavaScript libraries (i.e., jQuery, Prototype, etc.).
  • +
  • Flash 9 and Flash 10 support. (Flash 8 support dropped in version 2.2.0)
  • +
+

+

+ SWFUpload is different from other Flash based upload tools because of the philosophy + behind its design. SWFUpload gives developers control by leaving the UI in the browser (as much as possible). + Developers can use XHTML, CSS, and JavaScript to tailor the upload UI to the needs and + style of their site. Upload status updates are made through a set of simple JavaScript events. + The developer uses these events to update the webpage as the file upload progresses. +

+

+ Unfortunately Flash Player 10 has forced us have one button in a flash movie in order to trigger the file browser dialog window. SWFUpload + still empowers the devloper by provding the ability the button and overlay text from JavaScript. +

+ +

SWFUpload v2

+

+ SWFUpload v2 includes new advanced features, improved stability, Flash Player + bug work-arounds and a helpful set of Plug-ins. New features include: +

+

+

    +
  • Flash Player 10 security compatibility.
  • +
  • The ability to send additional POST values with the uploads.
  • +
  • Sending per file POST values.
  • +
  • Complete set of events.
  • +
  • All settings dynamically updateable.
  • +
  • Retrieve result data from the server.
  • +
  • Stop an upload without cancelling.
  • +
  • Upload files in any order.
  • +
  • Multi- & Single-file selection dialogs.
  • +
  • Queue size, files uploaded and file size limits.
  • +
  • Properly handling of zero-byte files.
  • +
  • Pre-upload validation event.
  • +
  • Work-arounds for bugs in Flash and Browser.
  • +
+

+ +

Overview

+

HTML Upload

+

+ The standard HTML upload input box provides a box and a button to the user for selecting a file. + The file is submitted with the form. The entire file must be uploaded before the next + page is displayed. No pre-upload validation can be made on the file (e.g., file size limits or valid extensions). + Very little feedback is given to the user while the file uploads. +

+

+ The usage pattern for standard HTML uploads is simple, straight forward, and supported by nearly all browser. +

+

SWFUpload

+

+ SWFUpload uses a Flash movie to handle file selection and upload. A customizable button is displayed by the + Flash movie that activates Flashes advanced file selection dialog window. + The file selection dialog is configured to allow the user select to a single file or multiple files. + The file types can be restricted so users only select the appropriate files (e.g., *.jpg;*.gif). +

+

+ Once selected each file is validated and queued. As the file is uploaded by Flash + several JavaScript events are fired which the developer handles in order to update the page's UI allowing you to provide + upload status and error messages in real-time. +

+

+ The uploaded file is submitted separately from the rest of the page and form. Each file is uploaded + individually keeping the server-side upload handling script simple. Since Flash is providing the upload + service the page does not have to be submitted or reloaded. The usage pattern for SWFUpload is more + like that of an AJAX application than that of a standard HTML form. The page's form will be processed + separately from the file upload. +

+

Getting Started

+

SWFUpload is not a drag & drop upload control. It requires knowledge of JavaScript and the DOM. Several demos are available that + show some of the things SWFUpload is capable of and how to accomplish common tasks.

+ +

SWFUpload consists of 4 pieces:

+
    +
  1. Initialization and Settings (JavaScript)
  2. +
  3. JavaScript library: SWFUpload.js
  4. +
  5. Flash Control: swfupload.swf & swfupload_fp9.swf
  6. +
  7. The Event Handlers (JavaScript)
  8. +
+ +

Most problems implementing SWFUpload are caused by incorrect settings, poorly built event handlers, Flash/Browser Bugs, or server configuration.

+ +

Initialization and Settings

+

SWFUpload must be initialized on the page. This is commonly done in the window.onload event. The SWFUpload constructor takes a settings object. + The settings object can be passed directly to the constructor as an object literal.

+

A reference to the initialized SWFUpload object should be kept as it is needed to start uploads and to control other aspects of SWFUpload.

+ +

Example: Initializing SWFUpload with an object literal

+ + var swfu; + +window.onload = function () { + swfu = new SWFUpload({ + upload_url : "http://www.swfupload.org/upload.php", + flash_url : "http://www.swfupload.org/swfupload.swf", + flash9_url : "http://www.swfupload.org/swfupload_fp9.swf", + file_size_limit : "20 MB" + }); +}; + +

Example: Initializing SWFUpload with a settings object stored in a variable

+ + var swfu; + +window.onload = function () { + var settings_object = { + upload_url : "http://www.swfupload.org/upload.php", + flash_url : "http://www.swfupload.org/swfupload.swf", + flash9_url : "http://www.swfupload.org/swfupload_fp9.swf", + file_size_limit : "20 MB" + }; + + swfu = new SWFUpload(settings_object); +}; + +

JavaScript library

+

The JavaScript library file (swfupload.js) must be included on the page where the user will upload files.

+

Once a SWFUpload object has been created access to several functions become available which allow the developer to control SWFUpload.

+ +

Example: Adding SWFUpload.js to a page

+ + +<script type="text/javascript" src="http://www.swfupload.org/swfupload.js"></script> + + +

Example: Initializing SWFUpload with the required settings.

+ var swfu = new SWFUpload({ + upload_url : "http://www.swfupload.org/upload.php", + flash_url : "http://www.swfupload.org/swfupload.swf", + flash9_url : "http://www.swfupload.org/swfupload_fp9.swf", + button_placeholder_id : "spanSWFUploadButton" +}); + + +

Flash Control

+

The SWFUpload JavaScript library dynamically loads the Flash Control (swfupload.swf/swfupload_fp9.swf).

+ +

The location of the Flash Control file must be provided in the SWFUpload settings object during setup.

+ +

The Flash Control is a small Flash Movie that handles file browsing, validation and upload. It appears on the page as + a button and communicates with JavaScript to notify the browser of upload status and other events.

+ +

New ActionScript syntax and classes require us to target Flash Player 10 and Flash Player 9 separately. SWFUpload will detect the installed version of flash and load the appropriate Flash Control.

+ +

Use the flash_url and flash9_url settings to defined custom locations and file names for the swfupload.swf and swfupload_fp9.swf files.

+ +

The Event Handlers

+ +

Developers must create a set of JavaScript functions that handle SWFUpload events. These functions are called as different important events occur.

+ +

By handling the SWFUpload events developers can provide feedback regarding the upload progress, error messages, and upload completion. Developers should not + overwrite functions stored in SWFUpload.prototype. Instead create your own set of functions and pass references to them in the settings object.

+ +

Example: SWFUpload event handlers and initialization.

+ + // The uploadStart event handler. This function variable is assigned to upload_start_handler in the settings object +var myCustomUploadStartEventHandler = function (file) { + var continue_with_upload; + + if (file.name === "the sky is blue") { + continue_with_upload = true; + } else { + continue_with_upload = false; + } + + return continue_with_upload; +}; + +// The uploadSuccess event handler. This function variable is assigned to upload_success_handler in the settings object +var myCustomUploadSuccessEventHandler = function (file, server_data, receivedResponse) { + alert("The file " + file.name + " has been delivered to the server. The server responded with " + server_data); +}; + +// Create the SWFUpload Object +var swfu = new SWFUpload({ + upload_url : "http://www.swfupload.org/upload.php", + flash_url : "http://www.swfupload.org/swfupload.swf", + flash9_url : "http://www.swfupload.org/swfupload_fp9.swf", + file_size_limit : "200 MB", + + upload_start_handler : myCustomUploadStartEventHandler, + upload_success_handler : myCustomUploadSuccessEventHandler +}); + + +

SWFUpload JavaScript Object

+ +

Constructor

+ +

SWFUpload(settings object)

+

Returns: A SWFUpload instance

+ var swfupload_instance = new SWFUpload(settings_object); + +

Globals and Constants

+

+ Several globals and constants are associated with the SWFUpload object. These are useful for + advanced SWFUpload applications and for handling errors. These are considered read-only. +

+

SWFUpload.instances

+

SWFUpload.instances is an object containing a reference to each SWFUpload instance loaded on a page. The Flash Player + relies on this object in order to call the correct event handlers. SWFUpload.instances indexes by the movieName property.

+ +

SWFUpload.movieCount

+

SWFUpload.movieCount is a global that tracks the number on SWFUpload instances that have been created and helps to ensure each movie is + given a unique movieName.

+ +

SWFUpload.QUEUE_ERROR

+

SWFUpload.QUEUE_ERROR is a simple object that contains Queue Error code constants. It is generally used to determine which error code was sent + in the fileQueueError event.

+ +
    +
  • QUEUE_LIMIT_EXCEEDED - indicates that the user has attempted to queue more files that is allowed by the settings. Once files have been updated and removed from the queue the user is again allowed to queue additional files.
  • +
  • FILE_EXCEEDS_SIZE_LIMIT - indicates the selected file is larger than is allwed by the file_size_limit.
  • +
  • ZERO_BYTE_FILE - indicates that the selected file is empty. The Flash Player cannot handle empty files and the file is rejected. Windows Shortcut files may also trigger this error.
  • +
  • INVALID_FILETYPE - The selected file's extension does not match a valid extension from the file_types setting. User's can circumvent the file_types restriction by manually entering a file name.
  • +
+ +

SWFUpload.UPLOAD_ERROR

+

SWFUpload.UPLOAD_ERROR is a simple object that contains Upload Error code constants. It is generally used to determine which error code was sent + in the uploadError event.

+ +
    +
  • HTTP_ERROR - The file upload was attempted but the server did not return a 200 status code.
  • +
  • MISSING_UPLOAD_URL - The upload_url setting was not set.
  • +
  • IO_ERROR - Some kind of error occurred while reading or transmitting the file. This most commonly occurs when the server unexpectedly terminates the connection.
  • +
  • SECURITY_ERROR - The upload violates a security restriction. This error is rare.
  • +
  • UPLOAD_LIMIT_EXCEEDED - The user has attempted to upload more files than is allowed by the file_upload_limit setting.
  • +
  • UPLOAD_FAILED - The attempt to initiate the upload caused an error. This error is rare.
  • +
  • SPECIFIED_FILE_ID_NOT_FOUND - A file ID was passed to startUpload but that file ID could not be found.
  • +
  • FILE_VALIDATION_FAILED - False was returned from the uploadStart event
  • +
  • FILE_CANCELLED - cancelUpload was called
  • +
  • UPLOAD_STOPPED - stopUpload was called.
  • +
  • RESIZE_ERROR - An error occurred resizing an image
  • +
+ +

SWFUpload.FILE_STATUS

+

SWFUpload.FILE_STATUS is a simple object that contains File Status code constants. It can be used to check the file status property on the File object.

+ +
    +
  • QUEUED - indicates the this file is waiting in the queue.
  • +
  • IN_PROGRESS - indicates that this file is currently being uploaded.
  • +
  • ERROR - indicates that this file caused a queue or upload error.
  • +
  • COMPLETE - indicates that this file was successfully transmitted to the server.
  • +
  • CANCELLED - indicates that this file was cancelled by a call to cancelUpload.
  • +
+ +

SWFUpload.UPLOAD_TYPE

+

SWFUpload.UPLOAD_TYPE is a simple object that contains Upload Type constants. It can be used to check the upload type property on the File object.

+ +
    +
  • NORMAL - a normal SWFUpload upload
  • +
  • RESIZED - a resized image upload. The data was not sent as an HTTP upload but as a simple post.
  • +
+ +

SWFUpload.BUTTON_ACTION

+

SWFUpload.BUTTON_ACTION is a simple object that contains button action code constants. It is used with the button_action setting to set the behavior of the Flash based file dialog button.

+ +
    +
  • SELECT_FILE - when the Flash button is clicked the file dialog will only allow a single file to be selected. The mouseClick event is not fired in this case.
  • +
  • SELECT_FILES - when the Flash button is clicked the file dialog allows multiple files to be selected. The mouseClick event is not fired in this case.
  • +
  • START_UPLOAD - when the Flash button is clicked the first queued file will be uploaded. The mouseClick event is not fired in this case.
  • +
  • NONE - when the Flash button is clicked the mouseClick event will be fired.
  • +
  • JAVASCRIPT - same as the NONE action. This value is deprecated.
  • +
+ +

SWFUpload.CURSOR

+

SWFUpload.CURSOR is a simple object that contains button cursor code constants. It is used with the button_cursor setting to set the mouse cursor when hovering over the Flash button.

+ +
    +
  • ARROW - the cursor will display as an arrow pointer
  • +
  • HAND - the cursor will display has a finger/hand pointer
  • +
+ +

SWFUpload.WINDOW_MODE

+

SWFUpload.WINDOW_MODE is a simple object that contains button movie wmode parameter constants. It is used to tell the browser how to display the Flash Movie.

+

The some WINDOW_MODE/WMODE settings can cause problems in some browsers. See the Known Issues.

+ +
    +
  • WINDOW is the default mode. The movie is drawn over the top of all other elements on the page.
  • +
  • OPAQUE causes the movie to be rendered in the page allowing other elements to cover up the button.
  • +
  • TRANSPARENT the button background is rendered transparent allowing html elements under the button to show through.
  • +
+ +

SWFUpload.RESIZE_ENCODING

+

SWFUpload.RESIZE_ENCODING is a simple object that contains resize encoding type constants. It to indicate the format for encoding resized images.

+ +
    +
  • JPEG - The JPEG encoding format
  • +
  • PNG - The PNG encoding format
  • +
+ +

SWFUpload.onload

+

onload is a function that will be executed by swfobject's addDOMLoadEvent. You can use onload to execute your script as soon as the page is ready. You should only use onload if you are not already using some other method (such as jQuery's Ready or Prototype's dom:loaded).

+ SWFUpload.onload = function () { + new SWFUpload(settingsObject); +} + +

The above example will initialize SWFUpload using the browser's earliest load event. In most browsers this is the DOMReady event which often executes much sooner than the window.onload event.

+ +

Properties

+

+ The following list of properties is intended for your use as described below. Using other properties or + writing to read-only properties can cause SWFUpload to malfunction. +

+

customSettings (read/write)

+

+ The customSettings property is an empty JavaScript object that can be used to store data associated with + an instance of SWFUpload. The customSettings object contents can be initialized using the custom_settings setting. +

+ +

Example:

+ // Initialize SWFUpload with some custom settings +var swfu = new SWFUpload({ + custom_settings : { + custom_setting_1 : "custom_setting_value_1", + custom_setting_2 : "custom_setting_value_2", + custom_setting_n : "custom_setting_value_n", + } +}); + +swfu.customSettings.custom_setting_1 = "custom_setting_value_1"; // Change an existing custom setting +swfu.customSettings.myNewCustomSetting = "new custom setting value"; // Add a new custom setting + +// Overwrite the customSettings with a completely new object +swfu.customSettings = { + custom_setting_A : "custom_setting_value_A", + custom_setting_B : "custom_setting_value_B" +}; + + +

The values stored in the customSettings object can then be easily accessed in the event handlers:

+ + +function uploadSuccess(file, serverData, receivedResponse) { + if (this.customSettings.custom_setting_A === true) { + alert("Checked the custom setting!"); + } +} + + +

movieName

+

Contains the unique movie name of the SWFUpload instance. This value is passed to Flash and is used to facilitate Flash-to-JavaScript communication. + This value is used to index the instance in the SWFUpload.instances array. You should not change the movieName value.

+ +

Methods

+

The following methods are used to operate SWFUpload. Some are bound to DOM element click event handlers and others are used inside + SWFUpload event handlers.

+ +

object addSetting(setting_name, value, default_value)

+

Deprecated The addSetting function sets a setting value. If the value is undefined then the default_value is used. The function is used by SWFUpload + during initialization. Using addSetting to update a setting will not change the setting in the Flash Control.

+

addSetting returns the value that was stored in the setting.

+ +

object getSetting(setting_name)

+

Deprecated The getSetting function retrieves the value of a setting. If the setting is not found an empty string is returned.

+ +

object retrieveSetting(setting_value, default_value)

+

Removed in v2.1.0 The retrieveSetting function is similar to the addSetting function except it does not modify the internal settings object. retrieveSetting + returns the setting_value unless it is undefined in which case it returns the default_value

+

This is a utility function.

+ +

bool destroy()

+

Added in v2.1.0

+

Removes the SWF DOM elements and then destroys SWFUpload internal references. Used for removing a SWFUpload instance from a page. It also attempts to prevent memory leaks in IE.

+

Returns true if the elements were removed successfully. Returns false if any error occurs leaving the SWFUpload instance in an inconsistent state.

+ +

void displayDebugInfo()

+

The displayDebugInfo outputs SWFUpload settings using the debug event. This function is automatically called after initialization if the debug setting is 'true'

+ +

void selectFile()

+

Deprecated. Not compatible with Flash Player 10.

+

selectFile causes the Flash Control to display a File Selection Dialog window. A single file may be selected from the Dialog window.

+

Calling selectFile begins the File Event Chain.

+ +

void selectFiles()

+

Deprecated. Not compatible with Flash Player 10.

+

selectFiles causes the Flash Control to display a File Selection Dialog window. A multiple files may be selected from the Dialog window.

+

Calling selectFiles begins the File Event Chain.

+ +

void startUpload(file_id)

+

startUpload causes the file specified by the file_id parameter to start the upload process. If the file_id parameter is omitted or undefined then the first file in the queue is uploaded.

+ +

Calling startUpload begins the Upload Event Chain.

+ +

void startResizedUpload(file_id, width, height, encoding, quality, allowEnlarging)

+

startResizedUpload causes the file specified by the file_id parameter to start the upload process. SWFUpload attempts to resize the file (assuming it is an image format supported by Flash). If the image cannot be resized an uploadError occurs.

+ +

The width and height parameters specify the maximum size the resized image can be in each dimension. The other dimension is scaled to maintain the image aspect ratio.

+ +

The encoding parameter should be a value from the SWFUpload.RESIZE_ENCODING constants.

+ +

The quality parameter is used only for JPEG encoding and can be value from 0 to 100. The value is clamped if it is out of range.

+ +

The allowEnlarging parameter defines whether SWFUpload is allowed to enlarge the original image when resizing. If set to false the image is still re-encoded but will not be enlarged.

+ +

Calling startResizedUpload begins the normal Upload Event Chain. However, the Flash Player does not provide periodic uploadProgress events. SWFUpload sends only simulated 0% and 100% uploadProgress events.

+ +

A resized image is uploaded as POST value (rather than a FILE) due to security "features" introduced in Flash Player 10. Resized images have to be handled differently than normal uploads:

+ + $resizedImageData = $_POST["Filedata"]; // Data from $_POST rather than $_FILE +$fileHandle = fopen("image.jpg", "w"); +fwrite($fileHandle, $resizedImageData); +fclose($fileHandle); + + +

void cancelUpload(file_id, trigger_error_event)

+ +

cancelUpload cancels the file specified by the file_id parameter. The file is then removed from the queue.

+

If the file_id parameter is omitted or undefined then the first file in the queue is cancelled.

+

The trigger_error_event is optional. If set to false the uploadError event is suppressed.

+ +

void stopUpload()

+ +

stopUpload stops and re-queues the file currently being uploaded.

+

After the uploading file is stopped the uploadError event is fired. If no file is being uploaded then nothing happens and no event is fired.

+ +

bool requeueUpload(file_id or index)

+ +

requeueUpload requeues any previously queued file.

+

If the file cannot be found or is currently uploading false is returned.

+

+ Requeued files are not checked against the file size, queue size, file upload limit or any other settings but are simply added to the queue if a valid + reference to a file exists. +

+ +

object getStats()

+

Retrieves the stats object.

+ +

void setStats(stats_object)

+

The Stats Object may be modified. This is useful if you wish to change the number of successful uploads or upload errors after an upload + has completed.

+ +

object getFile(file_id|index)

+

getFile is used to retrieve a File Object from the queue. The file retrieved by passing in a file id (the id property from a file object) or a file index (the index property from a file object).

+

When getting a file by file_id only files in the queue are available. If the file is not found null is returned.

+

When getting a file by index all queued (or files that generated a queue error) are available. If the index is out of range then null is returned

+ +

object getQueueFile(file_id|index)

+

getFile is used to retrieve a File Object from the upload queue. The file retrieved by passing in a file id (the id property from a file object) or a queue index (the position of the file in the queue). The index starts at zero.

+

When getting a file by file_id only files currently queued for upload are available. If the file is not found null is returned.

+

When getting a file by index only files currently queued for upload ar available. For example: getQueueFile(0) returns the file at front of the queue. This would be the next file uploaded when startUpload is called.

+ +

void addPostParam(name, value)

+

The addPostParam function adds a name/value pair that will be sent in the POST for all files uploaded.

+

The name/value pair will also appear in the post_params setting.

+ +

void removePostParam(name)

+

The removePostParam function removes a name/value pair from the values sent with the POST for file uploads.

+

The name/value pair is also be removed from the post_params setting.

+ +

bool addFileParam(file_id, name, value)

+

The addFileParam function adds a name/value pair that will be sent in the POST with the file specified by the file_id parameter.

+

The name/value pair will only be sent with the file it is added to. To send name/value pairs with all uploads use the post_param setting.

+ +

bool removeFileParam(file_id, name)

+

The removeFileParam function removes a name/value pair from a file upload that was added using addFileParam.

+

If the name/value pair was not found then 'false' is returned.

+ +

void setUploadURL(url)

+

Dynamically modifies the upload_url setting.

+ +

void setPostParams(param_object)

+

Dynamically modifies the post_params setting. Any previous values are over-written. The param_object should be a simple JavaScript object. All names and values must be strings.

+ +

void setFileTypes(types, description)

+

Dynamically updates the file_types and file_types_description settings. Both parameters are required.

+ +

void setFileSizeLimit(file_size_limit)

+

Dynamically modifies the file_size_limit setting. This applies to all future files that are queued. The file_size_limit parameter will accept a unit. Valid units are B, KB, MB, and GB. The default unit is KB.

+

Examples: 2147483648 B, 2097152, 2097152KB, 2048 MB, 2 GB

+ +

void setFileUploadLimit(file_upload_limit)

+

Dynamically modifies the file_upload_limit setting. The special value zero (0) indicates "no limit".

+ +

void setFileQueueLimit(file_queue_limit)

+

Dynamically modifies the file_queue_limit setting. The special value zero (0) indicates "no limit".

+ +

void setFilePostName(file_post_name)

+

Dynamically modifies the file_post_name setting. The Linux Flash Player ignores this setting.

+ +

void setUseQueryString(use_query_string)

+

Dynamically modifies the use_query_string setting. When true this forces SWFUpload to send post parameters on the query string rather than in the post. The use_query_string parameter should be a boolean true or false.

+ +

void setDebugEnabled(debug_enabled)

+

Dynamically enables or disables debug output. The debug_enabled parameter should be a boolean true or false.

+ +

void setButtonImageURL(url)

+

Dynamically change the image used in the Flash Button. The image url must be relative to the swfupload.swf file, an absolute path (e.g., starting with a /), or + a fully qualified url (e.g., http://www.swfupload.org/buttonImage.png). Any image format supported by Flash can be loaded. The most notable formats are jpg, gif, and png.

+ +

The button image is expected to be a button sprite (or a single image file with several images stacked together). The image will be used to represent + all the button states by moving the image up or down to only display the needed portion. These states include: normal, hover, click, disabled. See the sample button images.

+ +

void setButtonDimensions(width, height)

+

Dynamically change the Flash button's width and height. The values should be numeric and should not include any units. The height value should + be 1/4th of the total button image height so the button state sprite images can be displayed correctly

+ +

void setButtonText(text)

+

Sets the text that should be displayed over the Flash button. Text that is too large and overflows the button size will be clipped.

+

The text can be styled using HTML supported by the Flash Player (Adobe Documentation)

+ +

void setButtonTextStyle(css_style_text)

+

Sets the CSS styles used to style the Flash Button Text. CSS should be formatted according to the Flash Player documentation (Adobe Documentation)

+

Style classes defined here can then be referenced by HTML in the button_text setting.

+ +

void setButtonTextPadding(left, top)

+

Sets the top and left padding of the Flash button text. The values may be negative.

+ +

void setButtonDisabled(isDisabled)

+

When 'true' changes the Flash Button state to disabled and the BUTTON_ACTION is ignored. The mouseClick event is always fired when the button is disabled.

+ +

void setButtonAction(buttonAction)

+

Sets the action taken when the Flash button is clicked. Valid action values are taken from the BUTTON_ACTION constants.

+ +

void setButtonCursor(buttonCursor)

+

Sets the mouse cursor shown when hovering over the Flash button. Valid cursor values are taken from the CURSOR constants.

+ + + +

Events

+

SWFUpload fires various events during its operation. These events can be handled by the developer to update the UI, change behavior, or report errors.

+

All SWFUpload events are called in the context of a SWFUpload instance. This means that the 'this' object refers to the SWFUpload instance that + fired the event.

+ +

SWFUpload events should be set only by assigning the event handler function in the settings object during object initialization. You should not override the + internal functions belonging to the SWFUpload.prototype object.

+ +

During a file upload events are usually called in this order (the Upload Event Chain):

+
    +
  • uploadResizeStart
  • +
  • uploadStart
  • +
  • uploadProgress (called over and over again as the file uploads)
  • +
  • uploadError (called if some kind of error occurs, the file is canceled or stopped)
  • +
  • uploadSuccess (the upload finished successfully, data returned from the server is available)
  • +
  • uploadComplete (the upload is complete and SWFUpload is ready to start the next file)
  • +
+ +

flashReady()

+

flashReady is an internal event that should not be overwritten. It is called by the Flash Control to notify SWFUpload that the Flash + movie has loaded and is ready to accept commands.

+ +

swfUploadPreload()

+

+ The swfUploadPreload event is fired after SWFUpload has determined what features are available but before the Flash Movie is loaded. Returning false from this event prevents SWFUpload from loading. + This event can be used to gracefully degrade if the client's browser does not support the features needed. +

+

Set using the swfupload_preload_handler setting.

+ +

swfUploadLoadFailed()

+

The xxxxxx event is fired if SWFUpload determines it cannot load or an exception occurs while added the Flash Movie to the document. SWFUpload may not be able to load because the Flash Player is not installed or a version older than 9.0.28 is installed

+

Set using the swfupload_load_failed_handler setting.

+ +

swfUploadLoaded()

+

The swfUploadLoaded event is fired by flashReady. It is settable. swfUploadLoaded is called to let you know that it is safe to call SWFUpload methods.

+

Set using the swfupload_loaded_handler setting.

+ +

mouseClick()

+

mouseClick is fired when the button is clicked and the button_action setting is set to SWFUpload.BUTTON_ACTION.NONE or the button is disabled. This event is not fired for other button_action settings and the button is enabled.

+

Set using the mouse_click_handler setting.

+ +

mouseOver()

+

mouseOver is fired when the cursor moves over any part of the Flash Movie.

+

Set using the mouse_over_handler setting.

+ +

mouseOut()

+

mouseOut is fired when the cursor moves off the Flash Movie.

+

Set using the mouse_out_handler setting.

+ +

fileDialogStart()

+

fileDialogStart is fired after selectFile for selectFiles is called. This event is fired immediately before the File Selection Dialog + window is displayed. However, the event may not execute until after the Dialog window is closed.

+

Set using the file_dialog_start_handler setting.

+ +

fileQueued(file object)

+

The fileQueued event is fired for each file that is queued after the File Selection Dialog window is closed.

+

Set using the file_queued_handler setting.

+ +

fileQueueError(file object, error code, message)

+

The fileQueueError event is fired for each file that was not queued after the File Selection Dialog window is closed. A file may not be queued + for several reasons such as, the file exceeds the file size, the file is empty or a file or queue limit has been exceeded.

+

The cause of the queue error is specified by the error code parameter. The error code corresponds to a SWFUpload.QUEUE_ERROR constant.

+

Set using the file_queue_error_handler setting.

+ +

fileDialogComplete(number of files selected, number of files queued, total number of files in the queued)

+

The fileDialogComplete event fires after the File Selection Dialog window has been closed and all the selected files have been processed. The 'number of files queued' argument indicates the number of files that were queued from the dialog selection (as opposed to the number of files in the queue).

+

If you want file uploading to begin automatically this is a good place to call 'this.startUpload()'

+

Set using the file_dialog_complete_handler setting.

+ +

uploadResizeStart(file object, width, height, encoding, quality)

+

uploadResizeStart is called immediately before an image is resized. Resizing does not provide any progress events but can take some time due to re-encoding of the file. If an error occurs during resizing the uploadError event is fired.

+ +

When resizing is complete SWFUpload moves on to the uploadStart event and continues using the normal event chain.

+ +

uploadStart(file object)

+

uploadStart is called immediately before the file is uploaded. This event provides an opportunity to perform any last minute validation, add post + params or do any other work before the file is uploaded.

+ +

The upload can be cancelled by returning 'false' from uploadStart. If you return 'true' or do not return any value then the upload proceeds. Returning + 'false' will cause an uploadError event to fired.

+ +

Set using the upload_start_handler setting.

+ +

uploadProgress(file object, bytes complete, total bytes)

+

The uploadProgress event is fired periodically by the Flash Control. This event is useful for providing UI updates on the page.

+

Note: The Linux Flash Player fires a single uploadProgress event after the entire file has been uploaded. This is a bug in + the Linux Flash Player that we cannot work around.

+

Set using the upload_progress_handler setting.

+ +

uploadError(file object, error code, message)

+

The uploadError event is fired any time an upload is interrupted or does not complete successfully. The error code parameter indicates the type of error + that occurred. The error code parameter specifies a constant in SWFUpload.UPLOAD_ERROR.

+

Stopping, Cancelling or returning 'false' from uploadStart will cause uploadError to fire. Upload error will not fire for files that are cancelled + but still waiting in the queue.

+

Set using the upload_error_handler setting.

+ +

uploadSuccess(file object, server data, received response)

+

uploadSuccess is fired when the entire upload has been transmitted and the server returns a HTTP 200 status code. Any data outputted by the server is available + in the server data parameter.

+ +

+ Due to some bugs in the Flash Player the server response may not be acknowledged and no uploadSuccess event is fired by Flash. In this case the + assume_success_timeout setting is checked to see if enough time has passed to fire uploadSuccess anyway. In this case the received response parameter will be false. +

+ +

+ The http_success setting allows uploadSuccess to be fired for HTTP status codes other than 200. In this case no server data is available from the Flash Player. +

+ +

At this point the upload is not yet complete. Another upload cannot be started from uploadSuccess.

+ +

Set using the upload_success_handler setting.

+ +

uploadComplete(file object)

+

uploadComplete is always fired at the end of an upload cycle (after uploadError or uploadSuccess). At this point the upload is complete and + another upload can be started.

+

If you want the next upload to start automatically this is a good place to call this.uploadStart(). Use caution when calling uploadStart inside + the uploadComplete event if you also have code that cancels all the uploads in a queue.

+

Set using the upload_complete_handler setting.

+ +

debug(message)

+

The debug event is called by the SWFUpload library and the Flash Control when the debug setting is set to 'true'. If the debug + event is not overridden then SWFUpload writes debug messages to the SWFUpload console (a text box dynamically added to the end of the page body).

+

Set using the debug_handler setting.

+ +

SWFUpload Utility Objects

+

Settings object

+

The settings object is a JavaScript object that provides the settings for the SWFUpload instance. Each setting should only appear once. Many settings + are optional and provide suitable default values if omitted. See the setting details for required and optional settings.

+

Example:

+ { + upload_url : "http://www.swfupload.org/upload.php", + flash_url : "http://www.swfupload.org/swfupload.swf", + flash9_url : "http://www.swfupload.org/swfupload_fp9.swf", + + file_post_name : "Filedata", + post_params : { + "post_param_name_1" : "post_param_value_1", + "post_param_name_2" : "post_param_value_2", + "post_param_name_n" : "post_param_value_n" + }, + use_query_string : false, + requeue_on_error : false, + http_success : [201, 202], + assume_success_timeout : 0, + file_types : "*.jpg;*.gif", + file_types_description: "Web Image Files", + file_size_limit : "1024", + file_upload_limit : 10, + file_queue_limit : 2, + + debug : false, + + prevent_swf_caching : false, + preserve_relative_urls : false, + + button_placeholder_id : "element_id", + button_image_url : "http://www.swfupload.org/button_sprite.png", + button_width : 61, + button_height : 22, + button_text : "<b>Click</b> <span class="redText">here</span>", + button_text_style : ".redText { color: #FF0000; }", + button_text_left_padding : 3, + button_text_top_padding : 2, + button_action : SWFUpload.BUTTON_ACTION.SELECT_FILES, + button_disabled : false, + button_cursor : SWFUpload.CURSOR.HAND, + button_window_mode : SWFUpload.WINDOW_MODE.TRANSPARENT, + + swfupload_loaded_handler : swfupload_loaded_function, + mouse_click_handler : mouse_click_function, + mouse_over_handler : mouse_over_function, + mouse_out_handler : mouse_out_function, + file_dialog_start_handler : file_dialog_start_function, + file_queued_handler : file_queued_function, + file_queue_error_handler : file_queue_error_function, + file_dialog_complete_handler : file_dialog_complete_function, + upload_start_handler : upload_start_function, + upload_progress_handler : upload_progress_function, + upload_error_handler : upload_error_function, + upload_success_handler : upload_success_function, + upload_complete_handler : upload_complete_function, + debug_handler : debug_function, + + custom_settings : { + custom_setting_1 : "custom_setting_value_1", + custom_setting_2 : "custom_setting_value_2", + custom_setting_n : "custom_setting_value_n", + } +} +

Settings Description

+ +
upload_url
+

The upload_url setting accepts a full, absolute, or relative target URL for the uploaded file. Relative URLs should be relative to document. The upload_url should be in the same domain as the Flash Control for best compatibility.

+

If the preserve_relative_urls setting is false SWFUpload will convert the relative URL to an absolute URL to avoid the URL being interpreted differently by the Flash Player on different platforms. If you disable SWFUploads conversion of the URL relative URLs should be relative to the swfupload.swf file.

+ +
file_post_name
+

The file_post_name allows you to set the value name used to post the file. This is not related to the file name. + The default value is 'Filedata'. For maximum compatibility it is recommended that the default value is used.

+ +
post_params
+

+ The post_params setting defines the name/value pairs that will be posted with each uploaded file. This setting accepts a simple JavaScript object. + Multiple post name/value pairs should be defined as demonstrated in the sample settings object. Values must be either strings or numbers (as interpreted by the JavaScript typeof function). +

+

Note: Flash Player 8 does not support sending additional post parameters. SWFUpload will automatically send the post_params as part of the query string.

+ +
use_query_string
+

+ The use_query_string setting may be true or false. This value indicates whether SWFUpload should send the post_params and file params on the query string or the post. This setting was introduced in SWFUpload v2.1.0. +

+ +
preserve_relative_urls
+

A boolean value that indicates whether SWFUpload should attempt to convert relative URLs used by the Flash Player to absolute URLs. If set to true SWFUpload will not modify any URLs. The default value is false.

+ +
requeue_on_error
+

+ The requeue_on_error setting may be true or false. When this setting is true any files that has an + uploadError (excluding fileQueue errors and the FILE_CANCELLED uploadError) is returned to the front of + the queue rather than being discarded. The file can be uploaded again if needed. To remove the file from the + queue the cancelUpload method must be called. +

+

+ All the events associated with a failed upload are still called and so the requeuing the failed upload can conflict + with the Queue Plugin (or custom code that uploads the entire queue). Code that automatically uploads the next file + in the queue will upload the failed file over and over again if care is not taken to allow the failing upload to be + cancelled. +

+

+ This setting was introduced in SWFUpload v2.1.0. +

+ +
http_success
+

+ An array that defines the HTTP Status Codes that will trigger success. 200 is always a success. Also, only the 200 status code provides the serverData. +

+

+ When returning and accepting an HTTP Status Code other than 200 it is not necessary for the server to return content. +

+ +
assume_success_timeout
+

+ The number of seconds SWFUpload should wait for Flash to detect the server's response after the file has finished uploading. This setting allows you to + work around the Flash Player bugs where long running server side scripts causes Flash to ignore the server response or the Mac Flash Player bug that ignores + server responses with no content. +

+

+ Testing has shown that Flash will ignore server responses that take longer than 30 seconds after the last uploadProgress event. +

+

+ A timeout of zero (0) seconds disables this feature and is the default value. SWFUpload will wait indefinitely for the Flash Player to trigger the uploadSuccess event. +

+ +
file_types
+

+ The file_types setting accepts a semi-colon separated list of file extensions that are allowed to be selected by the user. Use '*.*' to allow all file types. +

+ +
file_types_description
+

+ A text description that is displayed to the user in the File Browser dialog. +

+ +
file_size_limit
+

+ The file_size_limit setting defines the maximum allowed size of a file to be uploaded. This setting accepts a value and unit. + Valid units are B, KB, MB and GB. If the unit is omitted default is KB. A value of 0 (zero) is interpreted as unlimited. +

+

+ Note: This setting only applies to the user's browser. It does not affect any settings or limits on the web server. +

+ +
file_upload_limit
+

+ Defines the number of files allowed to be uploaded by SWFUpload. This setting also sets the upper bound of the file_queue_limit + setting. Once the user has uploaded or queued the maximum number of files she will no longer be able to queue additional files. The + value of 0 (zero) is interpreted as unlimited. Only successful uploads (uploads the trigger the uploadSuccess event) are counted + toward the upload limit. The setStats function can be used to modify the number of successful uploads. +

+

Note: This value is not tracked across pages and is reset when a page is refreshed. File quotas should be managed by the web server.

+ +
file_queue_limit
+

+ Defines the number of unprocessed files allowed to be simultaneously queued. Once a file is uploaded, errored, or cancelled a new + files can be queued in its place until the queue limit has been reached. If the upload limit (or remaining uploads allowed) is less + than the queue limit then the lower number is used. +

+ +
flash_url
+

+ The full, absolute, or relative URL to the Flash Control swf file. This setting cannot be changed once the SWFUpload has been + instantiated. Relative URLs are relative to the page URL. +

+ +
flash9_url
+

+ (Added in v2.5.0) the full, absolute, or relative URL to the Flash Control swf file that supports Flash Player 9. +

+ +
flash_width
+

+ (Removed in v2.1.0) Defines the width of the HTML element that contains the flash. Some browsers do not function correctly if this setting is less than 1 px. + This setting is optional and has a default value of 1px. +

+ +
flash_height
+

+ (Removed in v2.1.0) Defines the height of the HTML element that contains the flash. Some browsers do not function correctly if this setting is less than 1 px. + This setting is optional and has a default value of 1px. +

+ +
flash_color
+

+ Removed in v2.2.0 This setting sets the background color of the HTML element that contains the flash. The default value is '#FFFFFF'. +

+

+ Note: This setting may not be effective in "skinning" 1px flash element in all browsers. +

+ +
prevent_swf_caching
+

+ Added in v2.2.0 This boolean setting indicates whether a random value should be added to the Flash URL in an attempt to + prevent the browser from caching the SWF movie. This works around a bug in some IE-engine based browsers. +

+

Note: The algorithm for adding the random number to the URL is dumb and cannot handle URLs that already have some parameters.

+ +
debug
+

A boolean value that defines whether the debug event handler should be fired.

+ +
button_placeholder_id
+

(Added in v2.2.0) This required setting sets the ID of DOM element that will be replaced by the Flash Button. This setting overrides the button_placeholder setting. The Flash button can be styled using the CSS class 'swfupload'.

+ +
button_placeholder
+

(Added in v2.2.0) This required setting sets the DOM element that will be replaced by the Flash Button. This setting is only applied if the button_placeholder_id is not set. The Flash button can be styled using the CSS class 'swfupload'.

+ +
button_image_url
+

(Added in v2.2.0) Fully qualified, absolute or relative URL to the image file to be used as the Flash button. Any Flash supported image file format can be used (another SWF file or gif, jpg, or png).

+

This URL is affected by the preserve_relative_urls setting and should follow the same rules as the upload_url setting.

+ +

The button image is treated as a sprite. There are 4 button states that must be represented by the button image. Each button state image + should be stacked above the other in this order: normal, hover, down/click, disabled.

+ +
button_width
+

(Added in v2.2.0) A number defining the width of the Flash button.

+ +
button_height
+

(Added in v2.2.0) A number defining the height of the Flash button. This value should be 1/4th of the height or + the button image.

+ +
button_text
+

(Added in v2.2.0) Plain or HTML text that is displayed over the Flash button. HTML text can be further styled using CSS + classes and the button_text_style setting. See Adobe's Flash documentation for details.

+ +
button_text_style
+

(Added in v2.2.0) CSS style string that defines how the button_text is displayed. + See Adobe's Flash documentation for details.

+ +
button_text_top_padding
+

(Added in v2.2.0) Used to vertically position the Flash button text. Negative values may be used.

+ +
button_text_left_padding
+

(Added in v2.2.0) Used to horizontally position the Flash button text. Negative values may be used.

+ +
button_action
+

(Added in v2.2.0) Defines the action taken when the Flash button is clicked. Valid action values can be found in the swfupload.js file under the BUTTON_ACTION object.

+ +
button_disabled
+

(Added in v2.2.0) A boolean value that sets whether the Flash button is in the disabled state. When in the disabled state the button will not execute any actions.

+ +
button_cursor
+

(Added in v2.2.0) Used to define what type of mouse cursor is displayed when hovering over the Flash button.

+ +
button_window_mode
+

(Added in v2.2.0) Sets the WMODE property of the Flash Movie. Valid values are available in the SWFUpload.WINDOW_MODE constants.

+ +
custom_settings
+

+ The custom_settings setting allows developers to safely attach additional information to a SWFUpload instance without + worrying about affecting internal SWFUpload values or changes in new SWFUpload versions. This setting accepts a JavaScript + object. +

+

+ Once instantiated the custom settings are accessed in the 'customSettings' property of the SWFUpload instance. +

+ var swfu = new SWFUpload({ + custom_settings : { + "My Setting" : "This is my setting", + myothersetting : "This is my other setting", + integer_setting : 100, + a_dom_setting : document.getElementById("some_element_id") + } +}); + +var my_setting = swfu.customSettings["My Setting"]); +swfu.customSettings["My Setting"] = "This is my new setting"; +swfu.customSetting.myothersetting = "another new value"; +swfu.customSetting.integer_setting += 25; +swfu.customSetting["a_dom_setting"].style.visibility = "hidden"; + +
Event Handlers
+

+ The remaining settings define the event handlers called by SWFUpload during its operation. JavaScript functions should + be defined to handle these events as needed. +

+ + +

Support Object

+

The support object is a property of a SWFUpload instance and identifies what features SWFUpload can support based on the Flash Player version installed in the client browser.

+ { + load : bool, // Indicates if SWFupload is able to load based of Flash Player version + imageResize : bool, // Indicates of Flash Player 10 or later is installed and SWFUpload supports client side resizing and uploading of images. +} + +

File Object

+

The file object is passed to several event handlers. It contains information about the file. Some operating systems do not fill in all the values (this + is especially true for the createdate and modificationdate values).

+ { + id : string, // SWFUpload file id, used for starting or cancelling and upload + index : number, // The index of this file for use in getFile(i) + name : string, // The file name. The path is not included. + size : number, // The file size in bytes + type : string, // The file type as reported by the client operating system + creationdate : Date, // The date the file was created + modificationdate : Date, // The date the file was last modified + filestatus : number, // The file's current status. Use SWFUpload.FILE_STATUS to interpret the value. + post : object // The post params that will be sent with this file (compiled from the post_params setting and any params added with the addFileParam function + +} + +

Stats Object

+

The Stats object provides information about the upload queue.

+

That stats object contains the following properties:

+ { + in_progress : number // 1 or 0 indicating if a file upload is currently in progress + files_queued : number // The number of files currently in the queue + successful_uploads : number // The number of files that have uploaded successfully (caused uploadSuccess to be fired) + upload_errors : number // The number of files that have had errors (excluding cancelled files) + upload_cancelled : number // The number of files that have been cancelled + queue_errors : number // The number of files that caused fileQueueError to be fired +} +

All these values can be updated using setStats() except the in_progress and files_queued values.

+ +

SWFUpload Plug-ins

+ +

With SWFUpload v2.0 several plugins have been introduced. They are provided to help with common tasks associated with implementing SWFUpload.

+

Currently most of the documentation for using the plugins is contained in the plugin JavaScript file.

+ +

SWFObject

+

+ The SWFObject plugin uses the SWFObject library to handle the embedding + of the SWFUpload Flash Component into the page. +

+

+ This plugin also provides support for Document Ready loading and Flash Version Detection. Usage details are documented + in the plugin file itself. You should not use the SWFObject's Document Ready loading mixed with another libraries DOMReady. Use + one or the other but not both. +

+ +

Flash Player 10: Because Flash Player 10 requires the SWFUpload swf to act is a button the movie must be visible in order + for it to load. If the button_placeholder_id is set to an element that is hidden (visibility set to hidden or display set to none) SWFUpload will + fail to load.

+ +

Cookies

+

In response to the Flash Cookie Bug the Cookies Plugin automatically retrieves your browser's cookies and sends them + with the upload. The are sent as POST or GET variables to the upload url.

+ +

Note that this plugin sends the cookies name/values in the POST or GET. On the server side they will not be accessible as cookies. Some frameworks that + automatically check cookies for session or authentication values still will not be able to find the values.

+ +

Queue Handling

+

This plugin provides Queue Handling features such as entire queue uploading, entire queue cancelling and automatic starting of uploads after being queued.

+ + +

Speed

+

This Plugin extends the 'file' object with several properties that describe the current upload. This includes current speed, average speed, elapsed time, remaining time and more.

+ + +

Known Issues

+

The Flash Player and many Browsers have bugs that have a direct impact on the performance of SWFUpload. While we have worked + hard to get around many issues but there are some things that we cannot fix.

+ +

Cancelling in Linux

+

Older Flash 9 Players for Linux cause the browser to crash if an upload is cancelled. Newer Flash 9 Players behave better.

+ +

Upload Progress in Linux

+

The Flash Player in Linux sends a single uploadProgress event after the file has finished uploading.

+

In some distributions the entire browser locks up while the upload is in progress.

+ +

Upload Progress in OS X

+

There have been some reports that uploadProgress events are not fired in MAC Flash Players. The specifics haven't been pinned down but be aware of the possible issue.

+ +

MIME Type

+

The Flash Player uploads all files with a mime type of application/octet-stream regardless of the file's actual mime type.

+ +

Maximum number of selected files

+

The Flash Player does not impose a maximum number of selected files. However, it builds a selected files string which does have a maximum length. + The string is built using the file's name and the separator [quote][space][quote]. The total number of files selected is determined by the sum + of the lengths of the file names and a prefixed and postfixed [quote] character (2 characters) and the number of files selected minus one times 3 (for the separator string)

+ +

This limitation may vary from system to system. If a user selects too many files they will be receive a Flash Player generated warning message and will + be left at the File Selection Dialog.

+ +

Proxies

+

The Flash Player may not properly use proxies. It does not handle authenticating proxies well (if at all) and will some-times crash.

+

Some anti-virus software uses a proxy to scan uploads and cause SWFUpload to believe the entire file has been uploaded. SWFUpload will fire uploadProgress events very quickly + until it reaches 100% and will then seem to pause until the proxy completes uploading the file to the server.

+

Kaspersky Anti-Virus: Kaspersky (and other) Anti-Virus suites implement a client-side proxy that intercepts and scans outgoing data. SWFUpload + cannot detect the presence of proxy servers. These types of proxy servers accept the entire upload very quickly and they wait while the data is scanned and transmitted + to the original destination. The Flash Player times out the connection (because of appearent inactivity) and returns a #2038 error. SWFUpload fires an uploadError event.

+ +

Apache mod_security

+

Apache's mod_security validates POST to the server. Flash Player has implemented an edge case (there is argument as to whether it is invalid or note) + POST for file uploads and so servers implementing + mod_security will reject the upload. mod_security can be disabled using your .htaccess file

+ +

SSL

+

There have been some reports that the Flash Player cannot upload through SSL. + The issue has not been pinned down but uploading over SSL may be unreliable. There especially seems to be an issue with + using self-signed certificates.

+

Also, SSL tickets from untrusted Certificate Authorities (CA) do not work as Flash does not provide a method for accepting the certificate. It has been noted that, like the cookie bug, + that Flash Player on Windows obtains its trusted CA list from Internet Explorer regardless of the browser in use.

+ +

Authentication

+

HTTP Authentication is not well supported by the Flash Player. Later versions of Flash Player behave better. Old version of Flash Player would crash the browser.

+ +

Prematurely terminated connections

+

Prematurely ending the response (such as a Response.end() in ASP.Net) can sometimes cause successful uploads to be reported as failed.

+ +

Filedata POST name in Linux

+

Changing the Filedata value (file_post_name setting) is ignored in Linux Flash Players.

+ +

Cookie bug

+

On Windows the Non-IE Flash Player plugin (FireFox, Opera, Safari, etc) will send the IE cookies regardless of the browser used. This breaks authentication and sessions for many server-side scripting technologies.

+

Developers should manually pass Session and Authentication cookie information and manually restore Sessions on the Server Side if they wish to use Sessions

+

The SWFUpload package contains work-around sample code for PHP and ASP.Net

+ +

ExternalInterface bugs

+

Flash Player does not properly escape data when communication with the browser/JavaScript. SWFUpload goes to great lengths to work-around this issue. If this + bug is fixed in future Flash Players/Browsers then SWFUpload will send extra escaped data.

+ +

Server Data length bugs

+

Very long server data is corrupted on Mac and Linux Flash Players. Server data will be truncated, garbled, and/or repeated. We recommend + keeping data returned from the server short and concise.

+ +

Avant Browser

+

For some users the Avant Browser does not work with SWFUpload after the Flash Control has been cached. This has been reproduced by the + SWFUpload developers but the Avant Browser developers did not experience any problems.

+

When the page is reloaded SWFUpload loads and fires the swfupload_loaded event. However, none of the ExternalInterface callback functions are defined + on the movie element. SWFUpload v2.0.2 has added checks which prevent swfupload_loaded from firing if the callback functions are not detected.

+ +

SWFUpload v2.2.0 added the prevent_swf_caching setting that attempts to work around this issue.

+ +

File Dialog & Page Changing

+

Leaving or reloading the current page while the File Browser Dialog window is open will cause the browser to crash (all browsers, all OSs). Most commonly this is caused by failing to + cancel a click event on an <a> tag where the onclick event calls the selectFile or selectFiles function.

+ +

Long Running Upload Scripts

+

After Flash has uploaded the file to the webserver the upload script is executed. This script handles the file whether that means saving it, creating a thumbnail, scanning for viruses, etc. + If the upload script does not return any data within 30 seconds Flash will disconnect and return an IO Error. You can prevent this by returning characters or data while + the script runs (if possible). For PHP, the script continues to run and complete successfully even though Flash has terminated the connection. Any data returned + by the script after Flash has disconnected is lost.

+ +

WMODE / BUTTON_WINDOW_MODE

+ +

In some browsers the selected WMODE (which is set using the BUTTON_WINDOW_MODE) can prevent the Flash Control from loading + if the control is not on screen. The control will finally load once the user scrolls the page so the control becomes visible.

+ +

This behavior can adversely affect the SWFObject plugin. No SWFUpload events will be fire and the button image will not be + loaded until the control becomes visible.

+ +

On some systems (Linux is affected) when the WMODE is transparent Flash opens the File Dialog window behind the browser window.

+ +

Memory Leaks

+

Some browsers (especially IE) cannot recover memory used by the Flash Player when JavaScript communication via the ExternalInterface classes is used (like in SWFUpload). + Creating many SWFUpload instances and/or reloading the page several times will cause the browser to consume more and more memory until it crashes or otherwise harms the + system.

+ +

In v2.2.0 SWFUpload we have implemented some preventative measures. Some of these measures are pro-active but it is still recommended that you + call the destroy method when the page unloads. If you are using hundreds of SWFUpload instances on a page you should use caution and test + carefully for memory leaks.

+ +

Other Mac Issues

+
    +
  • + Content must be returned by the server or the Mac Flash Player will not trigger the uploadSuccess event. We have added the assume_success_timeout setting to help work around this. + But in general it is simply easier and more reliable to return a short string after the upload is complete. +
  • +
  • + Users have reported that uploading to subdomains does not work with the Mac Flash Player. +
  • +
  • + Users have reported that pages that redirect (HTTP Status 302) are not handled by the Mac Flash Player. Windows + clients seem to handle this issue. 302 redirects are used in many authentication schemes and MVC frameworks. +
  • +
  • + The flash documentation states that on OS early than OS X 10.3 the bytes loaded is always reported as -1. SWFUpload converts + this to 0 but the total bytes will never be sent and 100% won't be reached. The UI should be updated to display 100% complete in + uploadSuccess or uploadComplete events to maintain a consistent UI. +
  • +
  • + Some users have reported issues if there is a space character in the upload_url for the Mac Flash Player. Avoid spaces or try replacing them with + or %20. +
  • +
  • + Users have reported that the Flash Player for Mac adds the PORT to the HTTP HOST header (e.g., http://www.example.com:80). If you are + checking this variable in your server-side script be aware of the possible issue. +
  • +
  • + Files that contain only a resource fork are treated as zero byte file by the Flash Player and cannot be uploaded. (Note: Flash Player 10.1 may resolve this issue) +
  • +
+ + + \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/plugins/SWFObject License.txt b/media_tree/static/media_tree/lib/swfupload/plugins/SWFObject License.txt new file mode 100644 index 0000000..189d009 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/plugins/SWFObject License.txt @@ -0,0 +1,4 @@ +/* SWFObject v2.0 rc4 + Copyright (c) 2007 Geoff Stearns, Michael Williams, and Bobby van der Sluis + This software is released under the MIT License +*/ diff --git a/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.cookies.js b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.cookies.js new file mode 100644 index 0000000..9c83c35 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.cookies.js @@ -0,0 +1,53 @@ +/* + Cookie Plug-in + + This plug in automatically gets all the cookies for this site and adds them to the post_params. + Cookies are loaded only on initialization. The refreshCookies function can be called to update the post_params. + The cookies will override any other post params with the same name. +*/ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.prototype.initSettings = function (oldInitSettings) { + return function (userSettings) { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this, userSettings); + } + + this.refreshCookies(false); // The false parameter must be sent since SWFUpload has not initialzed at this point + }; + }(SWFUpload.prototype.initSettings); + + // refreshes the post_params and updates SWFUpload. The sendToFlash parameters is optional and defaults to True + SWFUpload.prototype.refreshCookies = function (sendToFlash) { + if (sendToFlash === undefined) { + sendToFlash = true; + } + sendToFlash = !!sendToFlash; + + // Get the post_params object + var postParams = this.settings.post_params; + + // Get the cookies + var i, cookieArray = document.cookie.split(';'), caLength = cookieArray.length, c, eqIndex, name, value; + for (i = 0; i < caLength; i++) { + c = cookieArray[i]; + + // Left Trim spaces + while (c.charAt(0) === " ") { + c = c.substring(1, c.length); + } + eqIndex = c.indexOf("="); + if (eqIndex > 0) { + name = c.substring(0, eqIndex); + value = c.substring(eqIndex + 1); + postParams[name] = value; + } + } + + if (sendToFlash) { + this.setPostParams(postParams); + } + }; + +} diff --git a/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.proxy.js b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.proxy.js new file mode 100644 index 0000000..00f82c6 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.proxy.js @@ -0,0 +1,96 @@ +/* + Proxy Plug-in + + Features: + Times an upload to see if it appear suspiciously fast which might indicate a proxy server or anti-virus suite intercepting the upload. + If the upload seems too fast an uploadError event is fired with PROXY error code after the final uploadProgress event. + + Adds a SWFUpload setting allowing you to tweak the bytes/s for triggering the error: + proxy_detect_threshold : 256000 + + Adds an UPLOAD_ERROR entry called PROXY: + function uploadError(file, errorCode, message) { + if (errorCode === SWFUpload.UPLOAD_ERROR.PROXY) { + alert("You might have a proxy!"); + } + } + */ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.proxyDetect = {}; + SWFUpload.UPLOAD_ERROR.PROXY = -30300; + + SWFUpload.prototype.initSettings = (function (oldInitSettings) { + return function (userSettings) { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this, userSettings); + } + + this.ensureDefault = function (settingName, defaultValue) { + this.settings[settingName] = (userSettings[settingName] == undefined) ? defaultValue : userSettings[settingName]; + }; + + // List used to keep the speed stats for the files we are tracking + this.proxyDetectFileStartTimes = {}; + this.proxyDetectSettings = {}; + + this.ensureDefault("proxy_detect_threshold", 256000); // Default is 250 KB per second + + this.proxyDetectSettings.user_upload_progress_handler = this.settings.upload_progress_handler; + this.proxyDetectSettings.user_upload_complete_handler = this.settings.upload_complete_handler; + + this.settings.upload_progress_handler = SWFUpload.proxyDetect.uploadProgressHandler; + this.settings.upload_complete_handler = SWFUpload.proxyDetect.uploadCompleteHandler; + + + delete this.ensureDefault; + }; + }(SWFUpload.prototype.initSettings)); + + SWFUpload.proxyDetect.uploadProgressHandler = function (file, bytesComplete, bytesTotal) { + var ex1 = null, time, differenceMS, bps; + try { + if (typeof this.proxyDetectSettings.user_upload_progress_handler === "function") { + this.proxyDetectSettings.user_upload_progress_handler.call(this, file, bytesComplete, bytesTotal); + } + } catch (ex1) { } + + + if (bytesComplete === 0) { + this.proxyDetectFileStartTimes[file.ID] = new Date(); + + } else if (bytesComplete === bytesTotal) { + try { + // Calculate the Bps and decide if we should trigger the error + time = new Date(); + differenceMS = time.getTime() - this.proxyDetectFileStartTimes[file.ID].getTime(); + + if (differenceMS === 0) { + differenceMS = 1; + } + + bps = bytesTotal / (differenceMS * 1000); + if (bps > parseInt(this.settings.proxy_detect_threshold, 10)) { + this.queueEvent("upload_error_handler", [file, SWFUpload.UPLOAD_ERROR.PROXY, bps]); + } + } catch (ex) { + } + } + + if (ex1 !== null) { + throw(ex1); + } + }; + + SWFUpload.proxyDetect.uploadCompleteHandler = function (file) { + try { + delete this.proxyDetectFileStartTimes[file.ID]; + } catch (ex) { + } + + if (typeof this.proxyDetectSettings.user_upload_progress_handler === "function") { + return this.proxyDetectSettings.user_upload_progress_handler.call(this, file); + } + }; +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.queue.js b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.queue.js new file mode 100644 index 0000000..a712fcb --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.queue.js @@ -0,0 +1,98 @@ +/* + Queue Plug-in + + Features: + *Adds a cancelQueue() method for cancelling the entire queue. + *All queued files are uploaded when startUpload() is called. + *If false is returned from uploadComplete then the queue upload is stopped. + If false is not returned (strict comparison) then the queue upload is continued. + *Adds a QueueComplete event that is fired when all the queued files have finished uploading. + Set the event handler with the queue_complete_handler setting. + + */ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.queue = {}; + + SWFUpload.prototype.initSettings = (function (oldInitSettings) { + return function (userSettings) { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this, userSettings); + } + + this.queueSettings = {}; + + this.queueSettings.queue_cancelled_flag = false; + this.queueSettings.queue_upload_count = 0; + + this.queueSettings.user_upload_complete_handler = this.settings.upload_complete_handler; + this.queueSettings.user_upload_start_handler = this.settings.upload_start_handler; + this.settings.upload_complete_handler = SWFUpload.queue.uploadCompleteHandler; + this.settings.upload_start_handler = SWFUpload.queue.uploadStartHandler; + + this.settings.queue_complete_handler = userSettings.queue_complete_handler || null; + }; + })(SWFUpload.prototype.initSettings); + + SWFUpload.prototype.startUpload = function (fileID) { + this.queueSettings.queue_cancelled_flag = false; + this.callFlash("StartUpload", [fileID]); + }; + + SWFUpload.prototype.cancelQueue = function () { + this.queueSettings.queue_cancelled_flag = true; + this.stopUpload(); + + var stats = this.getStats(); + while (stats.files_queued > 0) { + this.cancelUpload(); + stats = this.getStats(); + } + }; + + SWFUpload.queue.uploadStartHandler = function (file) { + var returnValue; + if (typeof(this.queueSettings.user_upload_start_handler) === "function") { + returnValue = this.queueSettings.user_upload_start_handler.call(this, file); + } + + // To prevent upload a real "FALSE" value must be returned, otherwise default to a real "TRUE" value. + returnValue = (returnValue === false) ? false : true; + + this.queueSettings.queue_cancelled_flag = !returnValue; + + return returnValue; + }; + + SWFUpload.queue.uploadCompleteHandler = function (file) { + var user_upload_complete_handler = this.queueSettings.user_upload_complete_handler; + var continueUpload; + + if (file.filestatus === SWFUpload.FILE_STATUS.COMPLETE) { + this.queueSettings.queue_upload_count++; + } + + if (typeof(user_upload_complete_handler) === "function") { + continueUpload = (user_upload_complete_handler.call(this, file) === false) ? false : true; + } else if (file.filestatus === SWFUpload.FILE_STATUS.QUEUED) { + // If the file was stopped and re-queued don't restart the upload + continueUpload = false; + } else { + continueUpload = true; + } + + if (continueUpload) { + var stats = this.getStats(); + if (stats.files_queued > 0 && this.queueSettings.queue_cancelled_flag === false) { + this.startUpload(); + } else if (this.queueSettings.queue_cancelled_flag === false) { + this.queueEvent("queue_complete_handler", [this.queueSettings.queue_upload_count]); + this.queueSettings.queue_upload_count = 0; + } else { + this.queueSettings.queue_cancelled_flag = false; + this.queueSettings.queue_upload_count = 0; + } + } + }; +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.speed.js b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.speed.js new file mode 100644 index 0000000..4d96ffb --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/plugins/swfupload.speed.js @@ -0,0 +1,346 @@ +/* + Speed Plug-in + + Features: + *Adds several properties to the 'file' object indicated upload speed, time left, upload time, etc. + - currentSpeed -- String indicating the upload speed, bits per second + - averageSpeed -- Overall average upload speed, bits per second + - movingAverageSpeed -- Speed over averaged over the last several measurements, bits per second + - timeRemaining -- Estimated remaining upload time in seconds + - timeElapsed -- Number of seconds passed for this upload + - percentUploaded -- Percentage of the file uploaded (0 to 100) + - sizeUploaded -- Formatted size uploaded so far, bytes + + *Adds setting 'moving_average_history_size' for defining the window size used to calculate the moving average speed. + + *Adds several Formatting functions for formatting that values provided on the file object. + - SWFUpload.speed.formatBPS(bps) -- outputs string formatted in the best units (Gbps, Mbps, Kbps, bps) + - SWFUpload.speed.formatTime(seconds) -- outputs string formatted in the best units (x Hr y M z S) + - SWFUpload.speed.formatSize(bytes) -- outputs string formatted in the best units (w GB x MB y KB z B ) + - SWFUpload.speed.formatPercent(percent) -- outputs string formatted with a percent sign (x.xx %) + - SWFUpload.speed.formatUnits(baseNumber, divisionArray, unitLabelArray, fractionalBoolean) + - Formats a number using the division array to determine how to apply the labels in the Label Array + - factionalBoolean indicates whether the number should be returned as a single fractional number with a unit (speed) + or as several numbers labeled with units (time) + */ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.speed = {}; + + SWFUpload.prototype.initSettings = (function (oldInitSettings) { + return function (userSettings) { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this, userSettings); + } + + this.ensureDefault = function (settingName, defaultValue) { + this.settings[settingName] = (userSettings[settingName] == undefined) ? defaultValue : userSettings[settingName]; + }; + + // List used to keep the speed stats for the files we are tracking + this.fileSpeedStats = {}; + this.speedSettings = {}; + + this.ensureDefault("moving_average_history_size", "10"); + + this.speedSettings.user_file_queued_handler = this.settings.file_queued_handler; + this.speedSettings.user_file_queue_error_handler = this.settings.file_queue_error_handler; + this.speedSettings.user_upload_start_handler = this.settings.upload_start_handler; + this.speedSettings.user_upload_error_handler = this.settings.upload_error_handler; + this.speedSettings.user_upload_progress_handler = this.settings.upload_progress_handler; + this.speedSettings.user_upload_success_handler = this.settings.upload_success_handler; + this.speedSettings.user_upload_complete_handler = this.settings.upload_complete_handler; + + this.settings.file_queued_handler = SWFUpload.speed.fileQueuedHandler; + this.settings.file_queue_error_handler = SWFUpload.speed.fileQueueErrorHandler; + this.settings.upload_start_handler = SWFUpload.speed.uploadStartHandler; + this.settings.upload_error_handler = SWFUpload.speed.uploadErrorHandler; + this.settings.upload_progress_handler = SWFUpload.speed.uploadProgressHandler; + this.settings.upload_success_handler = SWFUpload.speed.uploadSuccessHandler; + this.settings.upload_complete_handler = SWFUpload.speed.uploadCompleteHandler; + + delete this.ensureDefault; + }; + })(SWFUpload.prototype.initSettings); + + + SWFUpload.speed.fileQueuedHandler = function (file) { + if (typeof this.speedSettings.user_file_queued_handler === "function") { + file = SWFUpload.speed.extendFile(file); + + return this.speedSettings.user_file_queued_handler.call(this, file); + } + }; + + SWFUpload.speed.fileQueueErrorHandler = function (file, errorCode, message) { + if (typeof this.speedSettings.user_file_queue_error_handler === "function") { + file = SWFUpload.speed.extendFile(file); + + return this.speedSettings.user_file_queue_error_handler.call(this, file, errorCode, message); + } + }; + + SWFUpload.speed.uploadStartHandler = function (file) { + if (typeof this.speedSettings.user_upload_start_handler === "function") { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + return this.speedSettings.user_upload_start_handler.call(this, file); + } + }; + + SWFUpload.speed.uploadErrorHandler = function (file, errorCode, message) { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + SWFUpload.speed.removeTracking(file, this.fileSpeedStats); + + if (typeof this.speedSettings.user_upload_error_handler === "function") { + return this.speedSettings.user_upload_error_handler.call(this, file, errorCode, message); + } + }; + SWFUpload.speed.uploadProgressHandler = function (file, bytesComplete, bytesTotal) { + this.updateTracking(file, bytesComplete); + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + + if (typeof this.speedSettings.user_upload_progress_handler === "function") { + return this.speedSettings.user_upload_progress_handler.call(this, file, bytesComplete, bytesTotal); + } + }; + + SWFUpload.speed.uploadSuccessHandler = function (file, serverData) { + if (typeof this.speedSettings.user_upload_success_handler === "function") { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + return this.speedSettings.user_upload_success_handler.call(this, file, serverData); + } + }; + SWFUpload.speed.uploadCompleteHandler = function (file) { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + SWFUpload.speed.removeTracking(file, this.fileSpeedStats); + + if (typeof this.speedSettings.user_upload_complete_handler === "function") { + return this.speedSettings.user_upload_complete_handler.call(this, file); + } + }; + + // Private: extends the file object with the speed plugin values + SWFUpload.speed.extendFile = function (file, trackingList) { + var tracking; + + if (!file) { + return file; + } + + if (trackingList) { + tracking = trackingList[file.id]; + } + + if (tracking) { + file.currentSpeed = tracking.currentSpeed; + file.averageSpeed = tracking.averageSpeed; + file.movingAverageSpeed = tracking.movingAverageSpeed; + file.timeRemaining = tracking.timeRemaining; + file.timeElapsed = tracking.timeElapsed; + file.percentUploaded = tracking.percentUploaded; + file.sizeUploaded = tracking.bytesUploaded; + + } else { + file.currentSpeed = 0; + file.averageSpeed = 0; + file.movingAverageSpeed = 0; + file.timeRemaining = 0; + file.timeElapsed = 0; + file.percentUploaded = 0; + file.sizeUploaded = 0; + } + + return file; + }; + + // Private: Updates the speed tracking object, or creates it if necessary + SWFUpload.prototype.updateTracking = function (file, bytesUploaded) { + var tracking = this.fileSpeedStats[file.id]; + if (!tracking) { + this.fileSpeedStats[file.id] = tracking = {}; + } + + // Sanity check inputs + bytesUploaded = bytesUploaded || tracking.bytesUploaded || 0; + if (bytesUploaded < 0) { + bytesUploaded = 0; + } + if (bytesUploaded > file.size) { + bytesUploaded = file.size; + } + + var tickTime = (new Date()).getTime(); + if (!tracking.startTime) { + tracking.startTime = (new Date()).getTime(); + tracking.lastTime = tracking.startTime; + tracking.currentSpeed = 0; + tracking.averageSpeed = 0; + tracking.movingAverageSpeed = 0; + tracking.movingAverageHistory = []; + tracking.timeRemaining = 0; + tracking.timeElapsed = 0; + tracking.percentUploaded = bytesUploaded / file.size; + tracking.bytesUploaded = bytesUploaded; + } else if (tracking.startTime > tickTime) { + this.debug("When backwards in time"); + } else { + // Get time and deltas + var now = (new Date()).getTime(); + var lastTime = tracking.lastTime; + var deltaTime = now - lastTime; + var deltaBytes = bytesUploaded - tracking.bytesUploaded; + + if (deltaBytes === 0 || deltaTime === 0) { + return tracking; + } + + // Update tracking object + tracking.lastTime = now; + tracking.bytesUploaded = bytesUploaded; + + // Calculate speeds + tracking.currentSpeed = (deltaBytes * 8 ) / (deltaTime / 1000); + tracking.averageSpeed = (tracking.bytesUploaded * 8) / ((now - tracking.startTime) / 1000); + + // Calculate moving average + tracking.movingAverageHistory.push(tracking.currentSpeed); + if (tracking.movingAverageHistory.length > this.settings.moving_average_history_size) { + tracking.movingAverageHistory.shift(); + } + + tracking.movingAverageSpeed = SWFUpload.speed.calculateMovingAverage(tracking.movingAverageHistory); + + // Update times + tracking.timeRemaining = (file.size - tracking.bytesUploaded) * 8 / tracking.movingAverageSpeed; + tracking.timeElapsed = (now - tracking.startTime) / 1000; + + // Update percent + tracking.percentUploaded = (tracking.bytesUploaded / file.size * 100); + } + + return tracking; + }; + SWFUpload.speed.removeTracking = function (file, trackingList) { + try { + trackingList[file.id] = null; + delete trackingList[file.id]; + } catch (ex) { + } + }; + + SWFUpload.speed.formatUnits = function (baseNumber, unitDivisors, unitLabels, singleFractional) { + var i, unit, unitDivisor, unitLabel; + + if (baseNumber === 0) { + return "0 " + unitLabels[unitLabels.length - 1]; + } + + if (singleFractional) { + unit = baseNumber; + unitLabel = unitLabels.length >= unitDivisors.length ? unitLabels[unitDivisors.length - 1] : ""; + for (i = 0; i < unitDivisors.length; i++) { + if (baseNumber >= unitDivisors[i]) { + unit = (baseNumber / unitDivisors[i]).toFixed(2); + unitLabel = unitLabels.length >= i ? " " + unitLabels[i] : ""; + break; + } + } + + return unit + unitLabel; + } else { + var formattedStrings = []; + var remainder = baseNumber; + + for (i = 0; i < unitDivisors.length; i++) { + unitDivisor = unitDivisors[i]; + unitLabel = unitLabels.length > i ? " " + unitLabels[i] : ""; + + unit = remainder / unitDivisor; + if (i < unitDivisors.length -1) { + unit = Math.floor(unit); + } else { + unit = unit.toFixed(2); + } + if (unit > 0) { + remainder = remainder % unitDivisor; + + formattedStrings.push(unit + unitLabel); + } + } + + return formattedStrings.join(" "); + } + }; + + SWFUpload.speed.formatBPS = function (baseNumber) { + var bpsUnits = [1073741824, 1048576, 1024, 1], bpsUnitLabels = ["Gbps", "Mbps", "Kbps", "bps"]; + return SWFUpload.speed.formatUnits(baseNumber, bpsUnits, bpsUnitLabels, true); + + }; + SWFUpload.speed.formatTime = function (baseNumber) { + var timeUnits = [86400, 3600, 60, 1], timeUnitLabels = ["d", "h", "m", "s"]; + return SWFUpload.speed.formatUnits(baseNumber, timeUnits, timeUnitLabels, false); + + }; + SWFUpload.speed.formatBytes = function (baseNumber) { + var sizeUnits = [1073741824, 1048576, 1024, 1], sizeUnitLabels = ["GB", "MB", "KB", "bytes"]; + return SWFUpload.speed.formatUnits(baseNumber, sizeUnits, sizeUnitLabels, true); + + }; + SWFUpload.speed.formatPercent = function (baseNumber) { + return baseNumber.toFixed(2) + " %"; + }; + + SWFUpload.speed.calculateMovingAverage = function (history) { + var vals = [], size, sum = 0.0, mean = 0.0, varianceTemp = 0.0, variance = 0.0, standardDev = 0.0; + var i; + var mSum = 0, mCount = 0; + + size = history.length; + + // Check for sufficient data + if (size >= 8) { + // Clone the array and Calculate sum of the values + for (i = 0; i < size; i++) { + vals[i] = history[i]; + sum += vals[i]; + } + + mean = sum / size; + + // Calculate variance for the set + for (i = 0; i < size; i++) { + varianceTemp += Math.pow((vals[i] - mean), 2); + } + + variance = varianceTemp / size; + standardDev = Math.sqrt(variance); + + //Standardize the Data + for (i = 0; i < size; i++) { + vals[i] = (vals[i] - mean) / standardDev; + } + + // Calculate the average excluding outliers + var deviationRange = 2.0; + for (i = 0; i < size; i++) { + + if (vals[i] <= deviationRange && vals[i] >= -deviationRange) { + mCount++; + mSum += history[i]; + } + } + + } else { + // Calculate the average (not enough data points to remove outliers) + mCount = size; + for (i = 0; i < size; i++) { + mSum += history[i]; + } + } + + return mSum / mCount; + }; + +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload license.txt b/media_tree/static/media_tree/lib/swfupload/swfupload license.txt new file mode 100644 index 0000000..80651f4 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload license.txt @@ -0,0 +1,12 @@ +/** + * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com + * + * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/ + * + * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + */ \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/AsyncJPEGEncoder.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/AsyncJPEGEncoder.as new file mode 100644 index 0000000..4ac3108 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/AsyncJPEGEncoder.as @@ -0,0 +1,761 @@ +ďťż// with thanks to http://www.bytearray.org/?p=775 for the optimizations +// flash 10 only +package +{ + import EncodeCompleteEvent; + + import flash.display.BitmapData; + import flash.events.EventDispatcher; + import flash.utils.ByteArray; + import flash.utils.getTimer; + import flash.utils.setTimeout; + + [Event(name=EncodeCompleteEvent.COMPLETE, type="EncodeCompleteEvent")] + [Event(name=EncodeProgressEvent.PROGRESS, type="EncodeProgressEvent")] + + public class AsyncJPEGEncoder extends EventDispatcher + { + private var width:int; + private var height:int; + private var transparent:Boolean; + + private var runTime:uint; + private var haltTime:uint; + + private var loopTime:int; + + private var sourceBitmapData:BitmapData; + private var sourceByteArray:ByteArray; + + private var DCY:Number; + private var DCU:Number; + private var DCV:Number; + + private var ypos:int; + + // Static table initialization + private const ZigZag:Vector. = Vector.([ + 0, 1, 5, 6,14,15,27,28, + 2, 4, 7,13,16,26,29,42, + 3, 8,12,17,25,30,41,43, + 9,11,18,24,31,40,44,53, + 10,19,23,32,39,45,52,54, + 20,22,33,38,46,51,55,60, + 21,34,37,47,50,56,59,61, + 35,36,48,49,57,58,62,63 + ]); + private var YTable:Vector. = new Vector.(64, true); + private var UVTable:Vector. = new Vector.(64, true); + private var outputfDCTQuant:Vector. = new Vector.(64, true); + private var fdtbl_Y:Vector. = new Vector.(64, true); + private var fdtbl_UV:Vector. = new Vector.(64, true); + private var sf:int; + + private const aasf:Vector. = Vector.([ + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + ]); + + private var YQT:Vector. = Vector.([ + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68,109,103, 77, + 24, 35, 55, 64, 81,104,113, 92, + 49, 64, 78, 87,103,121,120,101, + 72, 92, 95, 98,112,100,103, 99 + ]); + + private const UVQT:Vector. = Vector.([ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + ]); + + private function initQuantTables(sf:int):void + { + var i:int; + const I64:int = 64; + const I8:int = 8; + for (i = 0; i < I64; ++i) + { + var t:int = int((YQT[i]*sf+50)*0.01); + if (t < 1) { + t = 1; + } else if (t > 255) { + t = 255; + } + YTable[ZigZag[i]] = t; + } + + for (i = 0; i < I64; i++) + { + var u:int = int((UVQT[i]*sf+50)*0.01); + if (u < 1) { + u = 1; + } else if (u > 255) { + u = 255; + } + UVTable[ZigZag[i]] = u; + } + i = 0; + for (var row:int = 0; row < I8; ++row) + { + for (var col:int = 0; col < I8; ++col) + { + fdtbl_Y[i] = (1 / (YTable [ZigZag[i]] * aasf[row] * aasf[col] * I8)); + fdtbl_UV[i] = (1 / (UVTable[ZigZag[i]] * aasf[row] * aasf[col] * I8)); + i++; + } + } + } + + private var YDC_HT:Vector.; + private var UVDC_HT:Vector.; + private var YAC_HT:Vector.; + private var UVAC_HT:Vector.; + + private function computeHuffmanTbl(nrcodes:Vector., std_table:Vector.):Vector. + { + var codevalue:int = 0; + var pos_in_table:int = 0; + var HT:Vector. = new Vector.(251, true); + var bitString:BitString; + for (var k:int=1; k<=16; ++k) + { + for (var j:int=1; j<=nrcodes[k]; ++j) + { + HT[std_table[pos_in_table]] = bitString = new BitString(); + bitString.val = codevalue; + bitString.len = k; + pos_in_table++; + codevalue++; + } + codevalue<<=1; + } + return HT; + } + + private var std_dc_luminance_nrcodes:Vector. = Vector.([0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]); + private var std_dc_luminance_values:Vector. = Vector.([0,1,2,3,4,5,6,7,8,9,10,11]); + private var std_ac_luminance_nrcodes:Vector. = Vector.([0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]); + private var std_ac_luminance_values:Vector. = Vector.([0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, + 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, + 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, + 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, + 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, + 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, + 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, + 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, + 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, + 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, + 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, + 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, + 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, + 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, + 0xf9,0xfa]); + + private var std_dc_chrominance_nrcodes:Vector. = Vector.([0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]); + private var std_dc_chrominance_values:Vector. = Vector.([0,1,2,3,4,5,6,7,8,9,10,11]); + private var std_ac_chrominance_nrcodes:Vector. = Vector.([0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]); + private var std_ac_chrominance_values:Vector. = Vector.([0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, + 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, + 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, + 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, + 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, + 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, + 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, + 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, + 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, + 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, + 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, + 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, + 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, + 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, + 0xf9,0xfa + ]); + + private function initHuffmanTbl():void + { + YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); + UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); + YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); + UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); + } + + private var bitcode:Vector. = new Vector.(65535, true); + private var category:Vector. = new Vector.(65535, true); + + private function initCategoryNumber():void + { + var nrlower:int = 1; + var nrupper:int = 2; + var bitString:BitString; + const I15:int = 15; + var pos:int; + for (var cat:int=1; cat<=I15; ++cat) + { + //Positive numbers + for (var nr:int=nrlower; nr= 0 ) + { + if (value & uint(1 << posval) ) { + bytenew |= uint(1 << bytepos); + } + posval--; + bytepos--; + if (bytepos < 0) + { + if (bytenew == 0xFF) + { + byteout.writeByte(0xFF); + byteout.writeByte(0); + } + else { + byteout.writeByte(bytenew); + } + bytepos=7; + bytenew=0; + } + } + } + + // DCT & quantization core + + private function fDCTQuant(data:Vector., fdtbl:Vector.):Vector. + { + /* Pass 1: process rows. */ + var dataOff:int=0; + var d0:Number, d1:Number, d2:Number, d3:Number, d4:Number, d5:Number, d6:Number, d7:Number; + var i:int; + const I8:int = 8; + const I64:int = 64; + for (i=0; i 0.0) ? int(fDCTQuant + 0.5) : int(fDCTQuant - 0.5); + } + return outputfDCTQuant; + } + + // Chunk writing + private function writeAPP0():void + { + byteout.writeShort(0xFFE0); // marker + byteout.writeShort(16); // length + byteout.writeByte(0x4A); // J + byteout.writeByte(0x46); // F + byteout.writeByte(0x49); // I + byteout.writeByte(0x46); // F + byteout.writeByte(0); // = "JFIF",'\0' + byteout.writeByte(1); // versionhi + byteout.writeByte(1); // versionlo + byteout.writeByte(0); // xyunits + byteout.writeShort(1); // xdensity + byteout.writeShort(1); // ydensity + byteout.writeByte(0); // thumbnwidth + byteout.writeByte(0); // thumbnheight + } + + private function writeSOF0():void + { + byteout.writeShort(0xFFC0); // marker + byteout.writeShort(17); // length, truecolor YUV JPG + byteout.writeByte(8); // precision + byteout.writeShort(height); + byteout.writeShort(width); + byteout.writeByte(3); // nrofcomponents + byteout.writeByte(1); // IdY + byteout.writeByte(0x11); // HVY + byteout.writeByte(0); // QTY + byteout.writeByte(2); // IdU + byteout.writeByte(0x11); // HVU + byteout.writeByte(1); // QTU + byteout.writeByte(3); // IdV + byteout.writeByte(0x11); // HVV + byteout.writeByte(1); // QTV + } + + private function writeDQT():void + { + byteout.writeShort(0xFFDB); // marker + byteout.writeShort(132); // length + byteout.writeByte(0); + + var i:int; + const I64:int = 64; + for (i=0; i = new Vector.(64, true); + + private function processDU(CDU:Vector., fdtbl:Vector., DC:Number, HTDC:Vector., HTAC:Vector.):Number + { + var EOB:BitString = HTAC[0x00]; + var M16zeroes:BitString = HTAC[0xF0]; + var pos:int; + const I16:int = 16; + const I63:int = 63; + const I64:int = 64; + var DU_DCT:Vector. = fDCTQuant(CDU, fdtbl); + //ZigZag reorder + for (var j:int=0;j0)&&(DU[end0pos]==0); end0pos--) {}; + //end0pos = first element in reverse order !=0 + if ( end0pos == 0) { + writeBits(EOB); + return DC; + } + var i:int = 1; + var lng:int; + while ( i <= end0pos ) { + var startpos:int = i; + for (; (DU[i]==0) && (i<=end0pos); ++i) {} + var nrzeroes:int = i-startpos; + if ( nrzeroes >= I16 ) { + lng = nrzeroes>>4; + for (var nrmarker:int=1; nrmarker <= lng; ++nrmarker) + writeBits(M16zeroes); + nrzeroes = int(nrzeroes&0xF); + } + pos = int(32767+DU[i]); + writeBits(HTAC[int((nrzeroes<<4)+category[pos])]); + writeBits(bitcode[pos]); + i++; + } + if ( end0pos != I63 ) { + writeBits(EOB); + } + return DC; + } + + private var YDU:Vector. = new Vector.(64, true); + private var UDU:Vector. = new Vector.(64, true); + private var VDU:Vector. = new Vector.(64, true); + + private function RGB2YUV(xpos:int, ypos:int):void + { + var pos:int=0; + const I8:int = 8; + for (var y:int=0; y>16)&0xFF; + var G:int = (P>> 8)&0xFF; + var B:int = (P )&0xFF; + YDU[int(pos)]=((( 0.29900)*R+( 0.58700)*G+( 0.11400)*B))-0x80; + UDU[int(pos)]=(((-0.16874)*R+(-0.33126)*G+( 0.50000)*B)); + VDU[int(pos)]=((( 0.50000)*R+(-0.41869)*G+(-0.08131)*B)); + ++pos; + } + } + } + + public function AsyncJPEGEncoder(quality:int=50, run:uint=500, stop:uint=500) + { + if (quality <= 0) { + quality = 1; + } + + if (quality > 100) { + quality = 100; + } + + sf = quality < 50 ? int(5000 / quality) : int(200 - (quality<<1)); + + runTime=run; + haltTime=stop; + + init(); + } + + private function init():void + { + ZigZag.fixed = true; + aasf.fixed = true; + YQT.fixed = true; + UVQT.fixed = true; + std_ac_chrominance_nrcodes.fixed = true; + std_ac_chrominance_values.fixed = true; + std_ac_luminance_nrcodes.fixed = true; + std_ac_luminance_values.fixed = true; + std_dc_chrominance_nrcodes.fixed = true; + std_dc_chrominance_values.fixed = true; + std_dc_luminance_nrcodes.fixed = true; + std_dc_luminance_values.fixed = true; + // Create tables + initHuffmanTbl(); + initCategoryNumber(); + initQuantTables(sf); + } + + public function encode(bitmapData:BitmapData):void + { + internalEncode(bitmapData, bitmapData.width, bitmapData.height, + bitmapData.transparent); + } + + public function encodeByteArray(byteArray:ByteArray, width:int, height:int, + transparent:Boolean = true):void + { + internalEncode(byteArray, width, height, transparent); + } + + private function internalEncode(source:Object, width:int, height:int, + transparent:Boolean = true):void + { + this.width=width; + this.height=height; + this.transparent=transparent; + + startEncode(source); + loopEncode(); + } + private function startEncode(source:Object):void + { + // The source is either a BitmapData or a ByteArray. + sourceBitmapData = source as BitmapData; + sourceByteArray = source as ByteArray; + + if (sourceByteArray) { + sourceByteArray.position = 0; + } + + // Initialize bit writer + byteout = new ByteArray(); + + bytenew=0; + bytepos=7; + + // Add JPEG headers + byteout.writeShort(0xFFD8); // SOI + writeAPP0(); + writeDQT(); + writeSOF0(); + writeDHT(); + writeSOS(); + + // Encode 8x8 macroblocks + DCY=0; + DCU=0; + DCV=0; + + bytenew=0; + bytepos=7; + + ypos=0; + } + + private function loopEncode():void + { + //async + if(loopTime>runTime) + { + loopTime=0; + setTimeout(loopEncode, haltTime); + return; + } + + if (ypos= 0 ) + { + var fillbits:BitString = new BitString(); + fillbits.len = bytepos+1; + fillbits.val = (1<<(bytepos+1))-1; + writeBits(fillbits); + } + byteout.writeShort(0xFFD9); //EOI + + dispatchEvent(new EncodeCompleteEvent(byteout)); + } + } +} + +final class BitString +{ + public var len:int = 0; + public var val:int = 0; +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/EncodeCompleteEvent.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/EncodeCompleteEvent.as new file mode 100644 index 0000000..bd8f614 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/EncodeCompleteEvent.as @@ -0,0 +1,28 @@ +package +{ + import flash.events.Event; + import flash.utils.ByteArray; + + public class EncodeCompleteEvent extends Event + { + public static const COMPLETE:String='encodeCompleteEvent'; + + private var image:ByteArray; + + public function EncodeCompleteEvent(data:ByteArray, bubbles:Boolean=false, cancelable:Boolean=false) + { + super(COMPLETE, bubbles, cancelable); + image=data; + } + + public function get data():ByteArray + { + return image; + } + + override public function clone() : Event + { + return new EncodeCompleteEvent(image,bubbles,cancelable); + } + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ExternalCall.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ExternalCall.as new file mode 100644 index 0000000..cac8186 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ExternalCall.as @@ -0,0 +1,144 @@ +package { + import flash.external.ExternalInterface; + + internal class ExternalCall + { + + /*public function ExternalCall() + { + + } + */ + + public static function Simple(callback:String):void { + ExternalInterface.call(callback); + } + + public static function SendImage(callback:String, preview_name:String, file_id:String):void { + ExternalInterface.call(callback, EscapeMessage(preview_name), EscapeMessage(file_id)); + } + + public static function FileQueued(callback:String, file_object:Object):void { + ExternalInterface.call(callback, EscapeMessage(file_object)); + } + public static function FileQueueError(callback:String, error_code:Number, file_object:Object, message:String):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message)); + + } + public static function FileDialogComplete(callback:String, num_files_selected:Number, num_files_queued:Number, total_num_files_queued:Number):void { + + ExternalInterface.call(callback, EscapeMessage(num_files_selected), EscapeMessage(num_files_queued), EscapeMessage(total_num_files_queued)); + + } + + public static function UploadResizeStart(callback:String, file_object:Object, resizeSettings:Object):void { + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(resizeSettings)); + } + + public static function UploadStart(callback:String, file_object:Object):void { + ExternalInterface.call(callback, EscapeMessage(file_object)); + } + + public static function UploadProgress(callback:String, file_object:Object, bytes_loaded:uint, bytes_total:uint):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(bytes_loaded), EscapeMessage(bytes_total)); + + } + public static function UploadSuccess(callback:String, file_object:Object, server_data:String, responseReceived:Boolean):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(server_data), EscapeMessage(responseReceived)); + + } + public static function UploadError(callback:String, error_code:Number, file_object:Object, message:String):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message)); + + } + public static function UploadComplete(callback:String, file_object:Object):void { + + ExternalInterface.call(callback, EscapeMessage(file_object)); + + } + public static function Debug(callback:String, message:String):void { + + ExternalInterface.call(callback, EscapeMessage(message)); + } + + public static function Bool(callback:String):Boolean { + return ExternalInterface.call(callback); + } + + public static function Generic(callback:String, ... rest):* { + var escapedRest:Array = new Array(); + for each (var i:* in rest) + { + escapedRest.push(EscapeMessage(i)); + } + + return ExternalInterface.call(callback, escapedRest); + } + + + /* Escapes all the backslashes which are not translated correctly in the Flash -> JavaScript Interface + * + * These functions had to be developed because the ExternalInterface has a bug that simply places the + * value a string in quotes (except for a " which is escaped) in a JavaScript string literal which + * is executed by the browser. These often results in improperly escaped string literals if your + * input string has any backslash characters. For example the string: + * "c:\Program Files\uploadtools\" + * is placed in a string literal (with quotes escaped) and becomes: + * var __flash__temp = "\"c:\Program Files\uploadtools\\""; + * This statement will cause errors when executed by the JavaScript interpreter: + * 1) The first \" is succesfully transformed to a " + * 2) \P is translated to P and the \ is lost + * 3) \u is interpreted as a unicode character and causes an error in IE + * 4) \\ is translated to \ + * 5) leaving an unescaped " which causes an error + * + * I fixed this by escaping \ characters in all outgoing strings. The above escaped string becomes: + * var __flash__temp = "\"c:\\Program Files\\uploadtools\\\""; + * which contains the correct string literal. + * + * Note: The "var __flash__temp = " portion of the example is part of the ExternalInterface not part of + * my escaping routine. + */ + private static function EscapeMessage(message:*):* { + if (message is String) { + message = EscapeString(message); + } + else if (message is Array) { + message = EscapeArray(message); + } + else if (message is Object) { + message = EscapeObject(message); + } + + return message; + } + + private static function EscapeString(message:String):String { + var replaceSlashPattern:RegExp = /\\/g; + var replaceNewLinePattern:RegExp = /\n/g; + var replaceCarraigeReturnPattern:RegExp = /\r/g; + var replaceNullPattern:RegExp = /\0/g; + + return message.replace(replaceSlashPattern, "\\\\").replace(replaceNewLinePattern, "\\n").replace(replaceCarraigeReturnPattern, "\\r").replace(replaceNullPattern, "\\0"); + } + private static function EscapeArray(message_array:Array):Array { + var length:uint = message_array.length; + var i:uint = 0; + for (i; i < length; i++) { + message_array[i] = EscapeMessage(message_array[i]); + } + return message_array; + } + private static function EscapeObject(message_obj:Object):Object { + for (var name:String in message_obj) { + message_obj[name] = EscapeMessage(message_obj[name]); + } + return message_obj; + } + + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/FileItem.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/FileItem.as new file mode 100644 index 0000000..105eeaa --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/FileItem.as @@ -0,0 +1,126 @@ +package { + import flash.events.EventDispatcher; + import flash.net.FileReference; + + internal class FileItem + { + private static var file_id_sequence:Number = 0; // tracks the file id sequence + + private var postObject:Object; + public var file_reference:FileReference; + public var resized_uploader:MultipartURLLoader; + public var id:String; + public var index:Number = -1; + public var file_status:int = 0; + public var finalUploadProgress:Boolean = false; + public var upload_type:int = 0; + private var js_object:Object; + + public static var FILE_STATUS_QUEUED:int = -1; + public static var FILE_STATUS_IN_PROGRESS:int = -2; + public static var FILE_STATUS_ERROR:int = -3; + public static var FILE_STATUS_SUCCESS:int = -4; + public static var FILE_STATUS_CANCELLED:int = -5; + public static var FILE_STATUS_NEW:int = -6; // This file status should never be sent to JavaScript + + public static var UPLOAD_TYPE_NORMAL:int = -1; + public static var UPLOAD_TYPE_RESIZE:int = -2; + + public function FileItem(file_reference:FileReference, control_id:String, index:Number) + { + this.postObject = {}; + this.file_reference = file_reference; + this.resized_uploader = null; + this.id = control_id + "_" + (FileItem.file_id_sequence++); + this.file_status = FileItem.FILE_STATUS_NEW; + this.index = index; + + + this.js_object = { + id: this.id, + index: this.index, + uploadtype: this.upload_type, + post: this.GetPostObject() + }; + + // Cleanly attempt to retrieve the FileReference info + // this can fail and so is wrapped in try..catch + try { + this.js_object.name = this.file_reference.name; + this.js_object.size = this.file_reference.size; + this.js_object.type = this.file_reference.type || ""; + this.js_object.creationdate = this.file_reference.creationDate || new Date(0); + this.js_object.modificationdate = this.file_reference.modificationDate || new Date(0); + } catch (ex:Error) { + this.file_status = FileItem.FILE_STATUS_ERROR; + } + + this.js_object.filestatus = this.file_status; + } + + public function GetUploader():EventDispatcher { + return upload_type === FileItem.UPLOAD_TYPE_NORMAL ? this.file_reference : this.resized_uploader; + } + + public function AddParam(name:String, value:String):void { + this.postObject[name] = value; + } + + public function RemoveParam(name:String):void { + delete this.postObject[name]; + } + + public function GetPostObject(escape:Boolean = false):Object { + if (escape) { + var escapedPostObject:Object = { }; + for (var k:String in this.postObject) { + if (this.postObject.hasOwnProperty(k)) { + var escapedName:String = FileItem.EscapeParamName(k); + escapedPostObject[escapedName] = this.postObject[k]; + } + } + return escapedPostObject; + } else { + return this.postObject; + } + } + + // Create the simply file object that is passed to the browser + public function ToJavaScriptObject():Object { + this.js_object.filestatus = this.file_status; + this.js_object.post = this.GetPostObject(true); + + return this.js_object; + } + + public function toString():String { + return "FileItem - ID: " + this.id; + } + + /* + // The purpose of this function is to escape the property names so when Flash + // passes them back to javascript they can be interpretted correctly. + // ***They have to be unescaped again by JavaScript.** + // + // This works around a bug where Flash sends objects this way: + // object.parametername = "value"; + // instead of + // object["parametername"] = "value"; + // This can be a problem if the parameter name has characters that are not + // allowed in JavaScript identifiers: + // object.parameter.name! = "value"; + // does not work but, + // object["parameter.name!"] = "value"; + // would have worked. + */ + public static function EscapeParamName(name:String):String { + name = name.replace(/[^a-z0-9_]/gi, FileItem.EscapeCharacter); + name = name.replace(/^[0-9]/, FileItem.EscapeCharacter); + return name; + } + public static function EscapeCharacter():String { + return "$" + ("0000" + arguments[0].charCodeAt(0).toString(16)).substr(-4, 4); + } + + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizer.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizer.as new file mode 100644 index 0000000..956de82 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizer.as @@ -0,0 +1,203 @@ +ďťż/* + * http://code.google.com/p/sync-to-async/ -- Apache 2.0 License + */ + +// FIXME -- handler errors for the loader and file_reference objects + +package +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Loader; + import flash.errors.IOError; + import flash.events.ErrorEvent; + import flash.events.Event; + import flash.events.IOErrorEvent; + import flash.events.EventDispatcher; + import flash.filters.BitmapFilter; + import flash.filters.BitmapFilterQuality; + import flash.filters.BlurFilter; + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.net.FileReference; + import flash.utils.ByteArray; + import cmodule.jpegencoder.CLibInit; + + /* + Technique adapted from Kun Janos (http://kun-janos.ro/blog/?p=107) + */ + public class ImageResizer extends EventDispatcher + { + private var file:FileItem = null; + private var targetWidth:Number = 0; + private var targetHeight:Number = 0; + private var newWidth:Number = 1; + private var newHeight:Number = 1; + private var encoder:Number = ImageResizer.JPEGENCODER; + private var quality:Number = 100; + private var allowEnlarging:Boolean = true; + + public static const JPEGENCODER:Number = -1; + public static const PNGENCODE:Number = -2; + + public function ImageResizer(file:FileItem, targetWidth:Number, targetHeight:Number, encoder:Number, quality:Number = 100, allowEnlarging:Boolean = true) { + this.file = file; + this.targetHeight = targetHeight; + this.targetWidth = targetWidth; + this.encoder = encoder; + this.quality = quality; + this.allowEnlarging = allowEnlarging; + + if (this.encoder != ImageResizer.JPEGENCODER && this.encoder != ImageResizer.PNGENCODE) { + this.encoder = ImageResizer.JPEGENCODER; + } + if (this.quality < 0 || this.quality > 100) { + this.quality = 100; + } + } + + public function ResizeImage():void { + try { + this.file.file_reference.addEventListener(Event.COMPLETE, this.fileLoad_Complete); + this.file.file_reference.load(); + } catch (ex:Error) { + this.file.file_reference.removeEventListener(Event.COMPLETE, this.fileLoad_Complete); + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Loading from file reference: " + ex.message)); + this.file = null; + } + } + + private function fileLoad_Complete(event:Event):void { + try { + event.target.removeEventListener(Event.COMPLETE, this.fileLoad_Complete); + + var loader:Loader = new Loader(); + + // Load the image data, resizing takes place in the event handler + loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.loader_Complete); + loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.loader_Error); + + loader.loadBytes(FileReference(event.target).data); + } catch (ex:Error) { + loader.removeEventListener(Event.COMPLETE, this.loader_Complete); + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Loading Loader" + ex.message)); + this.file = null; + } + } + private function loader_Error(event:IOErrorEvent):void { + try { + event.target.removeEventListener(Event.COMPLETE, this.loader_Complete); + event.target.removeEventListener(IOErrorEvent.IO_ERROR, this.loader_Error); + } catch (ex:Error) {} + + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Resizing: " + event.text)); + } + + private function loader_Complete(event:Event):void { + try { + var bytes:ByteArray; + + event.target.removeEventListener(Event.COMPLETE, this.loader_Complete); + event.target.removeEventListener(IOErrorEvent.IO_ERROR, this.loader_Error); + + var loader:Loader = Loader(event.target.loader); + + var contentType:String = loader.contentLoaderInfo.contentType; + + // Calculate the new image size + var targetRatio:Number = this.targetWidth / this.targetHeight; + var imgRatio:Number = loader.width / loader.height; + this.newHeight = (targetRatio > imgRatio) ? this.targetHeight : Math.min(this.targetWidth / imgRatio, this.targetHeight); + this.newWidth = (targetRatio > imgRatio) ? Math.min(imgRatio * this.targetHeight, this.targetWidth) : this.targetWidth; + + var resizedBmp:BitmapData = null; + + // Get the image data + var bmp:BitmapData = Bitmap(loader.content).bitmapData; + + loader.unload(); + loader = null; + + // If enlarging is not allowed but the new width causes enlarging then adjust the dimensions + if (!this.allowEnlarging && (this.newWidth > bmp.width || this.newHeight > bmp.height)) { + this.newWidth = bmp.width; + this.newHeight = bmp.height; + } + + // Blur it a bit if it is sizing smaller + if (this.newWidth < bmp.width || this.newHeight < bmp.height) { + // Apply the blur filter that helps clean up the resized image result + var blurMultiplier:Number = 1.15; // 1.25; + var blurXValue:Number = Math.max(1, bmp.width / this.newWidth) * blurMultiplier; + var blurYValue:Number = Math.max(1, bmp.height / this.newHeight) * blurMultiplier; + + var blurFilter:BlurFilter = new BlurFilter(blurXValue, blurYValue, int(BitmapFilterQuality.LOW)); + bmp.applyFilter(bmp, new Rectangle(0, 0, bmp.width, bmp.height), new Point(0, 0), blurFilter); + } + + if (this.newWidth < bmp.width || this.newHeight < bmp.height || this.IsTranscoding(contentType)) { + // Apply the resizing + var matrix:Matrix = new Matrix(); + matrix.identity(); + matrix.createBox(this.newWidth / bmp.width, this.newHeight / bmp.height); + + resizedBmp = new BitmapData(this.newWidth, this.newHeight, true, 0x000000); + resizedBmp.draw(bmp, matrix, null, null, null, true); + + bmp.dispose(); + + if (this.encoder == ImageResizer.PNGENCODE) { + var pngEncoder:PNGEncoder = new PNGEncoder(PNGEncoder.TYPE_SUBFILTER, 1); + pngEncoder.addEventListener(EncodeCompleteEvent.COMPLETE, this.EncodeCompleteHandler); + pngEncoder.addEventListener(ErrorEvent.ERROR, this.EncodeErrorHandler); + pngEncoder.encode(resizedBmp); + } else { + //var jpegEncoder:AsyncJPEGEncoder = new AsyncJPEGEncoder(this.quality, 0, 100); + + //jpegEncoder.addEventListener(EncodeCompleteEvent.COMPLETE, this.EncodeCompleteHandler); + //jpegEncoder.encode(resizedBmp); + + this.ba = resizedBmp.getPixels(resizedBmp.rect); + this.ba.position = 0; + this.baOut = new ByteArray(); + + var cLibEncoder:Object = (new CLibInit).init(); + cLibEncoder.encodeAsync(compressFinished, this.ba, this.baOut, resizedBmp.width, resizedBmp.height, this.quality); + } + } else { + // Just send along the unmodified data + dispatchEvent(new ImageResizerEvent(ImageResizerEvent.COMPLETE, this.file.file_reference.data, this.encoder)); + } + + } catch (ex:Error) { + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Resizing: " + ex.message)); + } + + this.file = null; + } + + private function IsTranscoding(type:String):Boolean { + return !((type == "image/jpeg" && this.encoder == ImageResizer.JPEGENCODER) || (type == "image/png" && this.encoder == ImageResizer.PNGENCODE)); + } + + private function EncodeErrorHandler(e:ErrorEvent):void { + dispatchEvent(e); + } + + private var ba:ByteArray; + private var baOut:ByteArray; + private function compressFinished(out:ByteArray):void { + this.baOut.position = 0; + this.ba.length = 0; + + this.EncodeCompleteHandler(new EncodeCompleteEvent(baOut)); + } + + private function EncodeCompleteHandler(e:EncodeCompleteEvent):void { + dispatchEvent(new ImageResizerEvent(ImageResizerEvent.COMPLETE, e.data, this.encoder)); + } + + } + +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizerEvent.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizerEvent.as new file mode 100644 index 0000000..e1860cf --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/ImageResizerEvent.as @@ -0,0 +1,20 @@ +ďťżpackage +{ + import flash.events.Event; + import flash.utils.ByteArray; + + public class ImageResizerEvent extends Event + { + public var data:ByteArray; + public var encoding:Number; + + public static const COMPLETE:String = "COMPLETE"; + + public function ImageResizerEvent (type:String, data:ByteArray, encoding:Number) + { + super(type); + this.data = data; + this.encoding = encoding; + } + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/MultipartURLLoader.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/MultipartURLLoader.as new file mode 100644 index 0000000..1dcb296 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/MultipartURLLoader.as @@ -0,0 +1,352 @@ +ďťżpackage +{ + import flash.errors.IllegalOperationError; + import flash.errors.IOError; + import flash.events.DataEvent; + import flash.events.ErrorEvent; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.HTTPStatusEvent; + import flash.events.IOErrorEvent; + import flash.events.ProgressEvent; + import flash.events.SecurityErrorEvent; + import flash.net.URLLoader; + import flash.net.URLLoaderDataFormat; + import flash.net.URLRequest; + import flash.net.URLRequestHeader; + import flash.net.URLRequestMethod; + import flash.net.URLVariables; + import flash.utils.ByteArray; + import flash.utils.Dictionary; + import flash.utils.Endian; + import flash.utils.setTimeout; + import flash.utils.clearInterval; + + /** + * Multipart URL Loader + * + * Original idea by Marston Development Studio - http://marstonstudio.com/?p=36 + * + * License: MIT + * + * @author Eugene Zatepyakin + * @version 1.3 + * @link http://blog.inspirit.ru/ + * + * + * Heavily modified from the original version. Removed multiple file support. + * Removed optional async data preparation (always async) + * After data is preparted the upload immediately starts. + * Updated to look and behave similar to the FileReference upload API + */ + public class MultipartURLLoader extends EventDispatcher + { + public static var BLOCK_SIZE:uint = 64 * 1024; + + private var _loader:URLLoader; + private var _request:URLRequest; + private var _boundary:String; + + private var _fileName:String; + private var _uploadDataFieldName:String; + private var _fileData:ByteArray; + private var _data:ByteArray; + private var _httpStatus:Number; + + private var asyncWriteTimeoutId:Number; + + + public function MultipartURLLoader(fileData:ByteArray, fileName:String) { + _loader = new URLLoader(); + _fileData = fileData; + _fileName = fileName; + } + + public function upload(request:URLRequest, uploadDataFieldName:String = "Filedata"):void { + dispatchEvent(new Event(Event.OPEN, false, false)); + + this._httpStatus = undefined; + this._request = request; + this._uploadDataFieldName = uploadDataFieldName; + this.constructPostDataAsync(); + } + + /** + * Stop loader action + */ + public function cancel():void + { + try { + clearInterval(this.asyncWriteTimeoutId); + _loader.close(); + } catch ( e:Error ) { } + + this.destroy(); + } + + + + /** + * Dispose all class instance objects + */ + public function dispose(): void + { + try { + this.cancel(); + } catch (ex:Error) {} + } + + /** + * Generate random boundary + * @return Random boundary + */ + public function getBoundary():String + { + if (_boundary == null) { + _boundary = ''; + for (var i:int = 0; i < 0x20; i++ ) { + _boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) ); + } + } + return _boundary; + } + + private function doSend():void + { + var urlRequest:URLRequest = new URLRequest(); + urlRequest.url = this._request.url; + //urlRequest.contentType = 'multipart/form-data; boundary=' + getBoundary(); + urlRequest.method = URLRequestMethod.POST; + urlRequest.data = _data; + + urlRequest.requestHeaders = this._request.requestHeaders.concat(); + urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + getBoundary())); + + this.addListener(); + try { + _loader.load(urlRequest); + } catch (ex:Error) { + dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, false, false, "Exception: " + ex.message)); + this.destroy(); + } + + } + + private function constructPostDataAsync():void + { + clearInterval(this.asyncWriteTimeoutId); + + this._data = new ByteArray(); + this._data.endian = Endian.BIG_ENDIAN; + + this._data = constructVariablesPart(this._data); + this._data = getFilePartHeader(this._data, this._fileName); + + this.asyncWriteTimeoutId = setTimeout(this.writeChunkLoop, 10, this._data, this._fileData, 0); + + } + + private function writeChunkLoop(dest:ByteArray, data:ByteArray, p:uint = 0):void + { + try { + var len:uint = Math.min(BLOCK_SIZE, data.length - p); + dest.writeBytes(data, p, len); + + if (len < BLOCK_SIZE || p + len >= data.length) { + // Finished writing file bytearray + dest = LINEBREAK(dest); + this._data = closeFilePartsData(_data); + this._data = closeDataObject(_data); + this.doSend(); + return; + } + + this.asyncWriteTimeoutId = setTimeout(this.writeChunkLoop, 10, dest, data, p + len); + } catch (ex:Error) { + dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR)); + this.destroy(); + } + } + + private function closeDataObject(postData:ByteArray):ByteArray + { + postData = BOUNDARY(postData); + postData = DOUBLEDASH(postData); + return postData; + } + + private function constructVariablesPart(postData:ByteArray):ByteArray + { + var i:uint; + var bytes:String; + + if (this._request.data is URLVariables) { + for (var name:String in this._request.data) + { + postData = BOUNDARY(postData); + postData = LINEBREAK(postData); + bytes = 'Content-Disposition: form-data; name="' + name + '"'; + for ( i = 0; i < bytes.length; i++ ) { + postData.writeByte( bytes.charCodeAt(i) ); + } + postData = LINEBREAK(postData); + postData = LINEBREAK(postData); + postData.writeUTFBytes(this._request.data[name]); + postData = LINEBREAK(postData); + } + } + + return postData; + } + + private function closeFilePartsData(postData:ByteArray):ByteArray + { + //var i:uint; + //var bytes:String; + // + //postData = LINEBREAK(postData); + //postData = BOUNDARY(postData); + //postData = LINEBREAK(postData); + //bytes = 'Content-Disposition: form-data; name="Upload"'; + //for ( i = 0; i < bytes.length; i++ ) { + //postData.writeByte( bytes.charCodeAt(i) ); + //} + //postData = LINEBREAK(postData); + //postData = LINEBREAK(postData); + //bytes = 'Submit Query'; + //for ( i = 0; i < bytes.length; i++ ) { + //postData.writeByte( bytes.charCodeAt(i) ); + //} + //postData = LINEBREAK(postData); + + return postData; + } + + private function getFilePartHeader(postData:ByteArray, fileName:String):ByteArray + { + var i:uint; + var bytes:String; + + postData = BOUNDARY(postData); + postData = LINEBREAK(postData); + bytes = 'Content-Disposition: form-data; name="' + this._uploadDataFieldName + '"; filename="'; + for ( i = 0; i < bytes.length; i++ ) { + postData.writeByte( bytes.charCodeAt(i) ); + } + + postData.writeUTFBytes(fileName); + postData = QUOTATIONMARK(postData); + + postData = LINEBREAK(postData); + bytes = 'Content-Type: application/octet-stream'; + for ( i = 0; i < bytes.length; i++ ) { + postData.writeByte( bytes.charCodeAt(i) ); + } + postData = LINEBREAK(postData); + postData = LINEBREAK(postData); + + return postData; + } + + private function onComplete( event: Event ): void + { + if (this._httpStatus === 200) { + dispatchEvent(event); + if (this._loader && this._loader.data.length > 0) { + dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, this._loader.data)); + } + } else { + dispatchEvent(new HTTPStatusEvent(HTTPStatusEvent.HTTP_STATUS, event.bubbles, event.cancelable, this._httpStatus)); + } + this.destroy(); + } + + private function onIOError( event: IOErrorEvent ): void + { + dispatchEvent( event ); + this.destroy(); + } + + private function onSecurityError( event: SecurityErrorEvent ): void + { + dispatchEvent( event ); + this.destroy(); + } + + private function onHTTPStatus( event: HTTPStatusEvent ): void + { + this._httpStatus = event.status; + } + + private function addListener(): void + { + if (this._loader != null) { + this._loader.addEventListener( Event.COMPLETE, this.onComplete, false, 0, false ); + this._loader.addEventListener( IOErrorEvent.IO_ERROR, this.onIOError, false, 0, false ); + this._loader.addEventListener( HTTPStatusEvent.HTTP_STATUS, this.onHTTPStatus, false, 0, false ); + this._loader.addEventListener( SecurityErrorEvent.SECURITY_ERROR, this.onSecurityError, false, 0, false ); + } + } + + private function removeListener(): void + { + if (this._loader != null) { + this._loader.removeEventListener( Event.COMPLETE, this.onComplete ); + this._loader.removeEventListener( IOErrorEvent.IO_ERROR, this.onIOError ); + this._loader.removeEventListener( HTTPStatusEvent.HTTP_STATUS, this.onHTTPStatus ); + this._loader.removeEventListener( SecurityErrorEvent.SECURITY_ERROR, this.onSecurityError ); + } + } + + private function BOUNDARY(p:ByteArray):ByteArray + { + var l:int = getBoundary().length; + p = DOUBLEDASH(p); + for (var i:int = 0; i < l; i++ ) { + p.writeByte( _boundary.charCodeAt( i ) ); + } + return p; + } + + private function LINEBREAK(p:ByteArray):ByteArray + { + p.writeShort(0x0d0a); + return p; + } + + private function QUOTATIONMARK(p:ByteArray):ByteArray + { + p.writeByte(0x22); + return p; + } + + private function DOUBLEDASH(p:ByteArray):ByteArray + { + p.writeShort(0x2d2d); + return p; + } + + private function destroy():void { + try { + this.removeListener(); + } catch (ex:Error) {} + + this._loader = null; + this._request = null; + this._boundary = null; + + this._fileName = null; + this._uploadDataFieldName = null; + this._fileData = null; + this._data = null; + this._httpStatus = undefined; + + try { + clearInterval(this.asyncWriteTimeoutId); + } catch (ex:Error) { } + + this.asyncWriteTimeoutId = undefined; + } + + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/PNGEncoder.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/PNGEncoder.as new file mode 100644 index 0000000..26edeaa --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/PNGEncoder.as @@ -0,0 +1,295 @@ +ďťż/* + * http://code.google.com/p/as3corelib/ -- New BSD License + * Modified to work asynchronously + * */ + +package +{ + import flash.utils.ByteArray; + import flash.display.BitmapData; + import flash.geom.Rectangle; + import flash.events.EventDispatcher; + import flash.events.ErrorEvent; + import flash.utils.setTimeout; + + public class PNGEncoder extends EventDispatcher { + public static const TYPE_NORMAL:uint = 0; + public static const TYPE_SUBFILTER:uint = 1; + + private var img:BitmapData; + private var png:ByteArray; + private var IDAT:ByteArray; + + private var type:uint; + private var delay:int; + + public function PNGEncoder(type:uint = PNGEncoder.TYPE_NORMAL, delay:int = 0) { + super(); + + this.type = type; + this.delay = delay; + + PNGEncoder.computeCRCTable(); + + } + + public function encode(img:BitmapData):void { + try { + // Create output byte array + this.png = new ByteArray(); + this.img = img; + + // Write PNG signature + this.png.writeUnsignedInt(0x89504e47); + this.png.writeUnsignedInt(0x0D0A1A0A); + + // Build IHDR chunk + var IHDR:ByteArray = new ByteArray(); + IHDR.writeInt(this.img.width); + IHDR.writeInt(this.img.height); + if(img.transparent || this.type == PNGEncoder.TYPE_NORMAL) + { + IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA + } + else + { + IHDR.writeUnsignedInt(0x08020000); //24bit RGB + } + + IHDR.writeByte(0); + + PNGEncoder.writeChunk(this.png, 0x49484452, IHDR); + + // Build IDAT chunk + this.IDAT = new ByteArray(); + + // Start encoding loop + encodeLoop(); + } catch (ex:Error) { + this.CleanUp(); + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, ex.message)); + } + } + + private function encodeLoop():void { + try { + switch(this.type) + { + case PNGEncoder.TYPE_NORMAL: + setTimeout(this.writeRaw, this.delay); + break; + case PNGEncoder.TYPE_SUBFILTER: + setTimeout(this.writeSub, this.delay); + break; + default: + this.CleanUp(); + break; + } + } catch (ex:Error) { + this.CleanUp(); + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, ex.message)); + } + + } + + private function encodeEnd():void { + try { + this.IDAT.compress(); + PNGEncoder.writeChunk(this.png, 0x49444154, this.IDAT); + + // Build IEND chunk + PNGEncoder.writeChunk(this.png, 0x49454E44, null); + + // trigger complete event + dispatchEvent(new EncodeCompleteEvent(this.png)); + } catch (ex:Error) { + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, ex.message)); + } finally { + this.CleanUp(); + } + } + + private var encodingRow:int = 0; + private function writeRaw():void + { + try { + if (this.encodingRow < this.img.height) { + if (!this.img.transparent) { + // Get the whole row + var subImage:ByteArray = this.img.getPixels(new Rectangle(0, encodingRow, this.img.width, 1)); + //Here we overwrite the alpha value of the first pixel + //to be the filter 0 flag + subImage[0] = 0; + this.IDAT.writeBytes(subImage); + //And we add a byte at the end to wrap the alpha values + this.IDAT.writeByte(0xff); + } else { + // Read and write a whole image row + this.IDAT.writeByte(0); + var p:uint; + for (var j:int = 0; j < this.img.width; j++) { + p = this.img.getPixel32(j, this.encodingRow); + this.IDAT.writeUnsignedInt(uint(((p & 0xFFFFFF) << 8) | (p >>> 24))); + } + } + + this.encodingRow++; + setTimeout(this.writeRaw, this.delay); + } else { + this.encodeEnd(); + } + } catch (ex:Error) { + this.CleanUp(); + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, ex.message)); + } + } + + private function writeSub():void + { + try { + var r1:uint = 0; + var g1:uint = 0; + var b1:uint = 0; + var a1:uint = 0; + + var r2:uint; + var g2:uint; + var b2:uint; + var a2:uint; + + var r3:uint; + var g3:uint; + var b3:uint; + var a3:uint; + + var p:uint; + var h:int = this.img.height; + var w:int = this.img.width; + + var j:int = 0; + + if (this.encodingRow < h) { + // no filter + this.IDAT.writeByte(1); + if (!this.img.transparent) { + a1 = 0xff; + for(j = 0; j < w; j++) { + p = img.getPixel(j, this.encodingRow); + + r2 = p >> 16 & 0xff; + g2 = p >> 8 & 0xff; + b2 = p & 0xff; + + r3 = (r2 - r1 + 256) & 0xff; + g3 = (g2 - g1 + 256) & 0xff; + b3 = (b2 - b1 + 256) & 0xff; + + this.IDAT.writeByte(r3); + this.IDAT.writeByte(g3); + this.IDAT.writeByte(b3); + + r1 = r2; + g1 = g2; + b1 = b2; + a1 = 0; + } + } else { + for(j = 0; j < w; j++) { + p = img.getPixel32(j, this.encodingRow); + + a2 = p >> 24 & 0xff; + r2 = p >> 16 & 0xff; + g2 = p >> 8 & 0xff; + b2 = p & 0xff; + + r3 = (r2 - r1 + 256) & 0xff; + g3 = (g2 - g1 + 256) & 0xff; + b3 = (b2 - b1 + 256) & 0xff; + a3 = (a2 - a1 + 256) & 0xff; + + this.IDAT.writeByte(r3); + this.IDAT.writeByte(g3); + this.IDAT.writeByte(b3); + this.IDAT.writeByte(a3); + + r1 = r2; + g1 = g2; + b1 = b2; + a1 = a2; + } + } + + this.encodingRow++; + setTimeout(this.writeSub, this.delay); + } else { + this.encodeEnd(); + } + } catch (ex:Error) { + this.CleanUp(); + dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, ex.message)); + } + } + + private function CleanUp():void { + this.png = null; + this.img = null; + this.IDAT = null; + this.encodingRow = 0; + } + + private static var crcTable:Array; + private static var crcTableComputed:Boolean = false; + + private static function computeCRCTable():void { + if (!crcTableComputed) { + PNGEncoder.crcTableComputed = true; + PNGEncoder.crcTable = []; + for (var n:uint = 0; n < 256; n++) { + var c:uint = n; + for (var k:uint = 0; k < 8; k++) { + if (c & 1) { + c = uint(uint(0xedb88320) ^ + uint(c >>> 1)); + } else { + c = uint(c >>> 1); + } + } + PNGEncoder.crcTable[n] = c; + } + } + } + + private static function writeChunk(png:ByteArray, type:uint, data:ByteArray):void { + var len:uint = 0; + if (data != null) { + len = data.length; + } + + png.writeUnsignedInt(len); + + var p:uint = png.position; + + png.writeUnsignedInt(type); + + if (data != null) { + png.writeBytes(data); + } + + var e:uint = png.position; + png.position = p; + + var c:uint = 0xffffffff; + for (var i:int = 0; i < (e - p); i++) { + c = uint(PNGEncoder.crcTable[ + (c ^ png.readUnsignedByte()) & + 0xff] ^ (c >>> 8)); + } + + c = uint(c ^ uint(0xffffffff)); + + png.position = e; + + png.writeUnsignedInt(c); + } + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload FP10.as3proj b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload FP10.as3proj new file mode 100644 index 0000000..1b68138 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload FP10.as3proj @@ -0,0 +1,81 @@ +ďťż + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deploy.bat + + + + \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload.as new file mode 100644 index 0000000..bc33755 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/SWFUpload.as @@ -0,0 +1,1679 @@ +/* Todo: +* Missing initial progress event +* Improve resizing/encoding so it doesn't lock up browser. +* */ + +package { + import flash.display.BlendMode; + import flash.display.DisplayObjectContainer; + import flash.display.Loader; + import flash.display.Stage; + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.net.FileReferenceList; + import flash.net.FileReference; + import flash.net.FileFilter; + import flash.net.URLRequest; + import flash.net.URLRequestMethod; + import flash.net.URLVariables; + import flash.events.*; + import flash.external.ExternalInterface; + import flash.system.Security; + import flash.text.AntiAliasType; + import flash.text.GridFitType; + import flash.text.StaticText; + import flash.text.StyleSheet; + import flash.text.TextDisplayMode; + import flash.text.TextField; + import flash.text.TextFieldType; + import flash.text.TextFieldAutoSize; + import flash.text.TextFormat; + import flash.ui.Mouse; + import flash.utils.ByteArray; + import flash.utils.Timer; + + import FileItem; + import ExternalCall; + import ImageResizer; + import ImageResizerEvent; + import MultipartURLLoader; + + public class SWFUpload extends Sprite { + // Cause SWFUpload to start as soon as the movie starts + public static function main():void + { + var SWFUpload:SWFUpload = new SWFUpload(); + } + + + private const build_number:String = "2.5.0 2010-02-17 Beta 3"; + + // State tracking variables + private var fileBrowserMany:FileReferenceList = new FileReferenceList(); + private var fileBrowserOne:FileReference = null; // This isn't set because it can't be reused like the FileReferenceList. It gets setup in the SelectFile method + + private var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded. + private var current_file_item:FileItem = null; // the item that is currently being uploaded. + + private var file_index:Array = new Array(); + + private var successful_uploads:Number = 0; // Tracks the uploads that have been completed + private var queue_errors:Number = 0; // Tracks files rejected during queueing + private var upload_errors:Number = 0; // Tracks files that fail upload + private var upload_cancelled:Number = 0; // Tracks number of cancelled files + private var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded. + + private var valid_file_extensions:Array = new Array();// Holds the parsed valid extensions. + + private var serverDataTimer:Timer = null; + private var assumeSuccessTimer:Timer = null; + + private var sizeTimer:Timer; + private var hasCalledFlashReady:Boolean = false; + + // Callbacks + private var flashReady_Callback:String; + private var fileDialogStart_Callback:String; + private var fileQueued_Callback:String; + private var fileQueueError_Callback:String; + private var fileDialogComplete_Callback:String; + + private var uploadResizeStart_Callback:String; + private var uploadStart_Callback:String; + private var uploadProgress_Callback:String; + private var uploadError_Callback:String; + private var uploadSuccess_Callback:String; + + private var uploadComplete_Callback:String; + + private var debug_Callback:String; + + private var mouseOut_Callback:String; + private var mouseOver_Callback:String; + private var mouseClick_Callback:String; + + // Values passed in from the HTML + private var movieName:String; + private var uploadURL:String; + private var filePostName:String; + private var uploadPostObject:Object; + private var fileTypes:String; + private var fileTypesDescription:String; + private var fileSizeLimit:Number; + private var fileUploadLimit:Number = 0; + private var fileQueueLimit:Number = 0; + private var useQueryString:Boolean = false; + private var requeueOnError:Boolean = false; + private var httpSuccess:Array = []; + private var assumeSuccessTimeout:Number = 0; + private var debugEnabled:Boolean; + + private var buttonLoader:Loader; + private var buttonTextField:TextField; + private var buttonCursorSprite:Sprite; + private var buttonImageURL:String; + //private var buttonWidth:Number; + //private var buttonHeight:Number; + private var buttonText:String; + private var buttonTextStyle:String; + private var buttonTextTopPadding:Number; + private var buttonTextLeftPadding:Number; + private var buttonAction:Number; + private var buttonCursor:Number; + private var buttonStateOver:Boolean; + private var buttonStateMouseDown:Boolean; + private var buttonStateDisabled:Boolean; + + // Error code "constants" + // Size check constants + private var SIZE_TOO_BIG:Number = 1; + private var SIZE_ZERO_BYTE:Number = -1; + private var SIZE_OK:Number = 0; + + // Queue errors + private var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -100; + private var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -110; + private var ERROR_CODE_ZERO_BYTE_FILE:Number = -120; + private var ERROR_CODE_INVALID_FILETYPE:Number = -130; + + // Upload Errors + private var ERROR_CODE_HTTP_ERROR:Number = -200; + private var ERROR_CODE_MISSING_UPLOAD_URL:Number = -210; + private var ERROR_CODE_IO_ERROR:Number = -220; + private var ERROR_CODE_SECURITY_ERROR:Number = -230; + private var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -240; + private var ERROR_CODE_UPLOAD_FAILED:Number = -250; + private var ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND:Number = -260; + private var ERROR_CODE_FILE_VALIDATION_FAILED:Number = -270; + private var ERROR_CODE_FILE_CANCELLED:Number = -280; + private var ERROR_CODE_UPLOAD_STOPPED:Number = -290; + private var ERROR_CODE_RESIZE:Number = -300; + + + // Button Actions + private var BUTTON_ACTION_SELECT_FILE:Number = -100; + private var BUTTON_ACTION_SELECT_FILES:Number = -110; + private var BUTTON_ACTION_START_UPLOAD:Number = -120; + private var BUTTON_ACTION_NONE:Number = -130; + private var BUTTON_ACTION_JAVASCRIPT:Number = -130; // DEPRECATED + + private var BUTTON_CURSOR_ARROW:Number = -1; + private var BUTTON_CURSOR_HAND:Number = -2; + + // Resize encoder + private var ENCODER_JPEG:Number = -1; + private var ENCODER_PNG:Number = -2; + + public function SWFUpload() { + // Do the feature detection. Make sure this version of Flash supports the features we need. If not + // abort initialization. + if (!flash.net.FileReferenceList || !flash.net.FileReference || !flash.net.URLRequest || !flash.external.ExternalInterface || !flash.external.ExternalInterface.available || !DataEvent.UPLOAD_COMPLETE_DATA) { + return; + } + + var self:SWFUpload = this; + Security.allowDomain("*"); // Allow uploading to any domain + Security.allowInsecureDomain("*"); // Allow uploading from HTTP to HTTPS and HTTPS to HTTP + + // Keep Flash Player busy so it doesn't show the "flash script is running slowly" error + var counter:Number = 0; + root.addEventListener(Event.ENTER_FRAME, function ():void { if (++counter > 100) counter = 0; }); + + // Setup file FileReferenceList events + this.fileBrowserMany.addEventListener(Event.SELECT, this.Select_Many_Handler); + this.fileBrowserMany.addEventListener(Event.CANCEL, this.DialogCancelled_Handler); + + + this.stage.align = StageAlign.TOP_LEFT; + this.stage.scaleMode = StageScaleMode.NO_SCALE; + this.stage.addEventListener(Event.RESIZE, function (e:Event):void { + self.HandleStageResize(e); + }); + + // Setup the button and text label + this.buttonLoader = new Loader(); + var doNothing:Function = function ():void { }; + this.buttonLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, doNothing ); + this.buttonLoader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, doNothing ); + this.buttonLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.ButtonImageLoaded ); + this.stage.addChild(this.buttonLoader); + + this.stage.addEventListener(MouseEvent.CLICK, function (event:MouseEvent):void { + self.UpdateButtonState(); + self.ButtonClickHandler(event); + }); + this.stage.addEventListener(MouseEvent.MOUSE_DOWN, function (event:MouseEvent):void { + self.buttonStateMouseDown = true; + self.UpdateButtonState(); + }); + this.stage.addEventListener(MouseEvent.MOUSE_UP, function (event:MouseEvent):void { + self.buttonStateMouseDown = false; + self.UpdateButtonState(); + }); + this.stage.addEventListener(MouseEvent.MOUSE_OVER, function (event:MouseEvent):void { + self.buttonStateMouseDown = event.buttonDown; + self.buttonStateOver = true; + self.UpdateButtonState(); + ExternalCall.Simple(self.mouseOver_Callback); + }); + this.stage.addEventListener(MouseEvent.MOUSE_OUT, function (event:MouseEvent):void { + self.buttonStateMouseDown = false; + self.buttonStateOver = false; + self.UpdateButtonState(); + }); + // Handle the mouse leaving the flash movie altogether + this.stage.addEventListener(Event.MOUSE_LEAVE, function (event:Event):void { + self.buttonStateMouseDown = false; + self.buttonStateOver = false; + self.UpdateButtonState(); + ExternalCall.Simple(self.mouseOut_Callback); + }); + + this.buttonTextField = new TextField(); + this.buttonTextField.type = TextFieldType.DYNAMIC; + this.buttonTextField.antiAliasType = AntiAliasType.ADVANCED; + this.buttonTextField.autoSize = TextFieldAutoSize.NONE; + this.buttonTextField.cacheAsBitmap = true; + this.buttonTextField.multiline = true; + this.buttonTextField.wordWrap = false; + this.buttonTextField.tabEnabled = false; + this.buttonTextField.background = false; + this.buttonTextField.border = false; + this.buttonTextField.selectable = false; + this.buttonTextField.condenseWhite = true; + + this.stage.addChild(this.buttonTextField); + + + this.buttonCursorSprite = new Sprite(); + this.buttonCursorSprite.graphics.beginFill(0xFFFFFF, 0); + this.buttonCursorSprite.graphics.drawRect(0, 0, 1, 1); + this.buttonCursorSprite.graphics.endFill(); + this.buttonCursorSprite.buttonMode = true; + this.buttonCursorSprite.x = 0; + this.buttonCursorSprite.y = 0; + this.buttonCursorSprite.addEventListener(MouseEvent.CLICK, doNothing); + this.stage.addChild(this.buttonCursorSprite); + + this.sizeTimer = new Timer(10, 0); + this.sizeTimer.addEventListener(TimerEvent.TIMER, function ():void { + //self.Debug("Stage:" + self.stage.stageWidth + " by " + self.stage.stageHeight); + if (self.stage.stageWidth > 0 || self.stage.stageHeight > 0) { + self.HandleStageResize(null); + self.sizeTimer.stop(); + self.sizeTimer.removeEventListener(TimerEvent.TIMER, arguments.callee); + self.sizeTimer = null; + } + } ); + this.sizeTimer.start(); + + // Get the movie name + this.movieName = decodeURIComponent(root.loaderInfo.parameters.movieName); + + // **Configure the callbacks** + // The JavaScript tracks all the instances of SWFUpload on a page. We can access the instance + // associated with this SWF file using the movieName. Each callback is accessible by making + // a call directly to it on our instance. There is no error handling for undefined callback functions. + // A developer would have to deliberately remove the default functions,set the variable to null, or remove + // it from the init function. + this.flashReady_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].flashReady"; + this.fileDialogStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogStart"; + this.fileQueued_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueued"; + this.fileQueueError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueueError"; + this.fileDialogComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogComplete"; + + this.uploadResizeStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadResizeStart"; + this.uploadStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadStart"; + this.uploadProgress_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadProgress"; + this.uploadError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadError"; + this.uploadSuccess_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadSuccess"; + + this.uploadComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadComplete"; + + this.debug_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].debug"; + + this.mouseOut_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].mouseOut"; + this.mouseOver_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].mouseOver"; + this.mouseClick_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].mouseClick"; + + // Get the Flash Vars + this.uploadURL = decodeURIComponent(root.loaderInfo.parameters.uploadURL); + this.filePostName = decodeURIComponent(root.loaderInfo.parameters.filePostName); + this.fileTypes = decodeURIComponent(root.loaderInfo.parameters.fileTypes); + this.fileTypesDescription = decodeURIComponent(root.loaderInfo.parameters.fileTypesDescription) + " (" + this.fileTypes + ")"; + this.loadPostParams(decodeURIComponent(root.loaderInfo.parameters.params)); + + + if (!this.filePostName) { + this.filePostName = "Filedata"; + } + if (!this.fileTypes) { + this.fileTypes = "*.*"; + } + if (!this.fileTypesDescription) { + this.fileTypesDescription = "All Files"; + } + + this.LoadFileExensions(this.fileTypes); + + try { + this.debugEnabled = decodeURIComponent(root.loaderInfo.parameters.debugEnabled) == "true" ? true : false; + } catch (ex:Object) { + this.debugEnabled = false; + } + + try { + this.SetFileSizeLimit(String(decodeURIComponent(root.loaderInfo.parameters.fileSizeLimit))); + } catch (ex:Object) { + this.fileSizeLimit = 0; + } + + + try { + this.fileUploadLimit = Number(decodeURIComponent(root.loaderInfo.parameters.fileUploadLimit)); + if (this.fileUploadLimit < 0) this.fileUploadLimit = 0; + } catch (ex:Object) { + this.fileUploadLimit = 0; + } + + try { + this.fileQueueLimit = Number(decodeURIComponent(root.loaderInfo.parameters.fileQueueLimit)); + if (this.fileQueueLimit < 0) this.fileQueueLimit = 0; + } catch (ex:Object) { + this.fileQueueLimit = 0; + } + + // Set the queue limit to match the upload limit when the queue limit is bigger than the upload limit + if (this.fileQueueLimit > this.fileUploadLimit && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit; + // The the queue limit is unlimited and the upload limit is not then set the queue limit to the upload limit + if (this.fileQueueLimit == 0 && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit; + + try { + this.useQueryString = decodeURIComponent(root.loaderInfo.parameters.useQueryString) == "true" ? true : false; + } catch (ex:Object) { + this.useQueryString = false; + } + + try { + this.requeueOnError = decodeURIComponent(root.loaderInfo.parameters.requeueOnError) == "true" ? true : false; + } catch (ex:Object) { + this.requeueOnError = false; + } + + try { + this.SetHTTPSuccess(String(decodeURIComponent(root.loaderInfo.parameters.httpSuccess))); + } catch (ex:Object) { + this.SetHTTPSuccess([]); + } + + try { + this.SetAssumeSuccessTimeout(Number(decodeURIComponent(root.loaderInfo.parameters.assumeSuccessTimeout))); + } catch (ex:Object) { + this.SetAssumeSuccessTimeout(0); + } + + try { + this.SetButtonImageURL(String(decodeURIComponent(root.loaderInfo.parameters.buttonImageURL))); + } catch (ex:Object) { + this.SetButtonImageURL(""); + } + + try { + this.SetButtonText(String(decodeURIComponent(root.loaderInfo.parameters.buttonText))); + } catch (ex:Object) { + this.SetButtonText(""); + } + + try { + this.SetButtonTextPadding(Number(decodeURIComponent(root.loaderInfo.parameters.buttonTextLeftPadding)), Number(decodeURIComponent(root.loaderInfo.parameters.buttonTextTopPadding))); + } catch (ex:Object) { + this.SetButtonTextPadding(0, 0); + } + + try { + this.SetButtonTextStyle(String(decodeURIComponent(root.loaderInfo.parameters.buttonTextStyle))); + } catch (ex:Object) { + this.SetButtonTextStyle(""); + } + + try { + this.SetButtonAction(Number(decodeURIComponent(root.loaderInfo.parameters.buttonAction))); + } catch (ex:Object) { + this.SetButtonAction(this.BUTTON_ACTION_SELECT_FILES); + } + + try { + this.SetButtonDisabled(decodeURIComponent(root.loaderInfo.parameters.buttonDisabled) == "true" ? true : false); + } catch (ex:Object) { + this.SetButtonDisabled(Boolean(false)); + } + + try { + this.SetButtonCursor(Number(decodeURIComponent(root.loaderInfo.parameters.buttonCursor))); + } catch (ex:Object) { + this.SetButtonCursor(this.BUTTON_CURSOR_ARROW); + } + + this.SetupExternalInterface(); + + this.Debug("SWFUpload Init Complete"); + this.PrintDebugInfo(); + + ExternalCall.Simple(this.flashReady_Callback); + this.hasCalledFlashReady = true; + } + + private function HandleStageResize(e:Event):void { + if (this.stage.stageWidth > 0 || this.stage.stageHeight > 0) { + this.Debug("Stage Resize:" + this.stage.stageWidth + " by " + this.stage.stageHeight); + + // scale the button (if it's not loaded don't crash) + try { + var buttonHeight:Number = this.buttonLoader.contentLoaderInfo.height / 4; + this.buttonLoader.height = this.stage.stageHeight * 4; + this.buttonLoader.scaleX = this.buttonLoader.scaleY; + } catch (ex:Error) { } + + // Scale the button cursor + try { + this.buttonCursorSprite.width = this.stage.stageWidth; + this.buttonCursorSprite.height = this.stage.stageHeight; + } catch (ex:Error) {} + + // scale the text area (it doesn't resize but it still needs to fit) + try { + this.buttonTextField.width = this.stage.stageWidth; + this.buttonTextField.height = this.stage.stageHeight; + } catch (ex:Error) {} + } + } + + private function SetupExternalInterface():void { + try { + ExternalInterface.addCallback("SelectFile", this.SelectFile); + ExternalInterface.addCallback("SelectFiles", this.SelectFiles); + ExternalInterface.addCallback("StartUpload", this.StartUpload); + ExternalInterface.addCallback("ReturnUploadStart", this.ReturnUploadStart); + ExternalInterface.addCallback("StopUpload", this.StopUpload); + ExternalInterface.addCallback("CancelUpload", this.CancelUpload); + ExternalInterface.addCallback("RequeueUpload", this.RequeueUpload); + + ExternalInterface.addCallback("GetStats", this.GetStats); + ExternalInterface.addCallback("SetStats", this.SetStats); + ExternalInterface.addCallback("GetFile", this.GetFile); + ExternalInterface.addCallback("GetFileByIndex", this.GetFileByIndex); + ExternalInterface.addCallback("GetFileByQueueIndex", this.GetFileByQueueIndex); + + ExternalInterface.addCallback("AddFileParam", this.AddFileParam); + ExternalInterface.addCallback("RemoveFileParam", this.RemoveFileParam); + + ExternalInterface.addCallback("SetUploadURL", this.SetUploadURL); + ExternalInterface.addCallback("SetPostParams", this.SetPostParams); + ExternalInterface.addCallback("SetFileTypes", this.SetFileTypes); + ExternalInterface.addCallback("SetFileSizeLimit", this.SetFileSizeLimit); + ExternalInterface.addCallback("SetFileUploadLimit", this.SetFileUploadLimit); + ExternalInterface.addCallback("SetFileQueueLimit", this.SetFileQueueLimit); + ExternalInterface.addCallback("SetFilePostName", this.SetFilePostName); + ExternalInterface.addCallback("SetUseQueryString", this.SetUseQueryString); + ExternalInterface.addCallback("SetRequeueOnError", this.SetRequeueOnError); + ExternalInterface.addCallback("SetHTTPSuccess", this.SetHTTPSuccess); + ExternalInterface.addCallback("SetAssumeSuccessTimeout", this.SetAssumeSuccessTimeout); + ExternalInterface.addCallback("SetDebugEnabled", this.SetDebugEnabled); + + ExternalInterface.addCallback("SetButtonImageURL", this.SetButtonImageURL); + ExternalInterface.addCallback("SetButtonText", this.SetButtonText); + ExternalInterface.addCallback("SetButtonTextPadding", this.SetButtonTextPadding); + ExternalInterface.addCallback("SetButtonTextStyle", this.SetButtonTextStyle); + ExternalInterface.addCallback("SetButtonAction", this.SetButtonAction); + ExternalInterface.addCallback("SetButtonDisabled", this.SetButtonDisabled); + ExternalInterface.addCallback("SetButtonCursor", this.SetButtonCursor); + } catch (ex:Error) { + this.Debug("Callbacks where not set: " + ex.message); + return; + } + } + + /* ***************************************** + * FileReference Event Handlers + * *************************************** */ + private function DialogCancelled_Handler(event:Event):void { + this.Debug("Event: fileDialogComplete: File Dialog window cancelled."); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0, 0, this.queued_uploads); + } + + private function Open_Handler(event:Event):void { + this.Debug("Event: uploadProgress (OPEN): File ID: " + this.current_file_item.id); + this.current_file_item.finalUploadProgress = false; + ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), 0, this.current_file_item.file_reference.size); + } + + private function FileProgress_Handler(event:ProgressEvent):void { + // On early than Mac OS X 10.3 bytesLoaded is always -1, convert this to zero. Do bytesTotal for good measure. + // http://livedocs.adobe.com/flex/3/langref/flash/net/FileReference.html#event:progress + var bytesLoaded:Number = event.bytesLoaded < 0 ? 0 : event.bytesLoaded; + var bytesTotal:Number = event.bytesTotal < 0 ? 0 : event.bytesTotal; + + // Because Flash never fires a complete event if the server doesn't respond after 30 seconds or on Macs if there + // is no content in the response we'll set a timer and assume that the upload is successful after the defined amount of + // time. If the timeout is zero then we won't use the timer. + if (bytesLoaded === bytesTotal) { + this.current_file_item.finalUploadProgress = true; + if (bytesTotal > 0 && this.assumeSuccessTimeout > 0) { + if (this.assumeSuccessTimer !== null) { + this.assumeSuccessTimer.stop(); + this.assumeSuccessTimer = null; + } + + this.assumeSuccessTimer = new Timer(this.assumeSuccessTimeout * 1000, 1); + this.assumeSuccessTimer.addEventListener(TimerEvent.TIMER_COMPLETE, AssumeSuccessTimer_Handler); + this.assumeSuccessTimer.start(); + } + } + + this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + bytesLoaded + ". Total: " + bytesTotal); + ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), bytesLoaded, bytesTotal); + } + + private function AssumeSuccessTimer_Handler(event:TimerEvent):void { + this.Debug("Event: AssumeSuccess: " + this.assumeSuccessTimeout + " passed without server response"); + this.UploadSuccess(this.current_file_item, "", false); + } + + private function Complete_Handler(event:Event):void { + /* Because we can't do COMPLETE or DATA events (we have to do both) we can't + * just call uploadSuccess from the complete handler, we have to wait for + * the Data event which may never come. However, testing shows it always comes + * within a couple milliseconds if it is going to come so the solution is: + * + * Set a timer in the COMPLETE event (which always fires) and if DATA is fired + * it will stop the timer and call uploadComplete + * + * If the timer expires then DATA won't be fired and we call uploadComplete + * */ + + // Set the timer + if (serverDataTimer != null) { + this.serverDataTimer.stop(); + this.serverDataTimer = null; + } + + this.serverDataTimer = new Timer(100, 1); + //var self:SWFUpload = this; + this.serverDataTimer.addEventListener(TimerEvent.TIMER, this.ServerDataTimer_Handler); + this.serverDataTimer.start(); + } + private function ServerDataTimer_Handler(event:TimerEvent):void { + this.UploadSuccess(this.current_file_item, ""); + } + + private function ServerData_Handler(event:DataEvent):void { + this.UploadSuccess(this.current_file_item, event.data); + } + + private function UploadSuccess(file:FileItem, serverData:String, responseReceived:Boolean = true):void { + if (this.serverDataTimer !== null) { + this.serverDataTimer.stop(); + this.serverDataTimer = null; + } + if (this.assumeSuccessTimer !== null) { + this.assumeSuccessTimer.stop(); + this.assumeSuccessTimer = null; + } + + // If the 100% upload progress hasn't been called then call it now + if (!file.finalUploadProgress) { + file.finalUploadProgress = true; + this.Debug("Event: uploadProgress (simulated 100%): File ID: " + file.id + ". Bytes: " + file.file_reference.size + ". Total: " + file.file_reference.size); + ExternalCall.UploadProgress(this.uploadProgress_Callback, file.ToJavaScriptObject(), file.file_reference.size, file.file_reference.size); + } + + this.successful_uploads++; + file.file_status = FileItem.FILE_STATUS_SUCCESS; + + this.Debug("Event: uploadSuccess: File ID: " + file.id + " Response Received: " + responseReceived.toString() + " Data: " + serverData); + ExternalCall.UploadSuccess(this.uploadSuccess_Callback, file.ToJavaScriptObject(), serverData, responseReceived); + + this.UploadComplete(false); + + } + + private function HTTPError_Handler(event:HTTPStatusEvent):void { + var isSuccessStatus:Boolean = false; + for (var i:Number = 0; i < this.httpSuccess.length; i++) { + if (this.httpSuccess[i] === event.status) { + isSuccessStatus = true; + break; + } + } + + + if (isSuccessStatus) { + this.Debug("Event: httpError: Translating status code " + event.status + " to uploadSuccess"); + + var serverDataEvent:DataEvent = new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, ""); + this.ServerData_Handler(serverDataEvent); + } else { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError: HTTP ERROR : File ID: " + this.current_file_item.id + ". HTTP Status: " + event.status + "."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), event.status.toString()); + this.UploadComplete(true); // An IO Error is also called so we don't want to complete the upload yet. + } + } + + // Note: Flash Player does not support Uploads that require authentication. Attempting this will trigger an + // IO Error or it will prompt for a username and password and may crash the browser (FireFox/Opera) + private function IOError_Handler(event:IOErrorEvent):void { + // Only trigger an IO Error event if we haven't already done an HTTP error + if (this.current_file_item.file_status != FileItem.FILE_STATUS_ERROR) { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error: " + event.text); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), event.text); + } + + this.UploadComplete(true); + } + + private function SecurityError_Handler(event:SecurityErrorEvent):void { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError : Security Error : File Number: " + this.current_file_item.id + ". Error text: " + event.text); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), event.text); + + this.UploadComplete(true); + } + + private function Select_Many_Handler(event:Event):void { + this.Select_Handler(this.fileBrowserMany.fileList); + } + private function Select_One_Handler(event:Event):void { + var fileArray:Array = new Array(1); + fileArray[0] = this.fileBrowserOne; + this.Select_Handler(fileArray); + } + + private function Select_Handler(file_reference_list:Array):void { + this.Debug("Select Handler: Received the files selected from the dialog. Processing the file list..."); + + var num_files_queued:Number = 0; + + // Determine how many queue slots are remaining (check the unlimited (0) settings, successful uploads and queued uploads) + var queue_slots_remaining:Number = 0; + if (this.fileUploadLimit == 0) { + queue_slots_remaining = this.fileQueueLimit == 0 ? file_reference_list.length : (this.fileQueueLimit - this.queued_uploads); // If unlimited queue make the allowed size match however many files were selected. + } else { + var remaining_uploads:Number = this.fileUploadLimit - this.successful_uploads - this.queued_uploads; + if (remaining_uploads < 0) remaining_uploads = 0; + if (this.fileQueueLimit == 0 || this.fileQueueLimit >= remaining_uploads) { + queue_slots_remaining = remaining_uploads; + } else if (this.fileQueueLimit < remaining_uploads) { + queue_slots_remaining = this.fileQueueLimit - this.queued_uploads; + } + } + + if (queue_slots_remaining < 0) queue_slots_remaining = 0; + + // Check if the number of files selected is greater than the number allowed to queue up. + if (queue_slots_remaining < file_reference_list.length) { + this.Debug("Event: fileQueueError : Selected Files (" + file_reference_list.length + ") exceeds remaining Queue size (" + queue_slots_remaining + ")."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining.toString()); + } else { + // Process each selected file + for (var i:Number = 0; i < file_reference_list.length; i++) { + var file_item:FileItem = new FileItem(file_reference_list[i], this.movieName, this.file_index.length); + this.file_index[file_item.index] = file_item; + + // Verify that the file is accessible. Zero byte files and possibly other conditions can cause a file to be inaccessible. + var jsFileObj:Object = file_item.ToJavaScriptObject(); + var is_valid_file_reference:Boolean = (jsFileObj.filestatus !== FileItem.FILE_STATUS_ERROR); + + if (is_valid_file_reference) { + // Check the size, if it's within the limit add it to the upload list. + var size_result:Number = this.CheckFileSize(file_item); + var is_valid_filetype:Boolean = this.CheckFileType(file_item); + if(size_result == this.SIZE_OK && is_valid_filetype) { + file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.push(file_item); + this.queued_uploads++; + num_files_queued++; + this.Debug("Event: fileQueued : File ID: " + file_item.id); + ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject()); + } + else if (!is_valid_filetype) { + //file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File not of a valid type."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_INVALID_FILETYPE, file_item.ToJavaScriptObject(), "File is not an allowed file type."); + } + else if (size_result == this.SIZE_TOO_BIG) { + //file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File exceeds size limit."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit."); + } + else if (size_result == this.SIZE_ZERO_BYTE) { + file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File is zero bytes."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded."); + } + } else { + file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File is zero bytes or FileReference is invalid."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes or cannot be accessed and cannot be uploaded."); + } + } + } + + this.Debug("Event: fileDialogComplete : Finished processing selected files. Files selected: " + file_reference_list.length + ". Files Queued: " + num_files_queued); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length, num_files_queued, this.queued_uploads); + } + + /* **************************************************************** + Externally exposed functions + ****************************************************************** */ + // Opens a file browser dialog that allows one file to be selected. + private function SelectFile():void { + this.fileBrowserOne = new FileReference(); + this.fileBrowserOne.addEventListener(Event.SELECT, this.Select_One_Handler); + this.fileBrowserOne.addEventListener(Event.CANCEL, this.DialogCancelled_Handler); + + // Default file type settings + var allowed_file_types:String = "*.*"; + var allowed_file_types_description:String = "All Files"; + + // Get the instance settings + if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes; + if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription; + + this.Debug("Event: fileDialogStart : Browsing files. Single Select. Allowed file types: " + allowed_file_types); + ExternalCall.Simple(this.fileDialogStart_Callback); + + try { + this.fileBrowserOne.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]); + } catch (ex:Error) { + this.Debug("Exception: " + ex.toString()); + } + } + + // Opens a file browser dialog that allows multiple files to be selected. + private function SelectFiles():void { + var allowed_file_types:String = "*.*"; + var allowed_file_types_description:String = "All Files"; + + if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes; + if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription; + + this.Debug("Event: fileDialogStart : Browsing files. Multi Select. Allowed file types: " + allowed_file_types); + ExternalCall.Simple(this.fileDialogStart_Callback); + + try { + this.fileBrowserMany.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]); + } catch (ex:Error) { + this.Debug("Exception: " + ex.toString()); + } + } + + + // Cancel the current upload and stops. Doesn't advance the upload pointer. The current file is requeued at the beginning. + private function StopUpload():void { + if (this.current_file_item != null) { + // Cancel the upload and re-queue the FileItem + if (this.current_file_item.upload_type === FileItem.UPLOAD_TYPE_NORMAL) { + this.current_file_item.file_reference.cancel(); + } else { + if (this.current_file_item.resized_uploader != null) { + this.current_file_item.resized_uploader.cancel(); + this.current_file_item.resized_uploader = null; + } + } + + // Remove the event handlers + this.removeEventListeners(this.current_file_item); + + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(this.current_file_item); + var js_object:Object = this.current_file_item.ToJavaScriptObject(); + this.current_file_item = null; + + this.Debug("Event: uploadError: upload stopped. File ID: " + js_object.ID); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped"); + this.Debug("Event: uploadComplete. File ID: " + js_object.ID); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + + this.Debug("StopUpload(): upload stopped."); + } else { + this.Debug("StopUpload(): No file is currently uploading. Nothing to do."); + } + } + + /* Cancels the upload specified by file_id + * If the file is currently uploading it is cancelled and the uploadComplete + * event gets called. + * If the file is not currently uploading then only the uploadCancelled event is fired. + * + * The triggerComplete is used by the resize stuff to cancel the upload + * */ + private function CancelUpload(file_id:String, triggerErrorEvent:Boolean = true):void { + var file_item:FileItem = null; + // Check the current file item + if (this.current_file_item != null && (this.current_file_item.id == file_id || !file_id)) { + if (this.current_file_item.upload_type === FileItem.UPLOAD_TYPE_NORMAL) { + this.current_file_item.file_reference.cancel(); + } else { + if (this.current_file_item.resized_uploader != null) { + this.current_file_item.resized_uploader.cancel(); + this.current_file_item.resized_uploader = null; + } + } + this.current_file_item.file_status = FileItem.FILE_STATUS_CANCELLED; + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + this.upload_cancelled++; + + if (triggerErrorEvent) { + this.Debug("Event: uploadError: File ID: " + this.current_file_item.id + ". Cancelled current upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled."); + } else { + this.Debug("Event: cancelUpload: File ID: " + this.current_file_item.id + ". Cancelled current upload. Suppressed uploadError event."); + } + + this.UploadComplete(false); + } else if (file_id) { + // Find the file in the queue + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + // Remove the file from the queue + file_item = FileItem(this.file_queue[file_index]); + file_item.file_status = FileItem.FILE_STATUS_CANCELLED; + this.file_queue.splice(file_index, 1); + this.queued_uploads--; + this.upload_cancelled++; + + // Cancel the file (just for good measure) and make the callback + if (file_item.upload_type === FileItem.UPLOAD_TYPE_NORMAL) { + file_item.file_reference.cancel(); + } else { + if (file_item.resized_uploader != null) { + file_item.resized_uploader.cancel(); + file_item.resized_uploader = null; + } + } + this.removeEventListeners(file_item); + file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + + + if (triggerErrorEvent) { + this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + } else { + this.Debug("Event: cancelUpload: File ID: " + file_item.id + ". Cancelled current upload. Suppressed uploadError event."); + } + + // Get rid of the file object + file_item = null; + } + } else { + // Get the first file and cancel it + while (this.file_queue.length > 0 && file_item == null) { + // Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop) + file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem + if (typeof(file_item) == "undefined") { + file_item = null; + continue; + } + } + + if (file_item != null) { + file_item.file_status = FileItem.FILE_STATUS_CANCELLED; + this.queued_uploads--; + this.upload_cancelled++; + + + // Cancel the file (just for good measure) and make the callback + if (file_item.upload_type === FileItem.UPLOAD_TYPE_NORMAL) { + file_item.file_reference.cancel(); + } else { + if (file_item.resized_uploader != null) { + file_item.resized_uploader.cancel(); + file_item.resized_uploader = null; + } + } + + this.removeEventListeners(file_item); + file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + + if (triggerErrorEvent) { + this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + } else { + this.Debug("Event: cancelUpload: File ID: " + file_item.id + ". Cancelled current upload. Suppressed uploadError event."); + } + + // Get rid of the file object + file_item = null; + } + + } + + } + + /* Requeues the indicated file. Returns true if successful or if the file is + * already in the queue. Otherwise returns false. + * */ + private function RequeueUpload(fileIdentifier:*):Boolean { + var file:FileItem = null; + if (typeof(fileIdentifier) === "number") { + var fileIndex:Number = Number(fileIdentifier); + if (fileIndex >= 0 && fileIndex < this.file_index.length) { + file = this.file_index[fileIndex]; + } + } else if (typeof(fileIdentifier) === "string") { + file = FindFileInFileIndex(String(fileIdentifier)); + } else { + return false; + } + + if (file !== null && file.file_reference !== null) { + if (file.file_status === FileItem.FILE_STATUS_IN_PROGRESS || file.file_status === FileItem.FILE_STATUS_NEW) { + return false; + } else if (file.file_status !== FileItem.FILE_STATUS_QUEUED) { + file.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(file); + this.queued_uploads++; + } + return true; + } else { + return false; + } + } + + + private function GetStats():Object { + return { + in_progress : this.current_file_item == null ? 0 : 1, + files_queued : this.queued_uploads, + successful_uploads : this.successful_uploads, + upload_errors : this.upload_errors, + upload_cancelled : this.upload_cancelled, + queue_errors : this.queue_errors + }; + } + private function SetStats(stats:Object):void { + this.successful_uploads = typeof(stats["successful_uploads"]) === "number" ? stats["successful_uploads"] : this.successful_uploads; + this.upload_errors = typeof(stats["upload_errors"]) === "number" ? stats["upload_errors"] : this.upload_errors; + this.upload_cancelled = typeof(stats["upload_cancelled"]) === "number" ? stats["upload_cancelled"] : this.upload_cancelled; + this.queue_errors = typeof(stats["queue_errors"]) === "number" ? stats["queue_errors"] : this.queue_errors; + } + + private function GetFile(file_id:String):Object { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + var file:FileItem = this.file_queue[file_index]; + } else { + if (this.current_file_item != null) { + file = this.current_file_item; + } else { + for (var i:Number = 0; i < this.file_queue.length; i++) { + file = this.file_queue[i]; + if (file != null) break; + } + } + } + + if (file == null) { + return null; + } else { + return file.ToJavaScriptObject(); + } + + } + + private function GetFileByIndex(index:Number):Object { + if (index < 0 || index > this.file_index.length - 1) { + return null; + } else { + return this.file_index[index].ToJavaScriptObject(); + } + } + + private function GetFileByQueueIndex(index:Number):Object { + if (index < 0 || index > this.file_queue.length - 1) { + return null; + } else { + return this.file_queue[index].ToJavaScriptObject(); + } + } + + private function AddFileParam(file_id:String, name:String, value:String):Boolean { + var item:FileItem = this.FindFileInFileIndex(file_id); + if (item != null) { + item.AddParam(name, value); + return true; + } + else { + return false; + } + } + private function RemoveFileParam(file_id:String, name:String):Boolean { + var item:FileItem = this.FindFileInFileIndex(file_id); + if (item != null) { + item.RemoveParam(name); + return true; + } + else { + return false; + } + } + + private function SetUploadURL(url:String):void { + if (typeof(url) !== "undefined" && url !== "") { + this.uploadURL = url; + } + } + + private function SetPostParams(post_object:Object):void { + if (typeof(post_object) !== "undefined" && post_object !== null) { + this.uploadPostObject = post_object; + } + } + + private function SetFileTypes(types:String, description:String):void { + this.fileTypes = types; + this.fileTypesDescription = description; + + this.LoadFileExensions(this.fileTypes); + } + + // Sets the file size limit. Accepts size values with units: 100 b, 1KB, 23Mb, 4 Gb + // Parsing is not robust. "100 200 MB KB B GB" parses as "100 MB" + private function SetFileSizeLimit(size:String):void { + var value:Number = 0; + var unit:String = "kb"; + + // Trim the string + var trimPattern:RegExp = /^\s*|\s*$/; + + size = size.toLowerCase(); + size = size.replace(trimPattern, ""); + + + // Get the value part + var values:Array = size.match(/^\d+/); + if (values !== null && values.length > 0) { + value = parseInt(values[0]); + } + if (isNaN(value) || value < 0) value = 0; + + // Get the units part + var units:Array = size.match(/(b|kb|mb|gb)/); + if (units != null && units.length > 0) { + unit = units[0]; + } + + // Set the multiplier for converting the unit to bytes + var multiplier:Number = 1024; + if (unit === "b") + multiplier = 1; + else if (unit === "mb") + multiplier = 1048576; + else if (unit === "gb") + multiplier = 1073741824; + + this.fileSizeLimit = value * multiplier; + } + + private function SetFileUploadLimit(file_upload_limit:Number):void { + if (file_upload_limit < 0) file_upload_limit = 0; + this.fileUploadLimit = file_upload_limit; + } + + private function SetFileQueueLimit(file_queue_limit:Number):void { + if (file_queue_limit < 0) file_queue_limit = 0; + this.fileQueueLimit = file_queue_limit; + } + + private function SetFilePostName(file_post_name:String):void { + if (file_post_name != "") { + this.filePostName = file_post_name; + } + } + + private function SetUseQueryString(use_query_string:Boolean):void { + this.useQueryString = use_query_string; + } + + private function SetRequeueOnError(requeue_on_error:Boolean):void { + this.requeueOnError = requeue_on_error; + } + + private function SetHTTPSuccess(http_status_codes:*):void { + this.httpSuccess = []; + + if (typeof http_status_codes === "string") { + var status_code_strings:Array = http_status_codes.replace(" ", "").split(","); + for each (var http_status_string:String in status_code_strings) + { + try { + this.httpSuccess.push(Number(http_status_string)); + } catch (ex:Object) { + // Ignore errors + this.Debug("Could not add HTTP Success code: " + http_status_string); + } + } + } + else if (typeof http_status_codes === "object" && typeof http_status_codes.length === "number") { + for each (var http_status:* in http_status_codes) + { + try { + this.Debug("adding: " + http_status); + this.httpSuccess.push(Number(http_status)); + } catch (ex:Object) { + this.Debug("Could not add HTTP Success code: " + http_status); + } + } + } + } + + private function SetAssumeSuccessTimeout(timeout_seconds:Number):void { + this.assumeSuccessTimeout = timeout_seconds < 0 ? 0 : timeout_seconds; + } + + private function SetDebugEnabled(debug_enabled:Boolean):void { + this.debugEnabled = debug_enabled; + } + + /* ************************************************************* + Button Handling Functions + *************************************************************** */ + private function SetButtonImageURL(button_image_url:String):void { + this.buttonImageURL = button_image_url; + + try { + if (this.buttonImageURL !== null && this.buttonImageURL !== "") { + this.buttonLoader.load(new URLRequest(this.buttonImageURL)); + } + } catch (ex:Object) { + } + } + + private function ButtonImageLoaded(e:Event):void { + this.Debug("Button Image Loaded"); + this.HandleStageResize(null); + } + + + private function ButtonClickHandler(e:MouseEvent):void { + if (!this.buttonStateDisabled) { + if (this.buttonAction === this.BUTTON_ACTION_SELECT_FILE) { + this.SelectFile(); + } + else if (this.buttonAction === this.BUTTON_ACTION_START_UPLOAD) { + this.StartUpload(); + } + else if (this.buttonAction === this.BUTTON_ACTION_NONE) { + ExternalCall.Simple(this.mouseClick_Callback); + } + else { + this.SelectFiles(); + } + } else { + ExternalCall.Simple(this.mouseClick_Callback); + } + } + + private function UpdateButtonState():void { + var xOffset:Number = 0; + var yOffset:Number = 0; + + this.buttonLoader.x = xOffset; + this.buttonLoader.y = yOffset; + + if (this.buttonStateDisabled) { + this.buttonLoader.y = (this.buttonLoader.height / 4) * -3 + yOffset; + } + else if (this.buttonStateMouseDown) { + this.buttonLoader.y = (this.buttonLoader.height / 4) * -2 + yOffset; + } + else if (this.buttonStateOver) { + this.buttonLoader.y = (this.buttonLoader.height / 4) * -1 + yOffset; + } + else { + this.buttonLoader.y = -yOffset; + } + }; + + private function SetButtonText(button_text:String):void { + this.buttonText = button_text; + + this.SetButtonTextStyle(this.buttonTextStyle); + } + + private function SetButtonTextStyle(button_text_style:String):void { + this.buttonTextStyle = button_text_style; + + var style:StyleSheet = new StyleSheet(); + style.parseCSS(this.buttonTextStyle); + this.buttonTextField.styleSheet = style; + this.buttonTextField.htmlText = this.buttonText; + } + + private function SetButtonTextPadding(left:Number, top:Number):void { + this.buttonTextField.x = this.buttonTextLeftPadding = left; + this.buttonTextField.y = this.buttonTextTopPadding = top; + } + + private function SetButtonDisabled(disabled:Boolean):void { + this.buttonStateDisabled = disabled; + this.UpdateButtonState(); + } + + private function SetButtonAction(button_action:Number):void { + this.buttonAction = button_action; + } + + private function SetButtonCursor(button_cursor:Number):void { + this.buttonCursor = button_cursor; + + this.buttonCursorSprite.useHandCursor = (this.buttonCursor === this.BUTTON_CURSOR_HAND); + } + + /* ************************************************************* + File processing and handling functions + *************************************************************** */ + + private function StartUpload(file_id:String = "", resizeSettings:Object = null):void { + // Only upload a file uploads are being processed. + if (this.current_file_item != null) { + this.Debug("StartUpload(): Upload already in progress. Not starting another upload."); + return; + } + + this.Debug("StartUpload: " + (file_id ? "File ID: " + file_id : "First file in queue")); + + // Check the upload limit + if (this.successful_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) { + this.Debug("Event: uploadError : Upload limit reached. No more files can be uploaded."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, null, "The upload limit has been reached."); + this.current_file_item = null; + return; + } + + // Get the next file to upload + if (!file_id) { + while (this.file_queue.length > 0 && this.current_file_item == null) { + this.current_file_item = FileItem(this.file_queue.shift()); + if (typeof(this.current_file_item) == "undefined") { + this.current_file_item = null; + } + } + } else { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + // Set the file as the current upload and remove it from the queue + this.current_file_item = FileItem(this.file_queue[file_index]); + this.file_queue.splice([file_index], 1); + } else { + this.Debug("Event: uploadError : File ID not found in queue: " + file_id); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND, null, "File ID not found in the queue."); + } + } + + + if (this.current_file_item != null) { + // Trigger the uploadStart event which will call ReturnUploadStart to begin the actual upload + this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id); + + this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS; + if (resizeSettings != null) { + this.Debug("StartUpload(): Uploading Type: Resized Image."); + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_RESIZE; + this.PrepareResizedImage(resizeSettings); + } else { + this.Debug("StartUpload(): Upload Type: Normal."); + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject()); + } + } + // Otherwise we've would have looped through all the FileItems. This means the queue is empty) + else { + this.Debug("StartUpload(): No files found in the queue."); + } + } + + private function PrepareResizedImage(resizeSettings:Object):void { + try { + var resizer:ImageResizer = new ImageResizer(this.current_file_item, resizeSettings["width"], resizeSettings["height"], resizeSettings["encoding"], resizeSettings["quality"], resizeSettings["allowEnlarging"]); + resizer.addEventListener(ImageResizerEvent.COMPLETE, this.PrepareResizedImageCompleteHandler); + resizer.addEventListener(ErrorEvent.ERROR, this.PrepareResizedImageErrorHandler); + + this.Debug("PrepareThumbnail(): Beginning image resizing."); + this.Debug("Settings: Width: " + resizeSettings["width"] + ", Height: " + resizeSettings["height"] + ", Encoding: " + (resizeSettings["encoding"] == this.ENCODER_PNG ? "PNG" : "JPEG") + ", Quality: " + resizeSettings["quality"] + "."); + ExternalCall.UploadResizeStart(this.uploadResizeStart_Callback, this.current_file_item.ToJavaScriptObject(), resizeSettings); + resizer.ResizeImage(); + + } catch (ex:Object) { + if (resizer != null) { + resizer.removeEventListener(ImageResizerEvent.COMPLETE, this.PrepareResizedImageCompleteHandler); + resizer.removeEventListener(ErrorEvent.ERROR, this.PrepareResizedImageErrorHandler); + } + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_RESIZE, this.current_file_item.ToJavaScriptObject(), "Error getting file for resize."); + } + + } + + private function PrepareResizedImageCompleteHandler(event:ImageResizerEvent):void { + event.target.removeEventListener(ImageResizerEvent.COMPLETE, this.PrepareResizedImageCompleteHandler); + event.target.removeEventListener(ErrorEvent.ERROR, this.PrepareResizedImageErrorHandler); + this.Debug("PrepareResizedImageCompleteHandler(): Finished resizing. Initializing MultipartURLLoader."); + + if (this.current_file_item != null) { + var extension:String = event.encoding == this.ENCODER_PNG ? ".png" : ".jpg"; + this.current_file_item.resized_uploader = new MultipartURLLoader(event.data, this.current_file_item.file_reference.name + extension); + ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject()); + } + } + private function PrepareResizedImageErrorHandler(event:ErrorEvent):void { + event.target.removeEventListener(ImageResizerEvent.COMPLETE, this.PrepareResizedImageCompleteHandler); + event.target.removeEventListener(ErrorEvent.ERROR, this.PrepareResizedImageErrorHandler); + + this.Debug("PrepareResizedImageErrorHandler(): Error resizing image: " + event.text); + + if (this.current_file_item.file_status != FileItem.FILE_STATUS_ERROR) { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError : Resize Error : File ID: " + this.current_file_item.id + ". Error: " + event.text); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_RESIZE, this.current_file_item.ToJavaScriptObject(), "Error generating resized image. " + event.text); + } + + this.UploadComplete(true); + } + + // This starts the upload when the user returns TRUE from the uploadStart event. Rather than just have the value returned from + // the function we do a return function call so we can use the setTimeout work-around for Flash/JS circular calls. + private function ReturnUploadStart(start_upload:Boolean):void { + if (this.current_file_item == null) { + this.Debug("ReturnUploadStart called but no file was prepped for uploading. The file may have been cancelled or stopped."); + return; + } + + var js_object:Object; + + if (start_upload) { + try { + // Set the event handlers + this.addEventListeners(this.current_file_item); + + // Get the request (post values, etc) + var request:URLRequest = this.BuildRequest(); + + if (this.uploadURL.length == 0) { + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty."); + + // Remove the event handlers + this.removeEventListeners(this.current_file_item); + + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(this.current_file_item); + js_object = this.current_file_item.ToJavaScriptObject(); + this.current_file_item = null; + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, js_object, "Upload URL string is empty."); + } else { + this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS; + + if (this.current_file_item.upload_type === FileItem.UPLOAD_TYPE_NORMAL) { + this.Debug("ReturnUploadStart(): File accepted by startUpload event and readied for standard upload. Starting upload to " + request.url + " for File ID: " + this.current_file_item.id); + this.current_file_item.file_reference.upload(request, this.filePostName, false); + } else { + this.Debug("ReturnUploadStart(): File accepted by startUpload event and readied for resized upload. Starting upload to " + request.url + " for File ID: " + this.current_file_item.id); + this.current_file_item.resized_uploader.upload(request, this.filePostName); + } + } + } catch (ex:Error) { + this.Debug("ReturnUploadStart: Exception occurred: " + message); + + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + var message:String = ex.errorID + "\n" + ex.name + "\n" + ex.message + "\n" + ex.getStackTrace(); + this.Debug("Event: uploadError(): Upload Failed. Exception occurred: " + message); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message); + + this.UploadComplete(true); + } + } else { + // Remove the event handlers + this.removeEventListeners(this.current_file_item); + + // Re-queue the FileItem + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + if (this.current_file_item.resized_uploader != null) { + this.current_file_item.resized_uploader.dispose(); + this.current_file_item.resized_uploader = null; + } + js_object = this.current_file_item.ToJavaScriptObject(); + this.file_queue.unshift(this.current_file_item); + this.current_file_item = null; + + this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading the file."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file."); + this.Debug("Event: uploadComplete : Call to uploadStart returned false. Not uploading the file."); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + } + } + + // Completes the file upload by deleting it's reference, advancing the pointer. + // Once this event fires a new upload can be started. + private function UploadComplete(eligible_for_requeue:Boolean):void { + var jsFileObj:Object = this.current_file_item.ToJavaScriptObject(); + + this.removeEventListeners(this.current_file_item); + if (this.current_file_item.resized_uploader != null) { + this.current_file_item.resized_uploader.dispose(); + this.current_file_item.resized_uploader = null; + } + + if (!eligible_for_requeue || this.requeueOnError == false) { + //this.current_file_item.file_reference = null; + this.queued_uploads--; + } else if (this.requeueOnError == true) { + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(this.current_file_item); + } + + this.current_file_item = null; + + this.Debug("Event: uploadComplete : Upload cycle complete."); + ExternalCall.UploadComplete(this.uploadComplete_Callback, jsFileObj); + } + + + /* ************************************************************* + Utility Functions + *************************************************************** */ + + + // Check the size of the file against the allowed file size. If it is less the return TRUE. If it is too large return FALSE + private function CheckFileSize(file_item:FileItem):Number { + if (file_item.file_reference.size == 0) { + return this.SIZE_ZERO_BYTE; + } else if (this.fileSizeLimit != 0 && file_item.file_reference.size > this.fileSizeLimit) { + return this.SIZE_TOO_BIG; + } else { + return this.SIZE_OK; + } + } + + private function CheckFileType(file_item:FileItem):Boolean { + // If no extensions are defined then a *.* was passed and the check is unnecessary + if (this.valid_file_extensions.length == 0) { + return true; + } + + var fileRef:FileReference = file_item.file_reference; + var last_dot_index:Number = fileRef.name.lastIndexOf("."); + var extension:String = ""; + if (last_dot_index >= 0) { + extension = fileRef.name.substr(last_dot_index + 1).toLowerCase(); + } + + var is_valid_filetype:Boolean = false; + for (var i:Number=0; i < this.valid_file_extensions.length; i++) { + if (String(this.valid_file_extensions[i]) == extension) { + is_valid_filetype = true; + break; + } + } + + return is_valid_filetype; + } + + private function BuildRequest():URLRequest { + // Create the request object + var request:URLRequest = new URLRequest(); + request.method = URLRequestMethod.POST; + + var file_post:Object = this.current_file_item.GetPostObject(); + + if (this.useQueryString) { + var pairs:Array = new Array(); + for (key in this.uploadPostObject) { + this.Debug("Global URL Item: " + key + "=" + this.uploadPostObject[key]); + if (this.uploadPostObject.hasOwnProperty(key)) { + pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(this.uploadPostObject[key])); + } + } + + for (key in file_post) { + this.Debug("File Post Item: " + key + "=" + file_post[key]); + if (file_post.hasOwnProperty(key)) { + pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(file_post[key])); + } + } + + request.url = this.uploadURL + (this.uploadURL.indexOf("?") > -1 ? "&" : "?") + pairs.join("&"); + + } else { + var key:String; + var post:URLVariables = new URLVariables(); + for (key in this.uploadPostObject) { + this.Debug("Global Post Item: " + key + "=" + this.uploadPostObject[key]); + if (this.uploadPostObject.hasOwnProperty(key)) { + post[key] = this.uploadPostObject[key]; + } + } + + for (key in file_post) { + this.Debug("File Post Item: " + key + "=" + file_post[key]); + if (file_post.hasOwnProperty(key)) { + post[key] = file_post[key]; + } + } + + request.url = this.uploadURL; + request.data = post; + } + + return request; + } + + private function Debug(msg:String):void { + try { + if (this.debugEnabled) { + var lines:Array = msg.split("\n"); + for (var i:Number=0; i < lines.length; i++) { + lines[i] = "SWF DEBUG: " + lines[i]; + } + ExternalCall.Debug(this.debug_Callback, lines.join("\n")); + } + } catch (ex:Error) { + // pretend nothing happened + trace(ex); + } + } + + private function PrintDebugInfo():void { + var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n"; + debug_info += "Version: " + this.build_number + "\n"; + debug_info += "movieName: " + this.movieName + "\n"; + debug_info += "Upload URL: " + this.uploadURL + "\n"; + debug_info += "File Types String: " + this.fileTypes + "\n"; + debug_info += "Parsed File Types: " + this.valid_file_extensions.toString() + "\n"; + debug_info += "HTTP Success: " + this.httpSuccess.join(", ") + "\n"; + debug_info += "File Types Description: " + this.fileTypesDescription + "\n"; + debug_info += "File Size Limit: " + this.fileSizeLimit + " bytes\n"; + debug_info += "File Upload Limit: " + this.fileUploadLimit + "\n"; + debug_info += "File Queue Limit: " + this.fileQueueLimit + "\n"; + debug_info += "Post Params:\n"; + for (var key:String in this.uploadPostObject) { + if (this.uploadPostObject.hasOwnProperty(key)) { + debug_info += " " + key + "=" + this.uploadPostObject[key] + "\n"; + } + } + debug_info += "----- END SWF DEBUG OUTPUT ----\n"; + + this.Debug(debug_info); + } + + private function FindIndexInFileQueue(file_id:String):Number { + for (var i:Number = 0; i < this.file_queue.length; i++) { + var item:FileItem = this.file_queue[i]; + if (item != null && item.id == file_id) return i; + } + + return -1; + } + + private function FindFileInFileIndex(file_id:String):FileItem { + for (var i:Number = 0; i < this.file_index.length; i++) { + var item:FileItem = this.file_index[i]; + if (item != null && item.id == file_id) return item; + } + + return null; + } + + + // Parse the file extensions in to an array so we can validate them agains + // the files selected later. + private function LoadFileExensions(filetypes:String):void { + var extensions:Array = filetypes.split(";"); + this.valid_file_extensions = new Array(); + + for (var i:Number=0; i < extensions.length; i++) { + var extension:String = String(extensions[i]); + var dot_index:Number = extension.lastIndexOf("."); + + if (dot_index >= 0) { + extension = extension.substr(dot_index + 1).toLowerCase(); + } else { + extension = extension.toLowerCase(); + } + + // If one of the extensions is * then we allow all files + if (extension == "*") { + this.valid_file_extensions = new Array(); + break; + } + + this.valid_file_extensions.push(extension); + } + } + + private function loadPostParams(param_string:String):void { + var post_object:Object = {}; + + if (param_string != null) { + var name_value_pairs:Array = param_string.split("&"); + + for (var i:Number = 0; i < name_value_pairs.length; i++) { + var name_value:String = String(name_value_pairs[i]); + var index_of_equals:Number = name_value.indexOf("="); + if (index_of_equals > 0) { + post_object[decodeURIComponent(name_value.substring(0, index_of_equals))] = decodeURIComponent(name_value.substr(index_of_equals + 1)); + } + } + } + this.uploadPostObject = post_object; + } + + private function addEventListeners(fileItem:FileItem):void { + var item:EventDispatcher = fileItem.GetUploader(); + if (item != null) { + item.addEventListener(Event.OPEN, this.Open_Handler); + item.addEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler); + item.addEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler); + item.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler); + item.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler); + item.addEventListener(Event.COMPLETE, this.Complete_Handler); + item.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler); + } + } + + private function removeEventListeners(fileItem:FileItem):void { + var item:EventDispatcher = fileItem.GetUploader(); + if (item != null) { + item.removeEventListener(Event.OPEN, this.Open_Handler); + item.removeEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler); + item.removeEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler); + item.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler); + item.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler); + item.removeEventListener(Event.COMPLETE, this.Complete_Handler); + item.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler); + } + } + + } +} diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/deploy.bat b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/deploy.bat new file mode 100644 index 0000000..08fd171 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/deploy.bat @@ -0,0 +1,4 @@ +@echo off +copy swfupload.js ..\..\samples\demos\swfupload +copy swfupload.swf ..\..\samples\demos\swfupload +copy ..\plugins\*.js ..\..\samples\demos\swfupload \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/jpegencoder.swc b/media_tree/static/media_tree/lib/swfupload/swfupload_fp10/jpegencoder.swc new file mode 100644 index 0000000000000000000000000000000000000000..b527fddd429ee3131c49454078488599926f7817 GIT binary patch literal 167837 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%0D4FS$@`gA9l>h_3tqCnW(L?v28!Z~Gk< zs9Kd^_b2$T44TlH0Z&b6IM8Iu3@77_6d01o^5~y#Do;G6`DDAG676pH^0X#Rq!td5 zr@Vh1B@Nlg7d9!tJVc~fbV>O0Bc*D6(}r@6UMplNeTO!2#-QW5nl%*HOz_>evwVs6 z0p^&jN=yrA>9n!A>l%MzkcX0XQAEoUbHn4Afe;O~DY%qFyYYe#LU&R4iY-+&RR*vW zFi4U$11zCvYEuubXQQIOn&C1SJ;&0MaPX-`2!nKeW6dm-_v$;yE>d zxxHZfZoKs|5s+*^WmxhunG|c{0EHETHR@4+Gy}De^gtp|Tb`qhPQVX)Uq+z(IP=3W z8(r^bGYRiUL6}rOr6b#H_`O_9;!Jr^KVf#|ya;`M%Q22AQ1_xTZvs`NxOplrUiQ+5T1(fz3NKb$y;QhQ?amHI!VJfPB6{h@ zBuJ$0*!+v)*vtk_&Oaty6${r)RE31u1CqY8S8(Q#tZKoZD4gP;A!ydRsjWbzMyH!8BWxRIj9 z+P#>`)@(!uzECQK0!*p z`ENK|pXzG=PLLr_KOBd&RMrm6{x*HcB~l$>aXuMT;yB%da#M0~vD-K(rxr+ES%g$c zcx=x5J?g8LP)F%D{Av=;N(-`vh|G!lcI;pXjkzVti9>)M)B>PRv1}B8yosDflwh$) zSaE}VD#^xGjX42__Sv5%I0FNnx8!1Z^8WBhk%gX0TUF` zywn2N-b)?OLHZ4y( zA<0Qh%pcAqiI`-c^$e+TFr=T^QFW)K6hmT}CC0PCdSVsp$OKtYSTX$xNh6J71I(dI z9pK0(Fl%#w2}EMBWTg1gH%#j6&aHGBg$+;m>#$!WQ$FBj=J6zK;2z7hn+6j%=otcF0oU} z+Dm6uC|$A0Qf)@)5cA|54M+P@7{lcq?D^{h`uMfB5tDZ%wxL z53vknB4U%m7}{LzKm?O%KsCA$n-Mi7g_XlPw29*mLkv>^spP?1e%8*86{{ul~Qxf3(1=T7_wP<`uZsYv9P|aPNoZh3xCSQbI_O!F& zu$08g1|<-anE*!d!A=NLTwcL8$1PRz!yznvO{(b^@H1wg-A+FtlF0;x7s4+vcxa}` z@2j!Dl?iRykbEnkW4R82qzeGNqdezSp3`!_XMa-qx40^F%)eqm1H$uUjf{>J!V53K z9WFw~{HerFWJii%J8#P^AqK9-MW^1T=ne)`BAn{5kPt?uNl-xZy>ye9(#NA0xLp0d zbk|Q3L?YQ{#8HIqvz9R034JtCvrqYGB&u@r%=g%+h=m4KLCgFUsZm)r)5rU+@taKR zPsI(9w*_eM%cyLp{W@dP>7g-TO`Vj!-qYlASwp+T}(|4(lOV{DpL@ z)$^s6L0HJIS`zy%LP&`dEqOK+6f2}G2jI@+evg@b8F5L$UK())_%gqYKA&tQQ__$^ zWiL-EwWDGyRAhzCQ3iYrGzr%!#uzuVG*Yv;9U9WNJChSIQe}-&?MYNN2x1836%8ds z{Tk#f!)U9@t%@3drDCzv<*#Ovb^>`sQ6NcsIDuM|kP0*v8rKDn{ z{tF|&u?RO?QYUpm)=)`Nf)#}4*65rbv&sT%m)p$I)atzw?mnklEmB_EfT0j zzSTVH9X@kdr$h}<2vb6+b0~2!Lod^5WvQjWCoYeM}K0|k_psT&3aDM@_07BIUT6P zuAVTdl=y%=ZON_m;Ve|){-T@VD-O{{0G2aTeRIIXyrulP6NH8s@R3j@_(TGdc!$_# zj=SbUJ`J$CBfu8~&X&25%^8fh5ey5K=h9iOM{=V*EXOkKuO}pi132lvta&+fBN5wB z$E-^^v@;{h@-S<@a90>)b*f-TFD~|rbe3k0u~LkPLktkaCE^(j9S?xIgR#lmO}L(@ zZgT%)eiPP$)eOzl6Z>zH*D5%Bwkcj%Oq>%AM87GPS+pWJT+AOO4W6ypBM2iB=j%to zYsj_P!cai(1~iIu(BwEInq(3HCgnO>3Bh6YT#D&rsjK3qh_7`pWjE z`n3(w-4GD;lrtLi zo1Lo(F9cB)I!atL3JSS~wHb_X4N9n06Hg`B8nd9``wW<(YO^+RKBHNX#38}m2W}f^ zw5A^$;kg&*-%eicDuay(-ikhXXle(AaX?Bjpe1Yv9-kwr} zVl##rAlY01D?}n=W7&dDAFsxC=7_m}O*&yXhg==RaYiK!z`+~z+>Dnb>dH%(@(A=8 z->M**B!|p>Iiu7Z$Uy}uz+{Z(T=NtRjn}7`q0N@>|1Cvj?2azLaNdN^khS65(B=UM z-=%BvjN&H`{$hTii?U}7U}-BZij1O>3#hTO0)yPY;G%k{L$nL*6=b0rJkeCbWiU(; z!WVUi?Km}{4It#^`$cCUkpRFLd{p68LfBg?(?fu?2`M+NeQkA8P7uIXi`gLZYeNf* zjdKq6xhFlhkpxrsq-K2c=PSU0S7Wt90ZSqQHs#He0Zl68xoqe-fUGWnP8x1WV@4jn zR1jsz4TBBvPTyL z_a@IGq=1Rot*$iS{ykjT>)-_db>bl`iD$**s5EpxxfT258qSpb2StIYU9986RQ^@1Bb1LuDiFX(|Q%+Y`Ba>E5rKxqdOkqPty zsvrk+I0$zGoDpt(1KbXkmkHEf80Sr_03K2JFDu22?CSCOY8$+jx~czRP^Cx#k*w@D z0W+_`4dqu|(0n|oDrNbrkQ zdpwJ}O5>*LTUC@TSLpS>GF2}n6q(>KGU+8C(od_Hoq+I^`fzp1wvVL_fw(P99fbjw zWRWhE#l)F3ckLCZ5WTkY5PbW&R0I6@2iq{_GfT4 zL!~%{@SQ;SHY_;Bqcwr$3W-*ChWBlXYSjX`mm)rGHVkRT<)J-%IlCpQs{+lsBdOl>UCFlhX9Mh1bh#2GAgolYIY0fy zxZO<|FAPfkc`4pD2=S!+C4&Y~qMfnZEP7Ka}-OF(%Ux`Lg#96bcFNEmcvEjQQeLtSx&&*cEvJ zn=2soY4F9UpjEE2D&;oh&(dQRWiDIDMUt*8F9imUt1IpJObn5aseRl$vRoXASHz8~ zqbAJ;_@7`NN`MF$Lw6EDyfs2b5wG2&@T@W~p0OnpMJCh(AiA;)eG-Q;+BJozbJDbF z*_s;Ck!j>1Rjz{gC>7WVun&Xi4B1HKBkk_S+RC6_9Ez|Bf)P^`z@Y9`*Qc5G+ryy% zb>umqaSA?4`EgxKjoXv~#DtQu9OH`05T_L-jg3DR_?Jrbr5rzpczhxx9U#tGZl3S$ zAq2({0Yx#o7fkF;CRr&TZ^Z!P)R=1c@lLPtdl(Y#?u}mPg;^M!VHgs|3DYo5#J9~L z3!q)gU~zdTqO+1C5PR_!6TD{>dXJou&41&=FX~aotqzmQIp(9fKIQgG^ijSfb=0u{ zVYu&=GsJzd}osAlFC(nq_S{yZt=1?kr%q1=QQ zl=qP8im8DheG*&OpPDnQePFJAE3#WWFGNX>PwdBTH6ZiH^=SSR`!dxbjr>M7BzZ1` z`#vv^<&NZ0w_>4cf6s-EiY~fa8h0%#W1eG9AvxQ}=D|-uk;aZtApXG&l2kL7_mm^mSWEnms3x1=A!cq@;nmXvUn}Vuo<8Z{ zVbh#W5$ppVCI?Te!Va?zYxie>Vu6rh(SSE&seU7hZV~lhd!yiaA9v!Oq2OXzJg95NT z4?64}04HJhPHr@iH*%VyE6HllVpfYT$fX)x$r(G1mlp2!&xik*$iB7iX3)zQp~u$d z>91oA4EQBc(Bet+X;cF-PX1B<*N8qcdhh7LK}3X6BoUhM>-EaVas=YaFLp?CI7eHRnJ;{Q|@^5<}>?`fqkR7sOcj+dsHSKMNWmQG*JDQ;eK!>4y_ULX5Dj&Ol53 zhA!+B_v^ljbzb%6a3abf4C0~5vv_@&i}ol>v@Hu7%TmzD39g8XMG5cGw=;~UwMJD196?{ArycgR<>LZv3| zUT}xN7Q=0EB2)((I7MKB1S-7CA=#)u&rVB(g;O~jJQ|08+6{s1Qmj=bXFko=m zvSsJ<_sS7)+PD49K2ws)xTCT>OOUGn>%9WqrE_`^P2cf>nAIeVzgCROeyF~>Sd3J;6i7#eyiHq@Q^tCrIh$kdK?WEa!B)?P>fIWj7f8>wnXdtiKXcXEOXP zxNgXwW=ysb&dtC!rln~Aw~NlPrTcGSitvaZv|%m=A7&%mtj9sG;LCF1%iuF|Fg8C^T6f(XU;KS9km!^@gEn$ z_R^ai;sKAg2x`bxn$5H>vZzGgJ>f1u9D)5_{xH@4*zqkkr%I}t z(p+iLK3TH!9Ji2B8aa}du+_zbzmv38;hinAq;V5HrPlxYA+>WZm5HoMN9QrOD)fP% z@C4r6Qux`+<_=aA?L|frA4UDz3| zPg}(4j5LTDKdQKArQ1KBp7ENprEYUxV{{JZRd)3`IVJX%#njkID|`R)KPKbQHUPyVxYTJCg@A$CJ$1MhCCO<>_QVFe#vt8>5PNTD1t`#)@3;aM1_b^!g&=^O18HSLQ*mw zLNgR?r3JSdeG21#mbH>8n-fAZRgao#MbaSW_mHQq-~faj10F*62g`{X%HtifiYwGm zBJeJVh~D8cx*|BXkBx3=^;oafwTLVu?UBzFdFL8ce{sx5Jhf5pPj7l{yCp9lL+x7MQ-F5 z1!Dl!%(p&dR<0*ELgmBvHz0M#UP5m< zV^9dL@w@#%frgC>ei9a>8%)x%U!BoQgQ}JT5p~o$%RaHfMU#X6I;d0WG|hFjfBsom zo0X?^HXAzE6N5$2QyYZf#cqf}G^l$W2ppRmIBfPA`uB-6F!95*-(-6;fvMYd;RT3K zT3%u`&y}Jp6&?%ebI$_U^OkKCS86p1*!#4zeV^AHm!^XqRVmEX`4Au2Ox3PQpUY>< zZd+O8X%Z46%rYjn;u@bfagjRrmJ(bt$b560Rb0W#<=7K!UmeuMrM1I^817= zIn8B;)J`8YT+@wj|H(8uEjV%mgB*FPKaeBt2Y99LMU(a&6TNiwIPsMJpws_A?S5NSb z_S5iyX2mxx4uSU#SDM7?s>p2qcmK~z9KX)4W}@xpRrsr~CHO0`1-qT>yziN9k6v}h zt=F$bE*26-azNHt*TE&Xsq`ItMFst}C&~>uLx-j7KBt_6u+2Y23U*HNC-OCNP-J0b z5XE3hVMx*p#4ZxVucnD#@F=c+zH^F>~cUw`CPUy|1lFBDz+OSuoe)UnX-(_3Tr zj_t;S>W6YU@_q82J=d;~->e%%9~D+3VKe!7B;CgROY0OPrCRUYFDljqcidv${^yvt zr?l(1?e(a%gT zHJ|HC`u37?a#A)>rKDuRu;JEFC8Ya7*U^e63mu}7@{rbqlSjW-^6;DeKgr0dw7rHi?9?FmI(JY-F>V7a~T)PG6R&-QOj9F6Tv$db)l8EBy>ARP6*0`xvy(0m9|I%xM#X8n#M;fI!^dyoIj|&rL4}%%pWLQH z;uNu*&Zf^$0H#&o;%e=m?X&zw+fGb|{aQ^A9`01qi~KpP{l`TYf9!a|?!vKQY>s`< z(m!%Tr9y7l`52f#u--Lm-&F(x;|`e$?)@erIS7Ef1guB_;LfM)d!E4y^yJ*SjKY#~A z1^xuhK4FAO+Jp~67$s;Cr=g6|#SaR~oH&x_x2H$|2oWv9i$Fo@@I=53(1VR$DELt$ z;eNbAU8^7=6KW&^Xo7GZs0~oQh7n;Las(Nek+E>yf#50#e38LTf6LNp9GwL(yNR@o z4Iw*`%^rl2Nb}(EKSJmsWRN-{Z6lOl|6}5ff^eP50~M+?)L^hp_`n!qJ(zAR%o=j2 z2(gJYF23_6Zp0o^uuY#H zHbFfZzSGZ`-rj`#KNS9t#M3znv{Z43^%}o{Wk;?ti5JkNp3U=sz<3N2)m7 zA1M~i&nxMB-9VO(fg%mH>06hFUrWG^oDii%$B_76mEeEOLi~@I{}ELJ>Zc{j1vjF2 z_r^ZFco0-cxY^)b@~1VxT6i=isZ~(whcEvz=synr$D-A=I6A7aRZ-zO)J@)g_Kmb! zhgG(p&FEY*yjzcqk%CHq4&Na@LK+f0K>xusX1I6rt|(*UuIgXA{Htz{!qY}ZSc8?H zy)$1h4cNan5`o-EMrlx&mYj<=LI|<}lms~pDzd%9TVrv{Vu1d|Q69V|69uATRBj+|M)yi)OdeWbGsY%C^z=xd_uhu7Vyza?f{-RxndrwjY1 zvJavM7MOO)G>%jk6sgjtA~H}0&Z_k>3!L-c1I2@xf4*E>Ah3L^b}6!%+In%05&%@ zr?wcL^HzJj)Q+}>%y(BO&6#Y^tBT`0WH)+8k7OS8w$ACP%*_jmtnhy>7rxyv`x=P#0)4Egy z636~YWg&@HFG_IfW}KEG3{!ERaHTza=k}w(AuPQ&aEvA)dnTCOup(yIawU5ctR^XY zCRp9HB1f2VF?$nCCi#1)*xkY+a;#b5A++pSMg!_Lqe=r*Y~IBoS+<-Fa9leMM>v4| zJzCZSqXA8uQK^A4)&uSVP4iK>kcPgvOgQs~y$zdD+mME-I376jQle+fX?zhi9JwYU z9N5ynxag1tix+IT*IozwUf;tas`TnCXEK)L#4_)6DKi=h?J=Fh18V*11ds7CWk3)> zjFL-=t1xL&U9LL;Y@A8~kOdf~()^nxOct4>Tx3!Qh)AYYlnYN#PG(UZ!x~S|H%SMK zC()|QwIw(uv#5`~j;Gfg5FgSi0m=X=)NMLk6Umhda=~}1l$vE(Qe-QX=Fth($qi~_ zaN}!v2WE#hYJdtrGj*FT*GBSYSyr~xmD;o&*)z3yO~QLpgZ!A*;UcwWX%_ckh4Nh< z0G@hBhzuubAuMUl)~m9-$1qdKigHbZmVnShb( zq&PM)-d1^_dZ?p%*AA$r@lfXCNVZXtD@<@pauOf&8gDB-_;tvuP@jAHz+&pRaBBbd zmI=r^g=IBOIsMVD_7s-=P+;qZ` zAmLPo`4Dv>-RP|(4N!h6!*&R}kZw4xS;8{$FvWa`yO3@?{#wGalN2dq9#55;HjS)I z&yjstqGQQTBipv_tdSWD8DHLsv@OPhvQzGBbbDzR^&s*o*R ze^AY=Uv{>V)vuyLNSg*;aO+KiD_Jmj!&Q>nDPNJI0 znATiyTTa4|ISx4OFc_aGaaw!OWj|zFXfu1OPQNQT-DLIQkoDMbw#)3^DCsokewNwc zQ!dexD?gNOGVz-@wSRrnW*z`A)9QziL1<1BPH>DSaHS*~zXE8v1_2=&-9#z4W?Tcb zJB-pXjal(2_C;j(bQC*xhuVP3!>A^2V>+%r0J%mtR0?f<7V(6-$=Fy5%UVKlin7(% zX$s5MootIq`eBl0H?#C~bJkjleKXmy<}~jF$4mkYoq7JDw#67(O2c5n1g&}cp|;r= z^h8;$H(E+%W0ur}y5$)4L|MJJYKo3M*E!ver?g#tmTbzV5tlY?+Z3Q#6A*fMQF}l? zv1U5Pm(nnju#}=~JBB?`R_D!@QrVEjEsfup6_{dEn*}b7-<&m&!fVAPPUo=+h|#_) zI?QVHrl9i}225*ov!w9aa~;#|cu4EjXGy1ACy>=>O{-40EhTW!9)}!u7>krf+v>N|vDi{9JejUDZ@AXm>NnFl*;1@L z9j-IaxZc@t^}C9#b|W?8+VT|I?pSD}ugdvf($pThZ(!NK4{rbpE@c{hP;51Ac!rv5 zR#V8Dm5tt|Y`CUeOV$7(my@-o>6b}s^_#Baw)$;!$LrHN8yqVsFgE7?m)ZssL(L7V zDHAs4>2&I=)6g3&{V5eT=E;}ZrW0(<4Z}}Wb%)#=n&$7zYk-WX=9=l0$7W^w_cL3MRaf|p zyR=K(hMmZQC>Tr1h5AD7;NaYo2b1F)_v4<=4-Ddc{g3ywFityhq}!LE!RftQ2A?+T zmg8Jl_4dzu^=@p%gUp>bc2lHr7zSSo;;*gLZgJD!Wcn|L&*~(>1fQ+qMK_5ndahby-tpPMtpov@p7-2%a_i<_(%FLodxH_0170jVkrcnX^^k z8uz%R2Jjm9Fg!Ey&V5DKD7@zgid9M1ahe=6^Ht3n_n2&RjLv;g*KpmZ2=!G}*C`r3 zbBPGykD>PqVCh5rIs*BBpI!SvInyki`+_-R5FSIcIa5C~Fvyt* zbsx&-jYYVR7YNu4xTpIFjz%SL%*pc#fcP2h%@AQ@@q!UZ<(vQ}zDQXyEzI zs>VHu;NM8sK9p}l?p;LbE0*svP0qjLSFjrQc>Vpxg8pK#8ct{8hF%nqd4_R(iZEfOIU4kE$%0iHISiITnHmjpvv5ESr}iY0Q`bImod)e~ zg!B+|-6l;nBAQXLx(*rjkX22qm`Zq)o@2HK1H1cBv{B;f1zJm3l~JbpCcSG|ww`0^ zD&3R&P*;K0#eD=@f!YN|7PggjQh^Lpb(%_LLji&E1C^*i{s{%zBNcY(s#y7pS^1z^ z`3ZY1-USd(S%RH6#wy$sVK;^^+v5PAL~^@+M*M?Qy_;1?89F0A8iKs4e0Hr=uFm7-fgIlw7(|$pcVXiY4+3$ zCar40Z>d-7(Z9@2w9}iinXDmJ#}Iemd=NgmZl3+I6=0qMxgCITK_BygaX~`+kQ`8P ze~t7(IUr|&l=fjcpkL;SY+)dFL((%nE}A-k=>*HJge}IzRx)+(#G(MweMJ0}tM20Er2G*UQL=8wE7_y%rB-%FhE$Yk1Z`IU(ls*v6Cx z6b1G#FIv3oM^(ThM`ADBbGoCzBGo4K-i>shN+U1am-7qD?o5zAxF7ll;H9Atml5OE z(YtVL+qP}n6WjJ=V)KnXv8{>iN%F=M+t$Q3-ZQ`dxjE}xeCu3( zYgO;A>Z+Tk_ujRiT3x*hEQtI=`UZE<1R{w3!|Em@47(7b2C7<&S{G7bvm(;pq4RCu z_X_tcmas!>IJ}s!vlagfx4oZ(Cbbi}vptO1lvd$lpP?c0y^K3r5qmXOms_XteRrk5 zUOM3uZUy||XL0*tfC6kG>0zK`G)&wexeFe>Y`>ZdQm$Bn#x56M*o&SEO0JiK*jKC} zHF=&9_)5pyDB&)SEmZpeu?y}dJPF7KjT<%a=8ndz^F^6qsdz(hZjDvI^sZ+>9*N%<76_swj2 za2XtHSRYXh7`7?3E;Pe{z3P#EqW?MIEd$wZ~8!1?`_<6p@AmVLcLI#*oT>K%EIhcc~1BGB2*oZLiEoh(^BnxS$(C zWDH=tU>icN3?RE;ZbHOM1l;p?P}_SsVqA;xqm2Shdneeca5nqs5bRf`GgzQOX|+5BY8Y?~pNARGWdDTo6{9&5D9Gb_3lxOnei}9o++s!Wv^~ z6Jx(ZX+1=mDYq`<#IJt+mIiVCuq07L%g{Qtm`ka7k_KGOumC!BNc*z4tH2izFu`nh zuX#v0QMym!2BJa|ARBQ+kd(fx73?`5A9=DT#yY(VOz!AlXVk(c2T*78D#SrOi+6WN z%Bfw9vIF@_&Y?%pe^rtGNiF8{;mA+D*WW~cxToaJjQCl!sLLYpG(lMAiSr}xDDpg|tC{V2#x)F>?BRms55zC3(MC_+n?0{mD_QwA>GKH=LV=StT=z|Hw z)?;;te-k?SB=Lcfa>^MRpIT%vsp?!xgQB!nF7Lh-??ge>eL@gYu! z-0wnkz_El(>>_j^)Pmj|4ZG3T494gp3zrDM4}$FtHI%LL&B?Hz3u3F{Xhu@TdX5`UM)$9l-q!UjW$+bD&SabdsUQft84doPv;iV`rrnKzhS5=nU+i1(M&w@LgSAYNbzzsokWDdHA=*OnOcPH*4wJA8Al|=Kryzr2locfg)ouYK{V@9g5}wvwAPpnQ{t7l``hB}U`}$OpiozIq9BSy z|BGrEM*qulxQQt!#ypG})EtA{`T;6jb(sJq#>6*$%M?~n1;(k@zeVV^J|ZSNW9c0Kr=B@?H?usF%2Ik(gn#Dokt*1Z(use;Xh%5W#I4% z!v1XgFwt@3bajN@7*J^-ynucet33k2&tpF^ChR^Hb>$YFGqfW!$RuyugD*ElX{<$pli6eDN+R#C7C>qPs1;gU`E{!de zmm`R^@s;vXL~i(X0rdg)lnl}`RGf_PFxZd{3kj04pDqa%p`VWpTnQ>gjQ!tSHK56c zJPF1rj!y^MJYdL%$^?cuVBP@5CquHGf+KyePv z`M-kEI=CWQX~>r;m>ev=DcKlQm>A9&48JMc7_?-+Y7%Tj$et+_9=uG*92sdGIJ7Af z9_&aMd?A_=l(Z=Z9Zbpqmg@gTpajd=ucQiN9pd`W&%oUVgxE;`147Y&#{{l8z|ru3 zgqa2y&5+jr0Tdkn&-Rhpf@@6)S5Z8J{{!`Z#Fz_Mk6kc6xHEA`J}8th%3V4>n1nFQ zT`E3kl`zg-CO+8dFvMMo4k(r|#$AREn7S~+U78MPf&nKN*iGo?{(u{>QqQ4#Si3?| zy#F{B-v8zxP5+__lIs8(8`fcnnJFJ0xJ;;;Dc9J41Pc#wB;?inqVBW&O$%rk3L&z|ZAoTt-v;i_)BydS_5NDbe-k1R9x6?+1RFY5hZ%bW?0~++#Y_6I%nUv7terw z{}U?geEdr0%nNt9{8Od-oaOiWKG!oJ9}-F8gNt1$E+G(bZ{IxVzIpI_{enklsO3?4X38LVK0jHd9fwvx6PkLi z7E0+2A`$O6ebXq1Vs5H7a!m{fS%mH)TdC;M#D#3!JgD!3lNhKlfWP6WyfC?h8608I z!6by!RIBH+OdP^uWLRO4Qr6{cw4CQ^l>PS2E~6a9VxHH5im%5^5~%OHu~>iHSejgd zMr_YSm(&23e#8)iBj9d(s-wY*cbQ&k^tr2^rLE^ML$m5v!~7;>XNDZGwZXw#XUkDIEV16e?c6!x2710t9*c-tRGYMO zYI9fkm}T-9egG|qWYA_~PrspeZ_{Jc)(Z<&d)U@Il+a5y-L%g_;IAh@dRmiNmKH`f zUYO&Tfz{MPi?s8)E2-W^(E~_tmK%yxr(v*`slJ@Wq1~bV_=j(emJH>xftvyPIkS|@ zO39p&K92Uvv1%s0v&=FVY_{?@%%bs|8+Bxs;;2X5?3!xZ^KQ;^^2 z<;>nw{4?nLt*E9^5D7?h6tsA99+}m5nRgvj=?fwP1&QK*rJmf8ve)ehJScYgfxcbf ze6^82sqG?sY85Xf`-eS!BE24e@7B*7L$sv3w3kjH^}-VqOwpO&^B0Aj_JRSv8q?&G zB8A?&;^dNA51!^jo|Fx^{LVoG0e9L0&<@;N;h*m>6uJ$IhX0e6&5aCeqX zM*&plAOTWm0V6QpNEWwq66s-zA({DMm&iG4m&k=OHU}spa7IwJDOI}UWJUGn#Q}Y_ zm?M!9ycbhITlJ$4N6NZMi6ug9I9J$Jq+s51QH1=1VruFvBwG1ALr2*I*{0nCwoHtj zaA?XYddsbsCC*XgmYH8NZwF>D(Y%E>x?Rw>V+{`!J)4Dv0iw~pykRsoj*NU+ppY;4 z6_KxI;zdAeG&Q5`s6$r_VV8hcD((Ud(!KgR9JFw!&--MZ4wn(8#MWwqpCIz7eay}ELKmzCK8m+HT&>3?c0n?mNvz2PrL%`St^m1F03#+R30a=T)wALL3>tlj2G7KYR(WRn} z^(D=FR~2r%%kvz02gnk><~(Tgd8v}#X_Je0tngXWV@$*>;jwFL#9BeniD#!vO7(>V zi>|3CvU6NAze4q?ElNYmDa-PmEuG7{(M4n(ib#tL!m@bCTw|`n{j%%e6_&<4;;th7 zel8CPE3OO*i=*}ntF)7yoB5*cak^syBW;C&xPQ&G6>95~b2PaNv!#SSXLdtW%4d=$ zS~3F(+$e7BT;pLxB)O<7X_ zxUK1SK&~=$Ea%5vn)hq-E(`9+~rFfMY;BNW>ip5COq znaAjSj+nJkhQ0nk-L>X=%+sN(!(-xQ&9hMhJM3iHc{R^*>}d_|MyNNGKrWQ%B$a0r zB}ivgL%O`A=-!|^UhD>)PSp*>ud+N*n-UX;5nCUc{wuJ6zCHFfur<1PgNEmZt?}_+6{Cw)tm;vvl zxH`BQAH+L>d3A^|7l+Crb^36@u;Q-61!UxjBP1p`Nz0aVB5=`+|QN- z_Gq3|if;wTqpWCIxnYg%Ywe4)ijYb2n=t2-clM~C7>3=T{Wf-Q4^)o+ViI@ z2C3YI&CU6<6oaG@_}oe?UTHqT+M=%1sxnJ*9i{2wyAVxbB_Lh?x@1j5SaR&pZ&vjv zc6YaS%Xa%EvqIXS@RFpJ=yy9aYxp3$+Ng7+r04^h*owj)Gen!dKF2b`PIuDZPHrK` zgrOA6nLBZB!^YzM#$BhkUYg(t&ypjQJ*jDt;o^s|PKXmvQxBNAYXVD`2DDM^o*O(9 zZFx-!0%bj3pw5WnkAR|HdZXVRp7T9t;GUKEs<*n=JvIzRum8Gq5Yx8$DRft~`l)oE zwEC%ax1AgQn_$A~Th?|>SL*Davpwq_o+CWJ+duz=^SDg+By8O<@k}HLGm1z7hl?LH z25K2ZOkLh+=O=Jr(ya{(68VjN?FjFRPi*OX-cg#hSrv{%r)suu#q|1<{ZRs5~xci?dlHsQ7MoGLLjnqqaHZTNm}JD0_900?&&+{p4fZ0S-K;Ngf}k< z`0*2F%@wX(IUtJ4QcDq&%My4IXQUh#u|5%6TfG_Ih8;7Eou_aY$YD-k= z%#7n`|zfIB?-6iZ|Y?eX-mVI!LFcc|P@E4_(^<3|VrW>S{E zHDN(~eB4R)wlpGx&6JU-Vk{1 zx6yTcJF>f{JUIq`7U&=Ilt<~36&RF?dt+mC&>8yk%Zn8{?!sY4zc!(=aiG_b&HbEx z)FMcOIdU@=Mv^_>Fak0R5Wq_)^(hr6uEjJTZlUmtSJ@tjw^G06r4kcZZcgPth-_AYL4 z#~lj_35Zw7G0B>eC*HFP35sKCGh<+qtc72Rn>lqI?aZ4Zaic;?ummfrcX-Z>i3S`S z9edk*E)Sy4j9~K zG1b6>r{-BX?k?G_er%xk-%OS-QYO&W1QYTj&pyVTOK;ibFE6mO-q>n6gAtF2S6B8!0#N(n;K_0KquV( z;~BrIx|ly9zf@vxvQW1kS@a~N!h?6o*S}1-2fGTl$QPQz?1bMvH9~htGBuvCd%y8D z#_>9)(?6cyw%D~nCw6t4yC`(!yScKM@`Fw^IR$bgrSHApWn!__;e)oY%v=Qp|BcTf zsUL?;+Cy1uxi9>}2y0+tGtV<#3 zI}hzjTwllL{GHYTrH9ZrY24!%Jb(|QtiFuOBKZm^@jD$ul$q)T~miP~Prg@S3hmeqYo*9jZRHHq4?$3>J{xkzfH zX93JQ;iM{vsw=)K5%f)HMe(_DntWH?vQEcTCVQTGphCMJ~~QNm>h1^bnrouGC=pvP$Hr zB_2CI=$Z(MBy*Y6J5byc1%Tt~;e?QNTe+Go!+)?Zn_P+Y1;)qimySvCHQTCtk!`P$ zXwNm>H)xwS&h(eAuK9CjAAD4Js&-yevg0q$wsBg1M5j06wk%)sxSuf4c9T5Y zT{?mm*7trB|4^@8|Ixig*cfu*x)aN{2I?*da_Y_pkvtPer}#cD9w4s{JfZ5kBxrWm z8|4zI1x*&*l4uXWYaRUFgLjqifZxJO1Xa}r-*oov1T0}4P997@FI)!s!)`uni?dXM zunDT~gX}FHE4PZb1LAQG|6CjEXKb{#pmee>gl?+7+53Gadl1X~E(+aro*9|LYy3eZ z9X|-&xs$%|V%+`aO~f3JK{e$lsrX595T?PnV?H;JC(bd6`bhcY&Z&D~x5%NA#B|T@ znX{X5v70Ek-fM4Y0h2XQP@Wsk9v*s_UGPHb91u3Odj`j{f6r$3lWb7Qyhak!MZE+{HdFyN7H%vn-r1bd2mU9 zws^VwO0b0tQFIeSe^zrP;CV?asmxlit(qF$vy7of7(v}kWS?;^#?y(&uCiAlX^FIM zB~3XUMU43zTJL$!UT;HF5ypOvYOEafnTuB@PM_v%onegU-ohyWeciszZ)Z%RL=b9i z=}E?`t#6KqD~4m~SW=X9b)zmTIo3D%J81B6xvhG;sIpx<$@iYU502E)_xGUcS1_4u zsNllhvSZOJ@E7i3CVeEMf=cH5Dd%CT9_n+co~)>HLQX4O0Z*UGZ|R_gte|)4`LG^k z-xRNLzeAlv$UBGm#GgV6G8*b)<4|uy`U)=s3d&_Cr&3o9=zQupSxXJ* ze}E|KoKkgf+BATks!b^v7P!>i^z6^7cT3YvQCDSB+T9>bc^mfRs}jJns=ACkKQ$s< zx1`{G_`}cl30k=_XQvWp=vS0k6h-{TVv}9BEb;CnpGi}rF>~um7E5%_I2KEAPAS}` zu;j{oOL)%7I!h5wZ``IN{3b-bktpI%;@>|#j$yZOoYhUm=KC3w0?Mg1#@ortMfwUT=|#uCS)^dPV_LW6CHai&&G8y zDll!vv@|r?DDGcYtI;uZ85K6yh-Ab#L$lK4PSC~>nF@^C&(W%j$;jK}b<*Kc7Y-HJ z(QZ%}q|WD-XDZ@$m+9c2G!$Z7WyhSA$+&-NfXsm4HWj#UxSf^kb@C;Zit> zN3?bZ%#u#_5_Z=Y!f%BNZ5Ry`2bJ3Sv-QF^ZPkFR@3U4scT)l$ifoTd#~d52U6hrc z-?hCQJQ8WIMQWF!mfCZc1Zqj(eqD1_BkKzvHr`ZfCqgf6Po2p-?a>FyyxY9`T$=1= zv;<|DES1&y=yz0Ue81gT?;E^6HGB^144loaE^yIU7y{cXFGtLK+d5uU$`g>YbP$z2 zJTsJ(vh0gIUEDJ-RUyV!`XtMIqaZC=3N3t7cgwo%nuKCkdzUWOJ;`RGC~&P($zv#Y z2#TQlw@1ony`W^NlfspSY1RrSW_aC%BHb>ivvT%JL9Oa@saWCNVc7TbJ6cXlpPu8= z7f++)yWp_%N|K3McC@QQL?>o7Ld3;=_)nfnr$XET`R6XbypeIntiVJAD}3r zf@FPSD=Coyc75iXL*n`+_1oTmy|3{e86xJCCl4R*u?f4vJB;1nm>w5wm}I zeC>6Mm&!;y!nGOW#wzrxmX=67YPDDJlilq9A@Moe`nBXlGT~|OkwJTHT&wLT)1+Pf z9(KmJ&P2NC+KbLVhvM)URk44dFTBUT-=aO^E#PHbjVt8y9|l$89rGa%<{dVQ6OWR; zx}=Q}qdeak4Xt0UOMwhclVG*wev3-ACe7b6+k*MztY-zgXAE)%sa zT1@^C%%C2I#V;vt`S)S_n7Udi9~kd!mHawu`#z;KhoH9X>Q5j0kSE+<|Lm$b{nS3) zuUfC_NQ>c)p+TlvOT4v``reoMyJ~WK{FMIIK7P)n{?=Ztd3*YE<=A-Vw`%VEnDZ82 zvPu>Es$%hpNX^6A)Bkaq{q#xNR{uW^BYMmQh<{QH4rBFyA2uHR(yp%>i+0thZWj{n zELrN_HVZ5~(p;c?<~8yO#`lRPs9aLw1RiJn`vm9~dY76m5Mk?MTl#@P>U?HroiP7p z>lXKfIQc{1-J7$C6SGy{Qs&AarBz@!&ah6SRbVPEcbn7W)5^M2CGS!`uVU#nKY?Zb z%$(B|F2l~F6TkCD$KE5YB7$4L_O9~0wQIm)(7M(c{+EWq$oKAaFYlu*{cQ`GtGD+S z{R6dsu=?XR@d;ae$2iMnl_h2}C*p3^Mzo?cKIjhMdidMe(YBebEY+Stl$hZy8-^QW z08gEm%bH-6B#Oi@$zG?)$aH7?6dx{pOG0Ge=Qqt^1)ew@ndEmgTDk+)1w4E^0ziRZy4UWEp zXt1wlzRO%J?9V4}6gjNr*lp|(&@rjE_!4K#79vP|7KGJ>PCqGfK7H%Z=!0~qhujMK zpZ%^we{f{$ zI>RMXD0m+Ja)HaUHX_C4VuTNA>Pv#zD{4$TjFqlZuR8*Rm5V2>9{w(h8q@eNF`7EZ zn7!E$&piM6y>UpHCw99zZfmSzhr@a>ry?#$+hCD@pi$+9w( zJRfvCz?%Q%WKq6xO6MvYn2sgim>&53Cx?ja7}7)sz#p-UPd-Rsh3l&1VwUaz%JrHp zzU=Qkiq!{Qc3f(4?w3O=)`6a=uQ)!bR7zPoyJiQdJpym=%dg1kQ0|#9#`X}`u94#_ zCJDld4?1-*djw~ZbAaR5-2A^ac)Yr-qXqjNul|VVP`P|##+Pk}U5&&&XIW*5XeWe0 z=KBFdx-j;&6IiqEt#=8W}a z_~~j8Kbo*|P8lcCZq2!}$fcPt=PKif7TvRBzxkgPJAIfu{f7Mg%lzDHX1jSs#E-N; ziL4k)oR=aqsW2)ESyiHcTQ#W?es4AONTzexRWgZ1ncN+&*Xy*`Yf?U#Y`!`siVlGy zsr`5|dqDDT%WvoT>iVC^zY7CpgC5o#mi0N`a}NX>=fz7f6RffkMa|Phz%vt}N7nr@ z{hV_UEru0f;~j>-``-AOk3z47c_knsxRF#i zp;&Vg+v3<Rk;B)V0JBp~$_|-^HwbbTj}H87)8V znuUbrmjwR_~K6q z8qXb(@xH-y#SDwk8P>JGFVHpsz7KJAU)FUf)#sV z{jt4nW4Ia&tC5}c1Ned73ja{HnNl7IC)70f@8Z6%Ck_0xM&kBo?1C zsSDTB*9y2i=pl4qBYLrOLug(#QWo^Pp~l(%3zVD7n5xJTUQI;t$FhOa`?Mwu`PRzQ z1c~BTv@egUy(gya&8Er+vpZU2;mCJj^|DyFr_Jsw@IiYn$TJ(>%6H-no;fr`8~I}C z0J(A4K>M*xLNuV#=FLVxh!^fKkXWzMw*Qbx8yqR7;xscs8(T832L2cXEr$zEc_0-8A^AC^yL1Nf1Q{g)cuhEUy z_j#JvJN(JTyh({n;`h^E;l$lr-Bb1p?7SWlMIGAsCAHd`khxpEXdx{~lvi@g8 z&?PAM$B^%suLclI~h9d`G(cOq9 z22LntaJjn7J4;BI+J@7(K~rxb#1Oxy*T=14;u)0VDBQQLQ3wwQ=$SHSyg#$~aX@Qz zA2<%HFPNw(lpDig^-_#H1rCA01h5^`OY;q)C(U?n5+rn-+2g#)nh6?FhnRhzwG8Y|HsVMQ#xdjDw+(*^#e8DR`T+)981g*K;N;cITr$6< ztZN`G&&Vi~-A3jM-ih5W58hm7^$EG;}N?+QWOgW^cr;H(qP1?=k;Hyz3 z1bBdyOwqxR$k{|NQ7aYe6i4H+brv`=FUP=UL?7S;jHzh3YuHWRstXGiT;6@5X!#qA zX_3nl3)6^MK->tf#&9lom*|G|^R^$((uBYmjR4c@lgf(_n;Y1b)AByvjj}d6;c@?? z6DFA;m?XPH6>Ncdj)`9bky$HZSYwF@2@&>O8X_Y)OLqgLnHLyOtS!^ndfLX{0Xe4M zfLntJti8#kojo8H6St_X<=(=*DKE{NpNqtzyBQ2n2gSi{E3|Ncgd@;ev{?7m`+e;Y9+T<)AW>M`XSc6>a4%;&KLo&&PiZbX}h8O1@PF^@z%72+q`T4(Xq$U9%ye8~W>T_1OVI3Hk9 z8+8P(Byo%K;}A&0B0@P{VUVMHO-6+7O9ChX5Qhx0RSLfkPs!JSQ~2dzpqf9| zyYJ%r7vd4^TP{amAN4-RBQgRg_iBC&hLb!Dl*5{KL`>BnG{rR$G8;GkkwZdEg6n2O zkF9I-VSits!}YYtpe6Q(T7SU~K@QgS@{nDLn5pCvwm zK++zvb~WPxvZJO)!A*u0nZLuZYgd{PNl%bXQ*~SpH6l?$KRtj?`!z<;8tT%t!DK6= z6Z0oU=!5z9v?k=;nugtkljIIyjNaDJ7@`d}AMA8Y1rFG*6Akec+Q|AU&sMGv@xl4W zw}x54nq{amM;0{wHdBPWp-=Dl+h4)ATIC&Tu^-F!5yziKJk%TGbI$%Ekx3HVtmOX? zO4^jrVBIrUn}c6(Abc?(<>;I+h1%$uAw-9jPXmVAIAV%~=d$V$Cc`e#h$#4@w9`Ad zdB|)1Ma2#3JnmSdk}R7E@SVL9=o>J*VPT&RYt;Dfd^49(ESq*-(Rg;si`p%s0V98gu{nlJf*@WJIsKLODk*SpEH3B02Gm2y*&k|Bw;9SX2Zcz2klKcmhc=&yMGWaj%_&c=UlDWUB5ttoUlj$qBOlbI&leF?a?3>$63 zH=?g*kC>xOA(t)BIMON9h@$Snxm-!okN^^de&dUKCIKo%|JSTi_CLA`+MdB~u_cWb1%x!PYum(fy$h?Tl2ABQKFH&J61xOY=H_prcJOr||52P=) z4#*ov{S$af2Mdi)jj@$Pt3y^owNcH)HzreAHKh3!ddcHU|rTioaTW$WJH*w1h_1wnBgc6 zTKp@=&1-|B#NODvc=@cW7S-&((MEY;-^@OpNiGJn^OTG$)?M7&bgvtxvNv0?!(@t( z{^fvLG&Iyvx@>#3ZRQj_9Dum6j+u&Gjg2(aB}-5wdB8BEts})Dx&#{6@GOR3!fpLy zGSVW=ubS!em@gu3Tn5o9c!k%V|qwv3@r5xO(EU!3bATBg4GN8i1&jgR^o2~IVc*?fH zb`QM{gtcbg*A5!9h)#r9ZbI`EIC@$r8og+4>hL8cTxHyD{Bc4cAdS^sHcQ-U8Zb*- z*_?Xa`Mz^JpzFAs3YlfgX<3&Z(J4v)J+Lmp^m63<=mB+9dQ4U*1WvdR~XI=Cs(sn?{z-a zANA;f?+sPLB$E;^^I=&)$^EN-9+ZQ>4VH{gE|+yYq+?e(ClahwDi9#x`k*!>sY9(5 z&384RUW(8FUBn~))W3CNxJr{xOVZo% za;s~Ma!HHYjJwRa-LNcx8etYbfY;4_8w=zAYb`HNVj^~_0^-_o%8JksZ(uk>`jy>H zh|zWRekEuR=v@Sv>t+2YyJsf_umb-}|JLI3{k|U7No3(-e45fGIx+QLdKvek39?i7 zMlrIru4OxHZ{HdQH@9`2!18ipa?pm#u@=#-@iZJoC_-c{EWYi<73bP~$|5gR6;S2F zBRaCqir5dvv^^27w>pS7GSPi{@g)-*9AwRG#TcB}pxyYJgb0(Jc7#~hGZj`e_VQqW z%FOcCGUCt%ZxBHe_!{{raIp51+kQH~%5yD@Sl2ofag@E;1PmwER`Wx&C)4hTmK#@; zJIR*Y#?n{ z9!`OdvZw~&3L1B=Pe!9G>H+A&|G;37>v-xz|2V&|rrQ&MPopwq-GhNoqt<-=4h1rm zQ4lZTy&}DV{Qw8?igmG?_zLW(`*A|dm%smi6{+|6|EmbR%^$!1-(~Yfi&`_qr37$> zVK3Jq51hjAhv%l=q$o-U7*H>!tI7hMF!rgbJ zV}}2(=q(E@pt6+r)&crq2q?TX1N!7%ssVlS-r^Dl@KqSbsW(3X86eu3>i~FCxffNS zEXJ2gU=qZr3V;@+G(k?Lh&f0V!~_m@4GC3ViV{0((fn zEI>1y4dyg?uHp;-o)GXAfCz_+!9roE)B$jz-{Sxt0T|(QFzP4_l{+F_nD+#Lj{rhA zUJPf7O{I=-7sfpv;3dEW?HDrx9uW=^-V5!DYExQQsw3w{5hx5WLBFEjl)(tMu4R8V< zMBO9yLbxXlR0L$e$5Mao1%Y;M`jn}-(ryX?kyKnMNlZnZ04#WBs+QE727oM;5W^lj ze4?Va1TY07|L08$z=leQu_z62LM6mn)C%Ci`0@a~i@F5{z3hm7uFoczitqZs zPlHOx&m41;*EEa-9Lh6(N7O0dE)>|7Be3^!Youk0WLL>3nM`fUDTz*L(w6sG+oRoA zSvyl4b53n4O@awq%UF<9PP}N+=x}mJzDu)GDyiFiW4qMiFs$$#-twg@XdDspN$g4zZfE9AR}vzbLmZZy^l-F< z#cu`0v;|@*1;4VohLjeRDA*S~XdgjoD%Ky}G+7cUxjst*zZPI?1>!?h@Z4JpIid3? z{=SHvzzERKqK8bwv;^5z&)1bk6-Wfc+|vK5aLM0)dE;8v^Q~#hkP8A(M zzze6%%EA|9_C34dXE_w_bnBkI=*7G#8TtrdYXO(-xM(Z0FZzD_zeVbG;atdZ>Ig9ZyQl42a zhPMmQ3uv8BtBAAuHAJv=qaI6OY*4%xH|gPPv6$GCFf(RYpCuT*R+t6T;=^Nq%wo=C zS5D5J!|le)Zost=m06qJ8Tqzep5+(4R#$N*UBy%tK^@YT#SBixr{G7it5B#Nit$H| zj%eh zoSuD@`<{%QwvM2=OPdlG5A*1F%x4aHAwbQTV_k1V`c=Bithl$NEl76Zb?Y`Gq zAI(jllR5fz&pxrL9yPE}>y$_24QD5IJ+~CsJ+xOuVSH>_%H%x-J-UsAyVwz8%V)ue zpE&r`IsfWmyi}@Bk`A|QL#vp_Bd?N}FhEeH{mh;4TckY)uchK&C&*r*!e$|SysqE0 zgIEDeDmoADaD|W%#3X;Gz_y+jm;)cS{KG1hr6TCxfM0!h92_%-@NdmlH)?KMju3E(g$714BG*C%$b+~3Ehp?@j9J!%;1*Cl}otvaaA z&Nf=*@k8Ivjk)c&n(O?1mX|mQx77CcX{qa~0+!-H-?XJ~eSyA{zYMe}PJToq637YZ z@^?wMs@vDucQBYN8K`NP7j*nhuEf@MC@rtL(+c{v4W6+-M5@?_s#rQB^@o=}`axdy zRf0NMZT71M;W9`Y$g03s0k~Iq(SpZK zy~)8~Vc7eL!NORi3%|w#pO>0c0+?2K(SwoIT_PVthS4kzV}!vh z7h{CcEEI!=!R#jn4Wn5MMmd97Hbyz4SpN#HQ!{A*U8!qSfeO?$>Ogzy8a1Fkb&Uqlm)cPkC`0Y24z#9rR0C>LJO0}j zKt+)+E%& zxz;3<$3500w8uJALgJIqQ$q5S-?M#UGGnuSvNIL4eIha^vVGDs&8J!@Xg0Jb-GvwgBM^RwkC%QLL! zm#UekS~Tk0xKfIYo$ShceOt@2lKvAqi~o9he*JrR%>8Ju$XQ>lW036=mKl;guK92H zf_U%x{AAAAQo8lF`oH!)54$AyJqr6E*FEij!}b2BhuXT-dYjTZ)mi`}r6NnT`k68N zHP*MUIIHj1e^o;E1Fm}!_5<#FaCS_tdvbP6?t66h6s~(Oa~cPj19o}kIZaw=ph!)kxz2`!T$nhK|8R0}~s2d67yQ+t$9R7=fExT1}9PTT4xl zn_F3}n~U2>O*%WfsB$YiyQ-3vn_E%MkBi$t&BV?=q4>$(zOMMm?#xa4a%E9Sns|26 zUz&Jv5ki`Heo;c2czKaRns{!}T$*@k5ks1IVNqS0cx918I&XH-RXU)y3OnXaMuQRi zv9?^B{;afIoAFFgxmInFUb$9l@i|++qH-ZyzrKZ+AaRiMPxzOK}(f9=fb zIk77b`Bz>6S1WqeqN(d_<<=$}6DG*b|Mv3FGuyb;d|_$)T90(#yqkw7O{>jP^Mt1< ze%AHKGH2WqU2e1F5!6z?RnsCz5l4{&fx$StwNoC;PG+n-v*YDf+~l)55{^Sd6AW8Q z){j^D)oqofKhrLi5;10cWj02!dfgg(V z7ly6nV|z9Pw&Mx4+JEeg#T*Wkn)%RW`rOJ=hNfj0Y6*pSndfA71pY>UO;NIaIr?y* zc!@Hh4)yr>6(~6*(*<}LY;kYj0S3(nZGOKJy{LFeeEWt5iUXyBT6U&U-^#zgZng*9 z5R2}1|HfVVxu_lk!0@{Y2%++5$04)&z4g7UtEzk0s{zCOPo118oEV^*L+jr{KWA7NTbK&Knv0fk$2x&;(D1+RX zw?e@jlm2}ae`z=?`u3fVb?%FhPg1QS-DJ6Mko90?^mgf~`6Kbm47KdDbD(dk{$fX= zN?Xn6dUw4i@g;qS=4T*zlS%}L;waWbQK!tnDn~ty!^i$e^+>gK1bkhHBf!E!r&Nvk z(^`U7I^rcq+vuXg3m_3)Z@y8*t^OThi;9co)mo-F+PpTMTPg&Rhs6A1b2X*|@@60^ z?=y#aQEPUbJ6s~>0b1}9haO%UWeMXlUb*|xD-|!8pQjdu;U!VYw&{B*-v0tTK*PUw zMZ>OQ#2UP;aht*7jCjM0GZMrP{shI>$SdY2;rK&!JH|SZ4VIi}q=;%p84LIMwfRZd z*m)L?Y>Y6DEGv>(*0h*eAwS7CaCcs`vvx&BebFw!ymI2(=nX`(7wnF8I>v%w)E0$0 z##R$O+!#s{skC|`U;GW-9wC$OFvX_6=E~RHV>phWGLE3W&*LwNG1NC}V}iwBW-sS84Nb34W|!)RWRdQa${_#S3cmV7V6g4{Q<+SxH5q1{dA>2|0Rwv>^X zAY`BAg(@ZA$3(jsnbku{h2r@jCf|>B0);-$%PLBD!s&p;9uSSWk?iz9T0POKCl-w; zH{HWRjalt|+^bDNkK_6;}{N6aaJ zg8LuOssGh;dOOm4f}EC|2b7$LL-T-eq*!Dg0Xb+#&cprPJOVgwIS&`-;o`gkY91@~ zET#($M_sC&Zihvxe8p##Ch+ z{c1caRWqDMAL?CF|rXm zG|Vgz24c7=P17TxM{v<@66;ttwPmPf>Z74Y!^w|{mAu(@O#XHxn1Q4c;Y&Ifp-s9i z+z~!!;aehl-T_ovJb83iPJ5Qx43o%bqMRvpp+lg(2zdgHZ$h;fYnIi)#k8AZxQ`pW zsqutiH8q|zoGQjshW()Ns9`q2N5}3oo;CuFjAsmJWISuQakvt8B>F1|<2t9dDnxy2 zWhOr-#$Qup#R!Ft=5G;Jw+vM&lw2*rxuPr)w{kQCt4lR+&l=fHio)6!gd?^97r!ti zT)KgK^Pd>FbSLyY8nsr2He=`r3ep34S-WDc6Lno>GK5o45BPjE7Ecd!EPuGTxG){k zopcv>R9dC03|e>0#i`V`TxO0dVbMf)rB4Hg7>+zUHGwvI0-^=9xj0-WGTgwG;W~P_ zfvDj+k>TPb^>71X`-`mK*{y4b%fZ$W4PDnmC?!UuTTn_8=K_v3QLZOs8|8tRxLtJ7LL|Br6~>P+r-~(T#S)~p4z-?* ztAOsXFn&tJ!+qqT#Kyh(^2P`rE`XaVLRZWcxr?w5X4W&*? zj$_E*Dfk*!_!p<;;8?xLSVj7!X*sfB0~6&VRS5!RAE|O5e3~P7E1B(#W6vZS&*EDd39E06?H6svbb2R``tDk+%iO1){Ym1XNrY(g!?Sa3s3JDi?%B@ zUF^7Yt9DEy3{nbFc{ygHBGtIk<*GW!3O$j%U~*TeQd*^eO?~Z@eKivC%uN{2bd0S; zaa~wA+emz;gC9}y9#OK2(5cZPr`_mx#Y&}S6ozLeXRx$(LJ#lNW%43r@^qQ$y3Bo| zOvOk$rL=Ir&MmF(1Du=Aa3-wluEYTf7C8X^9g@S3k1W^3aO!)jt=8oY_o*g|D0E7h$7hOL!^hBZN5 zHQo4?X6l=vH}f#AXo}9Aw^UP%%qCJnbRZ1ADQ1uxi;p@ox|L?+3>(-)=mtZJ+w|oB2iSb zXz_Z{qL(Y2ZYy!f@Gupo8-V7&Z`-}PZMS6Gu5LR;x9whOTU1hQw-9Z|;*2vgtun%r zR4ou^Ak1lLS*FvOUAc2779#>0$pTb^?sF_{hMar0Xk`SF`g zI}5o-(I5kcoAFysRF%gVv$!n$xMlI+2UjN3KuC{+Kg&PRIk94dm_?MidpuCYDTE3(0=w8gEcUo5L1E3_;G+w zUK#6CrpfI%GP}y!pyNX|d%D!D_eIt-rDlB~vYst9>qC*%s?@BHMAmbqX8m1cJuk8< zT%DE8L|$vt;`X&yMlJPA zSC;x^&bj@L*jhyexv^Zt%~`wh^F#ev$5`C>Ldc4#LWW;lAC%E@*O%jx-S`^6$=V3ZhOQh233QL_PRuChS{QpJXmw-uCT?yaqRaFClY8EvS zwKW9>Y#2>M)R3gXq%=Kd`!YJ?Z>p0j;H6?<#-zud$^5f4&5kRJh_Y3)L(?>?Y%bsy zLD70ubhjePz9|Uq;zG?i=e}3(z3%G9M3VXbf4;A~>)mtjJ@?#u&wB59)fUtNAqn75 zXR)Vnb1Jto(YrZ?_CpI)P104v3RF$dRl`9`8X)DXH^c$S^S8&?# zU^>E}8i02o@B#>%g4mIu78v2is^XK>>9@Y!8{zKWNbJ~AYXB=b6LI~cTl-I~YwvVx zf9I>6jkUjbYb$+2{B9fZfn)n>?jvUuXB=!OJWb9&!6xLXn895<5Bs}5lJ79b(_t>yUa2}D zgY~n_t>_b`xC}@&82!?oO_$a*qr={0Rdu}rjnYtM)qrQ;9i3Egz`tIDXFe3Pqq+H} zp^6WIduoIfTJ~D50QvbEx%a>$`V_~J=5g;-=jTfbN(*QvNWJ+FP+UkwaOiCxei2T= z05=`o;KiO&OrE3(+nTafXYA#UCL26plM8lllMIV;pA2x ze;Su)8DaUZ#saKQYe3&pFtoLl<3XZxl?6Bq@p3Wrq;xrmOjaGu; zRK~Y-xM7ixkv0!-%RI;dkTwtT3_SL2HV^a3LlsaGKg==CK+ks-H-gG&=DIViDlx)? z5CnC{6Ge7YY_Js_jCeA!BiuZ~SD_j$-7YiHqnxEY_-mT8)NL4JH0^ZXp@w;kZ-Ma8 zNCe`?cqkAu(g2|V6a1e(NF)NFGxn^TURC@@SYd#`0ukESer_dH`F<|q2RSxRs0=)X zzYgJ;`DVTu&JG5{>FL1^(_jhYw$-%X-_-Lfsou|{M}bkxX7euy+|b`ab1c+4Zij}t#%PW%i;8xX`k=H(xAR=tIT94Ak6!16v%M?cX- zKLJ69GWzNPr{nQFm@#MsBuAg*Joz5i6UFLu{2d(rNm{m^P8akI30oQ`LBv9%E!5~5 zXCi$OFU}Tbpz*)QqfbCn7yBLWz`jKoj0oMgdZh0uj)uG=s=^)+_~fwuX%}{p1AB(n z4o{T8G5*K^$g>>8HoRK6p6w`sUh1VaI`XR#82Vj~aa?Sa=v_WaaJxmUMwFD-KoOgO zexb9r(Sq)Gcyx?leZqr41jDe2MFbiPpx(vrKyV&u?JMW~XYRs!7e5J&hu9Uu>lz=D z7&DqPbznA}_pU+;!4R0wa&w%pE2Aekhj{?1NKVI|fJZ{y2N2XUVNfGQeTuRnDI+=U zXX8wqSt!jMC>BQ~j&os%G1ipJQQW6)8*%4cZr(Ox44=czzkQg`=Cj~)A3wwyQ{x5W zqXe5En7V;Y6in6#_ODDZ_MBhQ{t=3yBE5^x1EUpnxEh6Rlz^3H$@>Mg(x>ANCGQX= zY1+TEdB32QhR)kOh_=IXQHT5(FK{>Lc@en62y{RXDifU|@KA9FFoh8)LQr(7KrwvD z1DQsUmpzaN36l0erW3^SKprAU#sir_kgNytFhO>DAde8Fkx1;QNt`Lj8*Rb^wm^PH z&7K*(!X+Sh9;`s(ggNk1*Ze_{UqGAnxt`QY$@pG-HScjkMM8%`{`KYK^kDu#B#RmM`ASyNr zuoxRf#U}89;+sUpW)R2tW}NBkWZiy49twt-dS%Y~W%X~t(hrt(sB*&f$QXqm?;tS1#vt1?mri%WDKK0(4J` z*m_~Ecj!x-&j4@WoT1erE&@j2F}fyb2EReCERtr`Im?L@X6p29A+YCD0u0~N!YZAo zQkm4)=GaDIZWLL-jNKCoyWtrrtmLzz3J=HN={cd*l5Ts)7EntG75yV9&+vBuyjhr= zMQoEWH;FF&kl&vdv>O_<5>a-fD?#0I1&xp{Ap|gs!^F9u24eHLRSwQPY}FBh>9F@1 zWT2w}HUhnnfo^YvI5G_%q}4N9Dw`~Gn}D)aRs41?hY`auP_RY-6|A{klrB}NG)nJy z^97MAt%H*JqOkYkX?VbTbBCZ?Dd_wrN!+FBE5Fegdr6ePB$)ZKxT|s4hzqS=M}rIE zX@Q^fq2*h^)kfepeZY)M4{kOdAxGFs=cYfCP{7LyRAi;Y!9CGmMJDLGrLr-bFfjb3 z5Z>kD9=mfnawv~ucoM(>Vl2iFrF?7?-p0B8qvJsrG-v5Z0)vT6Y=}7|8?^*ajcw{( zyi>2o39dqcz+`TRAr85Cxr7%oz<8^PS1mIms0l){HXXa;kBJKHJnr1vB#f--Gy@+Ue|c=hf2+WPk7HUV0A8%RfIWczIb z1_Lh%&wJ3JN=~(%=Tjal_6|ggw4<;OX>*U5j#3&+@$wYtC`^XQAk@4zx>txwbo09) z>OI1j62aKJq;JSqZGj`xqDuzv9gqp$BbmCeKoT(h$rjZ;JKr*NlkXe^VBBXL?D10*tmc|rHgh0GKqnM{e)zFTKjwN-|orOzu zjJ_w>bP|O`kgi$ZM32DmF`hPh^h~Sz(zrQ{!oBoDas^* zX8%M{ZG_j>Ru1i86VV~jA^H()L;_YgQH1n-Og&>qgn0y*44A&+w1}M+=4p`#rjn-x ztYe}BEF^HvXTUXq&>+uc5T0?M0!SVJn|eUl32mQn%wQwHC&GlEPes|M1cUwm4EFzX z!9FJm{;e&+r(x)BC~rqZSNjmdW_8k{5bPD1*r|VR@CGrZR`Aa3B~9jsec} z-CEgHRlG&tOP}$GM-IYT@=x?JO%5%3wSW1l|~6XShyX$&e1Z$lcQzD7#R`CF|uN;jL76zSusvVRC1iG z7%wA6a=fgVAR~d~1X(drMuN$SvSN~qgp!kF#oaPel)PJ3+#@3$lK04pdu60!@?Kn+ z8i5h=A28m+>zU+z5|1|Sr@s$KT*laBSw2~^o4&oqEOC^csQ zaJn?%=OF;jaDfj?6Mh~6;LJP_bnh%lUi@f*XXKqJOHPzdH0)%&F+T7l2Rv3Np!YdW zC+6W*WbbFo>0NI_KAR%VYa^j9KcLfzDKh$9mYgH=kFzC(&G*7K&5^0n`-~KJrQkVj z!n~mXF$g??hkg|rs&I@^9*{;{*_6R1odXD%E$ND5w#?K5UJTreG!7W*KN;S(*i4E? zl|&$Ri_Vp-kH%J2{C%?V<7k=#(H!W3A)b|e8<9jaB`t)4pC94~AUlioShyoae(rdI z=bsRnWIVOCG80b`G5Cy!owVd;|NJA!*+9J!(RoNy-)3mIMITDmN<5SV+!KH{!{`~C zFU|Qfwm@1pFOWRGP-?*C@kQ8ZA|50KU*b+)SIJneT1pq+%3LI?{Pt0|#hFw6LU~j8=h(J%n$i0fjtHDKlv|sU?xmwamyVd^ z1j~uq%_y57qKl=@(QP@@jm%ggnQ;wUDlxNZnZ%r@`4TVK7f4J~n*^I?7>{s@*(^*M zY^tb}5bD)wW{A~Evrbyk<&sXWU1p%R%@y?$(4TVaNT@`r2HNoZxrcx zqklv~oPzOoy)^43xSAYR3_b*Wz&vf;v>N=i26=OXq|=bQL9~tFb#5ZVHMj#hb-`X0 zy62DGE6sZ~mte7Ml3o|^_+SmhHLQWw;2x1brmVv(z!i8PH`1fheAGA6bl*r1k>K=fnGcyRdr*{3w}%t! zBzoJA(x5X0T307Vf0#(P){2dj<~W;#_0nAbg{gVON6k#oBS!GD{0kDkoWgY3!=mgF znM1x{;=*3EDu@o<~y!#+oS2 zi7m;p>{0cs=aY^YacK=+=V0O)j$hMM%4?cEgo3HTj4BJv1~kc{_W|w$`W<#ND8(`S z1e4KBnWCz6siDVAl`b<%h@iqv-pCHeJ;wui5+&u$PbNSBR(|rq4QUqlx*|zyC_{D6}T(@ zqAb*)k8bdax+}he8uX68M4z!YMd@0j{7u2*FKa+rn($-Ec*bs+1^c!QJb>64fq?Qe z&~~SW@mNiMxRSc^8Y!F_Vb}DwfkaULMT!rBxUeP;3;2FWxTBrYwaIa)>V=&}VGSEh zW@Cl059f!CxEL0>!7W|{#g&-EjTsGuWwT-5lzG(oD%!&z#kNaxyVPpO+$m#?(uAKT z8E=+tRhh5I_^T~zFOTm+H)Azn{YHz*NUqHeYeW##>l4B?e}naH&NbRUc};2uWw*?G zC}}`LXCOPJZj3o_Kh&?s^>8YvJErGdX?q@*myXviO#jkH==S6m>g^yyEU+_lU6M9m zkeI?s`GJEWzJ-9W4|I>@5YH_DyGai^it?piBLwBIfkU9FjqR4@yCt(}9$~~@mnQtY zA>(@_m9Vk{tl)y@suYT~S7j=bTmB8;efAnllmg(2}aIU$eRHIRmuSAB|Kd zpXLs>ES-%WgelS)tGUbXh*7U?*lW9yg6da6LRL~oyUwE_CQ3&~z@tz&> zA!&BZw@=`kD=v|AD%tHqo5U9&dP|kQ*pf!j!kS17@mmlH5F(|A55*3{>5ONa0ZPnC z1h!B(xJ@Pkz)X~l8`~$!_X$R1C4#YQ%CBMZJrEa~@W(Kl!G>X~Hk5R9fL#+o*etRH zFVRrAHbHZ~z6eYNVDjt98@rgIBBvi z#sW1*sAdyI96v)xZ!yPZ;$E>SzP!bA)0o zVBSossIxn!c@#a%$6A43MeySSb4%`^^IzEAX783_Dhh2^bGGGXW&L~gvH|;?)?0ZgW)0_ZvL{%TH&GXD5qN!sNA@Mjw-B(E z0C)pw9tUbx0Pd$SVZaywpuUX&+|p!a;>Tr3VCZ*3IPjuJa>u3;Pem`nw&dmPN=s4Ak5Er1pONaNi41;&Y}c`b+P<_=|kNPb&>q(l0~ z<#|GXwLkMKPi7{qJANd2KIh9O6FYAccw1VoJA=uwJKITf7uZR27lY-=mhLf9ge&DF z&h68QbD$hPmcYf)(-QC6x08P9LbvZ;_zNfj#;;4NKaowbPh2;p{1ebD0L#$5Y$rUB zjD9Lv=|Q8eCV#hOekR@L&p}{eZm}~G;aHBIfgl)f%40bh&1unmk(}Ei4`c_%c)@!- z7eL?#aNBDE593V`!Qr;6PB7xOSmT0w8% z#&mC>itd5H84!RQ+yD-4z)N%>ceU*hz6B0p>2Wlks08udisz-3DSsZk)5SuIVcHy} zqV+oc=Vel&SJ<83Hk?k!q*Z=Q@`8QR_A-qXq)h-~0SQLBJ;DmWs;0LF<1jcaETd zlkE4x>BuQMPJwa>c;%>9?vfPzKD30y2w%pFt`hhj_a(rG_(W8GplwQQyo&Jh@rox; zkgAT=C=-4rsIs)sua}SJsJ_F$Y@ttEE5I93MP1uE-zcA#BnspuNf%}t6g5gr6!(gx ziJxl)=rMeFUd+`GyHTSB>OhnAWD^xrJ=rA1cu#h>Vu5a~UZQV#kHWXkGal2LC6>C@ zc3vi{lJ1zdMDMPejg;VI@mqQM4}du~2zq5TIaz`5PKMQOLM5@2f|PHek(=V{y^U)e zcJJ+6_nv~irzm2tM&Zj%Y7*}4LbGs^B+%P@1^F=Grq{iShBcsD?h?Kmyw^NPb_@GT zUq_wO6sCZnoKsrGE^bAb`JjsKqV(N2Pz43RjmywYA*LDO9AmHYj1_&AL&$$CHrqq} zNrUO$59zt=C3rVnhiNMI1~^A=aLU)iR1Tt}ldiKc$qv&vJcJ&g&YkHMH^BO^((kh! z6hdbv0T>`O{vrQ#4|}j2@ndc7EG4^vV3DhoI(^uv3n7dNGVS zTiHWc5%3*UbdExF@A&8Pt_R_J0aQ2P-0gX%- z1<;g2fw>9^NGW})k0tIYIc@A$N}=u<@r3TVokXov)&Qz}5MTXz6TMMBnC`0Y0bpU489OXr+IK z77fFZ*hyY~lC$JIrI$A6A)p$MMCtyW*kl1%`s6~D&L$Tr2;h>7k>F7hMd}7(8!Oh+ z?70TBPAzi@sMgxbCi{)b5-VdaRiuD3TS2Kr7`;(RX>cVL!SV78a0P?EuUMvHb;^Vv zLK0u5$nT9UROUhzTc%9-S!y@Q7o*ETMxg}$`Z6^Y-SuS(-F4jn#(Nz7vXWT6GV4{m zPT=zjWv);)u?BT>0~oc~N`+VVH?M>N z!75oQi|U>Fw(#YuU2KF zW%+7_X?VI0-OgGVHvBsbGYAB~xS%xh2&Gru?B4~9R%{Keebw z)PlhVDEt=1VXvrT5p_iH)SBV!ijmY0c4jQ?jBTF*WZFfLRO4+}4Z&isj7?gUVE z7d!g`{EScsJd}TrAVCkLA3;JM$b|$cvLVjc$g)WBjUDESC&km~dEi4MdLBo`I5|nq z(GRoaT&&tDUxDClt~ex*!DmWsQO~G3s4lmtCN)<-991d(@QGq(QJuoX$a;kd%PYWN zVGM*RIDoALe}%C}6$53k415~K)++|ac>{SiW2KORP#4dcblDhr6XWZscAPY1u){9l z72Ea&3T1`k-mS#Q*(*g6l%nt?5`#QjOA-X7`xzu3%G?Mb{T(Mz6!EJFERa1?fGK=2 zp%`K0U$xmpT#xk;RZCQi2vilnn#(!ThOMKU6}MZCkdsM1kTIkqe?3g#yhX}fr07Lv z-BVDHJVRS$bK0EGEA_inX+Rvm3f~GT!W8(_6d10rcEQymrv)C<=2oSX4(8(<+?Dzb z6&Sm+8QfF6X3UuVvz;-+6Tj-f?2ncBf^^0{Ye1(XlOHvD9bHH~K%>$7joBY!0?$@7 z+mmVfdcISn(8f4)iawdsSY#i66e4==4 z0{YvjelbcZr;wz#>e81qlT6{P3V>eWtgwJ`YJ13p9tbQH0h?%|OPq<0qa~!8W-2&2v5HEa@)@!i@Gpg4rON_zg&9cU zN%nI1(1)@5`%etYe{dg->69^CS|5_;QM+P)Vm75L+J9Vs*ybZ4v)`= zTOIIxe}w(gJ($vh9OQ0sezvsVH}Z~`^#-T!1yE}@uvwlt&X$-_JB>R`gWid@L|`Da zZwiyskU{ynIDzbTOt%}aDjOpTnI@y16gsch$37(*jmD?D4fNqqDQ}%F0(Eozn={u# zc#70%jVAohr+N}ljeftjLw_cjwSF?^IYL_Tl8UJ0ORC~!kiz84sO;uTp-oCjby_uo z2;&FlBx>J(fiwWqDhO7KVmnY#T^JEefJ(oXvzeT=;4eMdJPYRGgIe=p!Jg@SSCHPM zr99SR8m>Gperg$T;F8(Vz=+ttkPBaw$BQNaG@0ZD}N@G9# zcYQQQ#?_#MJR{Nq10d@Ggi?blc8Ca1?kGH?VIv-=W3n4t5rJEkj^y(6;jpmj;W!Yk zY4T2OU~;D>5=ll9kkGWbQ$a%{KmY+w2Ry^nD}fE84O}Lz{2&$%(n3|__bgA|z(PBe zYTeEUbVUEg(k%f{WDEUFxX8ufW5Z5)HjIe+;whXtyYFB8=rYcU8=+mUp|~?gW}Kn&``{AW-Dbuqw4;%4 zdvZRQXoquEn@*!#=1zxA?m(UoI?Q{BWi86XZdw`ulvp*kAFMeYl3Q3 zcaL4yN-lk^s;Si=Kkxw?*zZ@Z+E1{nT*zL6v@|olra6c+9^eKWNX+JMzqb$}2MYm@ z76KkG1UzK}iR-%)jlHTl$Ph_Q#+Ht(Ej8{!gs5PUKBFfDIak*EsiS25wHJC%lLMb@s)mgTh)$DTkXo*E-i1n zyz;h&?CXAcQ>v1Ct3cC})&6$9tIzi|$|Emqjh;bIQdN*< zEYvzWON*Rx3eXrI;c3{2hZSvigx43F^dX`Pg`?{h z=J;jEoz9z9dm(#YRu>a`g#k2BVVcS-iC2T8Q$@$wI=0*+IrrOL8et1RQ}^o=blVKAQWmL>Ml)3lhP5Oj*gt@j-ESdKQyEAkg%t= zJGqTJ$(UyH&LU3gFYPj=Q5-k;#c`uo9ED#TH@V{Yx)#URz2dl;#4#mYaqPxP9G_(D z4Eqa+;}hVC6cR`6nk>un5MScN`groxo3XQq)5D^$4r^@@+CcDM*uSs}%;K6q=3n#2 zJ)!6mQWhD37iED%c}!Wy@1ej~=g&oyYCl6DQQ2f=@cSkPR5shdFaR-!`+|j)Lk+7E zzr!tZ!6N$JjYz1WxG!VbUOB!5zhF89Yr2Oq`Y!8iB$pb1AG!)O&9ivLlY=Vy-e~AP zfWBrt(xt9A#8|?pY(#;kulz^^fQTk)-9&0L1LI~BMqTl5pk;tM8j;*Gd`&(G(t`h* z0l$A8i;2MX7Ci@aLyd*L!5uNvc)v$1FdhiBMPK(ffSOYi_)MtlF;t)Sy52us1v~2iE9G|Auez3Rx$=kZpB^Y>yVQJzgPuisF))on0a89Av?ZNXXWK z&5^99VpkdLHvx7!a4wJDQrs_f=Sau^;RB?eBTB1PM``b*qv5vQ?UrbrgSr{yPYxS# zze?@}td}MC!QUSz_xlg*&S0qAGFI?;q1vkKZ+T$1x8;G|zH@qDHv_->b_Q>M>PVJ8 zup2gFx=J3v>E5T32XWaBe8J^|CI`RNN=Lq#Jmg!`>fAY7(?hM-w9TOCENl9wt>)w} zw-}$F-X!}+gRKv++d^!72lj)G?6R-0uXbU5!z^(gE53q#r9U&S zWTUQR6R%<`zsV+ki*2}?tsB5Tzn1m8o}Ke zvkPxx@@BT|X7-aGvE4sn4L@c-`3bxGr{~a^zrtIbFNO=9FTT9Qf6E!tk&g?&BWNe& z(GeH)3NAqeZlmrk&Z#380AQ~d&=Uarynqq_?6;3CW^jGh*-$(ZGm7*{t=jbpV8x|4pQBQGQ?(ZOME1`os1}4&t#Zh`O);eHMlP`3RJipgaJYF&M55rBE z5)L3)SNv0ZT%}Jq7Xw}}cHltdL(qL>JLao_t_M{7Ye9(IB?T1NW>EM}`GpXiSFrQf zDR5s%I%U|11@3NP)7rcr7C9J_D;IRl&JO@?EMI7GM0wX8TSb%p&Cx%(Z#=K ze$d_hR?UYw%~ihn5zPsReW^Rtk=A{OJAH+JD9t)cT^2m3Jcw)}XmJ{BULxl=e5HMP z+Hd$0y&yB_|H9ZkBMkqiT%^QKik`tM+ZOCUhg+O|H5Rrn+9kB3FutFMH0NhPz79C7 zQ2gTGx|3tAk8VS)iEVXf*9P^qywKTb$e%wz33VCeCrxj{PDip2C}*i#iK)$|18G7B z^np|t#RzO)b)z?>ZQUV#r0=;@&v>=B*DJOqV36j1G8p`Soe$SeVD>*_v#)Gvvz=y! zW5115{#L+tuC%3%IGz{v@HtyW@Zc02kBd^(!Z+Q-{x@!W=>MN>zXTktqBA*vYI1AM zSy0Ix_(Doktoyj5|A!B~Ti}h&gI;07S`kQVk{dj~DIgjc`^yJ8+i~RzN(O>Z7TXy^I z^OA42UM${m!Ft09i}(b5!+u;UhZSI;xvuPu=(^Y?yWy@gFE5 zEk|8i0R8#%oJZ_OeSm+U6zhh7Xg7{HZIt!+e=xLox58#)sKf6CA(C*(jygU;Mz4i- zZ5KL^xbS%=so$@OhlUYOI*K@-jv_kf+v8&@Jci89Y^A%y2y$Wac#G}a+j2V((hA{N z$L;;_w6*u}n7S_cmS>i>HwZ%(J5T;8X|JdjM@PH}?*K&NvkmMJ$#upr88AEK9i$Tu zJV<5j%N$pgGzZ+L`r?cAa2=rG37dmX=%JHutMC<0ZG<7HHhEGHuM2uuXa^rB6|};o z%-Ty?1RuU}voNlc?gqkp(tZqdDza;Jvy;%wtqaoSo--fnjxm`7ryhHu$0>z9@yO{0 zaD9eh!F#y$e>CQZs4Jf4u@bn;?;y@oD*3Mc`JM{@g6r0@uK2j!5x8uQ{b+z^@-(K* zag4GoT%q$QVBFh6N?_rDtp8^3y94y7s>Nr%oo!z>$$VsiP=tVA6og1DArMvs3w8u6 zPsOst7zJzO>3jN~0SjP7!59T(6G&qT8$|&-cCf(i_bnTIwyo|iY)$X;yQkeUGvAhE z6ZyU8AIZ))bLY;z=bU@)Ip>~|k5rfXL+t^r@2%#8?Lqh?ff+g(Ni4Myenq{S9INhP zJ<;r;bn$K)1@j+j*@yFLpypdpAyjXu;m35Et>~{v^7U3!KaZsW^`^3q(EvaLJ6geUwrTyi z#75d1+eovMQPsDWQUrSuaCJ0HQL>A8AY0_s?#*%uek)B1nr)y)y^OLVp5C&T4G7ES zGm!4LFaT%ZoJXchSb%W^5|s5J%I0L4!^se4t|w4#LuR*)z6C$EvnsPN)izkGS9FN4 z!CNpG2y)A3;EwwGc2e<{?qD0m&LDg(tT3C2J|1VHe{tfF9&b2T?;o@8VCHWgH#=K% zv)X>Q*<80(-Pq3KoM{0qcmMew9N_N2VeWM3YkRPC7re3~(61_ZtCs4HR=h97?cv)z zwVepD-Sk%TlHCeoJLt!`_Kvrjz_%T#b~|0tTRY{Jx)Wb9@O_5%HvZl&0A(|Lh|si? zhrA8D<0wJW7Fj4t-VxZ_ zSBMO#p^gzu5kxzYPZPt(;fQj1J{V`5-cYJOSj5oJU z&fGS|=k`6B8`0EGo@Ny<;$3)mmyCO@ngUFIhqGxCHzyLAMH?QBVpoDTcOs>bR@P6G zKh6NCkI8iluKWjxaGnfYxU7%EX?IHqquRfHt%`=bwX8dmnE1i{KQm4;M=t+iMh1dst(^%{n~kY!4NO z8<#$zzq~2;!b*|M(mo$fJH-QO1sGeKY+S-R6j!LuGrx@jvvKlOcnY&A3kSwB^yDt$iMa|AN^zwxI+&A2W%4ym8Oh;t-6IwiM!}i!_OElm21A_S*V2A^%BHDtYJU=Zesd zcd0|DLgBP$LDFC+rv$bSZf1>y-jZ>UjVj{iYZ>ecfn!}L;ska$ez#!FVJpGY6iwwr zFo5T8g+sKyem13QF5>jcv-^5g7IoTeTcZX02Sl@b}mYaqNQk?@K_ zYkPy$yaLP#O)p3v;j^skUlM=V#YD)fjkp&I7w-CKPg@@n;^*+u_&e6xd_tJ(K1O{U zQI97O^>`Yh{$dcN$Zt2xMSFPY`?c|UKVzzhiw>tK@ZZm-uKkK;U4ko8H-LR(Y2@Om41+wh#Va)A-MH;>5b+=h9IHe~(S*Ze8I$=e zeGV`F#FLAk6NBqRX>k2R0;vSM8BMu`fk!7p8*^s{yj2!htSMU~n<_z=5r*;r*jJ*a z+*d%F*n+0K&&cJ*+`D&0j^M6Cc5UH(?vE3Ye1IXzyKKIEwYBTtlbf+CDk*RDp}cYP z&dAuE6K0kID6x zLXp>WY7BV?H%Sah3!`~nbHV_Db*{gTilhh)1-pwKf#wEL!vb*|hlSt9`$9X8KYy-l zC~b(xAJclr_U(bySmxD=%m_~qVK6J{4403E6{MaG9n9B~#Y*3C=fF;JfGZ(XG<*$z zI)6Y7vYqCv2iYnpgEexl>RX>DtKobY=T5C#xkJzz=g7Ok`5R}J zRA=U{*LGBej z2ALCOO>Mavl+$PUF~Iu?+Nof9py;;{=A;_(%htKyHaYhjBp~SVl7VckzC<$$d&@M$KvumJ{<2J8*5ln#>2~ z7)NM+AP_f^Q#|o4k^^X>dEr=w_KQOM+OC2$b>0K z?PkIxyQp3xfUzcUHl??R0_*J}lYkO?@EmSC3V{Ee+E&`gJKOc12Vb3D%`Q&ne2{ol zoD`ustTd)>`Y|A6)+NLt^BTl?Ey=aP-C}Kv> ztf5Qw42|SN^kX3aUO=J0A0Svpl%b$bx7X7;3iGjr0v zku_ztV<@`Yv3qaEgA<<;3&ACTYy$teslY#Ss~&4pqdmjWL3c?y=#J3=6E424=s+$5 z*oS})x>M<(JDm=?Z91Sa*>upY=^!~Vri1Pn9UL_oI!Ni&rGxGm9dwHf*&U$&hBaoU z2Xg}nf-WtBm}NMGkop%AwltqPEhfAFYA*SY^+ll|F1`&Dy7x1t{&d4sZ4tcXw$6 zG`Z3*>4@DWAMFa(#G9u$VrRK^Hqki64V!d2=z4?s&S3I8DPGuFmYE0TRTQADZfF27 zPwj$@KELdOz0wC4Y;lMdXMPZU92e}a0A11ZbKu!31IJbijwi3To0xuF0f&1^I8{U2 zawU2BG%9dXN^9mIUSy~Sl7mRzYtUwqrre1*$5s}&23XNYfh;`Uk2|a;)={^!j#4tr z)Q%CYpIApSmE<~#4^Ro;YJmU2LTEtYc`s&sYs{D8Pm^#`)% zBUT3HnC$$)(W)0tYs^^OLpE00gGD<~3#U&OBY43MiyY7=!E?j3Scdr!1+D?s z!`Q|woMBlweJKm4RThr1CHEk%UO8KaiVLR|jQ}pH6l-Y2S z0a1eH&_6xJ9-b9XeWCW;CD= zwt%3y4G*Y(GLx{TcPhlt7(3Oru@)1oo#Q97Q?-sYQ-8fl5Gsm4rnU%Q>~mXSz}-#YCLOsykVYgG(Bc5OerrJP--Cg) z76#joOPm?(Ha+ca$dCF-7_o$oenjVxFjz35j|c}J664hfG1U<=Wa0G??#l4fFxU}c zutO;fcAGF*=Tsksz&zlM$AD7YYy&T|)#z znDQ!&b?9f95Hdy}Kt+(zuaVJ@$QThL;fMfz1d%Z+$QTv>{%-y;N{1PU0DLe?*Q0n8 z0Y42gwg@stQ;^Xw$mmC8^s|W7lv|vWq2g}W?e1Me+EBC59G5aZkuSYchn&C zr(k^lR>Sbwm^;VwwEO?Qi~;*dM!>H9?3_@6W@t-GG-vttWI@JgqwT4$l$FWln|;ak zdh%3hd$aqE*6*@r0Z3xvGerpr)qMUnz>wp?O-Ec=Meu@JO?j&A{_qINVwr8!NZ ze0Nb}?oB@D_F?6|po8kPV+g|-#}->$ruSE+EUq?Z9kfl9dT&90ap|Pb=|sb)bSHi0 z5VnVK0wB4ln&?@H?;!LdN6Fp0H=>-ha}ui#!PlU@dnUO6AFra6Mfr2Zo2B$*uz~F8 zoCPH5%gyWsJygh>yD@iOr6fjb<*$=;``!vqa}8I1=E2AoTwopP6T#SeW)@q!%X-@c z^vpR^VF_45hFryy)_CI$lbTLe9X@4h)M4_4f5tdEJHfJb(y%;rGJ{O&9jS@gYmzB& zCN`L-PX)wW(%fc9Qg5}zH zxRs95dS~lc4v?5t$(=)6p!r^bd0}6$3GL2TABfmUND|f>bC{!ZtMAB%-eR{_3h?9AdQ+dKt zf~5D6C^Gmkg8#4dN8-Z&irjjBMyv#Nf#+yRy-&wRISGekliwEG_k-t~zacRB{YaG=A{KApEp5c{bTe8oSII`8wfCPhNz})}YSBSH-(N&C1+PQhM6)J030mwQ&Q*CoCuhfJ6VsF~9D z6osi3C}e&KrJJb>&n9{=#-n#R-%RXHlg~z1!|Oc)W$!3QQ22uTqh^yXi;}{%d@rK( z4BfbE&uU7p#TKzjl{X16un?2Q46AD--VRH+YOMt)2t|%|ypNhh= zD=J%{I6+X{1ScO*j$4xpmyiwrJMf@p!m=x>>#G%YFS{aJsL!gO)9R10Nf}4fWvi#q~9~Y1U6uf9{}HVaXrGpQ;a*ZLY6Y!wZkgc=_680JddUOs}85 z?1~xi-%R)~te>{*id_B7WmgQaFJ;l)>N^OqdJB1Z6gV}`!8ZimoG1`Kc;@hMa#?8uNjwLQ(iw?J-enHpIuvCUt3>$(Y5gFXm~cW z{wV9&(e<J3KYAjUbZ>Bo{C*$i-_$So??yqHvUCo*c`qH@HSn^ zve01-63Fx-j%r2`Dqw6@M1|!xxaaBWooB1RS?EgrfPtwO;Vrn6Rd=TA!R1y*k$Sed zh=Z#yuR{u{Scug&DptYZ>MN)`M|N3TcrA{2Gg1V8Ty;~Si4tegf|HnD;JHjEQk||w z&NQno8*`^ZHl6zEM65Ll`A&W6SkJ>*Zmvg1-*5Wx5V1vqVn5lvHz=x6&PxUXn= zGp`0%Y}o?RKF#-v>6(IP)K^03k(u=~7sCHl@FT1b)sGyI)J(N*kff#oNo9qkvOqRD z^$f{o8j#Hl_3T$5(rQ!@vWnpOmg+FXD!wgFBQh?sn2oYN3h z;p-JjVAG{aEQ+zz5`Xas4^Xz`;k`KPuo5bxV2=fd6qL++Jit$Ng2h(UE$D>Gu>2nS zFRX%ECgrdY>nni!cu;lzAnTfp?vFh0cP>l+y`5;i!mVra2a{<1Gv%0QUHvhM*8hmb zgC4PJ2>(2T^-Tv4M;yGQ`xM|{Z;D4F-x z=#+lcIeC^!+}a5ZbZ<@IdLnRqTE1+ZjvgypD*~Dbf1oV2xxS|_^~e;dLo-_g+h2-( zP{}P(s_Wr3`FPT4?nZHUNLODe0Gwj>j!@+?3NSSTF3NyK?xLy|W9APO@#~0G9loYg zF}6qC74oRPLZ-PZq}i;H&(aF{ti3{7uoOX$Rr%+#O0O*EdEfE9T^aAg!4$5OD!oqf zLA=B^uN8A6UWr!xYs`&W4moYHBIR1N%94zf(-|_`#oACJLuN2mhD=_9?HDQ7fdapk zA+ufkI7^1ib|I*ClOc1?kxp*!Ydx+-574^9?)dG*8s@!oJx;9M5IX>)gVP+pPxe?3 z_t<&wTSjFS)QeZr(rwo(qgiwxQFI)$pX+I}3!8TY`Py~DDj{)qJ70zk@EO*y|HHRq zk9RecPJKLqx@xyiB2hMc95h_lGRQ8yoKn~x*g}&6nth_36wr7LN~PPMCeT8#E-i6i zc)aJuJ~L)#f|1!wi0rt`F6EhJx{Xp&TNGHuoa~_yn}|R4vNxlv*Kk zq}ZB?yZp}nx{D3cL*W2DtgtQ>PWtu;&tCH zLPCeUOBNKXBOU}h;cB<9qU1g=V(#;zV@)J!$#J}ImAst=OjoW=??uY)zl1D+M|7KJ z!W1(3>;CA>&%nlAa5wJa7@}>f*|`%L0U$*yTz7)vrtJRU|FToIvdnly$!+mf>zria0P|J8=LlN~L5WP3V+cxm)gFSN)QSw5 z?7Nnr)S3{KY*pbP1f{8|`r>Olt`U^1J1i(!cR#R@-3f>Z{I^eFWqz9l&{gWy5o)+s{>U1>C75BBy0YYJt2MGFDqb<<-AkTg+48v?D zdH*8GhPw$f2x-%IOTZ`EK*##oiZDS*y!+- zEAceG=?9`SuY-M{&D{q!+xx)L?mqCi*$1wpec(EKANUh0aO zxC2zhcYqpe2hdSO1uHOucs*s)Zjo%-gst##haf?&T?;FoA8PZGo-r)np*)3|JL{(^u{2 zK{*dV_@zj;yaX0@qHxrSLNN41P6-b~-q9j<`}~aQwlmOu(sH%S6V=-FO>*enSPLqQ zL{@YJRtbO}a|p_3^AH&)qs_9RsvEtthOzn>gc8KWEA4V}PTFSn|@i-Dgci?!7klRlQF|+MZ zn0`vH%=FugZL5uv(Abe#?dVeULC@1xIcnc#xU1y(AZe-%;nXG*x3jl3>b*eZG>&4n z;-Fix_g2~+jHBDhIuF6p_CSk-AGW+O$j{R$f=bwj=4}|t(*+0J2)DRIG27D+gty6oT< zwaodju`B$Q5}`T+NkLIm)*0>B6E8so8wDg6zwA5`&OFb+IQH}r#c%_{v+8>L`I$>Q$5dM)RBG=6AEi+QVirkG- zsIy8-3(zrb6X(c7vYeDs4rIuaGDakKiBiT0Ytf^jAz?c!;9VgitegO0r>)HUix_q) z6OsJJMyn@j?A4;xbMHwuV;%HT12KPSmA=TnZCX7O(X3!ZGh}luB7L@5pkrd!qFCWb zh+;Pa7wUAmP?ybxX1H9a+i;;T5*PZS&4qf93pHEEs4uQWE_7SQyXC2{KN zpY5;Hb9q=!1P!)7Co9`z_303#w~HF?$^dw6gtErme0I_95Et$C)Lb6T>>7wJpmt7= zHk6tkEjMXki>^DiWrzMU+e81i7ghWx)kEi;T}B3R+$3saZW7^-%h0x zXDl)M_CsO4DZQEihm+Uw`94yvoMy{N9RpU2WtNI9J_RHehV@p(&c>vmqxf%&_2LN4 z5@!ql;1Y@pMGi%W@yo9%m%57JHiqRGujLK5Cjj3ur&R~F>fly=xDR8i&cG7hdwJHq zvPb*t#g3-MbqWozZ3b<)lsa{-T=g(KJ&LN?Vc3pXDvJEvVrd|8%|x)zZXP&^w&Wy= zW~x25qS0y#6*Qk#*?oGQhZpf{ki=J?9bt6c1LI41bg&9+&#M2^6%~|E2}84Xf8|f@16>u z>=yKutX$8|j?V>uQD1&lvFr6wzPg){yY0APfotZvWo@DQRhaiddRFTk+p8u;)uJ#r z0*6j?=B;%O>ebCceE?1JGgXW7`f_d&m=@)Q`f}BxJ}7R0PnJ17Wvrfx_2tk4`zzK5 z*jj&=>;nsMa_gWmy2@U@xNwnGXe;k|^?{nv4D4CGo>|8pv@HP5A~5uscq0E8yMKZo z`oVgEdS1Va^d~H&KUr2bqrBn^@ z7l-)@1+lAuT25Y1KDaA#mn*QA7dPeFBXq)5NCwB}s`^Uoqf)bmdUcDu`4{5<^_6w1 z4Y-w8vKMf9fu5_*c2aa#_}eIw4qNK08lc}Q@|;!|1%>LW{G7rnc&i+}owN1X;zk%Q zjI_QAzW-%*E9ZWuam#WV)HECvIGN8T$L#6#)obW))du_uPNYs6RLPj@v;09CMtwQ@ zeXEX&WI+K>@~rE&%p+;^MJF3wS8!Gi%lDdT?-R zv5)R~A-B|5)Ezv%4!HC>XwmCXSy@$i#p*OBCG>g_bQvU=71AQjcryY5)_p*PNonk1 znneYvcarNp0jCJ)6gq2J$dp@OA&lV+AkqAs2Bho&UO@iP0&e|J{TSFRsB02z)_6P+w~UXc#gIh4hk&|xHb=s?F_qU9I?G$DCHxD_^hu4MxdHi zmGTV(PEds_*lL#a34?X37|;gjwW>brHcz^NL9c4TWAn=TYJLsBVzE;AG`N6qP44=t zdGB6N48uo;QDr>7RJV%4)Xn^y`pQ+rW~zZ5W}9+vg=hJr0WA4x__SQjJOHSvziP8o zL<@_d8kw9T{67oRK>BIHu|P1xItK6td=pxJ2ZA8TKo9E(cf&a;Zv%d6;1_JniuXVV zmHF+!z$)|86xW*0-)@1}DK+5y88FbzElM5$JQdVn6|(9pHV{@xovbfMUR6!}I0#_^ zDA^$az^g$Eyjc#BH+s;x8r48s<~?nL8VbXES3_Nkv-;@fxmRB?4MI4ky{qXER?rrA z1#KPUaBG{fsC<(ycq7xj56tjB zKhrxa=iPIZcjYYaceUQF$Nm%SEn^sfYvYdVvq_^_eAT^FatBXY6=}@$Jmb&`PZy~+ z_qlCOaogOl+Tg_la&aHNbvxuXa9(^_(xZsk(gt$F)z0&DPNd!@?KeNaG50hwV|1WS zqYtH^eFs(4qlcC%(;;WdsOhq*CB2Iy0{K?hpX33nPyX>3uh}@$cA{a|LtEwTT9YO_ zlMdrm?{`VZT|t9>eLGBC1%U&(qa&cQ%c z=VDMzUe2$P-$Vj^fz+4kkfT}ZV}*5++JQq6`-U1FPKx2Pz?}S9Xtszlx4+gCNYbdZ zYqq-A88{$XU}qPPgQO!c@W}n3*37ZooTQnq)O5z_O`kC$T4j|3|+ek{LZkZMr~Hq#m5arr*o^EAFz z=_B0C8o11R)?JtsCrrPDXbLVrq&nM_;V3Z+8k{j5n#^m&y4-MihWWG_Vd!!4;_x_P za6DJtN2Y=52;Ck?R_O&~rs!tN2dB(iAdltgQgRH+AJ>$dm7xSHxF&khzy*NY=ksFh zr8jZxY;L0*4ZZYZfz^TrsQMkzra8=Y){9@7&F(t*C7ncZ1@cidxIX@5H=bbM_SKuxP)uPw-mx=B&`Q9`I^(neys(m)|`x`lt$Q)ucyyy1^ z;&Q>rlf}{>=!|o+Z2P5W&g)L8_t{Znf%e##xn3^eduznAFfYbU`s4<^{(PyG(iekE zcBUtr3Q=h%Z7voJOV+TUR79A6$ST8<7EojHphWYOfSO)oHD)K0ya58tVU@`aW6cuA zpXfd%=B7uuMX$U|wN!fpJ4UJx;~1>CFslC5g|pZ|@RBKs15EpRPbm(zUb* zmcV9ydLYu^AVsU)P3R=i*nyjDp&;a6u#+vwaW#aF>Ybi|$_Iya&2#U-YPpTR*DOO> zWd)gT%&ivhpc-;)KrN^-_Y?Bv%V!#MyZO?GOv9eWq53sMzw()lx!X#G_{b8UG9H&a z<>uu>pnQu0bI2*~D19xja$i$+(ZGvN^-0Ikv0Ms>^#HVgH{JXsqJ*-1?&v1qvV$|U zijhzi1-h0)V|x-``AhT!`D`-$d%35f*e#ll@HDkFul1awj>}30TTKb~>L>yJyf3^M zxOhB29LS=UK(?@Cj%n=R)26BPN!sBOG)!XiHwN>UShfHgA1+alv6tvHHF z1b=_V6w2sCii#)Q>qIi?6vo(=^s$g`*N7t@+^`c)t^gJ2l%?d;!+G(@)zJ0c3op-j z26VED+tgXjASg$&VPPCE_k~Hh`{eyRMVxSV9Ot z9RBOTA4t{Ry*DZ~!VpLJ_9&%+3>m%c*^9_EW^e9`bd~@zCg0y5v2mg!NEkLj5ue40 zTY2ODJ2+u&QX!#%CpX`OfODl025 zujtCe&N~m@g`y!?_E>&CoKX7>>MG>iZ(tgLGivmNX{!(5@V;1B)UrVRp_688Ul0rD zAEHPR9rX1s-PiGl(${L}>%yt&>tIjKcDs1kW8q;h`Zz^#JWPR)Q;p@$Qsv~G1V0uy zR=g)S*Yk>2e#sV)nATdhH*|q|6C6g8>xiU=d@O8)GvwXHW%z1axHYu z+OkG9D>mkSsYFKX)t?eKqF8>xphXVM@f;nzr|2A=oMO<$O0BU^MGh=O@DtipINF+?iVgoh-0cJgcqvzZS>i zl=l6m{n43gVB10y{G}NbNTi|h1j^4r8~mq9 z!mFQFKVxI=BP6{*e$pz)R9X$GWV4W|v;y+pRzk+$Y2nHYI@lE06HWcC3Oh6Qd|?ZL z`GWfjOt})S)NSat0=li>w5{Q*GoJQDn~t7n*QxKho8)S4SYM{P7RE5I(V7R@c}28a96b1-Q84gK4l{V#{qNEw`tE6PsRY)1gC1 z)C+x8g*WYs>JiyB)bnHTtO{;bqqOva0}*_sioQ~f4shU6MoUjqbfu=~3PaHqDHKhk zV5eQ8)|&chluH>s_-fSO=^Nb((#R1aMg^3k$31-)Pu$c+o>L+jAb8l1OV>RKd^awsdP z9DV*Ii!>lJ5NQ|~eUbRQBE~i)c-E=rurU;1k=?#8D$HAQ7m1)a#ugDx7t$uNkKB04 z(;FA=my}q#8pP61yL;hh0!u8dcE!@q23jm#L1O6&TP*!t0NEtEtU&5F1(*r;1HSi; zjCa%FjA9H)7~{w?*PpJcL_oE0Ux4c>8A#j)4FO?kqmuZ3{RNVb@x8jB02r?EtBBuB*&nr?q00c+{Xkdfb zsJfPXmDu6-s&?J@lG@!~gcoIvxhA?p-5l?E^WG`1DlrJ+ttmUrYUvkOh%R%Y1y);CGY`lc;q-H1}w zb(ZmO4^^bN=y2xjskn9 zRvdP#RpP5Ci7{J)^h2e?Bt6mO*Xy}@v&P5=Nv+G*CNj)f`AXuJQ(cU}N@d-m(|EP& zqgEqdM0#URr15gA=e1ZV9Rw%_469oF9&b{Cp7_M1KlwH zJm{JFS!2f2*H9;24(NXRs2|0f&pQ5U+@tG*Lkixy*+s?IZ7w&Rxtvd2ZiV4;w-A@R z#pZI~2;hig>(dOE`^O9~`wY*^`QG%vTON4Fm3wch^j=ftonGyIIqSV;nzw)2l$RPY zOm0_dYNP9t11Zo=`KCU6cAHpEAdr#6eP+7##` z8#}$d-7;SOp;elT)jd{wcdS4)@w(7AW>fkebK^vE?=Uy!NYcDkQxUTXeHZ!A`XbA1 zzeBAipSF?(N_P0ZX*CD8l}(P1of3F{8%1uuj6~RymFnWPtmv2|dhB*~aC!7Ri9T>o zCGMIgzJ9sW!4ZHw03Tol3*2?Y&1GY*3K|s1W$8I)vd`t5AtqB2c`_OQm`4vp;k&KC zm=4^VIOIha%Iy%Z8X9!BiHecZxa4;Y^&2{jQ? zlJJd7X0@wI+dmHBlVdxmbj6(5!evs#gEPcPl$0Gf_k|Kgk&@?uW26(GxG$JKON+a= z%I0q$=oZKosZiQUO6Tb6AU`j=d5V)%w>0lgHcxSq>M?Y+!F;MmT*`{#s%UqU;xE-J z=Bgg{8Jq*Xu9EIkBZ<1@onHIXz4U2Ziz1kHpcFfw*C@DEX{75MW-+&_j$oNSoa_#$ z5GK?okow$;EIyj?I2Lb3uFUuBlTcdNM~JFj{%_SDk8c*h{gKHAl=a&t`z|(t&ZlX* zL5iDn;$AjGZp?ikpsXYJ2H3tk2!A9Xcq&fY#oP}mj~B4$qulJa9bAPvE&pZ}dst9T z+KwPTr4DyWQ|MdY73hdr8EAK4P+16Kz~~8N88&f@pVYNR@B8p{DXhzphX}H+7|4Vs zOii7l>h4(Cs6!`n{9Fkt)p;L5Vai`(`L5Q5*JaEm$*-y%XBs=tvt!jnowK_RZUZXi zoGpBRw%(>K_X!2Euk1A5dtJC2OGJ*Os-`2d@SvzY|mEd61URT{cc!~iPk3P;uH~1 zg4QL5h)Sh*;kUYyp0?om1ZJ}hoQuyCFRv4$57#s*N(5zr()K7tF8rO9I6TcO$SvKX zU$tj=9j79&vS=D46Mo`BB9aF_EvScNQ+*oaR!bFEG!kM(Qm3Q~@u=OyV9~e_EE+3a zHTs*j8eQfZ3%_O5=vzsRzSUNvZ$V?>iZY`{-{7OMuxcueg+byl*7a?p#T0+5CM7XS zS^>``Nwde86b$xaM2~2Ae_&N-j{sCb6Ru|K^S+=Wh2W8pvih;4xeu*R6l;kQz zW_hXYmK!Syf3RjDC7A?pBnDZ<)Dm44MJ=3xs9Jkn1Pjnl0bMPi5tO1XC!ZrYyLf>b zT5}W?rEK)&QAA1G@U+Hp=TKch%CYMDa_q7SKTs_WOprpVTy?e_%bs6Rj_zdTI>B9e z5&u-y2lZ8rxp#wK8hYkbiY&2K4FZ~MX3q@OYz1TzFT%&z>z9UGNsiACf+x;Gm^us> z2j8Gn@k^keYUt+y-QzuQoOHWO5DgYVtP0Ael~-)2nxJRm;q*cT1Qu|{YCdl-^9n7E zxi^<-KdTnx;8*${XTSn?B1kS-fmz{nIskZjSMupm;ILJUW!NhMUPDY;g>CZzIVu6t zE3rJhMoFb{G*n-pw1-vo)r)j4u*&)h!serj6rsUm1^8cKCxdygn2chBfEQj*j$x2R zP#pZqkzypVVg>$!{AOA0zGr=xPso0&$QK&6A=N!rgeehWAC>Wpe-gQymlGw6pHg{! z#Uit8ijBFy`(9X39YX#101B+@#A6k(pem8PV<4W1#93bjImHy<9u|K$LP)?s#_XQ% z8Hc*5!BQ?tw{Pc(3ox@Ayh;f|%dsnTRGn1;pT+!Z<&;$vyrVc5WCgm|D^^#M+kLkH9>7C=ThcGgnx zw@}+cpZMLOgoUhjxyc>2AW`lL5_cLw;&u`wZnp)AH7H1Y!>SbY`M@hzy4zL0SDx|i z&3GRy^G==0THht|rm;)=+1fHv<2a}-?omW-;U$^bpHgvXk{IkZ{=X0FM$T^0#A1@AdfPtGenR^udfngcLmtCsIm$H1PwZPIZ=qooXpA&Z|{>U(2#E1N#qb3;{V^ zsXcwOC2(m!sT$i)Dr}Ifx;m*gxJ_>a!83o15;9R(KxUVZ_-A-IgoJzxgD^PrcYT%F zd-YA;yz48LvFvS~WvI=JJfeZIzERyikW>l|*}~yPg4M2k0AmI;8Zj#jxE=$K@)c{S(*}3+ z8zw#a>t*yF#amP;$!t^ZG$eMAUJIB4xV%_bp9T2VVVD6qN~l3qszFuMXVF>(J*c5S z7Jg(OM*&RdOurRCivjzGsGcg^Ri=jZSorFFQTW6Gr2uYl`Pf|+A8QQCg7S(deT$Dx zwUQ^|^4#T=wqJ@A%m!|9LxAG-8l|yNy5i@f;h-?qvOGlFf#==r!eiqYj&t`I5%vxe zVeha-*d`QVHKHB5b-w}j-ngbRng7_&wGoMIg@7Qo~Q(ar^KRbntK zo6HWtn^z#vT4w4mQW7W_9Q$ODY&q1ULkHIAT8*{Dd z?moC%!A9UBiN6t>fIKuhD#2khoD|9fu~aQ*o955Zq$U=}iu$TW#eBtDOuvuy7BB`A zlm7oZcLrZ-L3eTlN;v=}L_j?@6{dEOKP*Myo+v6(?C0k2rC?pY-zB;SETa1Xy2_6D zQSXj32r{`H=;bA0tp6o?@Wb zQdd|adPC27y_$1@1gRp;_R!?1pSZLAsWsc51!a}x6~8HU;xvV%P;1S)%9=IRztA?@ zIVl$A@}!PIW^Ir^l`L%GntQD^6g;4 z2VZNKAj6j^m8`JAdb7w?Kb{6&VJ)j5?*dleYQa)F&_e<2_QYGK0M!q=sQ$Ty>R;gD z;x*%KF;j53_|HQ7y;P&99%>`g>+zEeb2jio^=C%>BqN0^ifA}N&6B=csf{O$<&!`S zZ^zs}#kCNFRJ8$TsW|;T88~rJs^kVZtN>lLEk-csS_G5RL{c9VAVo*ZI_R_o5-Syo z+-X2B7Y~U_e(6%luPiG0H3qQVa+p&`m7)p?U@K6ioAQ>zCY>J&RZ?7D_n?NFQ9l>p za)t6#A^>3Jn~k|gOX8T&E6}3~^{A+R;3I3mwJ;Dkox6*UKr8W3 zSFM!0=s>WX&J=kv6h%rBdwm68gJq?fuWK4~*ld^*&iZG&iWkoPo(vgIWjGYpdNR~1 zg#R}eij8P0RowdWYsw2PQ2le+W0zkGXR1*4bD4y`Oaae**rkTwS=8|RpzOr*ikoI1 zLS`ZLFI4|>>fcf7-z@d7R{c9#{X0heJ68Rht^OUS{vEIWop2CTHQ!pL%?+BkW{=6b zZK@IyLkXsWf3$=6%-16gbTVH-8E}wqUWB$ob#Vf1u!<@Bddc2F8}7%lRDiBfr&uC- zHO3l+H^Ejf1+TyeI5Wo7t2J*ZPkEYEv|170WTw?mV`iTvW?#L`411mo+mZ{Kt7F4t zE+781ni41xp(A+DQ`=Lx&c0WGdwkv6a9$A~XX~?kt4-bF%g0sA3Vf`F$oD$o9$$B> z>vDWvVbBTLXZY7Y;5lFSsq30Ndcdf^l=7qoy%(mdzmR}uhK0zPU@;8=E@a~j)Xp>$ zPPp%+ds$-$`{!dgNCz#4C<>j zhVMjf0_9M#B3ih{ayQUu*A1mXmMYD0su|s2S`q82s;@$`X~p_!8=z)s(V{0zQ&VZ* zsB}Ja2Yt8NzA+Q`?vv<{q1^88PEh*l>dOn12M50X7!9(GCZ%AAV+lY8cnqVL+rm#* zdiA-p0d|#$hDy~aLKI9xtM|0}oIdtgc_1?I+|2L{)C-)+hSKCzF58?BikPJ;0Ky9q zpo+p6o2tQMm{tfPt?{UYsy5OglN!pj78uE6x&-GmJ%GPOk;OPvg9t~M#epb1)K6Q5 zt{qu?4^`yHAR|&`zN}>xCrg6nNQSMC6%EZ%97|)V@ z;S(rcJhQ?B;?GiIGH^eC4dlx@n@0wpL8qLsJ_JL+#ng9bjzL1~H&oyWvDwV}P%TFL z{UXkZJNOWvH2BcTAE~!oK13iL1q*H#bvujZTw7mTf7CkqJ97hdI_o@nI#ZDk%q}ON z!Uw94`doe3n0sdwh4)g%B=d=C>k~oAPpEFrlLq5_1+cUUEft{#MqmMhunZ8X!DLTo zqfv_5Rn>>9I2Tw5Xvj%n*&H#Nv*9a82`G-Dw~wx`L83pVepdZ4igrPCti?OC)E)Tn zEQRG+wiQaz#=H-zF2dK+#Vjzu^GmgAIzUeFa_t8D<@%7AO|5(94)}B}yl@P>ErTJ} zc5MC8`1fe;b$0zR`1d#k-D6iTyW;rpMKHtT>yK3vKfeB$`V$(UljG`VH$dZANGm6( zFV))P|7@8@UpxUO0i-%hwW3?I>yO?*V?3V5Sc|WGlXIy`y-PqtEx7IBMG+BjG(-9r zg}h@WN!QUhklAPB*zwnK^~aqJgH^*@4R_VmDr{3iNE!&Uy7mk9hje!Q|M$E_rL%qm7(gTgb|(IDUAu)hZ&_ zJ8n&HRrC6<5iC!mRCQ<5o^$}#m0G^_HO$MhSd28}GOw!1c?#syNGQN5OyduMr`tWq z1J)@XKzqNBIbfzWP#k-)<-GbCXTv$_*=k#xem0)Vo{iq8YGFX}Y&em`3gs=ZYl7&d z({;dZv;#y5zTTp)MULI2u1%I2Vk^*B0e9sVP}HZ=AQOIPcLeKiR=8nb1WWH|BH@Sk zMV|KuS8w>Er8hi+TKI!yZW4pj4qgY(dsl0by$VtQpu^}Uh_OEFgsTC%^+6LjXU$uN z7-$RksV67$6HMeW@15#Nq@HxznMd)|hJ?z-%=sy!`|#qc+WjRP(RnPU!CCc~CI68H z^YD5SuoK_95l26%FShX)H^;uHbiV|DfH*6`QYG~3(Pl2WRQ5VV07Lr@g_PQsD z$ z{UuG~5Jiwe6klTpi*g*#jaS=N0QziTk;eqTokAYLks*(QOCCjwJX+C`v^Q=^BKn|& zW~Uvz5$UX_U9Hu2WC(!M@=du5K%cvsJd-!(p6jza%e+rEAXv{jX*IoPFG<#|760V# z7W6EWwj4x2x4+~&8Uc`^48OPPYA}nyP|D9bS$(e(ECw}bbiJC~X0E;(jI?mhcGk&P z!?t$T$?7Fc2L?vI`f6%dc{SWrFVCz1=R0u0D}rAV++i=A4EW{&-^ch3_5O72tq1p6 z$#aZM$T4WUkrzzgOs3&9Oap423mlql=`oi++AR9mjP&vCxIO={(8oE56r=#e)zuVL zvnlrmA(!OCjk$$FJ@^bsNKqM*P(_l2Dke=r_c|nmaT5O`p#%v%?vl`-EE4)3+<|V0 z8~YD!3tDWfgfU-(lh-cWe0dZUifCJIyL>sYRJ1HZSj$~_%yE~x|2DR{T=X_yp%Z=< zOd<7C89P%XU0v|D`@^dyo1PK=l3jzsm0??7bytsIZya~G9+4fko{l~w&3I11k6ZE>M z?z2$ckEnj=e;KOZG&ZU?m%y|Ra2)3%HqwGB38 z>=e)#S{`A3yp;28Qc^ch6}KOBsba{YieU^+zrEt06r5hb6SNjO98~+~V_Cpg^U9pTkFjmvaSv4Gvv=mn1Xz;M0bNvEv6xFfvA4sjTZt?pcKdE zvBrk>+jSTUhOy_AS)6i-W8I5T%Ew7Bg=8mf7>&J|kInKgeg zvRw*`E1K;wl}1C>NhNS;8foetWgP8vTuHKz6 zI~Ro$mm5x;0L)gK&u(Tuo7Bk*jkzY@3qJ|o>RjZ-tkH548(Eq1I54qhCKRnGPpkAA z&bklH8m^)R9`D%kNlA2UKgB9jc)(9b=T$LC)laLhK~F8!U^Sp11#0_5^u8 zEGY@N*JfCctCuA#t}z(X#tcBNApdPzT`_=Dm7M_K{w4ehI?AIT&^4gw!hW_HjIL?^ zTDY`MvH}ZPpqd&{2nTZ)_|9*27w$G|;ciF8u6L>wJ8NMk6uJ~Y*1!Uius&m~1`xs; zMZ@(}iHAE}SnRZ5u`4JmE3ddq!(z&JdPtz5ZXEdw5zKqs7woLu^_rm#Q{awHeWX(1TYtkb}{7tfM~B zcBW))Ut8{g`O_AdKZ9WIm?AKzXs=IQXireLoU#q$vo01sXJO&G@_)Kj6cn1mT~lWj zz22Se4dqrK?i>!pEvG=-&zI{!++R~5?yv1Y+%J?v8H6w2Z3V6SZ}LC#cHcWG@cymB z+gahg{ur)CW4Cyh-0HpeHt&oEZ{aHMPpiEDSna*= z4sY-dZ}l4QAM3rF@Al5y=!N%qx837?qsjYqx3~NsURj^_?0)a!C%kK)^uC($HwV62 zj`vsiS5*3+tMZ>$?SD4wcTMwetnr^e)Bj=4-*%M$l3M?V$N8T)!LN9lf7eO=#yS4! zr}`V7?hnuPfAmcMhSUA`o#79i>1WUKADQpJ^11$NpYKxtM-~1AP{zd+sFY_OHg`at~|Ebsb%P#ic^jiOlOZ^YN&i~UJ{2SkxC}elg z!Oe5;t~JrkPAbf~w{MA}nGK7Q2?ETGns~UgdhKUJ@LFB0xB?b+xHnkf6gZsw;C5*1 zlwz2>=>XoNGy>-K|HQkUyM9Y*!Rb04+cN%of)rr?(!X3E-se1Cjx(6`c((5B?|j&p<9gh@z96qp$&+e9)WZt2R9v=+F675U6lT8$eO zIVC2QLV~N91YN=&BX`@^Crj8Lh7zlhWIa1u=M21Hy`6-m#?-G2&wEwc0%%Eeu)$%{ z_DWeoC9}?>hg|$B9C@V=G?1XltxPjEcVD?B<>rsKC8%YKFm{JFH*L|J)ZBbvOZ@Fk zhO^rZzn5reQ5Q5_Ko?Y8Oju}DgHzdu2a8F$2@$h7MGYkGl7#GY73gmz7T$r`Z@7>w zr5*1KC_lOS+rxi*`EMWPEnnKtzX$klmi`R#;~~C1%zsDt?`V)RyDgj$XDj^O5-i;o zgtY`wJ}!u3t)&)=vh;BCs8#d(qHK#RlkDk!Za`0CaLAO zGwJML(%H#myo*t^o6)_;pj(k(V`ZYF4t{ZKplNb|XWGT|GRz%~FfEVr2e$ABxAFiw zm{xFL+j)KU!H3n~euiKtLvVnr~5LAfyUa z2DWfh0P$7}#9Il(YT{~q2;%6aK25ftS9_lg44HfnPrjQc*TtjU%?r24K-k4V*vXUJ z%4ipCZO27N8K_1WFry5ZUM8(>hKL%=Zl1;-{>Fa(cAWd)G9(ho*5t#l>4)2r4{sC12W?KwwwwOMddDC&qp0isVZ-j{DN$w<}} zDaBc_dDw41yumzFbFuW%L76Gd%z4-ZtLbR2jq=m1f=uuG5xS7`Eb2{IGB8RvmQt`@$BdN**3=AR>oOBRHo@9|DHY?!$Srg6y&(|6> zNGd}X`E|q|4rwatbhJt}f*&sQO;IlM#<%0PR%V3@`NQy;ML<4M7E&QtMC|S$JWqwof01KECz%8?6uT_ z0LRu9YjMEcl~}HArEi1t)}th8Z>O=FaHl|WZ)faX)RbGwfPHZjvJbeturc>6pYIZk zU%#7*_vOP4K=v=HprO zDYuh>P~VhW%a@lm=2r9M-$gy0vkTHl|C+VmLXbas9SBv6ulfzM6DWpxml)Ra??PYo zkJEX_Wy>6UitxliAQnK?B6$Z-Nd}}wZhx>$3~$`uhp4|xmdPinvL2jWjUZdNkxOVx z-D!0fDj{a^LnDC{1{{9E(qc?14!k@F=vZj6VF0TYiOq&m_cR@cO3~N9}kLK0zt?TK7A8gF+;8pXX z#$4ww>GH!gj7-|x^OKu;eiu#sqfNPY@O+mw=Kh_(`BD1NH@Bq%Ih@p&ez=pq^q*>f zWpIAHG55hg(dE@@8-4CJu5Q{HXvRCY;hgnsB?kDl6%9{TShgSSd$bhzO`BIds&~ll zz@dNMTLwszTgdk?hy6fI{+BI1pQRP6)zrAEvQVm((x#K8pF^(wcRe>93c?%zZsx!5 z7v}m^_nYgZW+h>i4{bQC3x%b87nf&TFUL{MT@J?&m)DbiGJp!mMfGK;j|sis&WJ;p z4FDD_`-5ib&w}Pai4(!3rGs=KTMZa+)c^|@bRt+nANXUuwsFcB=%`IiE>c4G456$( z6qj?MNBfUjjfJ+fh2~F+{C~79s-{Ltz@ZP36w(t|Vh+Br&FF;$OZ^0;K)mWi$*0qX zc0Z{hszsKW%1RM(L}tYOB_Zx)Q51Uz7N$k3MmWooA}vvx&JP@Y$flB>AfH#u_%7dL zidm8COIeTrrML}9$SmI0Hp$ST+(d}!XO>>nGd3}GxWoiXW<_Zwvx$_;5}EgHa`fyUHA(FE0=dLp)!ql+TTM}3H+qka2XI?-LUCBo~xwYzk1SE`Gz zd4j3jtF2ARL3@W`ajb4xqwO$W9W0v{Kj7UKO2eIvq|rr zG%iw|?-E9t_bou~2Y4a|BA{HhdlX>PFEh_>1cT>}^H z4{R>{>L?0{Lu|99Lg2fusRcS_3&GpVh@*%(Mn0)4kn7&RbGomK6yloY1=FG6ztS(k z))fN-wtn|`Y`ybHv2{AB9+RziCfR!Bl(BWO8~9{hCCUCZ?1Tdhv>;M_!Dj1B0~28w zUmhU7%`Xwl9GJ-a1Ni>M>V0PHmsyOxAcS2o)XAS920buN*A{<#@=0fi*84QtzKM6$ z@MKhN8?9tdV2=gZQWC4TBewpwvC#*+FYq3v=W7WWX=j4xmc7#SRL(=dC6uYjNt5q?BH~Hwz1Z29v$jh|zL8D&;{QYTt1DF> zN#ZuXpJzIT@c1|e9TfhxKiQY75^c1OnK~xK5R&%$QVhXiGHGWAu3sTDgsOvN2or_L z!+jC$%NN*If=fJ)F@C{@6iG`p`q_Hzl{EoYaYbJWIe~xQWb7X@($F61Ydx`Ylo;sn zqz3xK-n4;E&W7;sX?@IHJ~`Xwb9$qs3d@`el;eX}RlDO_Y~J-uqXC#$bPsp_HfvK_`BFc$N8}sd$=XdXeAFF7lL2L9&WcEJ{Ef@ zE{l$bq!_yR_!*qel7t}-ci9hr9(%Yu2=DH&j~<#+EQNJtsaC?Dj$)={IJD3KLp&(rGdoP@o@t6e}DT=?=hX?b0LHH40CRJrm z>OJF3>huYjJk_E+$~O(&k-*otmHG?~DYVp2I-VM^Vo1gUsi=M@uZ0ErCC>qb+_y*a zKGXXPCxx)A#SXw88_w~@g*v^Gbuf*I{a;6j9vav3JHY!Ay9SlE0Dt*mFWx*qPc>FJ z!84x}XMk(U={%?#263cM^WxSq1M4R|>diFRIJ^OX`e$&MiX72LGe>e7Wy1-se zuQLQc-q)3aV}1SI{B?7ezy4~|jykMOSumQ~`;H`n-}M`fVEuKqTH>}W>$oP1 zhQNVfmHyz=ji&f(PxFjyvvCW=m<6?d>5%o@rBB%JKR&F_Y?nNNr@QA{Pror@o#=K2 z(#$lt5vOLqa`P6wvhlQc<~o->-N?_;D{nBE_mTDFbvT3uJUCz;Mv{0IVtH6KxQjzC z;MWFyqI^E!6u=iQ#+It9E7#Lkf%A;0ru>5w%Y|gkt6B!{%%9jhoH8P_OzzX?%lN1= zoA(=YmuK)QL!$5*6<+hack=POnRNpP1;Hu%LVk*sU;{bx=?dr^A6nFf$CqQLVI!2& zfUBy;+zz@2o_xP9r&s1ipky<4)Qfz_@^Rexk(uY;=E7fP^mV_|P9U6+K*>EjvD{seBdQ zpS`pzp^$L!TbHH(fKHt)tyBLX;h>k&t2pRod9pG%{kw`{5%up7)GWHJ(U+>pe4Z(H zvPc@0qo|G-*oi^Uom<2q58r^J&>83*jIM zw#c`JDMn|Q8sYs>o%Ul}E*P@|$WTsC$G2msyv6QQg)MuZ_5tPYz*H>6m zpoOl6sVnZY38DB)gyINp?$A*Q(i@DC-i*?K{Hh-(y^#{6hg1?V5uo!N;+@0;018n7 zdvT6pbO8{+%qDiPA4J?fOls_)kk4qKeea<6u0Z=#ZefT-cY(QM$rdDmuP>S|r}cv@DPxFbxX7m8+0^HtlME{4#Tmp z)v!DZbGU$xW*DA2tP`Fbun~zsoVniOWQ$U!%c7~i08}xpFjXbr+0H#~XP;3`YRLC! zvRjzc2vu>Mya{n4klTuo6G;|aePuTR@*-o!G<=G?%KJGEG!JgMoP(oBBc4d5xN0>!f&hCu1O}U(*E!u_t zNCHBZSzA{+qr&xcso0X>Lk&W13mo!vh$*YtRu(8R%j=(gibwb|G0Kg(w+nG)!)qq9 zrE14kR{sluAv}nB4?j55LtT>$#dzzCnc6M+_>wNk$9WUhtsVXN`hbh*f6FSXCV+~ zyyiwFPO8G6b5!AvPEHkmT?tkA3atuXtX1I`R%lgtMg{Yn+I2<|o@tcgYj;H?0yVSQ z6PfT(5Psm$b>!0zmuf}6_7fJ;SCtadt5#X!^cV~a#HX;d_;mjSBzKBzJ7k^NL>U~k z&V1^j>&*YF5Sx1nAodk;#Kv{#AuS8ZhBI_X+So|dVbr1j8CX2(DS*X`$Hd~TQ;)?- z=+vZlJ%e=WSrgHzwHod0*vk_;_Gj$Pc_?k8iNC#&<$<}&w?*UY--9I>6>j#^XP__Y zAp4@uNLRtH@lXXHMprh?Z_Mg<=o zM+M))V@4H3$b?e8kZLx}n#U;V!qZ(P>q=6=HMMOmse*4e%F+(hiBd$_m|nC)b+#Sb z=4WioZS_b6Z{}nRHvG5fs5(U}Tg;bsva&yfUmC(M4aL3$quCiSB(k8_p|HGN5Vpn9 zI5i$e36CE@9OC!w0cEMXE_RX_Vo7#~S-n|{z;th?9T4#hq^R%(s3ecFHs59UIEvf! zcJ7qORS$WGGvpnykJQQs%#gxYkh08pq`_m0@Y@F>qHz7OVP~d8s0#03pw8rjrtJcL zXfW?R>uK6Mpk6qlA2pOk41JJ(ha7ElyFpJEqh~u0X@@&ip3_?p4LbsU+c1MH#P0~t zT|}b&M#qXoYO;do^rV_RBd#WI#bkoEn(PkTsL4YyHF>(DCQlbNS=7NpQIkiHjG8P% zrR}M-n#>`&wB|-xk98E#(S({j8dsCIj;kgQiJCkVG|0BGz}h?0)MP;xsmX&zO&(2A zlSgeed1w-9^57V1@+>xv7(NhQAh)!-+#K&!?~Cfot4J9wa2JF8-can6yTQB&OYohv z&$nOhkpVd+lp59~Sv3}k3JkCO_;N3N{Bqt{eG~5BVG)lCYmYS{)bQT()W2Vm6W&B# zdF}4hsB7}RwBxS(gK-0|v%H9D`MuDy1F!o7h`jC>a9R;{vFPh8+^Zv!b7C8=)hRs+ zK_c+sI|Fe)XUF4%0=5f*1VR6>M!RR^OL_2%S4JTSXIT~!4jGVGdbW}9n=uE3?+qL0 zmM3t;w(Qj~VvUgfK+MxGr?@AWr`s)0O2Upk_YGsd``wYT-u>RN^Da7Ub)`*8qtD5A zbnT!K63I=TJX7e1hnWXBP7K7%L1Kd`vurRx7IS|=C;##)1##Ice9&^UvCKw=fb2#F z>+Dw1C2kdD3+f6qg>TTaj+>9V#I0i7cZ>~5mIV)>tSL9*d$gPzb2nAeCEJs(s>Y$k zGI{6!lH52>jQL9wr*fS^dE>Itrn*9e580bmlAea#CbW}TZrG?!*;`wUv*^AvppWXT zHmGvh`(tf9_3&)c1CJ>!Vt*+iVkh;$6JrrY4O`4;<}P2Z$fz;5t5QV0tdNvGEzPFP zM7)&>X+$m$`_sIZzzh4$gygU>*UzZ0YRvtpj4r{#_t8q>)c=?9e(d4L`*SLe5YD&1 z(c=x_|1w5rAAXF!_eibcr@uiTvFuPwH=p^xj@;w^7a{ke*J0MK?U0Q(E!bsnA@U2|O?u*PX6) z1DQ~aFNl`X#e%qw(9wopBf48b%kdPiO_G^(0Mkvpkn?QJd*^zJ?i@gsU_dVKvLXX^ zTd`#W+8rLCVj0HzlIgJ5YRxev%l6+`b5wiw6R}6TM)gq5&3&VBXi>9smp-Xpe-br4 zCD{GFLhIFWUDZ9TGt=|RMKm1*G5|+b z`VuG^EJ6eBk=09RN}>hM7Ml>CSq%i7)w<3ApHo)XX}Eozj;)fRpltlNl)kPcki}Ix)YKZXK-H-+|PH( zXpwGKE_6`dkPv1JZir4C8D*`BFaO3%t%H*MuCxM9p(`Y_p;+yZy2<&jL|PTkBvB&+ zsa>b~2OytZ8pt*-RxdJTD9NiGUo z#QU3$SQ9ZY+~vEFTIj^Ei?N;7R557Kx%v5IOMUOVRXU0JDI0VBBvg#>V7^8V=YAOm z1$S1tG4jrc$nw5UDh)yNHHN{0Efd~-8?2}ztf-6L<|ljzMn>a41lvaG6V7)>_$CW! z^ZpBmDQBI1fh2bnZ(hmnU!+3_C3XKyPjv3k&ao%g7n{#GQ|L=fVIXY^eTO;)$C>N4 zL0irwT=Xf3$1@(Zi_qC`-Rw>Xo$V}i?uEYg24~G`fXmC@hSOGa-pBWAS5uRH!C6PI z)_0F-kQddO0Y%{R08$O#DXmg!?lEa>c6nT`7qXK=3g0pHWOq$66e={M!xLOuYJwJ3v-cx%S7 z`5&-EbKoOJi=m@Jprt`3G4d%^ZNoRx8D74uG576^7?QDA09=+UnS8BhRVHJHUd4!R z%cS16RHtGr=0Fry)+W>p27J$i6>!n8krJ<9f1~yi*&=cN;70N{8ctSEwvT%pjAhl3 zF4a4C!8;x_T}AH@A`pWN_!p!B{x;76{5n@BGO+!$QjHk4mwFnur(3YSDh}JML;xxL zo#&77@K#d0k{|6aHJTTA214UO=0-n8OPq&S;Zt?7RsDYyxN_tpNtQdzpBph9qX!4} zuvNTZ=-n8$#V?5?BbrS?EJpBFMDYGNdGnA;QTEsbDE!@QkbVP)`2b7Rn8mbV zrh@Sm1oG`;%vn0yK3->*2bsFxtLveRinF`5SxW5{Yrpt|eql>|E7?tt^d6^OOo7oc-#Q-*vCYdIt!l-MLR zbj-IH z7V@*@vLhj~p`#vSlFPzOVkBzFh+GnP7*f5Ryn!vtbQ(^7JSvQFd)(jVfh2O1Gu@P! z>87-qe$Sq%wGgrrCIwSit7c=lr?R*~ZjRfLhN~sKGaHXzO%h61QQM54i0u&T(;#to zg1Xlm0Py@bVT2gEoQn&pONA>Pm&Xx9YPeNw%q_-)^Szi0G!Dv!1SlKQKzXN2G`@tH zffVjUaE_RrIJv-Arh(wr1PE3#2%aEqC=2R}Ll67|f?eah`NqV2ZXT86t9Yx0M9A5B zOVK2@g~+%Z+&s41L2@!`YybXYV$j#yg!@F0-`ilRSodJsG_hxG4Q$gr@&h|^IR6|_ zi{g9bLRMKQ<(^nLEXP7L^hsqFR?48?AI>N2-?((I9?z!SC4pTxr&+w5a4t7AflF)wNs^?MbeT>?tA?p^mB4^>nc z>^^Ks_pd7VsFN)U6~E4yJJ=)%#$%=Q?n4DeYDYt z+84xm!~pJ@{c``wU}lN3TlA#{>S6$?yWhsO9O5j}J=I`O`+}>fr@g_|^vQzk4BW0U zH%IDS^`nlfNaN)hF$)nJ=Da2 z1P|9d%WP)kO<`$Z3HI`k@t?P(jS<6ME%|1E5dld*!+HCn6#2wb_H!m6rXP)@zbLa= z_-%uhJ_b_qUFOm;8vFw70NJ_sqRIfUMi`6rK}XPJ3C3CRWj~IsS;!?72j@4f9Fh8>pXcX@F^!K%Nk*X)$c#>cF)Bx-qX-o|BAv5T zw&jFrD4xXVl*t^Cru0fjq&@?Mr&q@Y8M(tpq}H7pI!3kb)RN%9*_!GoK_lhTz*B58 zfV~X3S|)ya=i)JcHiiae|8FAn)FXvZF;R1-ULHCl*1Cg;K zY@iZJUa2pe<=k{S?vO+FUW=U#;rCWaoD==%4@AC0lci47-dkA&4fy)=5ho-oBG(Su?26Z&>aNPoWcx4$uggk>DkAKR!#t&K`@ zcd3nPt1OwVL4&NHg*peA-l!nJTCZIbaeAE2kx^=Gb|uzki6jd_i6jdzu>$N;)@GNp zHpOGdUYlKdZB95@;oK=nR%q5{S7L2;C75kjg4uRi%$Acc7`pF`(LJ%=Qi4oI95SJi zxnwe;$s{EOU^2HCfa{r)A(K*x1ucY+B*M(=i%^@Af%GRoI{jA9%9GDm71*Hr9#byn6Mg z#)`G{mf7rlO}zOP(!5GH-y+SoB%3dm=BC^oA3s3v8_cJ+C*OUyyqj@9wH2FhkPQer zvpV8Iz-%MQ*?}il96vf5%zO9x#G%5dxX{mG;bJQZ7x_6PL1<_WS$m zC(K;2G1o0w7xp!{oes$l9hoh$`Dlr0Vsq4xKCmAO!mXCtUSf^8sRXl$)pUC4)%48? ziwUt|y^ZFd4>sa{B5=>2IN2$mrc)5;zR~WHm96R%-o&=4u_G*|ju4U2Wuq+!H?b~k&X}XS2#&<08|+oTRpjBVlG#PF z(`;qqajl$`Z>4L=3%gZ)^_jrl7RNKE;u?%-4f>GC?`fWA*c1y6KYoBm$p}(w&jDgUT0^R1t-9}KOlrsnw7H+i8 z$iCdE#!Z0}`wtuA=759=^aTP50MXDpuq+ z`krgmwk^t-aiqki)9283Om;0n!e5D3$19ZFWD9tVP!Pf!AfjSBE*EmE6(|pgpBaUP ztN`%o3h+q%?9#5J+;8UiKmjuYZOYB$V(~~wCAl1#x9u7C$5(S9xthSOv z$HMo5%P3ioYD7X(82e;N$zW)#A;SpgPewE-jjcqz>l$9LFzOfkCZMn&Yomn5Xcu9G z@jYiU4t^=U8fpq9E}>JMQ6s{&oUq^pN}7uA;~QfE~q2kmlo zhVpYK1LY)Fuaa<`{W9cG0!EuWrLnDt`yN)ozg)ccpAwETO{SP7`gwmjs=#bU%9!4_UYKjOI2W5kG~* zo?n{8ZcC8Z){;oW+;@~FY}lm8^C2JL2s=?O6yPFCcM)s>6srJkd@V@Yjj05`?{Nl6 zE0?B15`!69F*0@|lJ*Fa_Ne>&1(WatpX?{Z;DbGMt?sMa@Ut;@8Y5}HAZbqul7^*R zpiUAA-&V*OxLbzmkwWY2@4I;Bfe;A|*qO*K`hg9Qa<;;@z7@ez$6#HNd&rYmj(*bU z=~F9mcoMxGj%Zid$@6G$t_oNVdQO8lT;x4UY+4W19*yp$#TLs^89AAvkTyEt0b?#~H#!-#P5P3cHu425^S3$sDd0HRdAG$5_xmbP6An zvd^s2ly#afYcluYeGyM}yVgdI%u2hpQmdmRu%4@Z9CieYtX(rz7tm3QJ4FQ<2^_B@ zE@~{XhN*L5pNqy^Q@KfO#x9+pmJ|a|T>Tf3C!V76CoOQf-oI5oT#>EGzkV`7w+JSLUe3Zn~X9yLY=cf8zIy?a1 z5*COn5TouUv|}X=yNjlD!?G;3X5U$5QvEiXl*&Uv_>(kK?Vy-st+F<-VT}m)8L>C4 zk)T1JKh|2RLM@-W5p7bLTxY`?Ni)?pQ1{Y_SRj@nu&xqkM5*mbZG7lAd!3Mp>hi05B92SC*gA2GTV5oAaZ!#5 znkjd*Z3fGtuk2YACma4+c1_>TKHCsQ;WNlxPxh*!vsc~j(y8`hvmx zmm8<|e&a5g?CZy4n7s5F8l2ERD*2j*()Q6eh%GU)>`Mi5sTrr=v0x#>k0d9KSqAMy zR+c0;Bx_;FSXNDrVr>~jw-S<#-rT?h$wpZ>N3cLRE40grQ5thrk?0F$Q2o}RD7qn& z79_urldJ3D^lL-q3bvBFrbrP}PV)Ifk{Bn=E-E%-otaqdqQ$_r3gKT74f19ry_ zfXL4MS&w~iBmVAap!2?rL45C)#JITD ziFrQVJJwI;#nCCZ(h{c!~j2xk-HN`_{-Qsr=I6y zv4?2S+!Z5|oB7+_44Yl{!=J|drsLvN{B&d}B!%w3+@03E>f)NB_;< zaMl2bTDk~qnC?_M(FbQ;&_1lKuz~PRE``v^a(1Ux?}$5ynsU$eaX09+;oNA7DB!p; zEZ29&U)_|u%#DlUp^+$>e`#axXdQRuK6-a!ZVzYbF`ryLEQeF!F&V4heB@H~R=2(b zWQlW)Qfh43JG3g9T}>9wPBdk%(kDCf8~DgVPKWzV?3(sAguWJU%)QvN(h4ake*=`G zGu&a>5F6r78BFIYed@1v2;tm@1JD>*0ux0)4_CrGJ6RI#4Cq7=PzOV-*odCKNwBG5 zol6Jvont^te@wnRWSNiQD-Bj4#{!qvw3vc01h-DI9^?4ntW5TFzkx`1$n*#umN(_z zsQ~#^0N#8Tc&k%@XP)lUltKN^7wWYtc;Po=V)c4TaV@WTYwUH{AU_}wtTSz%L-wN8 z`RTgoyavo3epBwXzRMVRNuEQP*lnqJ!sR)9U9_=yeV!DCj=Uh1cZ-mi%Dd%ZD({wu zsk~brrt)rih`f6@GpAjFD~ld14==vnz-<>Ib=SdzQ2PJB1;QI&CrkkMjDpysIyJQ9 z_-`oCmaxu1mqt3l+Ul&sHgG4M%^MA^YFR$MzQlFWi(1As`T!Wx=1*`fBqbi1-QfQz z1U`yM;d_Gx!cacdn7jMS4pQCyPV}^1D1RoFkfC6ssp!X>i`ZZhNxB;7aI7+r$NmV| zMi+wg*XzSU7I1p)gaW-h08{MpE_$^yASYawWvF*ylJ0k!(ExlFx_jS{ zyKwR+uXRTB~@Bq9EbR;G$pZhN87O875Y z`4_E>Nh=?IfzZkvn^w;JXVc2Rwx<7YNa)$;5eeo0#clpYDr1vM*||(A7d#b7<-C89 z%2S6_9zQ!uCkooFF0jMMAw|v+rj*4&KUaZ*t80rglS}@?Oi;afw6~k-!lZhRW^L{ zJe5#Q>W;vZLAPo`q*;m{MZL-%J|NzapBNBvZlvi=2|Ulpdr$r4`?;q-rIzo` zQ`xg2v{&zI$639%9TEv9kv$M&_;Krc>gF;~f(-Jf9`YI%d`{7bzIF-&`GTni^4b3^ zApdNR4dk<*0zjTOXH0H-fz3@98E!feC@=VDLHXZKwxPV>DFEf)o-`@Q{nMDh$tOM4 zn825wc6e1e6@Cr~sso(3z)z-^2s z#alYXwK3YQ5pC8^e1-H$?<;a=W01h_OB^RVl9MP<{Yqo1gtxvMQ&4}yURc@YxyvD` zE#$i3H0^_!>Vm?DHbQWbogYR5lub_$lJ^@$jVw${))d3p{}_*~Sbmbhc(9+FII0Bh z{uob5k<3-aac=zxbt%9YOKqG#faP?oTxQ^65I#X!wP|HKF93uvlV*!}H9%e{$20Ip zBZ3Boj^%^%;8hYposaBtj6?|{fDbuC{1kqVQ!)%v$W#Io!{cJ$A;tiO<&TMh;ozW9 zP%32?cn(I5xxZfN=@2|21S^<`s8H`I6r>Z@Nz9Hs(bYo+1D$nB-8?|K?e9GhVOL9~ zPzCnXn7i^yFMXkK5}*>XnD{y;8On|UGGH|4PlnMXzekXwv}{LtX8BP-;+r$Po(I#` zvw|fo=wV#YL)x7aSF~iJCT4YsFBu0k=_N>MNOsdwG^LY|CrN2IPD(=uM@nO+wUdpT zyr72zyGkFe=UPs|qV=La%4{l*Rl*Nw8rmCn5K|O7L8n;ud!s39Wp#M0g5Xb)-m*5T zF#Zmb?w?M9RA>6;g?Yw_=Wyb&MLc9H_O;*Ic(6(wyQGsq@^)We zQ!__0_|=FbFP*Jd@C6&(O-mXUSwJZU=Z#@K?O?NJ56KcsTY_-Y6=UmCQ@j;(ug~_< z*mQJi~-S zXANW*&{j@B6x}o*O2O`!`r9$Tq7qg#b zHddUoVS&_ZPEE6E;1a1?=Me#AaT)3xy)2}4Bf2R!v@FMo>a%(flRT=O!13j zPO*+?ZP3u#Fi&*YOf;MRxvK;7t~1z@1AK9iJ{Ik|ugm!5mU|u#@q)3jK>2!3Uh7hn z_0dPlY-mc;@rJsxOuql-2~)vnT3@7b3^R7&7nOZmSaxtojxutnU~qijm^i+40swUN z&R^=WCzqib`X@7qq0_paTsI43CKpV}OwK-tnS7VcIAo(lTAIS zjP|Oh>-G7lty>Q3Y%E+fhAcOb1a8JO&zl>8y_=>VEi*YA27~pEm!SdpLm{nU_|gV* ze+8vkV>{gil0}4ZmCaXiy;o^{QquH(oz9*RK9X}vSK#Cq$mn#wcMwK{^#qWCR9s5# z<# zEy!|o+5$-xrU$c5j;7*h3ngo9cAVq>Bpvp>i~l4;(ky#lykd+H>c z`DpwsF^_G;gAumAS~)r#xlp*qmLOaU_)RI66-u$Jq4C@~#^o~yf!|RZW?LqOU-#s5 z%hZlF4Zjvjvxrd=@#nj>6mK+#!j!>x5hfcZ5do33C5EIM4U$YAdY`Lu^y8rjYk99K zv$HzE(NW38x;x?Mk;v-2JBWd3*Q7x7cnXO2*y!9pDRk~}2D^WX2b+e^UG`uVyb!xX zcp8W3<(d+JU^=72@^%q!89UVGLMbuipa&l<-JL#X?B`R8%N{6m4gq(=Nt%VyY-yBw z6G7>&IFxp~P-@Os4`wzvR#K!v}C?7?p5#YNs9xVXUcv6@)i5H7JHCQE3uoyfAE$r24j zNR?Ufui<5g7b%WUddo}@+7J<1e`!KvSG72uWw|?~vVNg5MQ*l#na;%)# zOg|g0-5(K4nY~7H7bW%!cgd+i4}r1d-m%UvPZ_gD_F*# z#<9O&eVOwM$+bG!1;x89IRccTr=cY0+To}NDfE=(_i9u4*^sRsAX0V&mP}q_F|4!8w6rIJ z#CEJY{D+-HRCI4Q-ut_{mycXIQ*O_~_&O=ZF+hSyAmiD4T1VG%~H89q8}mOz&9$qaCD6CTf$^tDR$u}bx= zO7*Eq^`+XvMvd*f62( z@GS(C1#~fhZkaQaS*EudJAXI5!8?&h-Dj4`s->Fe-ST6L{iBDw?v}250+JK-u*BJR zCv8+zrU?G-z;N{16w|h(OxP8K-@sGA z9<=jl&gXB;J%R?uB5jN!m&U;=_oc}e1_4)OwCZ{G1XP&Pp=}w)ZZ!-a^s5l$Qmh}j z3$s161q<~zLZflF;luV|p%!EP2K{C`KHQB*n7i>_zOu;_!BkV8?R#3lJ%Pkl>6u<` z#b%?B%firkCPt++L|2?%?mS9Rv#C5~81jdb^b0<{~P zpPa6(Eie^R{RU?=P?R|5F^j?nvNh)?R}nwcX8K?i(iP)=;4M3#rD|l>5&z zvHf9a0b6EdALQLr?SuE(``}KJ?GpQ-+?OVaeGsfjm&N*bIhF%2}sYH1a_7nft?d0ft^Vbc>QEZAmuYc0`|6{$yTNl?v7;qB!o)+ z@R0_6Yd7p(e+=}TcL~4~Vu{YZmOb-t5nUgo+}0L5}x0crkYK=XxkiLDTrT9O6_TIO$v zfzjY#mx}|yzh%+-?O9x&hkDjHuxcvIU7oQtm=(E|zDEW23X*-cU~iSWr1C%@_4Vu# zOZh&(K^N+yj}~ym1-u6%&Wp7NU6p!Smmdx&5C15Jk`{ETBfW~{izQca$yigDic*42 z-!vI2DwQe00ya{SKwYNs$kwQt^7+~sLjUWTykt8W7~;QGU|{qHVf1t=U4fgU<%ITv z6Kp6*Awp@;lq*(vM4SS;0tu_;r-fbZ=az+arx#Wfy>O6NXD-Zpsuh?$ikz^LC#Hf;N}Iiutp@m%0l3N-;|&NP~GHXCIqL0 zj$v)jK)ejdcn56kviZ!eTRQ>l4j^_1G`S(msV=07G7lw7zpCaHB4KsB{vVVTz5T1 zmg~PA2?W1>$ShaedJG%6+>(vl=9SDMWd(@vx-$(Eq)ZfB>=E1k-f|>x@aRB^ zMMl%o6_I73wDLl67s#I+AJs&i#J*sa`7s#G&?lDVY;U%f@@rhiJjmkpPF`4Ejr;OW z=jGqVIP<5+7M-+$Gmxs_OnAKl1HL3mC^(jomrfgniP1(ONgHpS3~i)*W^4hQ_mO*_ zh%5{gN?@SiVBp*|PHYx8)eNQuNmae(k|`qGnK3-pR+0qrg&@9ZhEpb>cUc1Z2?^Zk zVWslegU8|9jsy;e`yv@Xp-*^g!SGg@tF0uls3J3&RFXf&Go;Sxo|JC2t-`Y~HOzdL`0JIGUgF-HDL5MliT7_1Dy8FMq%>?UL_=u3x&7bQ1H^Vw>W6;+y7&Vx<`71vu<1KGhv4<-3#Nwf_k+J<9j8~!Kq&38-@-?V90lbTHD{w|wu>MDf8 z=&8uK%10#0>tc4o3EId@qSA;h zyWQiFoktpl?>rJ)-?k1Lg~Pb@4aaQZwmQK}Ms0c5!In3i25}0^PIA2os}maL!$%tC z?>Z98W_f#T*<4Cg-N%qK`uo| zpYELgje(-iO-nZ_62(l|s3@N4euI89zVnPeMU0AzkA%{7;7@5r#XiTV*f)+*vCkM4 z`_eE$+tQSuwYl`Mq}t%ny2=zKwKRbSx=v^&Sc0kyvW#=@|m#@;4%kiMYieTsd5&#;?d6v^`DK;j^~V99(F6m`sT7;H})F~7u-I|_TxkpSlDk5LwJfS-Ntm5XgQ;q_&< zm`C?M;(ON1#!m%Qk;}?Ft!7Gk-oC)WNFQ!I_C87Ykm!AiV!}7$_~>_4p1vbZ=u4*v zH|atGbpQFvG-jg`#^t=-hweY5hKbLt zB;jm=m__TY&Vs_-dG}#iw;%kZlr=04+AFG!31p3U(E>}*(rR(1` z@GNcG;y!`4OH|+Y+fvIFt@1G(jl|E-0@=Mi_F}yG(Jn3Q--k5+GMyat`2anbdOlFELrKe~U zwI)sVc^LBf&XC6l%QB5_){Vkf5Xr0NOIoZwlZ_itRV_+wfT8b;TmqVil9)?Y^Eia zp0LIG9&YQoYY9@tQp8Yjh@qGyh7V4rz)LB+nt{7iSYhGjY}WuTsXB`+IbX0|Te~^u zV@sgZA+`jxQ8wnD?hw<)Ay>F61{{oZtr#Ioz@q5DBE`n|p(%g`{e+3UYopuQ-I-)| znuYJWJNd=5EH?Jdhcina&b;eu{>#E2{@{SS7xeI6u+nc9Gwo-8b%2E?Y|#;fb&nmO zM8e?}BM0b&C%km4Tood|B38`#Y3TP3h_MdqGg0``%82MLeE1pas%&XCy!7-4j4Ii1 z-x(3TyM=|PtwDJ7yePcmq9~m6$|!u~k|_M(>!R>WZ;Zl4Z;Py5EDHmE$Gf8N7gt2g zVN8aiPW<=TS!chYENAIgS*ZH$sE_~5PCA3pr&p}F_}F$z0=l``yu-yA=31a!SqhNJLVzhbIz zyzz;iw_+3yamIh_iO&37mEsp)EcgA2%8KUs&9CrY=6PQ#&-mpPvzq6>{ShU{E`20; z`=hGwrH=;c*_X>RnevKbn&;11{!sa2%`fuc&*H02Zh;l$d0#0nQ$4knH_xxW{EwL> zf6SclaK`hedEOs=FLmk~yasl$b}4Jzb4%kX$msnnU(;~*>u9;+56vvA*7G(2Q2MD) z+l&(M7h1R1@$H36+jvK5;f(|Y`*yYwb#RYm&Z*`?-nsI%7IST9c@{R&RoH18Iv|}- zxiZ_S+*IP=F!{oC{Q_)fad=2sw^r9ha~eR5G!@DUdLvxfC)J|;Y% zJGHov&2WD3%VN!&iMHqiRJRSr|Dz~kX3!$FC>eUMR8Qvq_9!*B^qqi)W-qZiSeuFc zV2lNq6qr&z)*!2%*%b~hOR#AT!V#?o;KP~17vNVNq+Ju(?pwdKh520@N$A+BEx=4o z`D$v|63iW`FyLK$)z1PU7=UvAGQ}97eq}qW03Es_Ez=ay519lW#CH@e@k>Q!3LR4P zvIL>EBF>m&M@_juvc3oN#lnrm$j|YCzJFyga@;Fh1Gq`n7Nj!u)@ zH0f4cBBbQVOllg=vsreF?7}SsFS!n^>(IKcwXPwHm12{kHK{u@ROj+^zSZ*i*7ZxQ z>z6mw$(kkYi)4EIqJ1B4)*|IYsZm8uoB5*!gwREsgR;}Z3L%*z$yqfwxMg1y-nl&x zVtF$_YH10CD@dz#8Cr^j)QdLxWk-6~`wHMKad9`|g5EfJt(HyU`Md5p zqBeyyzYaKRaB;NC#*y#hXtlx79|=c)v~hF?>Qi5~aO4w?mLCBejgNtgzU%u(dKIh$ z2;b>Kc#RF=X)c7<8VEl^5Prml@H&*k*H;?|-x7G48J_oA-@9z;Me%eXE2TB2v%1Tn zHO{=+NOZ&82gaOPUaKmFJDQkNGlNmOCw;p!xaeAS%XBA(e|Q|rM%+ayc-zHi ze5IevSd8{ldOMbem+6+yP(hiabi6Blb`I?dmT5gt&GUs|x{g>k=%%FJMNaD7K?8^6 z&8=E~t`qqka27s|Bgf`Of5#$2mLlia9|~xH$V+(6My{n-K&jLdPJ4u;VZuCf25aOO zMbVkJ0PkDx^1cl=@2hZm-(80HH52b^ws~J8?k@W*Yu#`LdEceJ_v<6T`$VDs9;$0U zCtb_4G1*O<$-blDn+d5N98K3-yL zbveX1-0!*s_iIZm`hXc8*&Q~B6B;PH)-2hoqGtI@?afYB2{|oCARaqImRPtt~)&*wA9zFnLXduZyA*FmH*$yd=*}gL(4!6(cgwsLf%m6!?++E; zZIy?AZIR8-!p6rvw)qv<=2r~JP(-LZ$C!55EI^(bXoeAc*<}ryNPTUFaqWHdMYi`9 z*xsit5ke@Tb&MGrGtW5<%*%1aGig^B>j=6X+jbW|Oiq=b__^M2+WBL1)}!dtfeUA* zuA72n`LZgHtki+kXhrT4pKpiO5yA@e9E(x6uqSmH!JDaMkNCITZ( zK)Jdo(npvqRM_?^9G&7m*6rDHd*^x$Zna&SBNHQeEmb_}7e4Kl(o1W+xdFkss`*WF z_%h#;%;uAkpE}(uXDJsnAR8kpR=15r!^iohSzU2X-OJ5x=yqMFiEj#EwfRB)Ua;abP1wIaEx>%66{ zoQRZBLLuuITR&}0Dr6PoD`f4#lES1EGJj+4iljoO_7QWm^g&X`WKx$1xwAUfY1A=A zv1+gB;&Mh^fmX-h7N}#;sZyyx9W&jLHb6dNF;%OZ)Uggx$GU=~I@V?0j;Ukqw&P2? zR>!&|R(^T1&uTR*p8pcUox}&alp@)hOp|uwNC;1> zI2lE4mr8Lpa3uKrX=0fz(gu}0dJkhdS1p-j&5at`<8#sUb%KI)HzFhTMHHVqiq>uFPGSW9i5kF&Ij>iB-4x7im97;Ufpj9je z8p86DhC0EDlFYQ~)rymy6-8&R12*@7%jSMyv$EO>M zl49)mXvZ{bmj(JcTYzG8mO#XN2uZNR;I3dzmR{)q@d6GNs*?b&u~eX{29|z0WGNEp zev>qbl3rvTzs$V`OCTbTZl|K`YDp|3jf0%98kW+bkIfNRI)I3Uxr}$VmF5kajHY4z zsS{=I|H_I|i%L8Us54jvdI_pPtsK@xciZLYsWTw!4>fPheR6*k{`~-P0DxMjcph35 zlsnC`xD?Ri2|r0@#x8yO6kcEM=@ynDk{g4$z{OmH zLc$QrUPbaya!dW-Yen@p`eeCSsBQyxdqtZ=wQf`HLq4TWUr2qd^f|z_1B#e%YG&vV z631eTe(YVM$|S=0+6n=an>mT#l6I@s2RL|Hz>r5VR$yeD6dh6 zosx)xkWCK44vY}~QpUk+AB1XvH)L->L2v!y4cTcz9tq15A4dU`UG8HO^Gp+=kIl&> zh^XJCHmqrUPww{$3yC>x%6(6g7nK-eVh}RMt|ZNs;OS!+p}wA=tDaIky@%+ks=^D; zWDyhamz+~m?#?8^)ufq{d*X6>-z02LX$&(t_!>QU#V}vZ1%?1`OC>^`mpt%@dO~jx z5~4*TIv+v@rO3i-LnGGXR2m`t_K3B*SNKYg)mtk!r@P1ssbnEVLsRp{+@m|Aa9~d~ zQ4Y!mDEcJL+v2EwTqTz{>V_y{B%=cSXIgBC^OstpO40UUr_JFjo(@{5% zdzA7lLX&%Pzwo#ad6q^ph(wnqUO6Z<*KHD;P|uRuqw9Fs7TINq|EwkL! zGJk5c%*~`_Znm||pP@1B_f=cK zaK3NdKCYDa%7;1iKDY^3{s1trV@n)uw+d|7q}8HLT7?Okv>fHMTrrZzf0Eu70Pmg|AKF3p7#iv%4!Rxf@m%+eM|(){31*Rs=@d~;&>=gs z8|uOr2wJNQ1!o**s55ofx;t}hq%Be8yLF;U=*>UQyYeJ&@f`2z zr+O`Oy;;xl-h77lt~0$q%=1n=%X|NGy#vql_MGkgd7<}_bG>U8d1uslgLU4|U*P@c z3%#FR;C&-zW!qGyq9=SzsRe8nK$n>-kBGBC%o2s;U(UTOTByF;JxFG-o2N3 zU%lKr|Lxvw@ACe(%)8@b-s2zle*FpW1=n~te#U$2XT6WE^zOOM`%8njewDX+wfBQN zysxhJp5N&Gy3zaV-QFiQdKLG0U%kgWt;xIgJKnteyf@zO9r&TQixOh zJJ8{MyVEN~Qn)DnF|BFRk&f&-uTb<^RvIzJHwmx8wX9kN01Gg1_Ztf59pK zqj~?X(|vEA|H`xc3l{lP#^&eVIm6}ib4RvgRUx99WRzQdgC9)Jr7inkaforCQ@toUQw!@H#wZSm`5OF!<(@?r~S)3zZ%K!cSgaDk*dT-=mfoI$s${HvOBC-`)ER#WcfUmeh;`rInM zd=`B<_fn7Wt)ys|$_3xyf01I;%b#v;5W6*~%WL>_fsLT~W6=cqqnD6*ac)$(UYn^E zdUlr6{dvn32qayjxbu{Yja zvNV^J@!!hbey}NbG1J(4n{wBoilVM>X>gC3@@4AJ#@zG3tg0R_ZOr|!jNhWctbCCd zA4YqiV-V9O%E?;(r;P2bI1|iB6&}ljdw)~zwG7<%HRks4vbc)A_>31ZsoS)d>&b{4 zlvjh# z#kBb0#@y?88q4Ur?||=S4w^h#v(($insW90xsNvH4lqiurf=-MV4{>V_T1jXANzP? z?mhwFVGA1L;BWF<&_B-blUt9t<*Mhm zztH3H3q-AC?DqC6_c{VgJ`vj$oxcMCpK&IA#uirIypy+@yIK%RC)$GDX3PzA z-R{U))v_5)6tx6q7qw%Y{v}ac%Ih9PEI{qFXZhjXmE$iz0rc1_@s*V}2b7&XqjvTl zzAoe2HWTa#@^5d<9d8+ax=kw0t{_}!9Ys0g>kbx(uR`9H$MMwyNVGVwxIVB@Is*>B zIgk@VjGcu9FU`6DJ0So;b&i$BQ-Na+gM)<4E*ltyfcQ)orjlDw;n{EGk<1}uemIZB z5}d6qr*!gGNn)_Tw<}1hJ34PO!yDn#jpp4h^xvqV9=i~eEF9Bd1;$;$0{d${JaB?W z{_;jVWQEIljk#xQ`D;b)#oD->oH*qPuH(~LE!U~e-g}xaNu)RC-gcTJo)wuUdUzgq z7PyE&>>GK$JJ^&vJ&MBLQM}f8WIJbkvb`x7cW*Ef(Y0epM6V1I5mW{t zAivs9Qx|bifTfS7D3+3psg#0w`>3!*5LZp_%bnb(51NgIPE%LDiNLW1-K`&XVo0yC2E9?S-D+Q(O}cnMw3DG^hsjdu!*4#t zw>g!FaKg7mwhObeg_~k>3NUEA=n~@vGra|A)*munbUVh2u7fjPq?kq=xhIHd5s@X& z!juEQh;3=8r#0pJJdck(a}B;Y?xXBKEazGtMvg`7P|^}ClbE!86=$KQ%2Q_DUUDpH zYmAs@%&{Z=^aKrfV12$ZI z)pv18LAJ;qbpGlJ#MKX+lHzPA77`@~9J~!kdeu&w2PThDNS<~WV_ZLk+1ovF?Tdy9#C}hE2fk1IH&N_VWE}IG+L2moyLK~_n#of1wM)NoN6{S ziQEO8$`%mMeo(MSn2fhtOO;(4B?DT&r&v!qrEp8E5&iCxt=;6UKF(nlA_FKs_S3y3 z622tC7SE+Sju&3iR!}MclefHgg5i_5&(o?{8)*qd@(7O>re@tU$4jluLt6rkhzBaX z6y&;EQ3;;VDJwU*3kkDsGaHUbA|hC&Z6o1lx~0%;?gtsk4eZ+|OJED`6lc&D^7ek1 zonWm(Tb+R;B~284@nf}d+y|5B8!OuYg^cL%c$Rqt5uWdb#~|&~(pwcfJL`z=KhYKo zSFL=I(;DawjI!S0@Xb56CUB^`XbEC!vLIB1vlN6*k+5(>XCyf!_8#NM#IKl)!UvN{ zSailz_;Dyxl!qymdLHaZQ(qW>3)AHnR~1{*;-tq`>*CEB8ye(d;>sml0&Jlcr??zg zy5~oy-eyJQ+GxtzD3&qLPboyj6vz_JTYcL{44AJgY)N!dH}1z27lpPxauV*a>t+>P zVx8z}?Tl^Guv)W|S*^*_RX5swm5_;EToN;U>CwKZj4ViM%R8Cqom%zCur}suIq8d- z#o*;Ra<9$Y`_X|&=*}o{Z3xHKKngK29NPm;TQp*Ecr-X`jnARgRvj6g*Uj=%)|<$- zerz?R>;r|8>#>E`V=7}w-6`T~)ZO;-r#<;Ce~ZTDX_MNQCcXN@&3_%ksxld6;pkbS zEQqL*szDm1L5%uwrJD@xE?8Lr64&zBqmk=2;W%oEm z1dW_7V~N}fgGo8Po8|N#lG7Wze5T9e$*!0jWT_Umvfc$dT7 zApTDc;7w)r9>N}D$?@03VE(yn`U|mO%LGunU7(hL;UC9`VU30%M4aA+8NCPuW?Wy{ zHqn`{aB}DMs)g&w7VM2TJ|v#pdblCcTM81r1c|yRN*ND_zAcao7fyUgrQ_qe&7q(^ z6XNK!=Yu_sBNFF$#(yC}ri%G}H6vtdK6Q=FINOXkDJCt)*KNV26SO`i_x%*j8@B!# zrMLbK=|1*VS~j<|O;P{b0*Vd<$hbl90wsKZ_YI&3eI+0)q3hlbw6 zL+?crd$w#|i*{33x*WGhFQRZVizu1&h!+v*$Zv@>g#S?K>p?2mA1fM#y4GyR*G@5(k^QO@yc9_@Rj*Y` zqkmr{W*9k=jEpm{6oXr!?-WpMbopt_<(Ck!BA0Vkof`MsHD(Q~t4-nt(y1K^SIbQy zS9>=hgwlVUTj7UiMUz(#gtr(=j_81iHM3IO!B}lrAcwfL{b(mKeVn@@cRnX1u{cO$ z?mfqe9-kzIHrdEK*&IbUTQmW#n5y(0Zu8+wo1Fuv$(#WGsz&b*^BizruTTC`tvzn_ z8m{wR5{K`7M|qx2*c!4k_n&W#*#ynApXQjZZZW#m6N}smZe3pToo!rdhB#e z(SI9zl4gQwM@S_m*yBv_FdbFbSd+Uulbo1ce-Ilr&t!LExA@w4`*m`ylrqK3T4^&| za_X9R&L1P&olV3^%l4;u>n8%uTP?3F5hIo8zT`kuZE6Lg?2(QRA2<6pjjL%< zT>aIw0HF}a#1EQtuPuxFJ=iC@W1Zhlon5HQu?uxg#xB%j6u9p3?LskuAXRa933n29 zp(NpkHsO9}A1NkLW9}Vg zowiz%GoWFm-ylx9?z~Xj3*R~M!__o<|Lm!-_p=Oe0R7c_0&TNcX^i|I55)M&#?r(> z>M=Hh6-EX9_-KmZRVNK;V^3Qmck&qimpjCy-61v^cZkq(hj^>+g<FX0M=%D|E|cAcW61(?u;x!trPkE zgapvTBA+2QdI{O+x?(J-&1OO30OI;|>cx8dwh8m1w4jg^r!FWY_aRQjXxkh`0IhW- zYeLAUw@2Yu_G+8JEoEYLd5`g)C>a=J=dB^8!={l(*e6l}sW7%_1jGg;2z>^stu`g| z#JD12;ovBvRC*W&gdb_*4L3N}knmd*Hy)%}b55EiN#wu1> zv5L99`;DDx%1uipw%Ru1$UpfdFaguj6F*7wHz=jNU+)e*T}hv)+5dw-F&>cx%Am-e zC37PPhn`UdnUsU1v1{&w+S4!PWW)Ij?6{Qkd}CdjSHD3s#-D*fFT5o~UCeLH zJ;UdyzegL`B?l{jUcNYAi4HmNi6Qi=cqGu_EdClfXL8~a&VHRNy}3UF`53=-7Juu_ zO;EmwzV#50p)PNuzLmjm9V=t`S$r%N_#A|gdTq8)V=mXqkx*3=OGWO~?n!>|lQJwQM{Gv>j{_-sn#7id z>$Ltw;ix3HYq8R`zhF4_3e{ge)S!v>^Y_KIY?0Zz$P!@ozxc@&?QAd6POcWev$jn4 zbe#n>7UOOrItrKUJETM4Q>Jhyl=28Rwlt|P_w9;qovCaq-4*zoo<5FWP%?{-lbtVJ zJB+8(f$5)maHhJnb5Fzi5ihSFutwc#6c{U}2utoX=1%aWER_M#$B9>_BrD+Q+Ia~k zKHaAuv}IDXmd;pBNq7IT4ueuf$qyaN4&dUN%u&cx*%EpGC#)oxxuTpc#Lh}e8+%F0PVS}L~Tqak|?AaV%=^vI)p3yILAax2E zYx+vCadjazkWP;K&@NqySIlPL8vEUktKNUhrUl9NBw0rC9GX4GBLcsaaw9s-2b!7{*o=yioyHSv|o64Y7|u z9S3@pK9OYkG`xqBW+kob;VOObf-=y&ccP#)Z`m^P%<8hNeJnur@yAbIn^LS|Jg-4F z#dj%dDgXTN;;Rn$Oa6p~w5&xsVAs*gCT5hDXEjV#v6%X?F3%*#I>?6tL;QD`sbEB_ z6pEcFIvs`kqeM@mh|*DqPS+{&u!JEJxl6xv91`IH%il5ou#xL#uaQ^tv)97ql2IhK zjk$;1&-AdCH_T)^1SeYIyV2{^r#Z>l>u4Y`pfaWLzxF22EBO&mp7A<}nsR^T^ixV` zp&e4{aP^t&AZiTfmU-dZ)g0d988u=&NQE4=o!>D2xHsiGl^m>w2>~W&WT*hiu*htH zu2}g`t`N{8O$Jg*gpLvf)cdgtpf=W&QWq>wlQf zicelE^a!)y-Dm;K9t)=S;Kq#wQ$g5(f~mteQ!JP|>}Vf|a@;#odCS-vO4q)q#M*f2 zK1|y+B<2HW#|*dZjdTVa&@%JKusI%yJY1|}OR;b*g)?B`+8!=kOUIH}xVBq!?rJhU zSk+`%B9UipLQg&FS(}z>zfsL0mz_KhCA-e9PLT^HAnmM3mv(CSHcbAPhg-_IB_ZY9 zbf{8JmpQ{Qe!DAuPL>oO$_v|*;l1QQ{x50hh)Y9j$}w=Tv@}H04VBWd8C{|bQ9@$q zJ!FZY&*pB)`XytDH-~qyX`)zedjVhhhTHFl~5<+~L65LYWjB(*&N< zFVQx>v$Vj&ufpa4jDMI#ke|Ds8DvQ!3uN+MmUFU}aFV47@%%=hYD_7ir^FXu<$3WB zQ3nG7$GU;6^N(~{M@nTA9ls|<(ism#$EHgjw*>h%CB9Sxj~rCiy1IO?pzA zj1bV^{n`}4XH?5KBeEY2Q^_P46NdDId3X>-u8qE@JG+a}k%h0`C6j=L8{h%H5EFH< z{UA=1T7dZ2l)J2?sFStV)IfYq4UMy=4o%W=H61PJ45kP=!z}0w7(r)%E5m28U=+wX zBQZH=I3SuWCFke_%(iLYk#ojd8yAw$Gng*)4DFRz7K0>vYlsO!A2Xh|AOw*y#u0)j zP!xrrwuEt8XiUpIAP;rB&wAvU5}NFhuw1nI@uXbjIv1wJ#&c?fx^zRKaX1+7kTiTw znVsGz<`zm;=H!xZnrNZb_(myzPDUf=>gXajL1)I7d8Bv;*{u*;g zBv=FH4@U;b8nzs+PB4(0(BpX3lJ5WPEVhez< z5tKFMu4T`9+&-FeZ?t|w@SpvZRF!8zUb}%H|C**;MHN3CKsD}E>*p&PbKh5DU3rxk z8^*I0inf;C4+<4DL(Q_27;!a|ZXEGG}nJRadoT=1agq z_$zNRUwCl3)B9Pb_wV>l??vC~{fE9amwY*B}-$1<7&IQ^SU zVOa&&f2-1PJ;Bp(tz>Egxc-Ow?%ir`SF7dO6lf@1pOvZSve23U@)32H2=?P3%}#Ls zAJ2|W6Yc8r+xADvMSpF|qJPYH7X5nPS@b7o6N`RjwORBTchP5@WqGO^hLW5FRq+ph z$b9r==7SB@&d1I$AG_akd4T7#zx`2opQnl8b1pG-D`d>vr-|WwLkz3S9b))Vm4Fr$ zjVuCIp!fU;y-ve#az`!=l+HtAqx?_&-q^ zxYiK<7u>n-Qq1h>d-YsjZ034j;LP>WN@uP>_A*tM=ogo#5&cp7%!EIqzIp%NC|q9d zg+KK5gx6K-2_L;rPxz~qn&|gWe8RU^mc|UsrVL6~AfcVQU-SMuUC{hh;iGDg2F=?H zXutv70?qd-Qnt+BlnJv>h1t3?8)ox^G?<-NVHofwYA~PP6NRr1yzq1O9>SjXXEa(? z<{$5j_-ajs2Jh=CGh4k=fuH#afR9QDw=W+Re_(`qk)ONDH!=^!KF)k z*d72GKOg{j@m0+eX}cnKVHLZ`8zXjwy8WDqY%uob`R#Jg{`r-1-R))EZH7?!?Ue2L zw8q@~c10z2HC-B`%}U3jJ~2B*1h`lX59?*6A6{K%{ZrPec{H!~4Z`eMe&qF@$eX$DMrEX^!d zg0u}azWvfhP6_JXzC?-qaZ9=FM+a$}eVZfl41gz^CWB@tNhM~TZOk}r`dT>$<{%4bF{nbgezG70X7aSy_noYWB@k9noTbmqhp~>x@+<XN+p%w~X1V?K2=Z6*fi6685K4aa^U5#?77D%B3 zW7CHLZX@1mH z#^=e8-@@BxkH;O)3d;Ci2^;qzedeU&O<`L4%xidNYQDP;==sjJ<~!Td>F&c*P@bXd zPg)C;`qF@0AmIV?8zq^x+FCc3bG#Fxj!pkc0RI&0Ss!oAeN?^`PYF2oJPR~WQ{m?# zEl)aio6$FCcDX#G@7#*1S~dhJ5zV!+o&ON)T9*w%IvbseA&TjZA$)Jq=0;LuR?hVp5cJZM#FNW949pijtSa8}9J86iW zwt*P$&gWEwl6G(tXE%Xa==HId2D#FwK}))=K0{Ca%%LBJjx868d^Mu|Sr&wQP3c4f z>1MEl!1rGlICE{aTlOPPOf}sBCThw3)y>`a@Zfr5Uax&-_Wc1z^h=7g9v+pDVm~h9 z;S4>?SJ${^Ol|}18^C7SkB4+R+giNbv0CrL1cF|+@!%NjQj2%FeE?lRqQ8olq3uq! zA}5)6VX0f0+<>yctup5_hG`aC!OjPh6B(pQ+Zp!?fz-_I4m83Mwu9L1t2oda=FuQk z1gr`q(vT=aaLg3qf(DRrmr0-6VD4H~Em!1vJuP34!LEAEL`4+u7IxStASG9PNB&^e zbfG8F!G=@HGR+e0h!@oW6Xqb(91+Vmi08|Cj(5~U9rZGC7Px~>?m&^q>)}+c6Vdn_ zPa;kXaI-pap@%8ediSN)3;kv*0BFJ7`zFv)pGoN1&&{*cY%tK^FUT6FI$(P5V8*3<5{ev&t?$RdnfhI9-`3GOB-|V;>&k8qEC&2 zSHJmcnik-i_Hj)8LXHiSOZ6zyc@w<75Ca{ymNy|Wt!PiO7rUi1G66u z#NaA*40O>CI_!$vJtEjs5QZ->hS&~zqePz>=u=KT6lk-?b({fvj>q2cc7|XAREcGW z&

JnV|avfsr^PD<<~}?6My}I1pG4?fl6aeh#1fDyiNBfdRPsBb~9M^8k|!@!k1! zi1|J`L$bJPKf*X`oXDMF_tTEZ0G`iC;vRk)(QIHBWREu31^WZ@eRbzi;3+VlL1(j@ zP1rHS!ks>V{w7%%6djnP4J(VI(th&s=sJt!Aa(<7)w9Xj|46Z2ILxu_jMG5GtvP@K3?*XQIZhe(5Od8e+Q^X2w#yX z4F#c9nu4ohfet*#xwqdM4}r+a%Nz8|bZ$ilSKxjQZ}Guqf3Qp>1d|Y?xHz=z|8q6Jl~@dbNw*OSB`lD$J*wIW=l+_-Z{jmd9#O@O4?phJW?XRpb3at1-1K|wE$Z8*NmB)W6NX{dia?;~Od z8Os1|?%ZVMtt_y~E1rq3kZcoPHBY{E=zYzZH)%4xVeereQz6ftdnwRY+viVHH(%}p zL)e&RbdQ(+`mwfdI;lXD#&q+_pG+D+r&Z~aU`^wvH?7g>P0Q5;+!&RW+Z>N7eKGGN z8^%X!>bP|`V~5EI{gQfvy8xw#Gy{*ctvG zXgF7GyS(rB$iBZ9o<512|7s8d;G%iSUiDsSi2(O+-27)YS7{cPf=U{#g|Iu2T%JrBVE;2oSEkIU zaSQPhO2+)!N>)o}TJdT$i&uF`;%8f_b1HSNOFt!sLthyY^RTG(GcSVp=-*fxr=@+T zpE<;sX5u8FH-3^3tH=olj?XWB;P{=yfn%u>4948khUc!Qm!Y;l_xqWR;AL;ks9DYl z*_OnQPGw5~g_*1E(n-lCOpl}pm_}Wq>_{P{4WAg#>WadJc?l0)t8v~W2iR)F-$WFD zJ7CWYTZ9tB4D5YWr_xkQW6Nn0@pQ1!AY`I5c0f0J1Q|+~x&oXL(g}sa_hev6h%nb( zs5@7b$+qQrpY>yI{=yYIUh-U>;A~Td!3J} z?+(S_=Urc7x07VXos@$hb+@zH9KO}EU^1&EDQD1XL2=BsEm^#-02L9m5jZqdL;kxd zc`qLz8H#22g((G3$;r{4AI|0M(sTK0VlFAY>Y6zV$>!LLL+6M`I5HNK>j2-X0xK+( z&gc)w7STr~Ws_w0L=ti_$?ppuwY0RAO|F0#CdZU&xXTej_XO7N)D`GbL1XPsaXFRZiyAbj z{k4J(&fvWYwxl#cjVExoa0`TOR5-Y>Z`viYMfmBGM=h=u?vjkz)oH4VSIBckj~1f_O{5^b7m z1Z41I{nUKOY1P@VB{6jH{aKKVq6{he1*W6oFOIDq!1 zHAq#9uewuMI{|-{wi%VJ)t5mm>($(b4DRwTAxjURM1$do4jQ1>Izc7!rhG=b@Z%=I zDHVHy@NVd1PRST0I%_d0be_}G+(=a0*?FS*sSJ4ALCi$xUD-dZapBp11^5< zTeS^!`Z^?z0U3o(@yxMlpE)PiM?m3;<~*g1pSSTrU)DXi`yP8K4F(AbVIiHK45C3` z6&+B(p~tYopw5p3+2IG|$DnF9Dmwn4dV@2$d|Px-To-y4)Zsp$-N zL__9j-lJzjft@pxrVW$oHy>XmM{o3FkQr_hnc9*`oKmaD^(9GkQE3wW`UE5rBGI@w z0*m)x2Xx*T!7hmbrF^EufOG;-<8R1>h*HxC6POYnnZV$u&MJltmW*H@bwa)N_$^5c zC`s)uYrQmo)sk>=dC6cBE_J2h^34guC4InIp`%Zf%sSue3@@7)-3ul|MoE#LM>n7; z`jZnJ$v_fN=T8PuC5O^KfuXpx+@BoE3nw#_lKkJZQX^WVluVJx!|Hd2)temF1*L~| zTVhx#pJ8#kA9fndOToc(ocFDtyr`+H=e? z`5*=#;Y!TXt|3YLWVQD5Y8@5PWJI=>QSz`J<>g&VOC8e!k0{9lyg#j^$Z%6#)L41H z{bg!MC-dmQm@*hpZoa16^S?-&Be}U`LE(4_r&I8pJf{7a@AO4EFGW_;4$Zpu!HV3& zj>q&(DJ|h*-j$9fb2Fzt1?z_s;5F|HWan5Wjuo{H&ezaErVnjGmqZbH>rRNW+LDxY z;baKDR8dmxf8R+AdZGAnk+njk%ay*BOIK-1-xo(m%P~{YjZ;Wn&I&SXZ?q_l2DIHxh;^b6s1rx1=B${g_; zvhPVC_N#+5Wv3J&Fx~ms@>dqyt+PfI(yKy5!u;E z6>CRCAH(MFX)svckPabJ^Lc_0(muxuIDe=qJ(T~Nj@g1+Cq5LZh}2nfC=#hr>YLTm z$3qYuJlnNmzO^SxRd-7;G23caf>-^(Ik;`gEPZk;iKQ|38g}~umA3T3B7+~+`8&`b zEe?Wyv|m-!;2N<`!&a5kaZ~($m$Gc~1jj0k_iWsT_+H)P=^*lzRs6T51z`vz1)zNs4ln?tYqTEy9Evk|XEJ z^(JF3M)y;%D{>oCcVYc5rPcY;fhhb8?V3+$i^eAO7(MGa9L<-_XyzkNc``WbI6kR) zGUZ^hZ#$tjerKU81rBW2`ZmnsBa+i&-hWFzi9vD*C67aWoVQr}C<-gDl~h%{75q1u zjl)-zd*NO$eZ+)A@|t@hxSAB(CxfdKvuRJvraf&o-S%wyXyAj|&>(k*(s+(d8*l-j zp!~@IiW3ABeI!dr-d+Wg(F0NPW8wih9Q)dXEOZasH-E)9M+gU>MVHs)2tnA)f{dAN zl^ahXRE;0su2oBO^DlDqU>Mq7dmhyNxl8xKJk*mV`-0V~MJefQd4=Yz(_FfdRS?vkwqpS>5!0g7ItxGgeP@=fJ$N-(x^`#jm?KWw;xiFUt#Y7+n zNP(<;kQ}u`tF~AXKVTEFe1QE@wYfo#s)7cc%MW@R5Hsh1#0vCLvOoMrFLlIS_oVdL zLp^f#Q0TE&dhE4(q|hundo}j#e(dp?-I3K}zdlI9-ZN{&mP%14x<|2{_ESgLWB8XY zT2*+Wl!~YCd z8*?vGqPTObDRDU)z0%}-R<;TQiNX=TM@t<=g(|GpOp%b$b57_a5!b{2EB5Rx*<)iu&Y1rJQpHErJLVvB}$){!_v z(5BfaQYRDBJxU6Mz_|H9kB%XQ-KH06XTmngn~q&~rbPC4Qjf`83*-;10VT-{Iwdzv zn>oIAdn`;cS3rR}a+Ki@{e($XT(Sd$G@bN7(iv(OT061Al2-0ImFBFQZh}O<0N+Gp zX)R-INbth>;ro}R=HaUWpI&Vs>@vAXE)y$qG;cXsfbBtqK zsAl4HLH>#lU+D?xHXhKRn{9#aBpIV>-FCo^oPbehqSzhxV}n~{{+4sU$@rtXJUK(% zkDKwQEj{DUc=Tpv{Q0*rGX8wtx3Hlz{%o2^#-F=WGXC6{lJRH#)MfmM12`GRz-fRk znu;_)sf4x!lj#C=#JPhRQh4Qi$E0Z|Qic&1E_|;Pc@T;s;=!apo9HMZ?mG3bCbPqn zgybc@iDBx@y3 zU1tvHn-IkEALcX-67DWx@Lg=<5p|!dgZ8- zu|5B;s;)lXyqS5r9OwVr&nJ%Fyw~q_cU5&&b#)!i0lAxm#3^g)OG46Zl8|((X{sGA zCbYMbkgyYPf7v7?oW-Qie4sQ5i38juAFP%nA?epiNcz==n?*sF=O!Vk^r2zBnRJqn zbXVbe4C`*2pLH7o;N&A+Hsq4jb(dPa-FDKEpbRmocM6MTlutU+?Iay}gV0GwUM@`1 zk*+dHN4iRrjwqu`MyGciL1phsE7{1u>;1^q^gDz(Ykt#w>D_^f((~SIBvN^H&xv7X zlFOtkFqet*&AH_=30}R3l+;y^j$9^Y*mKWiqBD;C37~E{l_;5c7Gud;n#71-XR?}< zM_1D15E%{3YGS1_370EG16tzi7ebltM}6?}XEh0e81^kW)tINDG){e!*RRS!qDq$X z@cao*Mrfoi=xC(zx9QQ*Uk2U*`Z62?^0G$fG)9s?yNP2Sb?UJw zc9RB%o2C)r68qI7{g3mZb+B@>u_j?dAwwN>$=#;rC6djTxC2-d$_X zfun8dXInpGZRso2PtrEuRcq%lIe>Nse{Pd*qd723OiUFut2{nQc}_OAr#F&s4CFR> zZiFsh-j;s5Q#Y(UBMsyyfjqPEpXlRnZ%hAKbqTdlzJ)vYo6rRKD9W93qsg7Z3E-eA zs;82ouYCB z&R0J?kL1j7hB90kZ(im}7B;m-gWN0UEEDC-pZ?q{yJwzz<<|?Edu0Joe%<9F-~r^9 zh*A`k6rxXMd5&Jffvqhg4Ox$TiOQgmx3YQ!Bq{@!RW}>T1GIwc$lLh!LIu7uo3K-> z3|LaaNv+a9i_|J5UKDp6($1^q=AG*1fXB&Kpj;N5j9su7%JW*Wr<}^vd7l&-tgpMG z&SY)bnqK0#0TPk}2x7%{6K_{ynIG|7j|(^8BN0fhj#kdO_wAUcl;2k0yo zDY0l?yoLEYrEA{etOt6yvA<+iD!o9z#YD=$=v#hn5b!YywgKUs`g(BxNOI@CV~;vU zFB$%+7~dJtIWF4aAjtE^gI6*o71ofa5NMUSmBJX6#a?IJ$XwvF5SCjfmVhZ)Tm1nG zXn;9HNy;KzO#@X|lLK{LP5G*;soz~qE@!%pI1?S}Lviak^ThsA=Uw(sIN@U|Lhcve}H*YoML|#%V&;}Lq zb~sbAN*Ggg_u?A8W#1Non%k~*6Ea~CkZ@p2sX$}uXVD6r|AD>f4<1!i70CJfDT9_u zv_REucXBEj+9O!B6P`*+=%e4z+jT0n!9WR>^jD>le$Q>y;LlJJib`CJ?lwH-W%J8Z zb_em4n@V{~uBQr5acRgoZ&n2L=M%-$cTW_xsIl0CqXp*cvHEgVKHI>^esaHW;jXDx*x`8>`xsTh!vK@1U@a=1;k|o~% z@KM8*z}IdULrUtXZ*a?y_IoUO(M6J=`R1r~e!ysu!&y;-?+ln^>goUo_`Y&GAd1=f_Z`-w}+`o2UPmM+QK3PA*g7OA;{;$ z6FCcl524XN7zj6>EUJvZobKQC=@~F*&UQ7_t=eha0oE3VpbgfmN+7t!hTupw0zEqI z2jAA1{yQk-W?eCCl z-v{OqrBsxz^=s*@*NmuxA!Y|ubd^A?V|pfQ{Wm4R`;D5o6CZrnw6zh=n>F2!8LzlF zb9?%1Rm$0&{eX6VvScQ!#eN;ZpP%k}lb^06BVDE4lTSNV0sSwZNy55vnb{mdzNy4) zcKr!Uwe5E_5r3oB5{Ou@b<#!#xmmk$gvn#=1^>uYu#F3V0T%#eCcZte-kAz?wSBO2 zWv8mQ;{#s#sbbk)oinzRRBCORboOKY9xuyV(!UIF3%==uHYskP#LTGJHeVMhYYomk zK|P`Xg*jV9KmQ!RHdCqfy62ENnZp{??Ap_>u!>amm$(;{noU%aLZ=*_?M+)INnt?l z##hcPU#)AEAD9ZX5OLU_wwJs#KIwmHzztP_ zT$=uuc9!(ye<@Y^($}lL6mVkn1MQf*Euvkh`U3Nh{=lS&pjedBO4MFyC2BzBt7ejb zG~lbgZQ0iYbK*+*){xWL{3w9N578Fwh>JN9Aw?;LMWLD9O;LK2%kNZpPMI0oHbmEfvGD0-%9=?-PKTzMP%jONA)M+EPL(q&4*92=GoLTzokN~UcB(_u`ZzR= z5))#=Koxv8m$ztlq zVkR_bZS5+P<7~8=nGXN%6U7ep8pwOB>g-LQC8u@!36<${m@|FqGsj^vYvam|?b4U% zzpCTBOEP_O(E3U)?xiw)s-$GTjF}^x>65(3Ri@8!dHIyfqMIBgAZy`kW|HZ%Tz0-p zM0I9oD7EgWOrO>rmFd&EgVd(3Q-QjHa#a*~q^iDG+DbVA8b8d`Gb|6<5l?$B+Oh59 z?kSW?PC~~OI<+iYxXZx=|C}0 z4V~WqbJ?VyRdY$cb|%>x%TQ~mz5{lYubWAJ&#`inu-~&BD96wP{uLY>e85tWtW1GU z-dTl-7L_VS1w!|Gl;Bf+QtKA%I|-V>=gRIS`<Lc>d!|2((l|S3?Vt*VG+JXa$et&qveNw#2H11S-;QaEmSs_+@ieyjJIsb4?MNUkB1w3LLAN?O`aLsCT~$PGKm z_jPiT5wFem@4rK+h#wx)Fs7W;KH)i&UT(C{-+RY*ssxsbtzd z4j)7s(|OL+e&&Kk?LQ5lOj(~qjY{P`PZENXx-x1qbyG^dl`cPAa&DGg)Ttq)u3DsW z+6E}}?6T8D)Y6GHS6{}dZ(B@ux~=J3(Of)^wE@&{GE7bw1~Q=_!SM}A)RFYisUe4b znDlV?&BwQ=KMA=I#xWJ_*NMu3bjMh88^a*F!1a$Ggef>cnSsMoz;_k17)N*O5s$0g z?!~+V?1t^PAMx&_iXoPPUpCdPYcT9AWyk+qLE{gd>ed$``Hlg zUmkoCMO|dXQs4$@;s)nzF15zFUkB3M!4*|2_VAWw%84j7j6{?>!%`vVP-p4QWqAZI zh4e)anaGc)!!@nR3|y*1oaGM{iQSaJh@1F3U=Q}@`4W&Q2~>P$3kNFFHiAkOz;&Cc z?mXzFe(4Xy?m|g0%KMyAQ{JE?McW8V1HI|6lMgGQIHU!Zkp?>X@KAs>;M4CvEDk7C6J-GFb;BrouRdA?wkPM<=?R@p>v0Lq#y{s7?s|*17Ns4z z9ZZw76}%JRnB$HP6!n(! z6ty?R8u!Ytah>CuZ5&0NtSb{YmJfEI?e@rl)}D4ZMSyUY?<|u*N^dHyS9kiGujA$k z-MovNcd6z+{z>*&>l^F&8=hMRoPv(`nEpq-ot)eXX4mC@=B@mhsQ%0Vua8msOlBo{ z&5zQXs^Uv#CA?WjL27E7J?d>{LIBQV4Pt->fpVigJ#JZrF?!7_J#B4qp}@4KzNluR ztS+oMLZGO5{7iHWL5Kp`}bBNh{Es zyP$DC-cFVb8E!eLv(B1IS(z1%hVjQ94J$dOmg7Zb3Pnp^vWykBTRrb4GZZXoI^pat zV+v*=?zhXb5SKb(+2cu{sW?Dz?LJ$OoKe|>&H+4s;NhaS84jDA55rz?P^Ho3KF6O3 zW?|)7R7$6=fst5c0f_LPaj@vDX$_V$Xk14m)yYF#j9(;X;N@)Mq-Av_bZ$t|N^_i85hYWkHK2b7^muS>;>JGoYlQZN} zR^!@wxR^Q*N_>?kBGqz}Y*}--$i({@C$EH2PLO6`a(Y9ol$CC@&RW?W@med(m`>6< zHcwX@&%K9=seeDj#mSUDqgF2qrC|#$#HFs2+U15~<)q`TPUo{0mLZ{;CmsI|Z_`-| z7?~^v?<9E#dB8ScAJ97`xn;g3%2`4J1%nc|bO01|YLaPuZXj#_VCgA#WX<5*dhciqtb6+U(zCK4F+@t9GQm559WYVISP z)7HW5Z6#i2w@vF~8FTY?b(0dvs?!$hV1`p3I0ZxR*G?tL;PdrlfF9%~GiDvXAtLB5 zsP4r;J$<~)$|v1G6>i%w?y!Lar4nWROty9beG)$y50EIO^H)a(W^@N;Ey0^J3NV|$btc|=2YlwMTi9Q6)Vg`=bOpa9|1^B$d5iL_ ze2`X%RJy-W>4aibZLbehnq`^URXU+4CCX7YFsI*FqqgvpH~oosiS6cqP_mZt7W;|= z1$4Y^CR96GhH6WPKc z%>y>dliYl=w0XducnX?R#j!o6tEtj=12)2we9Aq=?^5|P>qBY~XeVXTod8>J>O8z2 zN2Pb1-ql9palg_#A(h^7PeaqHZkf~AY9h?pMMTO$b4)?g*Ss7w$J9i8%|k(ROhME4 z?#!WS4FYM?g60@Qa}1$5=7Z)~5H!auLVe1KqP3SyM1qVB>{%PIA=di~UkS^+J_Io5 z2+^G!VnXf;I_R2B2)cwN=EEKh_~Pgczc@-CQePy)o1J5X&hB0`P}Fw@afkku#Ul#W0FFT*=+0~`0i!`)k{js$LwKpa0++|=F8oxTpTNz5Y68#H{ljJ~ z>Om%&yU6|!_WF=_5wR8KzDJSS98z7%Pjkz0se3meYeGoibJ;F+01wHgJ>|+Z;k6i^ zKJG8JJgPo6s=cF*p%QUe2z|hM3}uP}^dU73f8E?8o>hs{F1|U5P@VR0Osro{pd~ zh4-&R|JH*H(kTjvpz~}jT;jA|6`^ZYpd)xW(k_x67u}Wl&Y7&tfDY8kJZLZvUf6ya zKZSQ$iY4%bH_Na?X$rpCY9bp+*6N_E7YMtS@|b57JO2E*t3nv{Z3;xi6h1{C#uAoi z)ps4W&_2dUZKXC5K66%Y`!+Dan&$zlAESk`u>OT#s~q+IPph0it@6PCX_fP$Rqpj| zm^cl#)Fqx8(PyYv1cVWqfg~!o&{p8W6_pIP zm%qOHL>#Rw;@8T|EIL%S?+#@N(9iw#k+^iVXDnjcwQI~HZ!XQ}$Z}-WM;bHrk@Oes z`bd7xIjEM2u8+iQ&aRKd+IEA`W%E)!cax#gwE{E127?ZEwFmwnbqJ@4gMj*Mgird@ zujA^KnH?M>`}F1eg4-)>+?f1{JgHT*5|eAPd@wCWvOA< zSGO!@ww-vS)~Qu<%&DH53M7%z_;B0auHtL#IOpJf^}o(!UmehAY1B~peYH+R#iI+} zS#u_N7R3s6yBNN^JCwMJ6Sc*cOJa4IDW3 zRz)(vL8H};X%`SxTfO7bZYYl5zskA;=GQ&L`RPh?eWos4`hA@*h+oo#8Jv0$Va+cG`E$Ow8lcZRz zQ{8VMA1H-A&sVDsW+X3e0jf>?&QTdYt#hDZFA_8sk>o6R2YpTp3<-+O&Sx24R7|kc z1l&ohl5^0O>6Sg4pnWUh%dLd?b<+)gGqGELzIHcv9>ow~9>-pF8EH!&R+mxIrKTf6 zSXQ4_7kS<@30z<2Zr;ot!m~Xk#S?dzB!r^7@Qfdt+? z_Y09+Wmw%*0;`)0to9)JJO(Br|6DU)z<4!&_AL7BS@ZN&+c{>c6uh`|>GUqPnqB8& z0T#Q1hp!&gcrZ7McEwL(h`y{?hXYeRmyXU#nBitzJ7_)6D}mZ+YVWwx&z?g+7Gvza z>*T^u^6G=mB!dQ93K2DivNI@!k(|zwyG{o0kWA}O2A4lXt@m9OLQ_kExJ%>kJv+(r z1%Ey1b6vkKXz+(^=ymd9Y-rFpjqsXFHpv_zMx`VtaS{jYp@|K%QXA%^(Yv25B zbi;3>Z}~#xg@Ncuy&tSV=Yj}r>pn@C>i+1swekI3M(n?KAvgMCf7OmX@)sg6Za#3|_y1U1QI10|xH=bYQ z9a@LUnkPS@>MiI)OgY3mUrcrJ$`+!+wn>0O{xWEKz_`bad8wMqi>aqW97L7A1DB0A)3@}mA?>i)sv>RS?0*xVV9L~3elI?n5Ov5-o|FXPcj zyk=>~d2jlol>Zz5=)LJrlFm2&$&=516^};aHOo5ATe;;g@qHc76YyvKrpI>z84B_1 zcuaojd&!RT>aO@hbi*H_k6jfPv9u6-B4SlcyxR*73tmJge^BsTVVsc+gEL?)Nxbpl^2mp_D_?O z`v7d*i`LTj2*ED%najv_ckTz;(vLMS`ro(&Rg%v}HM&;GajEX%^`?Xnva`)85B{b$ z2Ydt2)mzSJ-s?cWrxg0pnIszt=q9JSK?wbW<)=~bE-)JZ9kF4YJjHB#nh?wX@utjOYqg-1xEZEml6Ng zX2hP$h<|4o@jb+d@39&2@8h2Q(yM(2?QUlk>1(4v3mk-0f{5mc_R z1vw`pblTk{+u4Q+$@|=7znNPUt*znh;>Lor0|xAD3@96}+h-#7rkh_)SsU%&7K^%S zIJ~uf(=N>`;Pcx{rgy#1qd0S`f7o|Y$dmuY4mWM_Nj4QYjRyQ5yW8);OfULwUKMon z>Y$sS4!U{GNy}Q*lp_ud;_-_8S}X!IF5U0hx!*v%`uCm3gUf3qd;&X{3lLrb;Yk&Q z*Y*AxOSKcwPRdZZ;8%9a`KTv26WyxCc?AdR`zc5ACwGt?HpfZ29H-N8ocoC5 z+-Gx~JaU|`HW-exs0LNPOCn-NRD3@+e|(4LU#j`pG|8+6IQ`c=THB}CR-x&Sm(X;f+}5=~UH>SC zMy-Mpuv!5jjlN=UFCEIwtpzQ z{i~uhHZUo7Awj6apk6i7>KLepHBkmy zizdFJGijDM^7h&eI#Y5jUHH6DLg$A{5y<>)cPaIzh&E}lq{oz?*EG7Z+b&;?5^jCu zX3WY=s-i@kT3Kp*&@YyE?Dcpel5Vf1Xg50V(u=|wu*#@F=2}c=>JR!@0Nap6dciXT zGVsl`vb4X@tk@QdW3=i8$}efu;nLOz2$Jg8GWlz~7v z?@F6}M%w(9q|IM#Y156;<~x?OX|6&2c4zz%kTd3nqBV1)$!C6*4A*s3$*iEwsJXEc zigowecQWc2SWW`ac*Ncdc)hm6sb>gBOgDfq;`6F2)@M?eLfz(!$H^HAph2fTQY_ky>M;A~lZ3LI zoM#h>THJYcw3r%;se`4x6p+V&0a`_TceA)Gq@=hTq@hqNn4z%J4SGCWmhdZ zn+Hx7HAZQiev<-7rmMb=Ogmzr|#y*QB11Bgg>S+3S6!c(~-oTojgdT?e?*gJEcO@4Bc znUj>BqSKg0_$VK)*QVAAQAMTwl=4C1egyBndAD|oHfBEsDYV~}LU&g%cmLT)p}&z7 z`kO6<{(|Q2pVt{FbgL(7&JyAc5%IlB_@6s6KBt(-Md66rR>Adyx-tb@QW*D zE^N`e9>dr`jZ}09h@zag1&#;m@iuSd4TL0{ZdwFAWwdnsbaW@GNIi~P+B>5SeyLg- z(4$h-C2jRGYu*atrph)zQKkQBgE6nm3G|id)jk8So0PSDf#aVCx=HJr#sDyD?Mm4* z9#Plbe(P3L;i+3Q38$z=uSaaL2Q|1J+wNT9%H6saE;o!>Jf_K?|dy?{6SUO7; z@IA`EeoEv&{}S^v(w6?P z^;5Y4KJF6IFk4vRZPAnv+ev6Ic;v5HP@=2gwQ6EuEg3UcrPlmCP6Qn~d^3YXABV(9 z$L!N|x}pr7-Xw|PjVFq!gAnvDiRH;{>3h`WQ)voq5y2lTpI3~suW!UH$de;f`lk7gB2_))r26aX=|QI1xxHL% zLcmVVRVkI7`d)X-2&>szeb$5s1Y6S`Y1R|AMdXzE%()|{{8+m@d#eA^sHe)#JrZuc zjkFXmZO3}?aQXapN)jb6pWBwsMATh^d5b$piL2BeiM3R#N6lW)mabn)mw(@u9&%gv zf$m_fa3X*u$MK{uwf14c?#oU8FKkO+uP!g6{=aXt2>e0<`@c-R{ED{pMNgtP{-G`1 za2{R0hF<>9l9y@lkvKK`mv%Is!{z$+^mS_H+4l66>hkq%>ATcK-$K0WNh>!Iqbn)P*6V7*RrK$@(O~@> zm>tp2GS?1v7tfM(QGeJ0at?~#Ay(+y!GL#OuW#2ws`gWl5lYv!VNE2&3IYAwClt`z zQ*?i$WRJKI@L1*b&*+VdKkwZKB#(lPdmn7z~#Hz(`T#8 ze{D-|dW^bDoqba-qE|hUZv9(Zdf;hv`EGi;<+y(+*kNj!>gB!d>1V1>Z*EJkQzaXFAr{pWO%q+CYqZshQyO z4R4b=M+e`f(aWi)u2Jtu{UQ?8`o#@F`o;A}v>w6VlzPMmT|MHDQf}XVMC%NDB3fto zYQ)hQcFKkws-e^m{#R)I;8jBF2aKH8Q-HUnA8cU#VBm)m9MdxCJ4PnmAsm^s|70=sZ^yJ~d10Vvxtj0NJJ-o~4zJLnWrJ`;%SYwY zJ;$^d;m=4h@+?=3yiqp%$uTW4o-VY+$Ot1bE|9JA$BL=9JW1lcS)@L(M5=AIB6W|v z_lAdysb|akZ(1QzHzq{N6PEJssQci`?wr0*+^C9BT|*bvY0#htM>#{D%K;ubFg-04fEeVkISF)M`5q=R!Rqf?0`z}s==Zz->(>k9H3$t zu#tKdeW=T$(&3iE->)0#9hUO$FC8Ck2Yz{>&^@!|e~l9tgUp^P24^x7HA{eOsmSNv zbk5K8OUqORFnx;EwVD2apXv9NGW`mM!AeXYawhlr89o=Hr%JM>4Eumbs2}iJJJ>aX z82+}1pW$m`$^HXH5)Oe7DYX~`Km>Yn#h|ZXlxw+A5UF6+tOk)TKSYpD3Z8}t34)w< zr-9h&(8>qf0Ddy&P`8N~?emC`{RG|z8N4bGI5*;jH=xMg7FKoo$nuNDqT9SL3~hIn z!(?emvH(nyYmJS`em`w*i?*-zAJg{356+pk zL#TKGw4Ino+P2iYerE3d%IB?XhU#4hM;7%yK=H_ZB{Ieig+?l8$x&Tg+$mM@?=#gd z*XkpPAHFQZ$ltA$Eg0I(hl(9MGqq+0N&8nyeTI|VkOul+hg1NS)LxSh6xBu;cW4if z6s;XFZ5M||iniJ0ZrS8D4-`{NM~h}tRzfNZxErXOJ*k`;6UN(y4G;-vLEEgSMJjEv zP9;E-vjynlQv}duLq)bD*bsbw5CkhPYz^P>QwZPsS;E(aY_EZ=97bjl!M9hf`d-iW zekBMhx`SojHCTjKW{oc2=RP{f2uVE}5z?>_2&tbDLY@x^ee359A=CYeN3VP|;$zVQ z;KRX$R4@9Js$T{l-|Z_xtNFsm`o5wM@`1+9w?{-3Zm1<}2|h|=LuZGNd?`K}=LH{m zcYVYOfSf!g?-^h80G9v6-Xgq`p9wzlK73f8>*{%gl$FJ&yR!15zdb^bNzE2AXPhF) zyX1@IF%4QbG8s{IzZ$W(&~ycZCQB=!U|h z+Fp0oBX6(Et4|DXtvj_SO|R7%?KZ{KNaDX`NGGo ze|$9C>mIid_PUxG+3Pm{p$Mozj-0xUlWf0e}T&Doaz4;kC1G9(&?kLy0I}= z{Je-iIl~EPWY4WLVck8d3JM2NV=R2?z65?1N{7^Ybm_IFU#2eO6j<;Nv3ZPSey8d& z(N5*}3Q(aWy-M|1E18(OOi<_DQKxh2vzN!wr@yWJtYH8&kUlk#4derkIQ0)IfrIL< zr9SK0(if`klWplQsLOg9$z{=b1jBmOW4ax;uW;Geo_>$IY-meoRp*P^($}lYMjFSR zk$D`)y*2dh#ckc__rXF8CpFO@q_1I*lyQD4s?l@g8ZA(8!T{cnY`@}qU zKBm6CtUdiU)%h81>3>wyT}C~w^Y{2HD#~Kfo=s*CV2{r(Ls}nDJ)YT?{)_5nd0To# zT%=wbpT~(c&F9B{?akxI-anroJ9j=m)-#`c?TfKfz^DFhK1bX;pZx6F`TW=mPX*8K zi=2W^KIG?|b73Xt%_ke~pHH^Bbv{4#;@N(TzhHm2L}gX_R67RwqN3Dg3=17Uq-_;_ zq}S6)GU+8y=|2ZzgR*Sulw1MX*C{2Jt6v5rd4KXuGUQZD@~$U3-`TP{xC+y$XI5h{YP`?9ri70M4OSa)K-X$c6Yw?QG+Z~Bb9mGL%r!)Ov$oSz2_ ze<9L&X(1Z9^!(^k*|{CNr~^pQ&HTCV+zrExLa!Pi`Aox32S9ST zosc3wQY^0D^l4A_NCRFc>G5v(o6Oe9U77ry&m?!sPh~r^@)l(E&Yyd2M0V5(FSNp( z@Ag_@+}VWep=8+3M6<|03RqNiU0eZCLLOiW@XOY0$C2O&>aqzOK_V-!a`0CIpp_w+ zi6Ys)7Aa<3T)ivtjPJ<5*}5G4_K=tnQH1T7>Z_F>O5&49k{^lZiNyX4Q2DdB1^^kk z5&yrNY2XamV+YW?eBp}yo_wc(l`)V$Uk8Z%3#`ICXP@na8?C8};fb@-JN=8C8Nhdu z?Uj~r$a-@s`OxR`tbU7U4aB`|bgH5rSILc}dgjyaGUX zeO5&wHXyE_4=wj0$>>M8lH1d(;L+VoO+Wz>zuDRxV!8!rN0MKiY@RR~4%*Y_#Rbrq z#M5vMTt(W_f0Gw+TuDFOjNzEkd4Fe zkk)Gjut=!;nQ6j`B%z4=draI?oI7vj=qxtIS`EJDr*;Cf1&dP@L=aS=B5;Xzyg?V)1|MBL&e5afqO-IgP zg&uV+`~@ai3u1bw?6Q_{1C-gSBz+!lN!o8C|`+hH|m5M>?`Fw;r@Qb1LFnc zR4_u|CUK2nNypiEE20EjNglJGC|7?A$h#AeUd-W0K%oIx{wg9;FMg;f8B`*7frMlt zlaznEnOTD7cLVaTWDaS*NYXbOw>arXB8n+ydPpbB#BNZeMlxD{Nh%72AW6>oo$4nF z_`G@tzI0(k5DUaRlA+OGt3?_apd zmc=1=bn;)l{MR7=Et3Bl<-f)9UrPQ<%YRGeXUDf?Wy!W;k>Tatsi)Kk-t9DRN#WA& zj0;?s-TVNHcr*ZMEw<>+(XZcB14WMaYrGQAkoxehlBU2p?gLI2Wlfvbk!+3JsDRAK zmOpMFYWh%q)?7BzHMs{`+XFG>SXa`5x%Gx5_a@>rKkb3i30w@mzSvSuNJbjRv`x^7#O6hLWSSR& zXe17VEA65J60Fn=VZGsIs4)lIvII(>dP&CX8YFk9BNRLyF`OrzVv$Byc2Tx&Z~EMb z(Pj!r`g_ze~F#E>rA_S)Vc(xJth}nkhq8$8Q zM7+CEZXSy_!UK7Qqp!n9t=Z%S_zT4Y+)Z92g%s@E;|Nu-nb+ z7SpJTo7bhXi_YB&SWIWrNMUI?jC2nErsN&ytufP+gZCC^ho8ZKoa^VaRe^bs7rt^(>$ul}#Zupj&`~><*!l(%q*C3QMw!v#Fi%qP*Gz ze;4ah)5HkDo zu-}m*^a`X;u|AQXgIxml!l}YBSaaR$LrQ^Q{a~|PxP~(KosdF(*cIv{LB$PJ#g_+6 zafAD*xWWB)af1irPO1)s|xxML2$yA?-NF*)>OMMjW_yrF>)!LDHz5e-0uaz7n z5UKP%cp_1oTCzk?MK|~sy}5qV->5P#mDSyveo#3zNaA^dV4n-SBdR=v9i_fOi3zdf z?k7FB-~icdb5_lPPLKVGB5i2_&a$c^_WNp}zV|jS;8>tZ0qo0ofRR~;{yEDq;apFp z6N!=pYOV`>gFk&>s%o154+j^UX=0z4Jh`U89L~2o`vR6mV)F|Oi$Hf(9OnEbELxU< z$yInOa76NL?9G{ax!N^9!@*ZPfKe4msk-w09*pijDoBI|Cjk8VO}$j+44EaQNn5`O zvgHlH#XXoSGxY+Gi`*>VscVWY=PLJ^H?*gJLS8?a7q_S1pe|b}NCGaO-3Dq7UcQLL z$C8LDI~U0rLQUTu(G?Q7I5eK84WYZxPh<3nm$aon5T(mkw?VNSynHQvn2JvNKRg~S z`S5`G@LSb~-)#o}k8SD4tGgT9Ky|~*cTgX%3GCyS5uJ^c`{*})MAX;b(+2xgy!`jJ zbX?8j{cY)g#Y5#mYYSPMs{-XTqS|q`Grm|wJ+#}8+%22#;kE~2gWAB+T!(Q5JO;65!x zz9USZR{KP7pH_?I<-aI`$o2aoaEw-qUx~mztrph@lCZDOK@0zUlC1A!tv>wUxvyT7 zd_mW8AEGzbF!umq@vVo8)=-B`l}FhCSQavNU|DO4+4jOkzXvR9E#yN4mbI1y;5}HA zuh$+?jOhR6$BK%j>H4oXn#oUenQ%pK93%(W9JvoL#=*7J z7_8cT=03jCsb7)J0`B5??p8I#0-mc6=JdoGZqS$0IsFB2v4on*{ChvD4fl}-WATip z5;`GtWSTVQ8+O`Hg2kFGr^|^m+MS0JXJ}If&j+O&8PNOovJL!AZRYDHDlV%&?_^9u2~RCzUgjG{jl{UI1{b@c4VgtlHQdXfH`zg?neh{_FD@#PDJfY^mdn6cW zk8i}UMs)w#ot-|=x=U(_nGUWCNwp*T7~)-~+-nJCcfsDDJpXzSX=npbB3o|^$?*(0 zUFy~t0<=$&u7_6Q7oSiGh!eRB7$rpbomh8Pl^KRUcB_))#B5GrNeNY=8H_DN%!V#$ zSiRm2ngDxtbI2T(%%!{Bx=&FUH%aC6`ha#j%}{#Xp?vIkQBe<7kS{p!-wUUkv@j&^ z=c>sOcQPiYGt~{=eS$(4K%?$<*RcBNnMz8Y`z|d-p!oNXG_I>E9MO|SKzyf-_- z&}#F|Iq%tWF<;8YX+d`K;xr}1Vc4AtTwM!1Ylh^~i1&a>o!;Lk#((kd7ov*H9*a#tYk%}*;7=2I}ezfa4|P&V{WC3xhaFWVZz+7 zjk$wpj{VMYtJZAue~EX#I3mu7iMPhYlqYVk6?Y^=XHwi$FTU0&>eJ$HY4O)3;zLWt zQ<_AfN&Lqd;)%;d|6|0L&Js^}tZ09%Xk8(GyF#pcocP??V)f(2*(=58&Jj;|g81zd z#G5i=ZL^3zNu-`EZd@fkyIMTw>EZ?Fi6{R*@yTb1mp)T0K3}|Yjd)dyc-n;`_bhS8 zTJd+SqV8hx!sm!t8*!xcLqD|uNP2w~EDwe!U^u9~H_us_j?-t*Bw^;Wc@lTsY z^u410y<+l8@tuDcpZpK;%=d}D_lev7Q(X0aapniaT^|tpJ}9pI&^%hL?|zJ-e5YJ) z^PLZZ`qJv&yd&u5?R3*Ws1?42(?GS**Kb1OmSs%772kJEF>c=%i(||x( z@Q)nr(G?eIN!ba<6Q1nIy{;YEt*G6U^Hs23US;L0pYLA3=_B5Tk@))O1{j@JxG}5> zIsm#V6&pWM!c-h6-!WVY<&IJ)LwO0(f=sjC`xVyJ1}@1~pcIsd%B0i_v_;BY1p5Z- zPNTfDfwo+7a$sM$J*ub;?`81bPn~Lt)erwt`tUaW@UNv0Z`Tid zN*~@a6tK3p4F}x(deF@~^|udtCEw1eH!>e6d31fy$94zZyea7BE$XHpcBRt`9CXRs zYMD3;<*s2O8nruttyuFk6yDLBtxnullg=I7Yp>`3o^L1}mB#1XyepXB5&>r}-`HJ3 zyWOR;q%fXeV7-o^q@%k4*S2QJDe^mf&;iZ#WURm1nnv1(5(;Jz5vkQ%^=zzP^J zH~J{WJ!_K{xc<-olj~MVn z&OKnZ=YBb2-#X7(CwD1EYBg9*{B}TX|5rnyJ#7LT@?evIoA?WXpw;yyh&y?4Ty65H zs2+WlYn0zkN6~b^jNg6%Eh)Ty5p;XG{q!~G4k`d(kd zyMuV{mJ#M$1)x5iN97WxRZBc;Cf5t=vgG+khpgub=wB}qvzS^t6zkA7RCA8y(W>~p zcF;gGLtoHmGMDFC_4WE(h_BwRzv{|frTCxwIG5t&qfv%epLKJB1^#-K_{Tiz8s_=y z2xhWs2d{$6ihy#FB_1IIr8Hi)fSh6NeVVn0Aa_3oTQ;)f??a}4S5!?^*}~!8ADw%| z;5(dqDTgF;)|XX-Ca1xjhVUIE+*?ZBWm@(yW@06e2KZ6bRhrd8@gsh?q|dHlYgwWc z(b6E3QzF~6Zqo||1rwbTquk;Wta9=t#U+m_?d_^46P+Ws*&d3)X=kVYe;MyE?Gmv{x#HD zx`xUYf}8Uiy7m6jHIzw|kmPhZu3g$gN(u2H)IT%NfGNv~kCX_=ktzbx1x-WMl5M{Y z&Zv|Mvb^yihiQsHJgz>=xMq8`gwz9g?du68RG#3&2fYIjq^6v3KgCz zjg^LRzB)u6ade$wJ{^N2a#-uY=tjKEdvj}Hc@673%~wU>R@}aIE!;v`5n(|Cd8I%P z%8?%Amp;QI0m{xm(9O}Hn-e2t*x9hJpJ}ac>T~pAuld#p&2hwPdAF9LvcW9@4Zb{N zHMl9D!2|XeuBQfgy*t7K%hc_~^6VL`UGDF^1?dAoHA=d-A8JQ~U~xR?=9UKnZeA61 z^J;Zd@z7E0(KYyJt$XwHeAE8O7lK+|i#IO{R)Fk=TUA4{IgFANZoVUe+$U+;eRjww zI?cD2JpPlV2Q}XoDOGunv&u6nx5Vw~?|^^LC~hl7j4mT>>6b_7Qtg>Hg9||{KqK&- zXYt$|7zX7TtWsO-*TL%-?L?^$s$s_3(&wvT%I(JY)uqbc_jB+ZjnDIlE7bC;IO5+$ z6|v9fU@DZwsyLX7qYHq`3MHm04(1lHWO(yDmyGqdZukBO=1Z2vS4*XqAppAq%qmgSVi5vhMwhqH054k?XEwaTGILpur+9c#VMul|xr(J> z!Vf8|l_O)8sB7w|vP3IZObHsMW$6eQ3>n8AUG+ge8|{u-YYT4NZQoEwMSpSbCFoei zx|2fc8f2Zb;5Gjynp5s!rqiETTBD@VE09MIQNBVI1^8mesD4yg@6_|i-t<$P=WX}v z=g`1>s6CCMZ>1sLQI9rd4$HCL3rWspYp)oj6spqBpdRVDUx~CsZZHQ9=YCaA6jbWW zQ>u?&enBImVDrYA&#i2sl$p$}(_kUdmcA8cu{aDCj78BmkC~f2%N8gx^9XPD)W*GF zvP3(;g>+;A8L#)I{{&M$E9{iZp)QlOAFY@JR?8M836E7wdG0Rd>v;og-`xE`8C!zm z;&W?&XeVGKiFFJ|P~*bu0m?=SQnej}0pumf+`Xh`Cynq$8F0Yk>y6`Zc>uk`51?=K z0ng6jwi>@R>Y)?ZfpTrhiyC>!5^4oVnDC%Q$A$?TrRp1S5fU?H%b6I=9H_e9#<^=C z3N>fAyBPD{AK<+A<5F8bpoUFJU+t-ng?@L;V+#<2`f{buBr{}2fL6)&^v#zG{2n07 zsRgC+57~9H;()04Csa0%x&9lR#`I zwH?PvDAE{5bdQ_+*Xc;yX&7760?o5VGj31sg~hy-Ug+@@{XutTDUW)# z?rbq;i)Dpe<7F%6m^D6Hb&bOWhsgpeN2!J(w|_}B&#mcY#2MvK^;DnJmi{BzNBxk? zX~-!FWF9H^OT8u^)99{Ap>QuLOWi3VH>4HUN)+!I<{QZ=0?J%sTm69G6 zUBzNupaBOJeB5UBMC={mu;+-DaTG1%ju=vos}h_)jH2E!ZVQ=lzmOUCc&S=cIgYWv z1`U?Fpg0d)ge2IqC58;aj#nXAMEu_Lu?SC1fnyx8K0(9_gUvC!#-Kl%Kt}9U)C}kZ z-ZhE$+cET2F5F25(3E;MWdjIqB;mdVAH}ZWA|w#X*7-3&n5+hbaRkEMI2D@tB+qoDNfK7C+sOIIKoB9DVD9rW2QI}LivDIV%E?txW>~bi-uQ? zE55ayh4V1bXA1*E3PHpqvDJg}D+iHtpAXTwM9zC5ow&Vno`Wl=1yqwsq!-ZT03Oe2 zhpes*Z;)5Zm z2>AP*YK9UrFG-xT39Ooj2^>J7`&&4_tQEu>+R`7ZJ!Nxv%KXehF6EvY6Fz-VQLkLc z)3lI*!5`ABZdwlUu&2>yMQ)CJ8;^LY$9bYu>K=hF9hRGyog0tBrTo3&sHg59kza%x zN05rY3$idxPXI1QJ#KCR@CX6$hz&sNTOk4X)an2{Qk{y4g}20n1_5Q!n6xZGN03V% zHBdMPmt6`9cpv6@41egrYP{P*ZI5~zAHo*E74B2X0N*GAVD0aP`hLjmL;ZNj{2)jl z#qa+oeShA+4M3r!vH1B7hfv1JJNK&zT^$jrt0K69jxhn8zz=t9IAIgOF(QCtHUZ!p z$M7N~0X(fb0UWDF08=>CR-9*sr+fsZ#}P%kKp$tynXNvFqe3!1t?j5Osi6%JIaOCalcl=n3>D0C=S{(Ug{xp{R6xMkgjZ4RLWPsB zxy^)Xqm?OA&459jLf!N4=B|6n{fN;jTLbm9rIM_ECs&%1&#R7c3F;Bvvkaq$>~M)` zXD_(Vr_MfDEZJH2Oxat0+LyJk&lWWtMriI+3CYw>?X0NIPd4iC^)A}?!dp`2K@u5B zJ;T<W9w&EK&QW3344|FLXjj_kBg z%jEt%)j;&G!=lWGA1HH<4uo>U*_d5HMYC0;3UvZW<{$U)v6`nOP|yFH3H8kM^zrNZ z^zqu51$m(L0iIQ#$967=#7e>jFli?aeyGPPe&s>(e&qQ=rW3_lc8>jo%u-d=~x@&4I_S=G*-}I zMV-T*YX_a7x$~KGHg_s9L?M$Zz&H)(y*7T|*L*{A&nNd8*|7;1U_9Vls z1PREY*aEDCH4D2EAV;KH2_KK?m2mQS5hL}~#QqT16!O+uO(FN6C>p&Zun&WVVFam! ze5>FzTb=Z%3S{Z!wsfjaQQTuqSh0ETh852+cKCw%h#f9nVed(VAsab0^u(NRvS~SG zWb?|1CYxVBR7_2sC^Fe>jcKy^ySOHs_J@n9Kb$CrfQ?Y<>wLrJ!czvD^M!^@o7^mP zAJ?#Xe^kR}iKk)nMG3%f3oH&L|SopR*Du&T;H$rooKpM&3NbB+!N&wfH?yFK^|kY&Ln*~ zIVYX-d2`n}&75uxoD=y5k`Le-j`>Ty#lx$CAm2jmTZSGhsRnAwKP?kuk}TK*jswOWCyLQWX^Ft`I8!$bx_|y?qnO2e6o8fE`;l2 z+La^Os(iADrQsNA#*tLQ{aoR!h=WzdBcp zln(62unM1qH(Zw#!%27vhGZkpug*r2)rEIBxYEyYW-ZKz4|whAho7QOsQdhGtA7e$ zO^UDHPVs06@>>8h>%z%dJcTM3DX6mMxDsu^m1qFC7tBQhQ_?gk#b+dGXJ=9V`-POW z*Jw$5H0n!QG;C&tOumeeRS40dv%?XcAHi+gag!*ASb&ZK(mtrLEXOR$vf`A+@M}BD zLY9bR2d0w?rOpJT7`k+xgNDM0X5QTIO($xIGi4ff5J=@^YzGA>g7E$co!Bb>WX+Do zaN1)wXX;aDPN>Tmfi2-O&XhW#aH_q@3Z9ZoB$#*bsjhJhR}`Qcf!A^Av204=fJWe{OW3){%+74Q1n`X2ff(!5hJ0#J@ zE)IaIgBIFUBS719S+vndu0xkH7j091v`tq>+lyx6J!YY8sw~>Z6$+FG8BKN!Cv^zX z$STz|p^@;~$DSWkI6ZJR16&*?P_yTl&n*fZhrn~na`{m!4DOPn6uae21-6|IY-_pj z97bvZPdE5CrW`@rh#UX;`!1A;aBAox|ZU?CI^q zewf)Oawcs{gu3rGO4W*7aA3_2_7CR`gATM9us96|gp?yw3Lm0)iz=e@fS*il{-fyh z#WT>JEHVvF`@nqs0i_*A+S1Rq6veHmoBz@NunM-# zJNHzuZ9y*WNVzw<%Dhp)J~{&4=yvX{&~I!9_4Pa_=3C`5np4>9OXnd7QI{Dt`Fzyw zr&V85NYwz+=Kzf|6X8H)JE5e(;FpKW#1OZoe@M25F3-#x-BJoRFz{&?UW5e3Wz~Vv zRn<^ALS2J1PIPbj@loTO+~uV%0ekQ!-}>v~!7j)`eA#?2L@NKX3n}>fv5f?GgSOqA z_rFW6Nvj%6aN^O+awhTU8*{$IqsFJH%b$3(%V3>y1CJOYiL|Fbak7}o;V5^99c2j> z%P+Mu6&EONF<-yYy1vYx!y#zY*B6T}dB`yiI){|(xnxnUuGIEpDww1mVI=ZK`ZbSy zysMOaUOr3mDP45_eT$VT%l0z^ITYsUb(6+*ktt_HU9aR&zb<2)>>|ZvjMOH;bB_0&XyzJYRB_x{lNA+R z)NSE%Ouu%$3SWcIT_3RJSkAbe(64OQuZ(-!0}KKaew+_iR<>Qe2kTncNimy>iJ>GmIVQ=C@URSd;B^{s#W+r`gbu5g8Tbn7fHVb9frX>YU zf__@gv_JwVl*AR3R!x6hC6MUyLn0t0@juK&xeb@a-jK{TO@9wW_41;c4x{!B zrPm*D?orC`3FjWA{GM{|QB8;YoqJT%Vc5C1oc9U(4uu*6e7Tt1^4ttkwOl@*sam$V zDfn9gQ-+t-((Ou|Ftpdq=g@u>tZXJ)!gf-*|!v3dpWVE5* z8DPO3AdKhr<+J?#vO*%s4ys1VC5#}X`cewoN&RP(D{rwXI}!xbQcF`HOiPSk0Wf{T zEMXcj1U0vQ6{=$!Pe+v{uwN8%z7z1o>Nq#6@y*Cfz8MiQ{1(8`8pJS5ddjQ@Q4S+q2z8Wl6E&$26Ch>$f~5De zq-y$}p!_Q^#L5&jQjLQCY1S0v)b@}R)FU^}%Zpr}SHhy7M763b_r{8#>Q^;ihdqn8 z^m|UJ*nWc0+6Hn#1IUoScE87J0d8=CN0b4FOPv8ltFD-jf@`JBR8?Tr7n!f++zP*$ z2qr;zS5!E)ggdD^?tcL5dp^Caa;R;{8?jC|tv*6Z>o-*wLHQYr#FD8d0*m3%kV+`p zUmOXB`Ol|hn5mpo0^>(yo1V6ZZ4pjM;M*`iykCjs?I;&2=Pp{dzPk_>h|_fKzt~VgDwVA0x6LO@;-trca|u19jq^FAgTbsESj#A#nW?(* znM@!FBuWW8u$=AN=Ry;#cW#R?*73S2r%LI^oQ&+^f9*#{b5sv{sAgO|A1YF z{zyC~zx2(RsY3txh=|k+5sS=U9rcXZ9x!uX8BeZ&Zf=nBZQV#|5~;9O1I_XwYL3f& z`h#I@AE%NQR^JU6Kk8!qSOttfWH3HT7$3DU{_yF8@q{1a4W|_2Ct@4MPD_m60~kN< zV*Eq}jGr_ZKR_5iU}L<982`S7@ja(6#+w!r<0oSq#zWgTQ8GS$msV?F6mPlt)KbD< zffBaVIF#_=8jBJO9tqhC$-|Vr$s8*+=`*9Zs_GL>rr1KavTJnn{=1XS3g*<_D#g(0g>iM9 z@rpuY!og~3x8NwuOlxIM`wBo^Vs&zc$7Ma9V*&0Wqc?^jeZWk;%Q$8<`6pM=qLL}% zyPcI5YN&NtY1-BiLBg7K!a4v<-;5h4u$WNEdhN_g*wcO{*3*RhbVg{by5d8k@i$T9 zTZ&YxokP@Cuq^z`49uJRz}O2-yXExg#bH+1aYZK0ciA)Wa(czU``abB{e7^P3AJd# zS`xh;9UezEJL%gXvsZU@#iQF511|k3_mlWsz{0^9{>t2PeA?Usqs-~8e~)jIyZ}sm z0GRmIE))Mm1t$JU!^EeFiBH>1{8PxpC(bZTyg!M0$nzuOctX4@Iq!PNoY?pJ)0%xB zs=mC~zTQfzGcBcv_2cdcudut>$&Q-fRPvaB=Pl~MMRJRm)zD=`)mY8z!m3Y52s>#a zx*5l8BmRY|qs%1)>ddQ^g-cm|x(vJIYZVV{nvqXt-dL~sdb$7OFH}Z%e0sU{%{N-t zZ??X$-n!msU2n3#45#SNvnB(uvh;&5zsdA9u(fsSnl?C}VBu zl~KC%+R}fo0L*n@YuKi#VV3dA0=P>x17~6>78w{_3c%`G0k{Ww3En{f_ZS(?*USAM zS6{fC=hCG>tFGT{U9Y#UH(J-5eBYGx!g8~VK}$^)Xw{Ef?H{+Kd!NVKpn%>uj9hhq#{5EV#0K68YIjLWE}V$;cN#u%keGs zs?uI1*qA7Ss6aPcXeuaJR8X*}pkPr!!J>jf02P=6T!sIFy|fDU(kj?Xt6(p!g0-~H z@n*7WfO9-_)^!P6dRYlu%3EwHZ?PqoK-Tp}>w1&_%Wts0 zY^|Mwy>(E z!WSxul*dqkE>CYQu2`Q*;tt~*5v*5}w|5wML$`D@&+?ZsmbayI9_!p$k5;%Z&gc|9 z#0Df^MR3zl)hW81;y*`BR26G0m*^9I6UYd8#e!I8Iemmq+@6i(u*Me2=%8LEFMjnv2CqW1SH2S2gSAW}TQ~ zs+Su^8WYg2q*)IxnVdy+7pa%E&+Bujn#U6Epi#4A7(kE*TOZ-qwL>q;8ZAAP!x`6z!!FIK%%m)+12VKUeNjb5lxTO00ODfWV*tCY*+Ntcq#JR(O$0(x@M2 z+2Dz`c!Dr{M&*s>z0j+B3|9BG?&|)My}IM>>fUNr_d~S0AF@~Xmr>#QsdbF~Z92wY zJpad*&0AIm`l66ghK+=IiZJUcZ`jk(d?f4bW!ddtmI2Xx;S7KehhLU?vw_!kA!PW- zVHTVsCV(QgxfJo03jF@7h9Zs=MI5&&;%i6|`)UkDyomVyUn1h`^U3d5Rws((*1<%R zAqu?jIR47_jymX8=NrzA;hg2oC)>N;ZSq!6u2seJIJXBBOOktLH-m9tAi(m!ajURoyD%GhXBAVWW`={X0 z{va&Wue%HN8x{7d-!u#LBrVjF_Coy@F4SVeEY$aS!fO)Z0l9IAinTFuaz3}KD}%SI z%6eMD$R!Q{)Z3MvRQ_yBKi5vF0Sk~Bev`EuTAt%gsBdj|`PX)rZ*4oLn`$kSVa>mm zyJ;;mYe`X;&bsT>&!|~1uH{}*Qmlo``{jav;g&mZ_eCoNMBvS6;RbZWp};uI23)ya zo*a6L$}W?xtIltG@r0N5WybKW7k4Q4f*BmXg>xb(R^3)NRXR{F_&P1SwOzmIx0M{0 zbx$Tl-~xSp$!;qeM?q`8&2J$*0rHihwa_f>FtD_o%hI;nEUm$1X*&!{+fsupP5v<~ z?K)&>0~Skrorl_J>J%8>_+&(5im9KEYbBz;6d|)Zd3~@cVx@JR*}(!I_(INDZ20(1 zzC1xE0Ol^(qJKn&pI-BwKK4uKW5W@dr~A};-4@}k8Z_D#$x(o9Kk?vx+WM(HiSp*< zJLq1QYCH%(AgM;bhi_se^q|)~ZARCx0vu$6Z4urD;+Xq!+!R3c0o?A@fU zRFiWC(R^E!6{_X#gWL6k74CyO^n;Z-bJQRo+zDqL9SSMP(-oWm#@gxN!kpSFWa4|% z6T&^#DA{}Jwc2f&Lrab|Jp0jHjx|DTN8}>^a7{&)*lj5~>SzsU>fa@Hnm82Dy8k9& zGF7Q_D0S=r*w^PC%KWAz=H)qkHsP5ZEs>nX?T8-kB>R^i-Z?dUe!~Tz#0!x0uOGY<-Bsqiv=}t3hqjK_9lN5OczQSWr^b zZGeA$MBaGOKoS+@ULPN$_oF1;VdtQ1#RpPk$|(+_JjL6W@OJ>VgGA(mOyt`lNaQGw z2Q`sz;}eKMN#xd$UcjGPNki3N&>1nVCt)oIp+42m>qUOk{?vr}h?;PQ{uE94sLiI+eP!Ey8*dGlD*p zUYw${n@fHab5^o8fWURioUPNWYWZi*=E*cq@^8J69n1{Q z%{J7L>_*qxbKkYuW@dMuf|I15r zdVvtnjEK)i#M7hV=BRjfO#E+5yx9{?adCZIytGF4D6Xy*XD7sc32{-K=&TcOOTwn$ z6ZPWh4W~%9qPovpKkd(&xBXe8ZRy?MYgj-J&P-hBE%=>fJa1>f)Yoq+1n1rf#^ww+ z-|IK+tMGM#z)g{yllSo22$ulI$M;%PJnv5UAFZtXc48T<*>oSy)xLW79aXwdly)B& zV-e2Bz07zLEYuk%1G584%{&2R*w;PXy_Ix=P!I}8$5cCa2;nfgqShi_VNQ$7V~;YV zHG-!>jA9+c=789SfH5cLY0P)c6P4A?(|B%;u=b9&F_k?X%p^+%GY59|OueSY@57#Y zUQJlOm|Rf4SU(rOc$-2BGyC&c$lu{Sz(SLF|CWez|9KIUf<9o;%ofKD^SMqcWcF1t zWYK`|;sDF`qELx^L-05DCvC{JrXGe;<|d0N*(-d9hq^?7vn3-6wvczb2uqQT3rdlT zXGxL9rbC2O6>-&3mYO@tdSUMNH>1?ukk!`ighRox6P}6+q8cKo(c#ptdP6sm4!dpU zkYW#cU!ZEIRu(qqHTQlhS)p|3f_4cqo`1^mKDF@MwIXcSZgRJ4duz&PxVYJD*FHwu zwU61`wRYUDov>VkCL(Cmd`O57E>Kc~U_THYW6S6F4a&cO)(4+Ld$}378HBK(b$S~< zUXAkH5zHtbS~y3CN+Vckg8E3EB&b_lg8FuaL>b>P1a&nL)YUdYeHRI;%Sx2-!YH2Z z4VDqRbD$=djyi3j$cC5S*js=~IZ0<}d;LUZ+9_CP%LQ#nprB6?(V?Jk791eDHT^b$ znhtZ7F3(nDtfY6(F%~gpV`l=5m;h&dqB5RE^GTuUDfICeI@Y`{B5I=I&rxyp{2xbW zilN-#Y&Wa5q!u`s(e;B2=86vHyqs2#U6*Nf{K7Ppi6|myn18PNsha9h1YX%hoB5zb zn+xe{_NmGXxy$al;IWJF+LE0|r&_PZ*HvpM>N%%jvM|s&LkxtlsGUw22w{P%f`Nq= zf>#4`xy@xRKdiu9eq@--r-`|I+GZ|4M&|Nai@8L|YwwK_Q6Ck5jf&5lB3~EVYtI)? z(O~a7Zm^F=TCnw-?yFEPB)B_@ia;~X^EapDESmp^5=vDcM+GO}zf~?4Ogr;Cv8q&Tc&e>@ecxTD0>{(SAZ8Ytz6Cks#b~X;HI`%goN-R9cBc3)d-gm>d~aPCT$eq}4vp)|cm~zt z?@ktNGn`J92u%4D#L;~g^yNETefj4V^yOa|efb*Fm#?w)<^RPU`Nx+Sefgn;sC%Li zpNWWnj*9bR;tWsR>51RR#rtc-_FD1E#OaU)MybVzh1$ch7TxL7qSsqi?v#CY1(jlb&teNNb?MJnVn@%cHtWkBAe~bzYO6Lx=^K6cTIDvZ z^x<#RNPYOu3i|Nu)8tmxhwa@#k7um+v?VA>!O(+zu!n9_uuY#w38vaq$X_4kulhl; zSWeZJi+m|<2HZ*Y%%^hQsRKGGqR4pRuGP$i&og>FiaSDf&bIHO{oFAn(WflV-ZMi( zMmWpB=jUw5C?8=oBSS_AB|2Yu*;pR`+6}&66Wg|kC5$2Q#~=&iA&*7x8m-+9p*$X% zi$?6ogR(m;+P{#%4A-%~Oy~G9(s{@|BEc?7xRtW-^WuM0xa4f8RG)#^@Hwfd#4TGhL%)vt_d^#xL`zF@0Xzed$+|8k>Ry`(`bUL!>@di;MZ zDmKT&6Fl)%Pn;1KlW36qL9N)35O1#&%ah_rQtYZT-LfWujf_1k;ipc<#H}(`RzAGZV0TZgy|_+py}M zQaxDi+;^3Yl$)V&40kB;#cEFL?wJ^euAMJAq{)l8w!A*=|*P1vr>DgafxcBIes6kN+zzE@mSHJiJ+^{@5s7)r ztYzc3+!dRcZ?&QrGVJt*Q$LWNz5V0QB zF&4CVGO65Yuzt!0fSjJ#C>v2S6Kv=}`8;L=Vb@h9fciX>0>pdRH9IZ9R^?jQs{F>? zs{FQsU)Jx;R^?jSs$6StReq0Km22Z>tMYNG;RDXjlYIoE=K(4%>EEm$xQf`z8@F9V(5<!KLdaY;ms&wo^hOVeRI57$^RqK}Y<8_vh@%cqNm z8_vh@%a5Rk+gE{xJ6sy>tiY@DhK9Eh4R5n)xPUbLjprL0zUCZp?&pNKA|g&i#CxLR z4Kb1MM6V~ViHo<_h~>3nvR330;y>%es-#HNiyzdBFE@xw7l|h|ihYgZb&Ew|vDlRo zuStu)TO#gTBHp=F^eh#>ZW8}~hIqv?adMg1f2R1@a`DfP5shbwp|ix+$BOzDV#^9K zzCt|sIPtBs#oHb)u2?A=&YAzj$#+M}$4048#ZX*tW?5pZomW{=1uHlp>n=B7(RL$ zLKvh@Cp?5ONL9syI34j2!pw9!;UR>XsTv+iL-T_!O4jyM63-XH&U8AVBZQr4 zmgwj_Ezl7n9m?s1ju3fLW{HmcX@QOq*;Y;`bc9IC5>{wP6m&&$7lhsU1(?^$=3WW0 z$!YGMTi<8Qy6@9VST1{X(chU1q_oUKPJ+4NVfmvA509A-ww%Yh^0u|*@r|OR`M_MP zEoaTEwWTqfwdJw%YHfM$X=QC$@#rGV$ISzSSbsY1YR-Oi;o4=9XJi74k#CgEOTTe$kWFB33Xr3qeFkAiPN%L?jl=cKVo;=E)Fr0zka-Q5>UPAeWCo#gYdj@XJYeY> z2(B97KZDdJ7=zoq50N7r_lm9e0&2OKDu0ok9`$t5_cPikFS!$ z$5(BKkNxQI@!_O#_?Y(4Iq%|#*d7z__D+%ClJ)`bvAVW=QwNUoJGn}65?@1 z#JG#=fNXqdqJDu$N{i74#vAmOijN8o9la&hLj$_0;Ops&I_ARf6nT6zI(}ZvrQgL; zH`%fZJJdnW3UrhXzsvkE*@-Y3j`%l#F!gJ$z1ZU`MRISE>g{CCH}?}zcSY4DY|-2J z_H>dmh!(um`{=6MqpFKmLZ&8i;>P+w?div=0;*_fFt=f{HH2PH!F0Kz0u$FqF_-P> zXCrU!hFP2Js(uJ5$NqxMORc4fnxk_S@;J6Jse`z>pvW!f@G(GvtHg5%Jxqct=bekBKf%yf`j?9v3gJkuQjSwc@IT_)VSokEA$SFK%cMKUgF- zG>S_Xi$jaWLn*N*eTq^!-)*OIz9nLqF)|S@_GYDcEME&Do<~VSUHS1(EKi4OWnHp1u9s_wOX43Gt@K$ zk#oOG&hSN>OsmO+{$ToUQ0rAk#~3qeKf+Y9`>03_QiL)Od-?nhyMpiE?1%lbh)Q7N zLVGaWum?j7y8<_|;rLz0f6n0{WeOAZf1_w&XzH{Flfa z0$R&D%!P<@w~Ex}F%4M7F!!2}VeVyp;=-n=lM#)i$J1DD0lFXr z?_?lBLzD1m1nFhONs=^-^fFvFNz!molB8jxmr^BV_R*<>LG%*P0ny8#Loaq_D3uM$ zq8Bx(3d8l&O9-#Q!B8w>&|~AFRz_4Nqd{aIq_xze)>4{Yfmp_A5P%lK%kXsK@FBbm zt8@53bliSgpd*Br;pv2q5MGAW(ZLlgZEKCT-jg~X3S%|GB(Roc=5aRaQTson^@6LL+DwAgIShP;Z4G z+dhM!ZxMpNWh1B`5wzDTRI}0(@iT?ETZsRRi1$WCIwo$K|5`O=f?b2Lw_wO3rt|_l zcQofUaDtf%Zd}?t1+h;rr|xO_=x!as(A9M-Y&hytpO(`>5IgrHsj} zE1F~fD6my3>sWJ#yv;d+8*+Tas)%e5GY}2Z*E6;9PkZ_SAk%~|ZL~6sm70!Lgz#qo zn~uYn!TMt}T;KUeD(5p0H|2b1Hp{DM1%~v8ybad{ll}^=AZTk5xEuUi#4;h_T`$mN z6KH4epMhdv#yF_QSkZ=K4&T{ggY2di&e4@2@Vmlz2ljb^+cHS2#x7c^|IzQJR}4!%05wtm!B8Dv`3?vg=f8OkUfd^g!04Y9!&G2hjDPAqtiv3B4;qb~m$ zt02)182)oT@t^B${xeRwmn@0aPZI5nQzg+lqGd9RDK85pX$mZ9=Bdnkh~-xBHteht zcg7mkO0D@JiJoUk*7L3F3)O&|ha;8?S7fIxU0WBvD*JbUh9+DZnzU)?Y?p?n3=Qof z8ro&k&_S&3_w$sYq4N_c!>*2sP4QD%m?UK>M;%rz?H0GL@N%*exfIvqmsXBETj7^y zD>;#4K^@+7!{LnJ6(?|MPNWzwbv0JE~@KuY4~_ zch5JnP?FHbF)y{Y1_fZUW#a*OFec|ZPM>JmH~|+2i0L0t9%0d)8zh=rsmrL+@yaP zW$~EeVD#x*()Zyec$j(wTnX~ydkO7<52(#^0wBuY)2M|5Fon#PjZg4iU0+?sHoaqc%2IXNTki08Pi{vjgt8_t^F7C~0Fmjg~O z;eEldr%g8!uUjn3&46d==UwE1Q&8h*U^6{*)x$ixR}p(m6;SF%jcW(9R~W?(t0vhk z>26ESqx;*b@t`?Kmt;P~jHz3G4EgH_();j9(hw!% z1(KJ)HY9LPDL8=mDD!H^$pHLsb=LZ~Q?@jMIw$RF~UZYQbxVZXzpp;CzO35Kx zDOu?%C5MetvYV8W-L_J41eKD%q>WPY2U1ENj*6>l=TRwXsHT*xu#}Qv+bgs~SN+nJ z9ST@zqhMMGZnMpvRNgxxxrnO)_4+lLKELuKNyOD;RLMW3!E`ev^b@6IYE@OF3ymX7 z6=jABbd1o-1ifZdT_#AEQEJVYx_f?(c++lw&#g z;S&;*ej$bS_P;kN~chY!Xd=LA&pwl298JoX?5ZlTpab3f^pc}_z*H#(kVJN zJj7h~2y#LE<}`9yP*SEfm)%}uPYO$`IHWG4q+m=Va`vVdAqza@$(yMQip4rL=P5#9 zhR{dz&mkm8$+|iHB<(P*hzixOgE)vEkc*D|WdF&cqTvbp44^Wn^~GKk!3WAcK7fx$ zJ&T}-R!PYm_1L*(+~b$q()UF~>NU7DM%9yX676rsn+I5T9Z;yHcL{P|)JW&|+EsXm z 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 + * is released under the MIT License + */ + + + +/* ******************* */ +/* Constructor & Init */ +/* ******************* */ +var SWFUpload; +var swfobject; + +if (SWFUpload == undefined) { + SWFUpload = function (settings) { + this.initSWFUpload(settings); + }; +} + +SWFUpload.prototype.initSWFUpload = function (userSettings) { + try { + this.customSettings = {}; // A container where developers can place their own settings associated with this instance. + this.settings = {}; + this.eventQueue = []; + this.movieName = "SWFUpload_" + SWFUpload.movieCount++; + this.movieElement = null; + + + // Setup global control tracking + SWFUpload.instances[this.movieName] = this; + + // Load the settings. Load the Flash movie. + this.initSettings(userSettings); + this.loadSupport(); + if (this.swfuploadPreload()) { + this.loadFlash(); + } + + this.displayDebugInfo(); + } catch (ex) { + delete SWFUpload.instances[this.movieName]; + throw ex; + } +}; + +/* *************** */ +/* Static Members */ +/* *************** */ +SWFUpload.instances = {}; +SWFUpload.movieCount = 0; +SWFUpload.version = "2.5.0 2010-01-15 Beta 2"; +SWFUpload.QUEUE_ERROR = { + QUEUE_LIMIT_EXCEEDED : -100, + FILE_EXCEEDS_SIZE_LIMIT : -110, + ZERO_BYTE_FILE : -120, + INVALID_FILETYPE : -130 +}; +SWFUpload.UPLOAD_ERROR = { + HTTP_ERROR : -200, + MISSING_UPLOAD_URL : -210, + IO_ERROR : -220, + SECURITY_ERROR : -230, + UPLOAD_LIMIT_EXCEEDED : -240, + UPLOAD_FAILED : -250, + SPECIFIED_FILE_ID_NOT_FOUND : -260, + FILE_VALIDATION_FAILED : -270, + FILE_CANCELLED : -280, + UPLOAD_STOPPED : -290, + RESIZE : -300 +}; +SWFUpload.FILE_STATUS = { + QUEUED : -1, + IN_PROGRESS : -2, + ERROR : -3, + COMPLETE : -4, + CANCELLED : -5 +}; +SWFUpload.UPLOAD_TYPE = { + NORMAL : -1, + RESIZED : -2 +}; + +SWFUpload.BUTTON_ACTION = { + SELECT_FILE : -100, + SELECT_FILES : -110, + START_UPLOAD : -120, + JAVASCRIPT : -130, // DEPRECATED + NONE : -130 +}; +SWFUpload.CURSOR = { + ARROW : -1, + HAND : -2 +}; +SWFUpload.WINDOW_MODE = { + WINDOW : "window", + TRANSPARENT : "transparent", + OPAQUE : "opaque" +}; + +SWFUpload.RESIZE_ENCODING = { + JPEG : -1, + PNG : -2 +}; + +// Private: takes a URL, determines if it is relative and converts to an absolute URL +// using the current site. Only processes the URL if it can, otherwise returns the URL untouched +SWFUpload.completeURL = function (url) { + try { + var path = "", indexSlash = -1; + if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//) || url === "") { + return url; + } + + indexSlash = window.location.pathname.lastIndexOf("/"); + if (indexSlash <= 0) { + path = "/"; + } else { + path = window.location.pathname.substr(0, indexSlash) + "/"; + } + + return path + url; + } catch (ex) { + return url; + } +}; + +// Public: assign a new function to onload to use swfobject's domLoad functionality +SWFUpload.onload = function () {}; + + +/* ******************** */ +/* Instance Members */ +/* ******************** */ + +// Private: initSettings ensures that all the +// settings are set, getting a default value if one was not assigned. +SWFUpload.prototype.initSettings = function (userSettings) { + this.ensureDefault = function (settingName, defaultValue) { + var setting = userSettings[settingName]; + if (setting != undefined) { + this.settings[settingName] = setting; + } else { + this.settings[settingName] = defaultValue; + } + }; + + // Upload backend settings + this.ensureDefault("upload_url", ""); + this.ensureDefault("preserve_relative_urls", false); + this.ensureDefault("file_post_name", "Filedata"); + this.ensureDefault("post_params", {}); + this.ensureDefault("use_query_string", false); + this.ensureDefault("requeue_on_error", false); + this.ensureDefault("http_success", []); + this.ensureDefault("assume_success_timeout", 0); + + // File Settings + this.ensureDefault("file_types", "*.*"); + this.ensureDefault("file_types_description", "All Files"); + this.ensureDefault("file_size_limit", 0); // Default zero means "unlimited" + this.ensureDefault("file_upload_limit", 0); + this.ensureDefault("file_queue_limit", 0); + + // Flash Settings + this.ensureDefault("flash_url", "swfupload.swf"); + this.ensureDefault("flash9_url", "swfupload_fp9.swf"); + this.ensureDefault("prevent_swf_caching", true); + + // Button Settings + this.ensureDefault("button_image_url", ""); + this.ensureDefault("button_width", 1); + this.ensureDefault("button_height", 1); + this.ensureDefault("button_text", ""); + this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;"); + this.ensureDefault("button_text_top_padding", 0); + this.ensureDefault("button_text_left_padding", 0); + this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES); + this.ensureDefault("button_disabled", false); + this.ensureDefault("button_placeholder_id", ""); + this.ensureDefault("button_placeholder", null); + this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW); + this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW); + + // Debug Settings + this.ensureDefault("debug", false); + this.settings.debug_enabled = this.settings.debug; // Here to maintain v2 API + + // Event Handlers + this.settings.return_upload_start_handler = this.returnUploadStart; + this.ensureDefault("swfupload_preload_handler", null); + this.ensureDefault("swfupload_load_failed_handler", null); + this.ensureDefault("swfupload_loaded_handler", null); + this.ensureDefault("file_dialog_start_handler", null); + this.ensureDefault("file_queued_handler", null); + this.ensureDefault("file_queue_error_handler", null); + this.ensureDefault("file_dialog_complete_handler", null); + + this.ensureDefault("upload_resize_start_handler", null); + this.ensureDefault("upload_start_handler", null); + this.ensureDefault("upload_progress_handler", null); + this.ensureDefault("upload_error_handler", null); + this.ensureDefault("upload_success_handler", null); + this.ensureDefault("upload_complete_handler", null); + + this.ensureDefault("mouse_click_handler", null); + this.ensureDefault("mouse_out_handler", null); + this.ensureDefault("mouse_over_handler", null); + + this.ensureDefault("debug_handler", this.debugMessage); + + this.ensureDefault("custom_settings", {}); + + // Other settings + this.customSettings = this.settings.custom_settings; + + // Update the flash url if needed + if (!!this.settings.prevent_swf_caching) { + this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime(); + this.settings.flash9_url = this.settings.flash9_url + (this.settings.flash9_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime(); + } + + if (!this.settings.preserve_relative_urls) { + this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url); + this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url); + } + + delete this.ensureDefault; +}; + +// Initializes the supported functionality based the Flash Player version, state, and event that occur during initialization +SWFUpload.prototype.loadSupport = function () { + this.support = { + loading : swfobject.hasFlashPlayerVersion("9.0.28"), + imageResize : swfobject.hasFlashPlayerVersion("10.0.0") + }; + +}; + +// Private: loadFlash replaces the button_placeholder element with the flash movie. +SWFUpload.prototype.loadFlash = function () { + var targetElement, tempParent, wrapperType, flashHTML, els; + + if (!this.support.loading) { + this.queueEvent("swfupload_load_failed_handler", ["Flash Player doesn't support SWFUpload"]); + return; + } + + // Make sure an element with the ID we are going to use doesn't already exist + if (document.getElementById(this.movieName) !== null) { + this.support.loading = false; + this.queueEvent("swfupload_load_failed_handler", ["Element ID already in use"]); + return; + } + + // Get the element where we will be placing the flash movie + targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder; + + if (targetElement == undefined) { + this.support.loading = false; + this.queueEvent("swfupload_load_failed_handler", ["button place holder not found"]); + return; + } + + wrapperType = (targetElement.currentStyle && targetElement.currentStyle["display"] || window.getComputedStyle && document.defaultView.getComputedStyle(targetElement, null).getPropertyValue("display")) !== "block" ? "span" : "div"; + + // Append the container and load the flash + tempParent = document.createElement(wrapperType); + + flashHTML = this.getFlashHTML(); + + try { + tempParent.innerHTML = flashHTML; // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers) + } catch (ex) { + this.support.loading = false; + this.queueEvent("swfupload_load_failed_handler", ["Exception loading Flash HTML into placeholder"]); + return; + } + + // Try to get the movie element immediately + els = tempParent.getElementsByTagName("object"); + if (!els || els.length > 1 || els.length === 0) { + this.support.loading = false; + this.queueEvent("swfupload_load_failed_handler", ["Unable to find movie after adding to DOM"]); + return; + } else if (els.length === 1) { + this.movieElement = els[0]; + } + + targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement); + + // Fix IE Flash/Form bug + if (window[this.movieName] == undefined) { + window[this.movieName] = this.getMovieElement(); + } +}; + +// Private: getFlashHTML generates the object tag needed to embed the flash in to the document +SWFUpload.prototype.getFlashHTML = function (flashVersion) { + // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay + return ['', + '', + '', + '', + '', + '', + ''].join(""); +}; + +// Private: getFlashVars builds the parameter string that will be passed +// to flash in the flashvars param. +SWFUpload.prototype.getFlashVars = function () { + // Build a string from the post param object + var httpSuccessString, paramString; + + paramString = this.buildParamString(); + httpSuccessString = this.settings.http_success.join(","); + + // Build the parameter string + return ["movieName=", encodeURIComponent(this.movieName), + "&uploadURL=", encodeURIComponent(this.settings.upload_url), + "&useQueryString=", encodeURIComponent(this.settings.use_query_string), + "&requeueOnError=", encodeURIComponent(this.settings.requeue_on_error), + "&httpSuccess=", encodeURIComponent(httpSuccessString), + "&assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout), + "&params=", encodeURIComponent(paramString), + "&filePostName=", encodeURIComponent(this.settings.file_post_name), + "&fileTypes=", encodeURIComponent(this.settings.file_types), + "&fileTypesDescription=", encodeURIComponent(this.settings.file_types_description), + "&fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit), + "&fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit), + "&fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit), + "&debugEnabled=", encodeURIComponent(this.settings.debug_enabled), + "&buttonImageURL=", encodeURIComponent(this.settings.button_image_url), + "&buttonWidth=", encodeURIComponent(this.settings.button_width), + "&buttonHeight=", encodeURIComponent(this.settings.button_height), + "&buttonText=", encodeURIComponent(this.settings.button_text), + "&buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding), + "&buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding), + "&buttonTextStyle=", encodeURIComponent(this.settings.button_text_style), + "&buttonAction=", encodeURIComponent(this.settings.button_action), + "&buttonDisabled=", encodeURIComponent(this.settings.button_disabled), + "&buttonCursor=", encodeURIComponent(this.settings.button_cursor) + ].join(""); +}; + +// Public: get retrieves the DOM reference to the Flash element added by SWFUpload +// The element is cached after the first lookup +SWFUpload.prototype.getMovieElement = function () { + if (this.movieElement == undefined) { + this.movieElement = document.getElementById(this.movieName); + } + + if (this.movieElement === null) { + throw "Could not find Flash element"; + } + + return this.movieElement; +}; + +// Private: buildParamString takes the name/value pairs in the post_params setting object +// and joins them up in to a string formatted "name=value&name=value" +SWFUpload.prototype.buildParamString = function () { + var name, postParams, paramStringPairs = []; + + postParams = this.settings.post_params; + + if (typeof(postParams) === "object") { + for (name in postParams) { + if (postParams.hasOwnProperty(name)) { + paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString())); + } + } + } + + return paramStringPairs.join("&"); +}; + +// Public: Used to remove a SWFUpload instance from the page. This method strives to remove +// all references to the SWF, and other objects so memory is properly freed. +// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state. +// Credits: Major improvements provided by steffen +SWFUpload.prototype.destroy = function () { + var movieElement; + + try { + // Make sure Flash is done before we try to remove it + this.cancelUpload(null, false); + + movieElement = this.cleanUp(); + + // Remove the SWFUpload DOM nodes + if (movieElement) { + // Remove the Movie Element from the page + try { + movieElement.parentNode.removeChild(movieElement); + } catch (ex) {} + } + + // Remove IE form fix reference + window[this.movieName] = null; + + // Destroy other references + SWFUpload.instances[this.movieName] = null; + delete SWFUpload.instances[this.movieName]; + + this.movieElement = null; + this.settings = null; + this.customSettings = null; + this.eventQueue = null; + this.movieName = null; + + + return true; + } catch (ex2) { + return false; + } +}; + + +// Public: displayDebugInfo prints out settings and configuration +// information about this SWFUpload instance. +// This function (and any references to it) can be deleted when placing +// SWFUpload in production. +SWFUpload.prototype.displayDebugInfo = function () { + this.debug( + [ + "---SWFUpload Instance Info---\n", + "Version: ", SWFUpload.version, "\n", + "Movie Name: ", this.movieName, "\n", + "Settings:\n", + "\t", "upload_url: ", this.settings.upload_url, "\n", + "\t", "flash_url: ", this.settings.flash_url, "\n", + "\t", "flash9_url: ", this.settings.flash9_url, "\n", + "\t", "use_query_string: ", this.settings.use_query_string.toString(), "\n", + "\t", "requeue_on_error: ", this.settings.requeue_on_error.toString(), "\n", + "\t", "http_success: ", this.settings.http_success.join(", "), "\n", + "\t", "assume_success_timeout: ", this.settings.assume_success_timeout, "\n", + "\t", "file_post_name: ", this.settings.file_post_name, "\n", + "\t", "post_params: ", this.settings.post_params.toString(), "\n", + "\t", "file_types: ", this.settings.file_types, "\n", + "\t", "file_types_description: ", this.settings.file_types_description, "\n", + "\t", "file_size_limit: ", this.settings.file_size_limit, "\n", + "\t", "file_upload_limit: ", this.settings.file_upload_limit, "\n", + "\t", "file_queue_limit: ", this.settings.file_queue_limit, "\n", + "\t", "debug: ", this.settings.debug.toString(), "\n", + + "\t", "prevent_swf_caching: ", this.settings.prevent_swf_caching.toString(), "\n", + + "\t", "button_placeholder_id: ", this.settings.button_placeholder_id.toString(), "\n", + "\t", "button_placeholder: ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n", + "\t", "button_image_url: ", this.settings.button_image_url.toString(), "\n", + "\t", "button_width: ", this.settings.button_width.toString(), "\n", + "\t", "button_height: ", this.settings.button_height.toString(), "\n", + "\t", "button_text: ", this.settings.button_text.toString(), "\n", + "\t", "button_text_style: ", this.settings.button_text_style.toString(), "\n", + "\t", "button_text_top_padding: ", this.settings.button_text_top_padding.toString(), "\n", + "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n", + "\t", "button_action: ", this.settings.button_action.toString(), "\n", + "\t", "button_cursor: ", this.settings.button_cursor.toString(), "\n", + "\t", "button_disabled: ", this.settings.button_disabled.toString(), "\n", + + "\t", "custom_settings: ", this.settings.custom_settings.toString(), "\n", + "Event Handlers:\n", + "\t", "swfupload_preload_handler assigned: ", (typeof this.settings.swfupload_preload_handler === "function").toString(), "\n", + "\t", "swfupload_load_failed_handler assigned: ", (typeof this.settings.swfupload_load_failed_handler === "function").toString(), "\n", + "\t", "swfupload_loaded_handler assigned: ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n", + "\t", "mouse_click_handler assigned: ", (typeof this.settings.mouse_click_handler === "function").toString(), "\n", + "\t", "mouse_over_handler assigned: ", (typeof this.settings.mouse_over_handler === "function").toString(), "\n", + "\t", "mouse_out_handler assigned: ", (typeof this.settings.mouse_out_handler === "function").toString(), "\n", + "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n", + "\t", "file_queued_handler assigned: ", (typeof this.settings.file_queued_handler === "function").toString(), "\n", + "\t", "file_queue_error_handler assigned: ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n", + "\t", "upload_resize_start_handler assigned: ", (typeof this.settings.upload_resize_start_handler === "function").toString(), "\n", + "\t", "upload_start_handler assigned: ", (typeof this.settings.upload_start_handler === "function").toString(), "\n", + "\t", "upload_progress_handler assigned: ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n", + "\t", "upload_error_handler assigned: ", (typeof this.settings.upload_error_handler === "function").toString(), "\n", + "\t", "upload_success_handler assigned: ", (typeof this.settings.upload_success_handler === "function").toString(), "\n", + "\t", "upload_complete_handler assigned: ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n", + "\t", "debug_handler assigned: ", (typeof this.settings.debug_handler === "function").toString(), "\n", + + "Support:\n", + "\t", "Load: ", (this.support.loading ? "Yes" : "No"), "\n", + "\t", "Image Resize: ", (this.support.imageResize ? "Yes" : "No"), "\n" + + ].join("") + ); +}; + +/* Note: addSetting and getSetting are no longer used by SWFUpload but are included + the maintain v2 API compatibility +*/ +// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used. +SWFUpload.prototype.addSetting = function (name, value, default_value) { + if (value == undefined) { + return (this.settings[name] = default_value); + } else { + return (this.settings[name] = value); + } +}; + +// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found. +SWFUpload.prototype.getSetting = function (name) { + if (this.settings[name] != undefined) { + return this.settings[name]; + } + + return ""; +}; + + + +// Private: callFlash handles function calls made to the Flash element. +// Calls are made with a setTimeout for some functions to work around +// bugs in the ExternalInterface library. +SWFUpload.prototype.callFlash = function (functionName, argumentArray) { + var movieElement, returnValue, returnString; + + argumentArray = argumentArray || []; + movieElement = this.getMovieElement(); + + // Flash's method if calling ExternalInterface methods (code adapted from MooTools). + try { + if (movieElement != undefined) { + returnString = movieElement.CallFunction('' + __flash__argumentsToXML(argumentArray, 0) + ''); + returnValue = eval(returnString); + } else { + this.debug("Can't call flash because the movie wasn't found."); + } + } catch (ex) { + this.debug("Exception calling flash function '" + functionName + "': " + ex.message); + } + + // Unescape file post param values + if (returnValue != undefined && typeof returnValue.post === "object") { + returnValue = this.unescapeFilePostParams(returnValue); + } + + return returnValue; +}; + +/* ***************************** + -- Flash control methods -- + Your UI should use these + to operate SWFUpload + ***************************** */ + +// WARNING: this function does not work in Flash Player 10 +// Public: selectFile causes a File Selection Dialog window to appear. This +// dialog only allows 1 file to be selected. +SWFUpload.prototype.selectFile = function () { + this.callFlash("SelectFile"); +}; + +// WARNING: this function does not work in Flash Player 10 +// Public: selectFiles causes a File Selection Dialog window to appear/ This +// dialog allows the user to select any number of files +// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names. +// If the selection name length is too long the dialog will fail in an unpredictable manner. There is no work-around +// for this bug. +SWFUpload.prototype.selectFiles = function () { + this.callFlash("SelectFiles"); +}; + + +// Public: startUpload starts uploading the first file in the queue unless +// the optional parameter 'fileID' specifies the ID +SWFUpload.prototype.startUpload = function (fileID) { + this.callFlash("StartUpload", [fileID]); +}; + +// Public: startUpload starts uploading the first file in the queue unless +// the optional parameter 'fileID' specifies the ID +SWFUpload.prototype.startResizedUpload = function (fileID, width, height, encoding, quality, allowEnlarging) { + this.callFlash("StartUpload", [fileID, { "width": width, "height" : height, "encoding" : encoding, "quality" : quality, "allowEnlarging" : allowEnlarging }]); +}; + +// Public: cancelUpload cancels any queued file. The fileID parameter may be the file ID or index. +// If you do not specify a fileID the current uploading file or first file in the queue is cancelled. +// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter. +SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) { + if (triggerErrorEvent !== false) { + triggerErrorEvent = true; + } + this.callFlash("CancelUpload", [fileID, triggerErrorEvent]); +}; + +// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue. +// If nothing is currently uploading then nothing happens. +SWFUpload.prototype.stopUpload = function () { + this.callFlash("StopUpload"); +}; + + +// Public: requeueUpload requeues any file. If the file is requeued or already queued true is returned. +// If the file is not found or is currently uploading false is returned. Requeuing a file bypasses the +// file size, queue size, upload limit and other queue checks. Certain files can't be requeued (e.g, invalid or zero bytes files). +SWFUpload.prototype.requeueUpload = function (indexOrFileID) { + return this.callFlash("RequeueUpload", [indexOrFileID]); +}; + + +/* ************************ + * Settings methods + * These methods change the SWFUpload settings. + * SWFUpload settings should not be changed directly on the settings object + * since many of the settings need to be passed to Flash in order to take + * effect. + * *********************** */ + +// Public: getStats gets the file statistics object. +SWFUpload.prototype.getStats = function () { + return this.callFlash("GetStats"); +}; + +// Public: setStats changes the SWFUpload statistics. You shouldn't need to +// change the statistics but you can. Changing the statistics does not +// affect SWFUpload accept for the successful_uploads count which is used +// by the upload_limit setting to determine how many files the user may upload. +SWFUpload.prototype.setStats = function (statsObject) { + this.callFlash("SetStats", [statsObject]); +}; + +// Public: getFile retrieves a File object by ID or Index. If the file is +// not found then 'null' is returned. +SWFUpload.prototype.getFile = function (fileID) { + if (typeof(fileID) === "number") { + return this.callFlash("GetFileByIndex", [fileID]); + } else { + return this.callFlash("GetFile", [fileID]); + } +}; + +// Public: getFileFromQueue retrieves a File object by ID or Index. If the file is +// not found then 'null' is returned. +SWFUpload.prototype.getQueueFile = function (fileID) { + if (typeof(fileID) === "number") { + return this.callFlash("GetFileByQueueIndex", [fileID]); + } else { + return this.callFlash("GetFile", [fileID]); + } +}; + + +// Public: addFileParam sets a name/value pair that will be posted with the +// file specified by the Files ID. If the name already exists then the +// exiting value will be overwritten. +SWFUpload.prototype.addFileParam = function (fileID, name, value) { + return this.callFlash("AddFileParam", [fileID, name, value]); +}; + +// Public: removeFileParam removes a previously set (by addFileParam) name/value +// pair from the specified file. +SWFUpload.prototype.removeFileParam = function (fileID, name) { + this.callFlash("RemoveFileParam", [fileID, name]); +}; + +// Public: setUploadUrl changes the upload_url setting. +SWFUpload.prototype.setUploadURL = function (url) { + this.settings.upload_url = url.toString(); + this.callFlash("SetUploadURL", [url]); +}; + +// Public: setPostParams changes the post_params setting +SWFUpload.prototype.setPostParams = function (paramsObject) { + this.settings.post_params = paramsObject; + this.callFlash("SetPostParams", [paramsObject]); +}; + +// Public: addPostParam adds post name/value pair. Each name can have only one value. +SWFUpload.prototype.addPostParam = function (name, value) { + this.settings.post_params[name] = value; + this.callFlash("SetPostParams", [this.settings.post_params]); +}; + +// Public: removePostParam deletes post name/value pair. +SWFUpload.prototype.removePostParam = function (name) { + delete this.settings.post_params[name]; + this.callFlash("SetPostParams", [this.settings.post_params]); +}; + +// Public: setFileTypes changes the file_types setting and the file_types_description setting +SWFUpload.prototype.setFileTypes = function (types, description) { + this.settings.file_types = types; + this.settings.file_types_description = description; + this.callFlash("SetFileTypes", [types, description]); +}; + +// Public: setFileSizeLimit changes the file_size_limit setting +SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) { + this.settings.file_size_limit = fileSizeLimit; + this.callFlash("SetFileSizeLimit", [fileSizeLimit]); +}; + +// Public: setFileUploadLimit changes the file_upload_limit setting +SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) { + this.settings.file_upload_limit = fileUploadLimit; + this.callFlash("SetFileUploadLimit", [fileUploadLimit]); +}; + +// Public: setFileQueueLimit changes the file_queue_limit setting +SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) { + this.settings.file_queue_limit = fileQueueLimit; + this.callFlash("SetFileQueueLimit", [fileQueueLimit]); +}; + +// Public: setFilePostName changes the file_post_name setting +SWFUpload.prototype.setFilePostName = function (filePostName) { + this.settings.file_post_name = filePostName; + this.callFlash("SetFilePostName", [filePostName]); +}; + +// Public: setUseQueryString changes the use_query_string setting +SWFUpload.prototype.setUseQueryString = function (useQueryString) { + this.settings.use_query_string = useQueryString; + this.callFlash("SetUseQueryString", [useQueryString]); +}; + +// Public: setRequeueOnError changes the requeue_on_error setting +SWFUpload.prototype.setRequeueOnError = function (requeueOnError) { + this.settings.requeue_on_error = requeueOnError; + this.callFlash("SetRequeueOnError", [requeueOnError]); +}; + +// Public: setHTTPSuccess changes the http_success setting +SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) { + if (typeof http_status_codes === "string") { + http_status_codes = http_status_codes.replace(" ", "").split(","); + } + + this.settings.http_success = http_status_codes; + this.callFlash("SetHTTPSuccess", [http_status_codes]); +}; + +// Public: setHTTPSuccess changes the http_success setting +SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) { + this.settings.assume_success_timeout = timeout_seconds; + this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]); +}; + +// Public: setDebugEnabled changes the debug_enabled setting +SWFUpload.prototype.setDebugEnabled = function (debugEnabled) { + this.settings.debug_enabled = debugEnabled; + this.callFlash("SetDebugEnabled", [debugEnabled]); +}; + +// Public: setButtonImageURL loads a button image sprite +SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) { + if (buttonImageURL == undefined) { + buttonImageURL = ""; + } + + this.settings.button_image_url = buttonImageURL; + this.callFlash("SetButtonImageURL", [buttonImageURL]); +}; + +// Public: setButtonDimensions resizes the Flash Movie and button +SWFUpload.prototype.setButtonDimensions = function (width, height) { + this.settings.button_width = width; + this.settings.button_height = height; + + var movie = this.getMovieElement(); + if (movie != undefined) { + movie.style.width = width + "px"; + movie.style.height = height + "px"; + } + + this.callFlash("SetButtonDimensions", [width, height]); +}; +// Public: setButtonText Changes the text overlaid on the button +SWFUpload.prototype.setButtonText = function (html) { + this.settings.button_text = html; + this.callFlash("SetButtonText", [html]); +}; +// Public: setButtonTextPadding changes the top and left padding of the text overlay +SWFUpload.prototype.setButtonTextPadding = function (left, top) { + this.settings.button_text_top_padding = top; + this.settings.button_text_left_padding = left; + this.callFlash("SetButtonTextPadding", [left, top]); +}; + +// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button +SWFUpload.prototype.setButtonTextStyle = function (css) { + this.settings.button_text_style = css; + this.callFlash("SetButtonTextStyle", [css]); +}; +// Public: setButtonDisabled disables/enables the button +SWFUpload.prototype.setButtonDisabled = function (isDisabled) { + this.settings.button_disabled = isDisabled; + this.callFlash("SetButtonDisabled", [isDisabled]); +}; +// Public: setButtonAction sets the action that occurs when the button is clicked +SWFUpload.prototype.setButtonAction = function (buttonAction) { + this.settings.button_action = buttonAction; + this.callFlash("SetButtonAction", [buttonAction]); +}; + +// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button +SWFUpload.prototype.setButtonCursor = function (cursor) { + this.settings.button_cursor = cursor; + this.callFlash("SetButtonCursor", [cursor]); +}; + +/* ******************************* + Flash Event Interfaces + These functions are used by Flash to trigger the various + events. + + All these functions a Private. + + Because the ExternalInterface library is buggy the event calls + are added to a queue and the queue then executed by a setTimeout. + This ensures that events are executed in a determinate order and that + the ExternalInterface bugs are avoided. +******************************* */ + +SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) { + // Warning: Don't call this.debug inside here or you'll create an infinite loop + var self = this; + + if (argumentArray == undefined) { + argumentArray = []; + } else if (!(argumentArray instanceof Array)) { + argumentArray = [argumentArray]; + } + + if (typeof this.settings[handlerName] === "function") { + // Queue the event + this.eventQueue.push(function () { + this.settings[handlerName].apply(this, argumentArray); + }); + + // Execute the next queued event + setTimeout(function () { + self.executeNextEvent(); + }, 0); + + } else if (this.settings[handlerName] !== null) { + throw "Event handler " + handlerName + " is unknown or is not a function"; + } +}; + +// Private: Causes the next event in the queue to be executed. Since events are queued using a setTimeout +// we must queue them in order to garentee that they are executed in order. +SWFUpload.prototype.executeNextEvent = function () { + // Warning: Don't call this.debug inside here or you'll create an infinite loop + + var f = this.eventQueue ? this.eventQueue.shift() : null; + if (typeof(f) === "function") { + f.apply(this); + } +}; + +// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have +// properties that contain characters that are not valid for JavaScript identifiers. To work around this +// the Flash Component escapes the parameter names and we must unescape again before passing them along. +SWFUpload.prototype.unescapeFilePostParams = function (file) { + var reg = /[$]([0-9a-f]{4})/i, unescapedPost = {}, uk, k, match; + + if (file != undefined) { + for (k in file.post) { + if (file.post.hasOwnProperty(k)) { + uk = k; + while ((match = reg.exec(uk)) !== null) { + uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16))); + } + unescapedPost[uk] = file.post[k]; + } + } + + file.post = unescapedPost; + } + + return file; +}; + +// Private: This event is called by SWFUpload Init after we've determined what the user's Flash Player supports. +// Use the swfupload_preload_handler event setting to execute custom code when SWFUpload has loaded. +// Return false to prevent SWFUpload from loading and allow your script to do something else if your required feature is +// not supported +SWFUpload.prototype.swfuploadPreload = function () { + var returnValue; + if (typeof this.settings.swfupload_preload_handler === "function") { + returnValue = this.settings.swfupload_preload_handler.call(this); + } else if (this.settings.swfupload_preload_handler != undefined) { + throw "upload_start_handler must be a function"; + } + + // Convert undefined to true so if nothing is returned from the upload_start_handler it is + // interpretted as 'true'. + if (returnValue === undefined) { + returnValue = true; + } + + return !!returnValue; +} + +// Private: This event is called by Flash when it has finished loading. Don't modify this. +// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded. +SWFUpload.prototype.flashReady = function () { + // Check that the movie element is loaded correctly with its ExternalInterface methods defined + var movieElement = this.cleanUp(); + + if (!movieElement) { + this.debug("Flash called back ready but the flash movie can't be found."); + return; + } + + this.queueEvent("swfupload_loaded_handler"); +}; + +// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE. +// This function is called by Flash each time the ExternalInterface functions are created. +SWFUpload.prototype.cleanUp = function () { + var key, movieElement = this.getMovieElement(); + + // Pro-actively unhook all the Flash functions + try { + if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE + this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)"); + for (key in movieElement) { + try { + if (typeof(movieElement[key]) === "function") { + movieElement[key] = null; + } + } catch (ex) { + } + } + } + } catch (ex1) { + + } + + // Fix Flashes own cleanup code so if the SWF Movie was removed from the page + // it doesn't display errors. + window["__flash__removeCallback"] = function (instance, name) { + try { + if (instance) { + instance[name] = null; + } + } catch (flashEx) { + + } + }; + + return movieElement; +}; + +/* When the button_action is set to None this event gets fired and executes the mouse_click_handler */ +SWFUpload.prototype.mouseClick = function () { + this.queueEvent("mouse_click_handler"); +}; +SWFUpload.prototype.mouseOver = function () { + this.queueEvent("mouse_over_handler"); +}; +SWFUpload.prototype.mouseOut = function () { + this.queueEvent("mouse_out_handler"); +}; + +/* This is a chance to do something before the browse window opens */ +SWFUpload.prototype.fileDialogStart = function () { + this.queueEvent("file_dialog_start_handler"); +}; + + +/* Called when a file is successfully added to the queue. */ +SWFUpload.prototype.fileQueued = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("file_queued_handler", file); +}; + + +/* Handle errors that occur when an attempt to queue a file fails. */ +SWFUpload.prototype.fileQueueError = function (file, errorCode, message) { + file = this.unescapeFilePostParams(file); + this.queueEvent("file_queue_error_handler", [file, errorCode, message]); +}; + +/* Called after the file dialog has closed and the selected files have been queued. + You could call startUpload here if you want the queued files to begin uploading immediately. */ +SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) { + this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]); +}; + +SWFUpload.prototype.uploadResizeStart = function (file, resizeSettings) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_resize_start_handler", [file, resizeSettings.width, resizeSettings.height, resizeSettings.encoding, resizeSettings.quality]); +}; + +SWFUpload.prototype.uploadStart = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("return_upload_start_handler", file); +}; + +SWFUpload.prototype.returnUploadStart = function (file) { + var returnValue; + if (typeof this.settings.upload_start_handler === "function") { + file = this.unescapeFilePostParams(file); + returnValue = this.settings.upload_start_handler.call(this, file); + } else if (this.settings.upload_start_handler != undefined) { + throw "upload_start_handler must be a function"; + } + + // Convert undefined to true so if nothing is returned from the upload_start_handler it is + // interpretted as 'true'. + if (returnValue === undefined) { + returnValue = true; + } + + returnValue = !!returnValue; + + this.callFlash("ReturnUploadStart", [returnValue]); +}; + + + +SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]); +}; + +SWFUpload.prototype.uploadError = function (file, errorCode, message) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_error_handler", [file, errorCode, message]); +}; + +SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_success_handler", [file, serverData, responseReceived]); +}; + +SWFUpload.prototype.uploadComplete = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_complete_handler", file); +}; + +/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the + internal debug console. You can override this event and have messages written where you want. */ +SWFUpload.prototype.debug = function (message) { + this.queueEvent("debug_handler", message); +}; + + +/* ********************************** + Debug Console + The debug console is a self contained, in page location + for debug message to be sent. The Debug Console adds + itself to the body if necessary. + + The console is automatically scrolled as messages appear. + + If you are using your own debug handler or when you deploy to production and + have debug disabled you can remove these functions to reduce the file size + and complexity. +********************************** */ + +// Private: debugMessage is the default debug_handler. If you want to print debug messages +// call the debug() function. When overriding the function your own function should +// check to see if the debug setting is true before outputting debug information. +SWFUpload.prototype.debugMessage = function (message) { + var exceptionMessage, exceptionValues, key; + + if (this.settings.debug) { + exceptionValues = []; + + // Check for an exception object and print it nicely + if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") { + for (key in message) { + if (message.hasOwnProperty(key)) { + exceptionValues.push(key + ": " + message[key]); + } + } + exceptionMessage = exceptionValues.join("\n") || ""; + exceptionValues = exceptionMessage.split("\n"); + exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: "); + SWFUpload.Console.writeLine(exceptionMessage); + } else { + SWFUpload.Console.writeLine(message); + } + } +}; + +SWFUpload.Console = {}; +SWFUpload.Console.writeLine = function (message) { + var console, documentForm; + + try { + console = document.getElementById("SWFUpload_Console"); + + if (!console) { + documentForm = document.createElement("form"); + document.getElementsByTagName("body")[0].appendChild(documentForm); + + console = document.createElement("textarea"); + console.id = "SWFUpload_Console"; + console.style.fontFamily = "monospace"; + console.setAttribute("wrap", "off"); + console.wrap = "off"; + console.style.overflow = "auto"; + console.style.width = "700px"; + console.style.height = "350px"; + console.style.margin = "5px"; + documentForm.appendChild(console); + } + + console.value += message + "\n"; + + console.scrollTop = console.scrollHeight - console.clientHeight; + } catch (ex) { + alert("Exception: " + ex.name + " Message: " + ex.message); + } +}; + + +/* SWFObject v2.2 + is released under the MIT License +*/ +swfobject = function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var 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�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 literal 0 HcmV?d00001 diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/ExternalCall.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/ExternalCall.as new file mode 100644 index 0000000..b8c9499 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/ExternalCall.as @@ -0,0 +1,140 @@ +package { + import flash.external.ExternalInterface; + + internal class ExternalCall + { + + /*public function ExternalCall() + { + + } + */ + + public static function Simple(callback:String):void { + ExternalInterface.call(callback); + } + + public static function SendImage(callback:String, preview_name:String, file_id:String):void { + ExternalInterface.call(callback, EscapeMessage(preview_name), EscapeMessage(file_id)); + } + + public static function FileQueued(callback:String, file_object:Object):void { + ExternalInterface.call(callback, EscapeMessage(file_object)); + } + public static function FileQueueError(callback:String, error_code:Number, file_object:Object, message:String):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message)); + + } + public static function FileDialogComplete(callback:String, num_files_selected:Number, num_files_queued:Number, total_num_files_queued:Number):void { + + ExternalInterface.call(callback, EscapeMessage(num_files_selected), EscapeMessage(num_files_queued), EscapeMessage(total_num_files_queued)); + + } + + public static function UploadStart(callback:String, file_object:Object):void { + ExternalInterface.call(callback, EscapeMessage(file_object)); + } + + public static function UploadProgress(callback:String, file_object:Object, bytes_loaded:uint, bytes_total:uint):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(bytes_loaded), EscapeMessage(bytes_total)); + + } + public static function UploadSuccess(callback:String, file_object:Object, server_data:String, responseReceived:Boolean):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(server_data), EscapeMessage(responseReceived)); + + } + public static function UploadError(callback:String, error_code:Number, file_object:Object, message:String):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message)); + + } + public static function UploadComplete(callback:String, file_object:Object):void { + + ExternalInterface.call(callback, EscapeMessage(file_object)); + + } + public static function Debug(callback:String, message:String):void { + + ExternalInterface.call(callback, EscapeMessage(message)); + } + + public static function Bool(callback:String):Boolean { + return ExternalInterface.call(callback); + } + + public static function Generic(callback:String, ... rest):* { + var escapedRest:Array = new Array(); + for each (var i:* in rest) + { + escapedRest.push(EscapeMessage(i)); + } + + return ExternalInterface.call(callback, escapedRest); + } + + + /* Escapes all the backslashes which are not translated correctly in the Flash -> JavaScript Interface + * + * These functions had to be developed because the ExternalInterface has a bug that simply places the + * value a string in quotes (except for a " which is escaped) in a JavaScript string literal which + * is executed by the browser. These often results in improperly escaped string literals if your + * input string has any backslash characters. For example the string: + * "c:\Program Files\uploadtools\" + * is placed in a string literal (with quotes escaped) and becomes: + * var __flash__temp = "\"c:\Program Files\uploadtools\\""; + * This statement will cause errors when executed by the JavaScript interpreter: + * 1) The first \" is succesfully transformed to a " + * 2) \P is translated to P and the \ is lost + * 3) \u is interpreted as a unicode character and causes an error in IE + * 4) \\ is translated to \ + * 5) leaving an unescaped " which causes an error + * + * I fixed this by escaping \ characters in all outgoing strings. The above escaped string becomes: + * var __flash__temp = "\"c:\\Program Files\\uploadtools\\\""; + * which contains the correct string literal. + * + * Note: The "var __flash__temp = " portion of the example is part of the ExternalInterface not part of + * my escaping routine. + */ + private static function EscapeMessage(message:*):* { + if (message is String) { + message = EscapeString(message); + } + else if (message is Array) { + message = EscapeArray(message); + } + else if (message is Object) { + message = EscapeObject(message); + } + + return message; + } + + private static function EscapeString(message:String):String { + var replaceSlashPattern:RegExp = /\\/g; + var replaceNewLinePattern:RegExp = /\n/g; + var replaceCarraigeReturnPattern:RegExp = /\r/g; + var replaceNullPattern:RegExp = /\0/g; + + return message.replace(replaceSlashPattern, "\\\\").replace(replaceNewLinePattern, "\\n").replace(replaceCarraigeReturnPattern, "\\r").replace(replaceNullPattern, "\\0"); + } + private static function EscapeArray(message_array:Array):Array { + var length:uint = message_array.length; + var i:uint = 0; + for (i; i < length; i++) { + message_array[i] = EscapeMessage(message_array[i]); + } + return message_array; + } + private static function EscapeObject(message_obj:Object):Object { + for (var name:String in message_obj) { + message_obj[name] = EscapeMessage(message_obj[name]); + } + return message_obj; + } + + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/FileItem.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/FileItem.as new file mode 100644 index 0000000..1b06dd7 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/FileItem.as @@ -0,0 +1,123 @@ +package { + import flash.events.EventDispatcher; + import flash.net.FileReference; + + internal class FileItem + { + private static var file_id_sequence:Number = 0; // tracks the file id sequence + + private var postObject:Object; + public var file_reference:FileReference; + public var id:String; + public var index:Number = -1; + public var file_status:int = 0; + public var finalUploadProgress:Boolean = false; + public var upload_type:int = 0; + private var js_object:Object; + + public static var FILE_STATUS_QUEUED:int = -1; + public static var FILE_STATUS_IN_PROGRESS:int = -2; + public static var FILE_STATUS_ERROR:int = -3; + public static var FILE_STATUS_SUCCESS:int = -4; + public static var FILE_STATUS_CANCELLED:int = -5; + public static var FILE_STATUS_NEW:int = -6; // This file status should never be sent to JavaScript + + public static var UPLOAD_TYPE_NORMAL:int = -1; + + public function FileItem(file_reference:FileReference, control_id:String, index:Number) + { + this.postObject = {}; + this.file_reference = file_reference; + this.id = control_id + "_" + (FileItem.file_id_sequence++); + this.file_status = FileItem.FILE_STATUS_NEW; + this.index = index; + + + this.js_object = { + id: this.id, + index: this.index, + uploadtype: this.upload_type, + post: this.GetPostObject() + }; + + // Cleanly attempt to retrieve the FileReference info + // this can fail and so is wrapped in try..catch + try { + this.js_object.name = this.file_reference.name; + this.js_object.size = this.file_reference.size; + this.js_object.type = this.file_reference.type || ""; + this.js_object.creationdate = this.file_reference.creationDate || new Date(0); + this.js_object.modificationdate = this.file_reference.modificationDate || new Date(0); + } catch (ex:Error) { + this.file_status = FileItem.FILE_STATUS_ERROR; + } + + this.js_object.filestatus = this.file_status; + } + + public function GetUploader():EventDispatcher { + return this.file_reference; + } + + public function AddParam(name:String, value:String):void { + this.postObject[name] = value; + } + + public function RemoveParam(name:String):void { + delete this.postObject[name]; + } + + public function GetPostObject(escape:Boolean = false):Object { + if (escape) { + var escapedPostObject:Object = { }; + for (var k:String in this.postObject) { + if (this.postObject.hasOwnProperty(k)) { + var escapedName:String = FileItem.EscapeParamName(k); + escapedPostObject[escapedName] = this.postObject[k]; + } + } + return escapedPostObject; + } else { + return this.postObject; + } + } + + // Create the simply file object that is passed to the browser + public function ToJavaScriptObject():Object { + this.js_object.filestatus = this.file_status; + this.js_object.post = this.GetPostObject(true); + + return this.js_object; + } + + public function toString():String { + return "FileItem - ID: " + this.id; + } + + /* + // The purpose of this function is to escape the property names so when Flash + // passes them back to javascript they can be interpretted correctly. + // ***They have to be unescaped again by JavaScript.** + // + // This works around a bug where Flash sends objects this way: + // object.parametername = "value"; + // instead of + // object["parametername"] = "value"; + // This can be a problem if the parameter name has characters that are not + // allowed in JavaScript identifiers: + // object.parameter.name! = "value"; + // does not work but, + // object["parameter.name!"] = "value"; + // would have worked. + */ + public static function EscapeParamName(name:String):String { + name = name.replace(/[^a-z0-9_]/gi, FileItem.EscapeCharacter); + name = name.replace(/^[0-9]/, FileItem.EscapeCharacter); + return name; + } + public static function EscapeCharacter():String { + return "$" + ("0000" + arguments[0].charCodeAt(0).toString(16)).substr(-4, 4); + } + + } +} \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload FP9.as3proj b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload FP9.as3proj new file mode 100644 index 0000000..e166d25 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload FP9.as3proj @@ -0,0 +1,81 @@ +ďťż + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deploy.bat + + + + \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload.as b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload.as new file mode 100644 index 0000000..014d9da --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/SWFUpload.as @@ -0,0 +1,1572 @@ +/* Todo: +* Missing initial progress event +* Improve resizing/encoding so it doesn't lock up browser. +* */ + +package { + import flash.display.BlendMode; + import flash.display.DisplayObjectContainer; + import flash.display.Loader; + import flash.display.Stage; + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.net.FileReferenceList; + import flash.net.FileReference; + import flash.net.FileFilter; + import flash.net.URLRequest; + import flash.net.URLRequestMethod; + import flash.net.URLVariables; + import flash.events.*; + import flash.external.ExternalInterface; + import flash.system.Security; + import flash.text.AntiAliasType; + import flash.text.GridFitType; + import flash.text.StaticText; + import flash.text.StyleSheet; + import flash.text.TextDisplayMode; + import flash.text.TextField; + import flash.text.TextFieldType; + import flash.text.TextFieldAutoSize; + import flash.text.TextFormat; + import flash.ui.Mouse; + import flash.utils.ByteArray; + import flash.utils.Timer; + + import FileItem; + import ExternalCall; + + public class SWFUpload extends Sprite { + // Cause SWFUpload to start as soon as the movie starts + public static function main():void + { + var SWFUpload:SWFUpload = new SWFUpload(); + } + + private const build_number:String = "2.5.0 FP9 2010-02-17 Beta 3"; + + // State tracking variables + private var fileBrowserMany:FileReferenceList = new FileReferenceList(); + private var fileBrowserOne:FileReference = null; // This isn't set because it can't be reused like the FileReferenceList. It gets setup in the SelectFile method + + private var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded. + private var current_file_item:FileItem = null; // the item that is currently being uploaded. + + private var file_index:Array = new Array(); + + private var successful_uploads:Number = 0; // Tracks the uploads that have been completed + private var queue_errors:Number = 0; // Tracks files rejected during queueing + private var upload_errors:Number = 0; // Tracks files that fail upload + private var upload_cancelled:Number = 0; // Tracks number of cancelled files + private var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded. + + private var valid_file_extensions:Array = new Array();// Holds the parsed valid extensions. + + private var serverDataTimer:Timer = null; + private var assumeSuccessTimer:Timer = null; + + private var sizeTimer:Timer; + private var hasCalledFlashReady:Boolean = false; + + // Callbacks + private var flashReady_Callback:String; + private var fileDialogStart_Callback:String; + private var fileQueued_Callback:String; + private var fileQueueError_Callback:String; + private var fileDialogComplete_Callback:String; + + private var uploadStart_Callback:String; + private var uploadProgress_Callback:String; + private var uploadError_Callback:String; + private var uploadSuccess_Callback:String; + + private var uploadComplete_Callback:String; + + private var debug_Callback:String; + + private var mouseOut_Callback:String; + private var mouseOver_Callback:String; + private var mouseClick_Callback:String; + + // Values passed in from the HTML + private var movieName:String; + private var uploadURL:String; + private var filePostName:String; + private var uploadPostObject:Object; + private var fileTypes:String; + private var fileTypesDescription:String; + private var fileSizeLimit:Number; + private var fileUploadLimit:Number = 0; + private var fileQueueLimit:Number = 0; + private var useQueryString:Boolean = false; + private var requeueOnError:Boolean = false; + private var httpSuccess:Array = []; + private var assumeSuccessTimeout:Number = 0; + private var debugEnabled:Boolean; + + private var buttonLoader:Loader; + private var buttonTextField:TextField; + private var buttonCursorSprite:Sprite; + private var buttonImageURL:String; + //private var buttonWidth:Number; + //private var buttonHeight:Number; + private var buttonText:String; + private var buttonTextStyle:String; + private var buttonTextTopPadding:Number; + private var buttonTextLeftPadding:Number; + private var buttonAction:Number; + private var buttonCursor:Number; + private var buttonStateOver:Boolean; + private var buttonStateMouseDown:Boolean; + private var buttonStateDisabled:Boolean; + + // Error code "constants" + // Size check constants + private var SIZE_TOO_BIG:Number = 1; + private var SIZE_ZERO_BYTE:Number = -1; + private var SIZE_OK:Number = 0; + + // Queue errors + private var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -100; + private var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -110; + private var ERROR_CODE_ZERO_BYTE_FILE:Number = -120; + private var ERROR_CODE_INVALID_FILETYPE:Number = -130; + + // Upload Errors + private var ERROR_CODE_HTTP_ERROR:Number = -200; + private var ERROR_CODE_MISSING_UPLOAD_URL:Number = -210; + private var ERROR_CODE_IO_ERROR:Number = -220; + private var ERROR_CODE_SECURITY_ERROR:Number = -230; + private var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -240; + private var ERROR_CODE_UPLOAD_FAILED:Number = -250; + private var ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND:Number = -260; + private var ERROR_CODE_FILE_VALIDATION_FAILED:Number = -270; + private var ERROR_CODE_FILE_CANCELLED:Number = -280; + private var ERROR_CODE_UPLOAD_STOPPED:Number = -290; + + + // Button Actions + private var BUTTON_ACTION_SELECT_FILE:Number = -100; + private var BUTTON_ACTION_SELECT_FILES:Number = -110; + private var BUTTON_ACTION_START_UPLOAD:Number = -120; + private var BUTTON_ACTION_NONE:Number = -130; + private var BUTTON_ACTION_JAVASCRIPT:Number = -130; // DEPRECATED + + private var BUTTON_CURSOR_ARROW:Number = -1; + private var BUTTON_CURSOR_HAND:Number = -2; + + public function SWFUpload() { + // Do the feature detection. Make sure this version of Flash supports the features we need. If not + // abort initialization. + if (!flash.net.FileReferenceList || !flash.net.FileReference || !flash.net.URLRequest || !flash.external.ExternalInterface || !flash.external.ExternalInterface.available || !DataEvent.UPLOAD_COMPLETE_DATA) { + return; + } + + var self:SWFUpload = this; + Security.allowDomain("*"); // Allow uploading to any domain + Security.allowInsecureDomain("*"); // Allow uploading from HTTP to HTTPS and HTTPS to HTTP + + // Keep Flash Player busy so it doesn't show the "flash script is running slowly" error + var counter:Number = 0; + root.addEventListener(Event.ENTER_FRAME, function ():void { if (++counter > 100) counter = 0; }); + + // Setup file FileReferenceList events + this.fileBrowserMany.addEventListener(Event.SELECT, this.Select_Many_Handler); + this.fileBrowserMany.addEventListener(Event.CANCEL, this.DialogCancelled_Handler); + + + this.stage.align = StageAlign.TOP_LEFT; + this.stage.scaleMode = StageScaleMode.NO_SCALE; + this.stage.addEventListener(Event.RESIZE, function (e:Event):void { + self.HandleStageResize(e); + }); + + // Setup the button and text label + this.buttonLoader = new Loader(); + var doNothing:Function = function ():void { }; + this.buttonLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, doNothing ); + this.buttonLoader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, doNothing ); + this.buttonLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.ButtonImageLoaded ); + this.stage.addChild(this.buttonLoader); + + this.stage.addEventListener(MouseEvent.CLICK, function (event:MouseEvent):void { + self.UpdateButtonState(); + self.ButtonClickHandler(event); + }); + this.stage.addEventListener(MouseEvent.MOUSE_DOWN, function (event:MouseEvent):void { + self.buttonStateMouseDown = true; + self.UpdateButtonState(); + }); + this.stage.addEventListener(MouseEvent.MOUSE_UP, function (event:MouseEvent):void { + self.buttonStateMouseDown = false; + self.UpdateButtonState(); + }); + this.stage.addEventListener(MouseEvent.MOUSE_OVER, function (event:MouseEvent):void { + self.buttonStateMouseDown = event.buttonDown; + self.buttonStateOver = true; + self.UpdateButtonState(); + ExternalCall.Simple(self.mouseOver_Callback); + }); + this.stage.addEventListener(MouseEvent.MOUSE_OUT, function (event:MouseEvent):void { + self.buttonStateMouseDown = false; + self.buttonStateOver = false; + self.UpdateButtonState(); + }); + // Handle the mouse leaving the flash movie altogether + this.stage.addEventListener(Event.MOUSE_LEAVE, function (event:Event):void { + self.buttonStateMouseDown = false; + self.buttonStateOver = false; + self.UpdateButtonState(); + ExternalCall.Simple(self.mouseOut_Callback); + }); + + this.buttonTextField = new TextField(); + this.buttonTextField.type = TextFieldType.DYNAMIC; + this.buttonTextField.antiAliasType = AntiAliasType.ADVANCED; + this.buttonTextField.autoSize = TextFieldAutoSize.NONE; + this.buttonTextField.cacheAsBitmap = true; + this.buttonTextField.multiline = true; + this.buttonTextField.wordWrap = false; + this.buttonTextField.tabEnabled = false; + this.buttonTextField.background = false; + this.buttonTextField.border = false; + this.buttonTextField.selectable = false; + this.buttonTextField.condenseWhite = true; + + this.stage.addChild(this.buttonTextField); + + + this.buttonCursorSprite = new Sprite(); + this.buttonCursorSprite.graphics.beginFill(0xFFFFFF, 0); + this.buttonCursorSprite.graphics.drawRect(0, 0, 1, 1); + this.buttonCursorSprite.graphics.endFill(); + this.buttonCursorSprite.buttonMode = true; + this.buttonCursorSprite.x = 0; + this.buttonCursorSprite.y = 0; + this.buttonCursorSprite.addEventListener(MouseEvent.CLICK, doNothing); + this.stage.addChild(this.buttonCursorSprite); + + this.sizeTimer = new Timer(10, 0); + this.sizeTimer.addEventListener(TimerEvent.TIMER, function ():void { + //self.Debug("Stage:" + self.stage.stageWidth + " by " + self.stage.stageHeight); + if (self.stage.stageWidth > 0 || self.stage.stageHeight > 0) { + self.HandleStageResize(null); + self.sizeTimer.stop(); + self.sizeTimer.removeEventListener(TimerEvent.TIMER, arguments.callee); + self.sizeTimer = null; + } + } ); + this.sizeTimer.start(); + + // Get the movie name + this.movieName = decodeURIComponent(root.loaderInfo.parameters.movieName); + + // **Configure the callbacks** + // The JavaScript tracks all the instances of SWFUpload on a page. We can access the instance + // associated with this SWF file using the movieName. Each callback is accessible by making + // a call directly to it on our instance. There is no error handling for undefined callback functions. + // A developer would have to deliberately remove the default functions,set the variable to null, or remove + // it from the init function. + this.flashReady_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].flashReady"; + this.fileDialogStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogStart"; + this.fileQueued_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueued"; + this.fileQueueError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueueError"; + this.fileDialogComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogComplete"; + + this.uploadStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadStart"; + this.uploadProgress_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadProgress"; + this.uploadError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadError"; + this.uploadSuccess_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadSuccess"; + + this.uploadComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadComplete"; + + this.debug_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].debug"; + + this.mouseOut_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].mouseOut"; + this.mouseOver_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].mouseOver"; + this.mouseClick_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].mouseClick"; + + // Get the Flash Vars + this.uploadURL = decodeURIComponent(root.loaderInfo.parameters.uploadURL); + this.filePostName = decodeURIComponent(root.loaderInfo.parameters.filePostName); + this.fileTypes = decodeURIComponent(root.loaderInfo.parameters.fileTypes); + this.fileTypesDescription = decodeURIComponent(root.loaderInfo.parameters.fileTypesDescription) + " (" + this.fileTypes + ")"; + this.loadPostParams(decodeURIComponent(root.loaderInfo.parameters.params)); + + + if (!this.filePostName) { + this.filePostName = "Filedata"; + } + if (!this.fileTypes) { + this.fileTypes = "*.*"; + } + if (!this.fileTypesDescription) { + this.fileTypesDescription = "All Files"; + } + + this.LoadFileExensions(this.fileTypes); + + try { + this.debugEnabled = decodeURIComponent(root.loaderInfo.parameters.debugEnabled) == "true" ? true : false; + } catch (ex:Object) { + this.debugEnabled = false; + } + + try { + this.SetFileSizeLimit(String(decodeURIComponent(root.loaderInfo.parameters.fileSizeLimit))); + } catch (ex:Object) { + this.fileSizeLimit = 0; + } + + + try { + this.fileUploadLimit = Number(decodeURIComponent(root.loaderInfo.parameters.fileUploadLimit)); + if (this.fileUploadLimit < 0) this.fileUploadLimit = 0; + } catch (ex:Object) { + this.fileUploadLimit = 0; + } + + try { + this.fileQueueLimit = Number(decodeURIComponent(root.loaderInfo.parameters.fileQueueLimit)); + if (this.fileQueueLimit < 0) this.fileQueueLimit = 0; + } catch (ex:Object) { + this.fileQueueLimit = 0; + } + + // Set the queue limit to match the upload limit when the queue limit is bigger than the upload limit + if (this.fileQueueLimit > this.fileUploadLimit && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit; + // The the queue limit is unlimited and the upload limit is not then set the queue limit to the upload limit + if (this.fileQueueLimit == 0 && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit; + + try { + this.useQueryString = decodeURIComponent(root.loaderInfo.parameters.useQueryString) == "true" ? true : false; + } catch (ex:Object) { + this.useQueryString = false; + } + + try { + this.requeueOnError = decodeURIComponent(root.loaderInfo.parameters.requeueOnError) == "true" ? true : false; + } catch (ex:Object) { + this.requeueOnError = false; + } + + try { + this.SetHTTPSuccess(String(decodeURIComponent(root.loaderInfo.parameters.httpSuccess))); + } catch (ex:Object) { + this.SetHTTPSuccess([]); + } + + try { + this.SetAssumeSuccessTimeout(Number(decodeURIComponent(root.loaderInfo.parameters.assumeSuccessTimeout))); + } catch (ex:Object) { + this.SetAssumeSuccessTimeout(0); + } + + try { + this.SetButtonImageURL(String(decodeURIComponent(root.loaderInfo.parameters.buttonImageURL))); + } catch (ex:Object) { + this.SetButtonImageURL(""); + } + + try { + this.SetButtonText(String(decodeURIComponent(root.loaderInfo.parameters.buttonText))); + } catch (ex:Object) { + this.SetButtonText(""); + } + + try { + this.SetButtonTextPadding(Number(decodeURIComponent(root.loaderInfo.parameters.buttonTextLeftPadding)), Number(decodeURIComponent(root.loaderInfo.parameters.buttonTextTopPadding))); + } catch (ex:Object) { + this.SetButtonTextPadding(0, 0); + } + + try { + this.SetButtonTextStyle(String(decodeURIComponent(root.loaderInfo.parameters.buttonTextStyle))); + } catch (ex:Object) { + this.SetButtonTextStyle(""); + } + + try { + this.SetButtonAction(Number(decodeURIComponent(root.loaderInfo.parameters.buttonAction))); + } catch (ex:Object) { + this.SetButtonAction(this.BUTTON_ACTION_SELECT_FILES); + } + + try { + this.SetButtonDisabled(decodeURIComponent(root.loaderInfo.parameters.buttonDisabled) == "true" ? true : false); + } catch (ex:Object) { + this.SetButtonDisabled(Boolean(false)); + } + + try { + this.SetButtonCursor(Number(decodeURIComponent(root.loaderInfo.parameters.buttonCursor))); + } catch (ex:Object) { + this.SetButtonCursor(this.BUTTON_CURSOR_ARROW); + } + + this.SetupExternalInterface(); + + this.Debug("SWFUpload Init Complete"); + this.PrintDebugInfo(); + + ExternalCall.Simple(this.flashReady_Callback); + this.hasCalledFlashReady = true; + } + + private function HandleStageResize(e:Event):void { + if (this.stage.stageWidth > 0 || this.stage.stageHeight > 0) { + this.Debug("Stage Resize:" + this.stage.stageWidth + " by " + this.stage.stageHeight); + + // scale the button (if it's not loaded don't crash) + try { + var buttonHeight:Number = this.buttonLoader.contentLoaderInfo.height / 4; + this.buttonLoader.height = this.stage.stageHeight * 4; + this.buttonLoader.scaleX = this.buttonLoader.scaleY; + } catch (ex:Error) { } + + // Scale the button cursor + try { + this.buttonCursorSprite.width = this.stage.stageWidth; + this.buttonCursorSprite.height = this.stage.stageHeight; + } catch (ex:Error) {} + + // scale the text area (it doesn't resize but it still needs to fit) + try { + this.buttonTextField.width = this.stage.stageWidth; + this.buttonTextField.height = this.stage.stageHeight; + } catch (ex:Error) {} + } + } + + private function SetupExternalInterface():void { + try { + ExternalInterface.addCallback("SelectFile", this.SelectFile); + ExternalInterface.addCallback("SelectFiles", this.SelectFiles); + ExternalInterface.addCallback("StartUpload", this.StartUpload); + ExternalInterface.addCallback("ReturnUploadStart", this.ReturnUploadStart); + ExternalInterface.addCallback("StopUpload", this.StopUpload); + ExternalInterface.addCallback("CancelUpload", this.CancelUpload); + ExternalInterface.addCallback("RequeueUpload", this.RequeueUpload); + + ExternalInterface.addCallback("GetStats", this.GetStats); + ExternalInterface.addCallback("SetStats", this.SetStats); + ExternalInterface.addCallback("GetFile", this.GetFile); + ExternalInterface.addCallback("GetFileByIndex", this.GetFileByIndex); + ExternalInterface.addCallback("GetFileByQueueIndex", this.GetFileByQueueIndex); + + ExternalInterface.addCallback("AddFileParam", this.AddFileParam); + ExternalInterface.addCallback("RemoveFileParam", this.RemoveFileParam); + + ExternalInterface.addCallback("SetUploadURL", this.SetUploadURL); + ExternalInterface.addCallback("SetPostParams", this.SetPostParams); + ExternalInterface.addCallback("SetFileTypes", this.SetFileTypes); + ExternalInterface.addCallback("SetFileSizeLimit", this.SetFileSizeLimit); + ExternalInterface.addCallback("SetFileUploadLimit", this.SetFileUploadLimit); + ExternalInterface.addCallback("SetFileQueueLimit", this.SetFileQueueLimit); + ExternalInterface.addCallback("SetFilePostName", this.SetFilePostName); + ExternalInterface.addCallback("SetUseQueryString", this.SetUseQueryString); + ExternalInterface.addCallback("SetRequeueOnError", this.SetRequeueOnError); + ExternalInterface.addCallback("SetHTTPSuccess", this.SetHTTPSuccess); + ExternalInterface.addCallback("SetAssumeSuccessTimeout", this.SetAssumeSuccessTimeout); + ExternalInterface.addCallback("SetDebugEnabled", this.SetDebugEnabled); + + ExternalInterface.addCallback("SetButtonImageURL", this.SetButtonImageURL); + ExternalInterface.addCallback("SetButtonText", this.SetButtonText); + ExternalInterface.addCallback("SetButtonTextPadding", this.SetButtonTextPadding); + ExternalInterface.addCallback("SetButtonTextStyle", this.SetButtonTextStyle); + ExternalInterface.addCallback("SetButtonAction", this.SetButtonAction); + ExternalInterface.addCallback("SetButtonDisabled", this.SetButtonDisabled); + ExternalInterface.addCallback("SetButtonCursor", this.SetButtonCursor); + } catch (ex:Error) { + this.Debug("Callbacks where not set: " + ex.message); + return; + } + } + + /* ***************************************** + * FileReference Event Handlers + * *************************************** */ + private function DialogCancelled_Handler(event:Event):void { + this.Debug("Event: fileDialogComplete: File Dialog window cancelled."); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0, 0, this.queued_uploads); + } + + private function Open_Handler(event:Event):void { + this.Debug("Event: uploadProgress (OPEN): File ID: " + this.current_file_item.id); + this.current_file_item.finalUploadProgress = false; + ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), 0, this.current_file_item.file_reference.size); + } + + private function FileProgress_Handler(event:ProgressEvent):void { + // On early than Mac OS X 10.3 bytesLoaded is always -1, convert this to zero. Do bytesTotal for good measure. + // http://livedocs.adobe.com/flex/3/langref/flash/net/FileReference.html#event:progress + var bytesLoaded:Number = event.bytesLoaded < 0 ? 0 : event.bytesLoaded; + var bytesTotal:Number = event.bytesTotal < 0 ? 0 : event.bytesTotal; + + // Because Flash never fires a complete event if the server doesn't respond after 30 seconds or on Macs if there + // is no content in the response we'll set a timer and assume that the upload is successful after the defined amount of + // time. If the timeout is zero then we won't use the timer. + if (bytesLoaded === bytesTotal) { + this.current_file_item.finalUploadProgress = true; + if (bytesTotal > 0 && this.assumeSuccessTimeout > 0) { + if (this.assumeSuccessTimer !== null) { + this.assumeSuccessTimer.stop(); + this.assumeSuccessTimer = null; + } + + this.assumeSuccessTimer = new Timer(this.assumeSuccessTimeout * 1000, 1); + this.assumeSuccessTimer.addEventListener(TimerEvent.TIMER_COMPLETE, AssumeSuccessTimer_Handler); + this.assumeSuccessTimer.start(); + } + } + + this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + bytesLoaded + ". Total: " + bytesTotal); + ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), bytesLoaded, bytesTotal); + } + + private function AssumeSuccessTimer_Handler(event:TimerEvent):void { + this.Debug("Event: AssumeSuccess: " + this.assumeSuccessTimeout + " passed without server response"); + this.UploadSuccess(this.current_file_item, "", false); + } + + private function Complete_Handler(event:Event):void { + /* Because we can't do COMPLETE or DATA events (we have to do both) we can't + * just call uploadSuccess from the complete handler, we have to wait for + * the Data event which may never come. However, testing shows it always comes + * within a couple milliseconds if it is going to come so the solution is: + * + * Set a timer in the COMPLETE event (which always fires) and if DATA is fired + * it will stop the timer and call uploadComplete + * + * If the timer expires then DATA won't be fired and we call uploadComplete + * */ + + // Set the timer + if (serverDataTimer != null) { + this.serverDataTimer.stop(); + this.serverDataTimer = null; + } + + this.serverDataTimer = new Timer(100, 1); + //var self:SWFUpload = this; + this.serverDataTimer.addEventListener(TimerEvent.TIMER, this.ServerDataTimer_Handler); + this.serverDataTimer.start(); + } + private function ServerDataTimer_Handler(event:TimerEvent):void { + this.UploadSuccess(this.current_file_item, ""); + } + + private function ServerData_Handler(event:DataEvent):void { + this.UploadSuccess(this.current_file_item, event.data); + } + + private function UploadSuccess(file:FileItem, serverData:String, responseReceived:Boolean = true):void { + if (this.serverDataTimer !== null) { + this.serverDataTimer.stop(); + this.serverDataTimer = null; + } + if (this.assumeSuccessTimer !== null) { + this.assumeSuccessTimer.stop(); + this.assumeSuccessTimer = null; + } + + // If the 100% upload progress hasn't been called then call it now + if (!file.finalUploadProgress) { + file.finalUploadProgress = true; + this.Debug("Event: uploadProgress (simulated 100%): File ID: " + file.id + ". Bytes: " + file.file_reference.size + ". Total: " + file.file_reference.size); + ExternalCall.UploadProgress(this.uploadProgress_Callback, file.ToJavaScriptObject(), file.file_reference.size, file.file_reference.size); + } + + this.successful_uploads++; + file.file_status = FileItem.FILE_STATUS_SUCCESS; + + this.Debug("Event: uploadSuccess: File ID: " + file.id + " Response Received: " + responseReceived.toString() + " Data: " + serverData); + ExternalCall.UploadSuccess(this.uploadSuccess_Callback, file.ToJavaScriptObject(), serverData, responseReceived); + + this.UploadComplete(false); + + } + + private function HTTPError_Handler(event:HTTPStatusEvent):void { + var isSuccessStatus:Boolean = false; + for (var i:Number = 0; i < this.httpSuccess.length; i++) { + if (this.httpSuccess[i] === event.status) { + isSuccessStatus = true; + break; + } + } + + + if (isSuccessStatus) { + this.Debug("Event: httpError: Translating status code " + event.status + " to uploadSuccess"); + + var serverDataEvent:DataEvent = new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, ""); + this.ServerData_Handler(serverDataEvent); + } else { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError: HTTP ERROR : File ID: " + this.current_file_item.id + ". HTTP Status: " + event.status + "."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), event.status.toString()); + this.UploadComplete(true); // An IO Error is also called so we don't want to complete the upload yet. + } + } + + // Note: Flash Player does not support Uploads that require authentication. Attempting this will trigger an + // IO Error or it will prompt for a username and password and may crash the browser (FireFox/Opera) + private function IOError_Handler(event:IOErrorEvent):void { + // Only trigger an IO Error event if we haven't already done an HTTP error + if (this.current_file_item.file_status != FileItem.FILE_STATUS_ERROR) { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error: " + event.text); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), event.text); + } + + this.UploadComplete(true); + } + + private function SecurityError_Handler(event:SecurityErrorEvent):void { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + this.Debug("Event: uploadError : Security Error : File Number: " + this.current_file_item.id + ". Error text: " + event.text); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), event.text); + + this.UploadComplete(true); + } + + private function Select_Many_Handler(event:Event):void { + this.Select_Handler(this.fileBrowserMany.fileList); + } + private function Select_One_Handler(event:Event):void { + var fileArray:Array = new Array(1); + fileArray[0] = this.fileBrowserOne; + this.Select_Handler(fileArray); + } + + private function Select_Handler(file_reference_list:Array):void { + this.Debug("Select Handler: Received the files selected from the dialog. Processing the file list..."); + + var num_files_queued:Number = 0; + + // Determine how many queue slots are remaining (check the unlimited (0) settings, successful uploads and queued uploads) + var queue_slots_remaining:Number = 0; + if (this.fileUploadLimit == 0) { + queue_slots_remaining = this.fileQueueLimit == 0 ? file_reference_list.length : (this.fileQueueLimit - this.queued_uploads); // If unlimited queue make the allowed size match however many files were selected. + } else { + var remaining_uploads:Number = this.fileUploadLimit - this.successful_uploads - this.queued_uploads; + if (remaining_uploads < 0) remaining_uploads = 0; + if (this.fileQueueLimit == 0 || this.fileQueueLimit >= remaining_uploads) { + queue_slots_remaining = remaining_uploads; + } else if (this.fileQueueLimit < remaining_uploads) { + queue_slots_remaining = this.fileQueueLimit - this.queued_uploads; + } + } + + if (queue_slots_remaining < 0) queue_slots_remaining = 0; + + // Check if the number of files selected is greater than the number allowed to queue up. + if (queue_slots_remaining < file_reference_list.length) { + this.Debug("Event: fileQueueError : Selected Files (" + file_reference_list.length + ") exceeds remaining Queue size (" + queue_slots_remaining + ")."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining.toString()); + } else { + // Process each selected file + for (var i:Number = 0; i < file_reference_list.length; i++) { + var file_item:FileItem = new FileItem(file_reference_list[i], this.movieName, this.file_index.length); + this.file_index[file_item.index] = file_item; + + // Verify that the file is accessible. Zero byte files and possibly other conditions can cause a file to be inaccessible. + var jsFileObj:Object = file_item.ToJavaScriptObject(); + var is_valid_file_reference:Boolean = (jsFileObj.filestatus !== FileItem.FILE_STATUS_ERROR); + + if (is_valid_file_reference) { + // Check the size, if it's within the limit add it to the upload list. + var size_result:Number = this.CheckFileSize(file_item); + var is_valid_filetype:Boolean = this.CheckFileType(file_item); + if(size_result == this.SIZE_OK && is_valid_filetype) { + file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.push(file_item); + this.queued_uploads++; + num_files_queued++; + this.Debug("Event: fileQueued : File ID: " + file_item.id); + ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject()); + } + else if (!is_valid_filetype) { + //file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File not of a valid type."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_INVALID_FILETYPE, file_item.ToJavaScriptObject(), "File is not an allowed file type."); + } + else if (size_result == this.SIZE_TOO_BIG) { + //file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File exceeds size limit."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit."); + } + else if (size_result == this.SIZE_ZERO_BYTE) { + file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File is zero bytes."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded."); + } + } else { + file_item.file_reference = null; // Cleanup the object + file_item.file_status = FileItem.FILE_STATUS_ERROR; + this.queue_errors++; + this.Debug("Event: fileQueueError : File is zero bytes or FileReference is invalid."); + ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes or cannot be accessed and cannot be uploaded."); + } + } + } + + this.Debug("Event: fileDialogComplete : Finished processing selected files. Files selected: " + file_reference_list.length + ". Files Queued: " + num_files_queued); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length, num_files_queued, this.queued_uploads); + } + + /* **************************************************************** + Externally exposed functions + ****************************************************************** */ + // Opens a file browser dialog that allows one file to be selected. + private function SelectFile():void { + this.fileBrowserOne = new FileReference(); + this.fileBrowserOne.addEventListener(Event.SELECT, this.Select_One_Handler); + this.fileBrowserOne.addEventListener(Event.CANCEL, this.DialogCancelled_Handler); + + // Default file type settings + var allowed_file_types:String = "*.*"; + var allowed_file_types_description:String = "All Files"; + + // Get the instance settings + if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes; + if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription; + + this.Debug("Event: fileDialogStart : Browsing files. Single Select. Allowed file types: " + allowed_file_types); + ExternalCall.Simple(this.fileDialogStart_Callback); + + try { + this.fileBrowserOne.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]); + } catch (ex:Error) { + this.Debug("Exception: " + ex.toString()); + } + } + + // Opens a file browser dialog that allows multiple files to be selected. + private function SelectFiles():void { + var allowed_file_types:String = "*.*"; + var allowed_file_types_description:String = "All Files"; + + if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes; + if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription; + + this.Debug("Event: fileDialogStart : Browsing files. Multi Select. Allowed file types: " + allowed_file_types); + ExternalCall.Simple(this.fileDialogStart_Callback); + + try { + this.fileBrowserMany.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]); + } catch (ex:Error) { + this.Debug("Exception: " + ex.toString()); + } + } + + + // Cancel the current upload and stops. Doesn't advance the upload pointer. The current file is requeued at the beginning. + private function StopUpload():void { + if (this.current_file_item != null) { + // Cancel the upload and re-queue the FileItem + this.current_file_item.file_reference.cancel(); + + // Remove the event handlers + this.removeEventListeners(this.current_file_item); + + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(this.current_file_item); + var js_object:Object = this.current_file_item.ToJavaScriptObject(); + this.current_file_item = null; + + this.Debug("Event: uploadError: upload stopped. File ID: " + js_object.ID); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped"); + this.Debug("Event: uploadComplete. File ID: " + js_object.ID); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + + this.Debug("StopUpload(): upload stopped."); + } else { + this.Debug("StopUpload(): No file is currently uploading. Nothing to do."); + } + } + + /* Cancels the upload specified by file_id + * If the file is currently uploading it is cancelled and the uploadComplete + * event gets called. + * If the file is not currently uploading then only the uploadCancelled event is fired. + * */ + private function CancelUpload(file_id:String, triggerErrorEvent:Boolean = true):void { + var file_item:FileItem = null; + // Check the current file item + if (this.current_file_item != null && (this.current_file_item.id == file_id || !file_id)) { + this.current_file_item.file_reference.cancel(); + + this.current_file_item.file_status = FileItem.FILE_STATUS_CANCELLED; + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + this.upload_cancelled++; + + if (triggerErrorEvent) { + this.Debug("Event: uploadError: File ID: " + this.current_file_item.id + ". Cancelled current upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled."); + } else { + this.Debug("Event: cancelUpload: File ID: " + this.current_file_item.id + ". Cancelled current upload. Suppressed uploadError event."); + } + + this.UploadComplete(false); + } else if (file_id) { + // Find the file in the queue + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + // Remove the file from the queue + file_item = FileItem(this.file_queue[file_index]); + file_item.file_status = FileItem.FILE_STATUS_CANCELLED; + this.file_queue.splice(file_index, 1); + this.queued_uploads--; + this.upload_cancelled++; + + // Cancel the file (just for good measure) and make the callback + file_item.file_reference.cancel(); + this.removeEventListeners(file_item); + file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + + + if (triggerErrorEvent) { + this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + } else { + this.Debug("Event: cancelUpload: File ID: " + file_item.id + ". Cancelled current upload. Suppressed uploadError event."); + } + + // Get rid of the file object + file_item = null; + } + } else { + // Get the first file and cancel it + while (this.file_queue.length > 0 && file_item == null) { + // Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop) + file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem + if (typeof(file_item) == "undefined") { + file_item = null; + continue; + } + } + + if (file_item != null) { + file_item.file_status = FileItem.FILE_STATUS_CANCELLED; + this.queued_uploads--; + this.upload_cancelled++; + + + // Cancel the file (just for good measure) and make the callback + file_item.file_reference.cancel(); + + this.removeEventListeners(file_item); + file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + + if (triggerErrorEvent) { + this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + } else { + this.Debug("Event: cancelUpload: File ID: " + file_item.id + ". Cancelled current upload. Suppressed uploadError event."); + } + + // Get rid of the file object + file_item = null; + } + + } + + } + + /* Requeues the indicated file. Returns true if successful or if the file is + * already in the queue. Otherwise returns false. + * */ + private function RequeueUpload(fileIdentifier:*):Boolean { + var file:FileItem = null; + if (typeof(fileIdentifier) === "number") { + var fileIndex:Number = Number(fileIdentifier); + if (fileIndex >= 0 && fileIndex < this.file_index.length) { + file = this.file_index[fileIndex]; + } + } else if (typeof(fileIdentifier) === "string") { + file = FindFileInFileIndex(String(fileIdentifier)); + } else { + return false; + } + + if (file !== null && file.file_reference !== null) { + if (file.file_status === FileItem.FILE_STATUS_IN_PROGRESS || file.file_status === FileItem.FILE_STATUS_NEW) { + return false; + } else if (file.file_status !== FileItem.FILE_STATUS_QUEUED) { + file.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(file); + this.queued_uploads++; + } + return true; + } else { + return false; + } + } + + + private function GetStats():Object { + return { + in_progress : this.current_file_item == null ? 0 : 1, + files_queued : this.queued_uploads, + successful_uploads : this.successful_uploads, + upload_errors : this.upload_errors, + upload_cancelled : this.upload_cancelled, + queue_errors : this.queue_errors + }; + } + private function SetStats(stats:Object):void { + this.successful_uploads = typeof(stats["successful_uploads"]) === "number" ? stats["successful_uploads"] : this.successful_uploads; + this.upload_errors = typeof(stats["upload_errors"]) === "number" ? stats["upload_errors"] : this.upload_errors; + this.upload_cancelled = typeof(stats["upload_cancelled"]) === "number" ? stats["upload_cancelled"] : this.upload_cancelled; + this.queue_errors = typeof(stats["queue_errors"]) === "number" ? stats["queue_errors"] : this.queue_errors; + } + + private function GetFile(file_id:String):Object { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + var file:FileItem = this.file_queue[file_index]; + } else { + if (this.current_file_item != null) { + file = this.current_file_item; + } else { + for (var i:Number = 0; i < this.file_queue.length; i++) { + file = this.file_queue[i]; + if (file != null) break; + } + } + } + + if (file == null) { + return null; + } else { + return file.ToJavaScriptObject(); + } + + } + + private function GetFileByIndex(index:Number):Object { + if (index < 0 || index > this.file_index.length - 1) { + return null; + } else { + return this.file_index[index].ToJavaScriptObject(); + } + } + + private function GetFileByQueueIndex(index:Number):Object { + if (index < 0 || index > this.file_queue.length - 1) { + return null; + } else { + return this.file_queue[index].ToJavaScriptObject(); + } + } + + private function AddFileParam(file_id:String, name:String, value:String):Boolean { + var item:FileItem = this.FindFileInFileIndex(file_id); + if (item != null) { + item.AddParam(name, value); + return true; + } + else { + return false; + } + } + private function RemoveFileParam(file_id:String, name:String):Boolean { + var item:FileItem = this.FindFileInFileIndex(file_id); + if (item != null) { + item.RemoveParam(name); + return true; + } + else { + return false; + } + } + + private function SetUploadURL(url:String):void { + if (typeof(url) !== "undefined" && url !== "") { + this.uploadURL = url; + } + } + + private function SetPostParams(post_object:Object):void { + if (typeof(post_object) !== "undefined" && post_object !== null) { + this.uploadPostObject = post_object; + } + } + + private function SetFileTypes(types:String, description:String):void { + this.fileTypes = types; + this.fileTypesDescription = description; + + this.LoadFileExensions(this.fileTypes); + } + + // Sets the file size limit. Accepts size values with units: 100 b, 1KB, 23Mb, 4 Gb + // Parsing is not robust. "100 200 MB KB B GB" parses as "100 MB" + private function SetFileSizeLimit(size:String):void { + var value:Number = 0; + var unit:String = "kb"; + + // Trim the string + var trimPattern:RegExp = /^\s*|\s*$/; + + size = size.toLowerCase(); + size = size.replace(trimPattern, ""); + + + // Get the value part + var values:Array = size.match(/^\d+/); + if (values !== null && values.length > 0) { + value = parseInt(values[0]); + } + if (isNaN(value) || value < 0) value = 0; + + // Get the units part + var units:Array = size.match(/(b|kb|mb|gb)/); + if (units != null && units.length > 0) { + unit = units[0]; + } + + // Set the multiplier for converting the unit to bytes + var multiplier:Number = 1024; + if (unit === "b") + multiplier = 1; + else if (unit === "mb") + multiplier = 1048576; + else if (unit === "gb") + multiplier = 1073741824; + + this.fileSizeLimit = value * multiplier; + } + + private function SetFileUploadLimit(file_upload_limit:Number):void { + if (file_upload_limit < 0) file_upload_limit = 0; + this.fileUploadLimit = file_upload_limit; + } + + private function SetFileQueueLimit(file_queue_limit:Number):void { + if (file_queue_limit < 0) file_queue_limit = 0; + this.fileQueueLimit = file_queue_limit; + } + + private function SetFilePostName(file_post_name:String):void { + if (file_post_name != "") { + this.filePostName = file_post_name; + } + } + + private function SetUseQueryString(use_query_string:Boolean):void { + this.useQueryString = use_query_string; + } + + private function SetRequeueOnError(requeue_on_error:Boolean):void { + this.requeueOnError = requeue_on_error; + } + + private function SetHTTPSuccess(http_status_codes:*):void { + this.httpSuccess = []; + + if (typeof http_status_codes === "string") { + var status_code_strings:Array = http_status_codes.replace(" ", "").split(","); + for each (var http_status_string:String in status_code_strings) + { + try { + this.httpSuccess.push(Number(http_status_string)); + } catch (ex:Object) { + // Ignore errors + this.Debug("Could not add HTTP Success code: " + http_status_string); + } + } + } + else if (typeof http_status_codes === "object" && typeof http_status_codes.length === "number") { + for each (var http_status:* in http_status_codes) + { + try { + this.Debug("adding: " + http_status); + this.httpSuccess.push(Number(http_status)); + } catch (ex:Object) { + this.Debug("Could not add HTTP Success code: " + http_status); + } + } + } + } + + private function SetAssumeSuccessTimeout(timeout_seconds:Number):void { + this.assumeSuccessTimeout = timeout_seconds < 0 ? 0 : timeout_seconds; + } + + private function SetDebugEnabled(debug_enabled:Boolean):void { + this.debugEnabled = debug_enabled; + } + + /* ************************************************************* + Button Handling Functions + *************************************************************** */ + private function SetButtonImageURL(button_image_url:String):void { + this.buttonImageURL = button_image_url; + + try { + if (this.buttonImageURL !== null && this.buttonImageURL !== "") { + this.buttonLoader.load(new URLRequest(this.buttonImageURL)); + } + } catch (ex:Object) { + } + } + + private function ButtonImageLoaded(e:Event):void { + this.Debug("Button Image Loaded"); + this.HandleStageResize(null); + } + + + private function ButtonClickHandler(e:MouseEvent):void { + if (!this.buttonStateDisabled) { + if (this.buttonAction === this.BUTTON_ACTION_SELECT_FILE) { + this.SelectFile(); + } + else if (this.buttonAction === this.BUTTON_ACTION_START_UPLOAD) { + this.StartUpload(); + } + else if (this.buttonAction === this.BUTTON_ACTION_NONE) { + ExternalCall.Simple(this.mouseClick_Callback); + } + else { + this.SelectFiles(); + } + } else { + ExternalCall.Simple(this.mouseClick_Callback); + } + } + + private function UpdateButtonState():void { + var xOffset:Number = 0; + var yOffset:Number = 0; + + this.buttonLoader.x = xOffset; + this.buttonLoader.y = yOffset; + + if (this.buttonStateDisabled) { + this.buttonLoader.y = (this.buttonLoader.height / 4) * -3 + yOffset; + } + else if (this.buttonStateMouseDown) { + this.buttonLoader.y = (this.buttonLoader.height / 4) * -2 + yOffset; + } + else if (this.buttonStateOver) { + this.buttonLoader.y = (this.buttonLoader.height / 4) * -1 + yOffset; + } + else { + this.buttonLoader.y = -yOffset; + } + }; + + private function SetButtonText(button_text:String):void { + this.buttonText = button_text; + + this.SetButtonTextStyle(this.buttonTextStyle); + } + + private function SetButtonTextStyle(button_text_style:String):void { + this.buttonTextStyle = button_text_style; + + var style:StyleSheet = new StyleSheet(); + style.parseCSS(this.buttonTextStyle); + this.buttonTextField.styleSheet = style; + this.buttonTextField.htmlText = this.buttonText; + } + + private function SetButtonTextPadding(left:Number, top:Number):void { + this.buttonTextField.x = this.buttonTextLeftPadding = left; + this.buttonTextField.y = this.buttonTextTopPadding = top; + } + + private function SetButtonDisabled(disabled:Boolean):void { + this.buttonStateDisabled = disabled; + this.UpdateButtonState(); + } + + private function SetButtonAction(button_action:Number):void { + this.buttonAction = button_action; + } + + private function SetButtonCursor(button_cursor:Number):void { + this.buttonCursor = button_cursor; + + this.buttonCursorSprite.useHandCursor = (this.buttonCursor === this.BUTTON_CURSOR_HAND); + } + + /* ************************************************************* + File processing and handling functions + *************************************************************** */ + + private function StartUpload(file_id:String = ""):void { + // Only upload a file uploads are being processed. + if (this.current_file_item != null) { + this.Debug("StartUpload(): Upload already in progress. Not starting another upload."); + return; + } + + this.Debug("StartUpload: " + (file_id ? "File ID: " + file_id : "First file in queue")); + + // Check the upload limit + if (this.successful_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) { + this.Debug("Event: uploadError : Upload limit reached. No more files can be uploaded."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, null, "The upload limit has been reached."); + this.current_file_item = null; + return; + } + + // Get the next file to upload + if (!file_id) { + while (this.file_queue.length > 0 && this.current_file_item == null) { + this.current_file_item = FileItem(this.file_queue.shift()); + if (typeof(this.current_file_item) == "undefined") { + this.current_file_item = null; + } + } + } else { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + // Set the file as the current upload and remove it from the queue + this.current_file_item = FileItem(this.file_queue[file_index]); + this.file_queue.splice([file_index], 1); + } else { + this.Debug("Event: uploadError : File ID not found in queue: " + file_id); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND, null, "File ID not found in the queue."); + } + } + + + if (this.current_file_item != null) { + // Trigger the uploadStart event which will call ReturnUploadStart to begin the actual upload + this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id); + + this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS; + this.Debug("StartUpload(): Upload Type: Normal."); + this.current_file_item.upload_type = FileItem.UPLOAD_TYPE_NORMAL; + ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject()); + } + // Otherwise we've would have looped through all the FileItems. This means the queue is empty) + else { + this.Debug("StartUpload(): No files found in the queue."); + } + } + + // This starts the upload when the user returns TRUE from the uploadStart event. Rather than just have the value returned from + // the function we do a return function call so we can use the setTimeout work-around for Flash/JS circular calls. + private function ReturnUploadStart(start_upload:Boolean):void { + if (this.current_file_item == null) { + this.Debug("ReturnUploadStart called but no file was prepped for uploading. The file may have been cancelled or stopped."); + return; + } + + var js_object:Object; + + if (start_upload) { + try { + // Set the event handlers + this.addEventListeners(this.current_file_item); + + // Get the request (post values, etc) + var request:URLRequest = this.BuildRequest(); + + if (this.uploadURL.length == 0) { + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty."); + + // Remove the event handlers + this.removeEventListeners(this.current_file_item); + + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(this.current_file_item); + js_object = this.current_file_item.ToJavaScriptObject(); + this.current_file_item = null; + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, js_object, "Upload URL string is empty."); + } else { + this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS; + + this.Debug("ReturnUploadStart(): File accepted by startUpload event and readied for standard upload. Starting upload to " + request.url + " for File ID: " + this.current_file_item.id); + this.current_file_item.file_reference.upload(request, this.filePostName, false); + } + } catch (ex:Error) { + this.Debug("ReturnUploadStart: Exception occurred: " + message); + + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + var message:String = ex.errorID + "\n" + ex.name + "\n" + ex.message + "\n" + ex.getStackTrace(); + this.Debug("Event: uploadError(): Upload Failed. Exception occurred: " + message); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message); + + this.UploadComplete(true); + } + } else { + // Remove the event handlers + this.removeEventListeners(this.current_file_item); + + // Re-queue the FileItem + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + + js_object = this.current_file_item.ToJavaScriptObject(); + this.file_queue.unshift(this.current_file_item); + this.current_file_item = null; + + this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading the file."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file."); + this.Debug("Event: uploadComplete : Call to uploadStart returned false. Not uploading the file."); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + } + } + + // Completes the file upload by deleting it's reference, advancing the pointer. + // Once this event fires a new upload can be started. + private function UploadComplete(eligible_for_requeue:Boolean):void { + var jsFileObj:Object = this.current_file_item.ToJavaScriptObject(); + + this.removeEventListeners(this.current_file_item); + + if (!eligible_for_requeue || this.requeueOnError == false) { + //this.current_file_item.file_reference = null; + this.queued_uploads--; + } else if (this.requeueOnError == true) { + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.unshift(this.current_file_item); + } + + this.current_file_item = null; + + this.Debug("Event: uploadComplete : Upload cycle complete."); + ExternalCall.UploadComplete(this.uploadComplete_Callback, jsFileObj); + } + + + /* ************************************************************* + Utility Functions + *************************************************************** */ + + + // Check the size of the file against the allowed file size. If it is less the return TRUE. If it is too large return FALSE + private function CheckFileSize(file_item:FileItem):Number { + if (file_item.file_reference.size == 0) { + return this.SIZE_ZERO_BYTE; + } else if (this.fileSizeLimit != 0 && file_item.file_reference.size > this.fileSizeLimit) { + return this.SIZE_TOO_BIG; + } else { + return this.SIZE_OK; + } + } + + private function CheckFileType(file_item:FileItem):Boolean { + // If no extensions are defined then a *.* was passed and the check is unnecessary + if (this.valid_file_extensions.length == 0) { + return true; + } + + var fileRef:FileReference = file_item.file_reference; + var last_dot_index:Number = fileRef.name.lastIndexOf("."); + var extension:String = ""; + if (last_dot_index >= 0) { + extension = fileRef.name.substr(last_dot_index + 1).toLowerCase(); + } + + var is_valid_filetype:Boolean = false; + for (var i:Number=0; i < this.valid_file_extensions.length; i++) { + if (String(this.valid_file_extensions[i]) == extension) { + is_valid_filetype = true; + break; + } + } + + return is_valid_filetype; + } + + private function BuildRequest():URLRequest { + // Create the request object + var request:URLRequest = new URLRequest(); + request.method = URLRequestMethod.POST; + + var file_post:Object = this.current_file_item.GetPostObject(); + + if (this.useQueryString) { + var pairs:Array = new Array(); + for (key in this.uploadPostObject) { + this.Debug("Global URL Item: " + key + "=" + this.uploadPostObject[key]); + if (this.uploadPostObject.hasOwnProperty(key)) { + pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(this.uploadPostObject[key])); + } + } + + for (key in file_post) { + this.Debug("File Post Item: " + key + "=" + file_post[key]); + if (file_post.hasOwnProperty(key)) { + pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(file_post[key])); + } + } + + request.url = this.uploadURL + (this.uploadURL.indexOf("?") > -1 ? "&" : "?") + pairs.join("&"); + + } else { + var key:String; + var post:URLVariables = new URLVariables(); + for (key in this.uploadPostObject) { + this.Debug("Global Post Item: " + key + "=" + this.uploadPostObject[key]); + if (this.uploadPostObject.hasOwnProperty(key)) { + post[key] = this.uploadPostObject[key]; + } + } + + for (key in file_post) { + this.Debug("File Post Item: " + key + "=" + file_post[key]); + if (file_post.hasOwnProperty(key)) { + post[key] = file_post[key]; + } + } + + request.url = this.uploadURL; + request.data = post; + } + + return request; + } + + private function Debug(msg:String):void { + try { + if (this.debugEnabled) { + var lines:Array = msg.split("\n"); + for (var i:Number=0; i < lines.length; i++) { + lines[i] = "SWF DEBUG: " + lines[i]; + } + ExternalCall.Debug(this.debug_Callback, lines.join("\n")); + } + } catch (ex:Error) { + // pretend nothing happened + trace(ex); + } + } + + private function PrintDebugInfo():void { + var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n"; + debug_info += "Version: " + this.build_number + "\n"; + debug_info += "movieName: " + this.movieName + "\n"; + debug_info += "Upload URL: " + this.uploadURL + "\n"; + debug_info += "File Types String: " + this.fileTypes + "\n"; + debug_info += "Parsed File Types: " + this.valid_file_extensions.toString() + "\n"; + debug_info += "HTTP Success: " + this.httpSuccess.join(", ") + "\n"; + debug_info += "File Types Description: " + this.fileTypesDescription + "\n"; + debug_info += "File Size Limit: " + this.fileSizeLimit + " bytes\n"; + debug_info += "File Upload Limit: " + this.fileUploadLimit + "\n"; + debug_info += "File Queue Limit: " + this.fileQueueLimit + "\n"; + debug_info += "Post Params:\n"; + for (var key:String in this.uploadPostObject) { + if (this.uploadPostObject.hasOwnProperty(key)) { + debug_info += " " + key + "=" + this.uploadPostObject[key] + "\n"; + } + } + debug_info += "----- END SWF DEBUG OUTPUT ----\n"; + + this.Debug(debug_info); + } + + private function FindIndexInFileQueue(file_id:String):Number { + for (var i:Number = 0; i < this.file_queue.length; i++) { + var item:FileItem = this.file_queue[i]; + if (item != null && item.id == file_id) return i; + } + + return -1; + } + + private function FindFileInFileIndex(file_id:String):FileItem { + for (var i:Number = 0; i < this.file_index.length; i++) { + var item:FileItem = this.file_index[i]; + if (item != null && item.id == file_id) return item; + } + + return null; + } + + + // Parse the file extensions in to an array so we can validate them agains + // the files selected later. + private function LoadFileExensions(filetypes:String):void { + var extensions:Array = filetypes.split(";"); + this.valid_file_extensions = new Array(); + + for (var i:Number=0; i < extensions.length; i++) { + var extension:String = String(extensions[i]); + var dot_index:Number = extension.lastIndexOf("."); + + if (dot_index >= 0) { + extension = extension.substr(dot_index + 1).toLowerCase(); + } else { + extension = extension.toLowerCase(); + } + + // If one of the extensions is * then we allow all files + if (extension == "*") { + this.valid_file_extensions = new Array(); + break; + } + + this.valid_file_extensions.push(extension); + } + } + + private function loadPostParams(param_string:String):void { + var post_object:Object = {}; + + if (param_string != null) { + var name_value_pairs:Array = param_string.split("&"); + + for (var i:Number = 0; i < name_value_pairs.length; i++) { + var name_value:String = String(name_value_pairs[i]); + var index_of_equals:Number = name_value.indexOf("="); + if (index_of_equals > 0) { + post_object[decodeURIComponent(name_value.substring(0, index_of_equals))] = decodeURIComponent(name_value.substr(index_of_equals + 1)); + } + } + } + this.uploadPostObject = post_object; + } + + private function addEventListeners(fileItem:FileItem):void { + var item:EventDispatcher = fileItem.GetUploader(); + if (item != null) { + item.addEventListener(Event.OPEN, this.Open_Handler); + item.addEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler); + item.addEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler); + item.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler); + item.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler); + item.addEventListener(Event.COMPLETE, this.Complete_Handler); + item.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler); + } + } + + private function removeEventListeners(fileItem:FileItem):void { + var item:EventDispatcher = fileItem.GetUploader(); + if (item != null) { + item.removeEventListener(Event.OPEN, this.Open_Handler); + item.removeEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler); + item.removeEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler); + item.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler); + item.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler); + item.removeEventListener(Event.COMPLETE, this.Complete_Handler); + item.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler); + } + } + + } +} diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/deploy.bat b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/deploy.bat new file mode 100644 index 0000000..c583847 --- /dev/null +++ b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/deploy.bat @@ -0,0 +1,4 @@ +@echo off +copy ..\swfupload_fp10\swfupload.js ..\..\samples\demos\swfupload +copy swfupload_fp9.swf ..\..\samples\demos\swfupload +copy ..\plugins\*.js ..\..\samples\demos\swfupload \ No newline at end of file diff --git a/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/swfupload_fp9.swf b/media_tree/static/media_tree/lib/swfupload/swfupload_fp9/swfupload_fp9.swf new file mode 100644 index 0000000000000000000000000000000000000000..837addc4a983aa1821617e47427b8b7911ee0fe0 GIT binary patch literal 13333 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 literal 0 HcmV?d00001 diff --git a/media_tree/templates/admin/media_tree/filenode/change_list_results_thumbnails.html b/media_tree/templates/admin/media_tree/filenode/change_list_results_thumbnails.html new file mode 100644 index 0000000..beff326 --- /dev/null +++ b/media_tree/templates/admin/media_tree/filenode/change_list_results_thumbnails.html @@ -0,0 +1,41 @@ +{% load media_tree_admin thumbnail %} +{% if results %} +

+
  + +{% endif %} diff --git a/media_tree/templates/media_tree/filenode/image.html b/media_tree/templates/media_tree/filenode/image.html new file mode 100644 index 0000000..bf37dce --- /dev/null +++ b/media_tree/templates/media_tree/filenode/image.html @@ -0,0 +1,13 @@ +{% extends request.current_page.template %} +{% block content %}{% spaceless %} + {% if back_link_url %} +

{{ back_link_text }}

+ {% endif %} + {% if not thumbnail_size %} + {% load media_tree_thumbnail %} + {% thumbnail_size "full" as thumbnail_size %} + {% endif %} + {% with object as image_node %} +

{% include "media_tree/filenode/includes/figure.html" %}

+ {% endwith %} +{% endspaceless %}{% endblock %} \ No newline at end of file diff --git a/media_tree/urls.py b/media_tree/urls.py new file mode 100644 index 0000000..c37b7d1 --- /dev/null +++ b/media_tree/urls.py @@ -0,0 +1,19 @@ +from django.conf.urls.defaults import * +from media_tree.models import FileNode +from media_tree import media_types +import datetime + +image_args = { + 'queryset': FileNode.objects.filter(published__exact=True, media_type__exact=media_types.SUPPORTED_IMAGE), +} + +js_info_dict = { + 'packages': ('media_tree',), +} + +image_detail_args = dict(image_args, template_name='media_tree/filenode/image.html') + +urlpatterns = patterns('', + url(r'^image/(?P\d+)/$', 'media_tree.views.image_detail', name='media_tree_image_detail'), +) + diff --git a/media_tree/views.py b/media_tree/views.py new file mode 100644 index 0000000..8141c15 --- /dev/null +++ b/media_tree/views.py @@ -0,0 +1,17 @@ +from django.views.generic.list_detail import object_detail +from django.shortcuts import get_object_or_404 +from django.utils.translation import ugettext_lazy as _ +from media_tree.urls import image_detail_args + +def image_detail(request, object_id): + kwargs = {'object_id': object_id, 'extra_context': {}} + kwargs.update(image_detail_args) + back_page_id = request.GET.get('back_page') + if back_page_id: + from cms.models import Page + page = get_object_or_404(Page, pk=back_page_id) + kwargs['extra_context'].update({ + 'back_link_url': page.get_absolute_url(), + 'back_link_text': _('Back to %s') % page.get_title(), + }) + return object_detail(request, **kwargs)