diff --git a/code/AssetAdmin.php b/code/AssetAdmin.php index 6e8e79e0..fc6392fc 100755 --- a/code/AssetAdmin.php +++ b/code/AssetAdmin.php @@ -86,7 +86,7 @@ class AssetAdmin extends LeftAndMain { Requirements::javascript(CMS_DIR . "/javascript/CMSMain_upload.js"); Requirements::javascript(CMS_DIR . "/javascript/Upload.js"); - Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/swfupload/swfupload.js"); + Requirements::javascript(CMS_DIR . "/thirdparty/swfupload/swfupload.js"); Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js"); Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js"); diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index 7a0a840a..da89b5d8 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -186,8 +186,8 @@ class LeftAndMain extends Controller { Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.slide.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.drop.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.scale.js'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-layout/jquery.layout.js'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-fitheighttoparent/jquery.fitheighttoparent.js'); + Requirements::javascript(CMS_DIR . '/thirdparty/jquery-layout/jquery.layout.js'); + Requirements::javascript(CMS_DIR . '/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js'); Requirements::javascript(CMS_DIR . '/javascript/ssui.core.js'); // @todo Load separately so the CSS files can be inlined Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.all.css'); @@ -276,7 +276,7 @@ class LeftAndMain extends Controller { 'cms/javascript/LeftAndMain_right.js', 'jsparty/tree/tree.js', 'cms/javascript/TinyMCEImageEnhancement.js', - 'sapphire/thirdparty/swfupload/swfupload.js', + 'cms/thirdparty/swfupload/swfupload.js', 'cms/javascript/Upload.js', 'cms/javascript/TinyMCEImageEnhancement.js', 'sapphire/javascript/TreeSelectorField.js', diff --git a/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js b/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js new file mode 100644 index 00000000..9c952d56 --- /dev/null +++ b/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js @@ -0,0 +1,53 @@ +/** + * Fits an element's height to its parent by substracting + * all (visible) siblings heights from the element. + * Caution: This will set overflow: hidden on the parent + * + * Copyright 2009 Ingo Schommer, SilverStripe Ltd. + * Licensed under MIT License: http://www.opensource.org/licenses/mit-license.php + * + * @todo Implement selectors to ignore certain elements + * + * @author Ingo Schommer, SilverStripe Ltd. + * @version 0.1 + */ +jQuery.fn.extend({ + fitHeightToParent: function() { + return jQuery(this).each(function() { + var $this = jQuery(this); + + var boxmodel = ['marginTop','marginBottom','paddingTop','paddingBottom','borderBottomWidth','borderTopWidth']; + + // don't bother if element or parent arent visible, + // we won't get height readings + if($this.is(':visible') && $this.parent().is(':visible')) { + + // we set overflow = hidden so that large children don't muck things up in IE6 box model + $this.parent().css('overflow', 'hidden'); + + // get height from parent without any margins as a starting point, + // and reduce any top/bottom paddings + var height = $this.parent().innerHeight() + - parseFloat($this.parent().css('paddingTop')) + - parseFloat($this.parent().css('paddingBottom')); + + // substract height of any siblings of the current element + // including their margins/paddings/borders + $this.siblings(':visible').filter(function() { + // remove all absolutely positioned elements + return (jQuery(this).css('position') != 'absolute'); + }).each(function() { + height -= jQuery(this).outerHeight(true); + }); + + // remove margins/paddings/borders on inner element + jQuery.each(boxmodel, function(i, name) { + height -= parseFloat($this.css(name)) || 0; + }); + + // set new height + $this.height(height); + } + }); + } +}); \ No newline at end of file diff --git a/javascript/jquery-fitheighttoparent/test.html b/javascript/jquery-fitheighttoparent/test.html new file mode 100644 index 00000000..8963b9dc --- /dev/null +++ b/javascript/jquery-fitheighttoparent/test.html @@ -0,0 +1,11 @@ + + + + + + + + test + + \ No newline at end of file diff --git a/javascript/jquery-fitheighttoparent/tests/unit.html b/javascript/jquery-fitheighttoparent/tests/unit.html new file mode 100644 index 00000000..ace86ead --- /dev/null +++ b/javascript/jquery-fitheighttoparent/tests/unit.html @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + +

jquery.fitheighttoparent unit test

+ +

+ +
    + +
    + +
    +
    +
    +

    test 1

    +
    +
    +
    + +
    +
    +
    +

    test 2

    +
    +
    + +
    +
    +
    +

    test 3

    +
    +
    +
    + +
    +
    +
    +

    test 4

    +
    +
    + +
    +
    +

    test 5

    + +
    +
    +
    + +
    +
    +
    +

    test 6

    +

    Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.

    +

    +Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.

    +

    +Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.

    +
    +
    + + \ No newline at end of file diff --git a/thirdparty/jquery-layout/README.txt b/thirdparty/jquery-layout/README.txt new file mode 100644 index 00000000..7e5f878b --- /dev/null +++ b/thirdparty/jquery-layout/README.txt @@ -0,0 +1 @@ +See http://layout.jquery-dev.net/downloads.html \ No newline at end of file diff --git a/thirdparty/jquery-layout/changelog.txt b/thirdparty/jquery-layout/changelog.txt new file mode 100755 index 00000000..4217cfdf --- /dev/null +++ b/thirdparty/jquery-layout/changelog.txt @@ -0,0 +1,60 @@ +1.2.0 +* ADDED maskIframesOnResize option: true=ALL -OR- a selector string +* ADDED options to set different animations on open and close +* ADDED new callback events, ie: onshow, onhide +* ADDED start/end callbacks, eg: onopen_start, onopen_end, etc. +* ADDED ability to cancel events using callbacks, eg: onopen_start +* CHANGED Layout.config.fxDefaults to Layout.effects (internal use) +* FIXED missing semi-colon so minified version works in IE + +1.1.3 +* FIXED typo in cursor-hotkeys code +* ADDED scrollToBookmarkOnLoad options - enables use of URL hash: + o www.site.com/page.html#myBookmark + o AFTER layout is created, attempts to scroll to bookmark + o default = true - otherwise bookmarks are non-functional + +1.1.2 +* UPDATED paneSelector rules to handle FORMS and pane-nesting + o automatically looks for panes inside 'first form' in container + o if using an ID as paneSelector, pane can be 'deeply nested' +* ADDED auto-CSS for 'containers' other than BODY + o overflow: hidden - ensures no scrollbars on container + o position: relative - IF NOT: fixed, absolute or relative + o height: 100% - IF NOT specified or is 'auto' +* ADDED noAnimation param to open() and close() - not used internally + +1.1.1 +* CHANGED toggler element from a SPAN to a DIV +* CHANGED auto-generated custom-buttons classes for better consistency + o [buttonClass]-[pane]-[buttonType] ==> [buttonClass]-[buttonType]-[pane] + o ui-layout-button-west-open ==> ui-layout-button-open-west + o ui-layout-button-west-pin-up ==> ui-layout-button-pin-west-up +* CHANGED default for hideTogglerOnSlide to false +* CHANGED internal 'cDims' hash to alias for state.container +* CHANGED internal aliases: s = state[pane] and o = options[pane] +* UPDATED toggler-text to auto-show correct spans (content-open/closed) +* FIXED toggler-text - now centers text span correctly +* FIXED bug affecting IE6 when layout has no north or south pane +* ADDED new layout property 'state' - eg: myLayout.state.west.size +* REMOVED layout.containerDimensions property - USE: layout.state.container +* CHANGED data returned to callbacks - added pane-state as 3rd param + +1.1.0 +* RENAMED raisePaneZindexOnHover ==> showOverflowOnHover +* REMOVED "overflow: auto" from base-styles. Overflow must now be set by + CSS - unless applyDefaultStyles==true. No longer need "!important" to + set pane overflow in your stylesheet. +* CHANGED minSize default from 50 to 0 (still auto-limited to 'css size') +* FIXED bug in allowOverflow - now works with 'custom paneClass' +* EXPOSED two CSS utility methods + o myLayout.cssWidth( elem ) + o myLayout.cssHeight( elem ) +* NEW auto-resize for ALL layouts on windows.resize +* UPDATED auto-resizing of panes after a container-resize +* NEW flow-code to prevent simultaneous pane animations +* NEW options to add text inside toggler-buttons +* NEW options for hotkeys - standard (cursors) and user-defined + +1.0 +* Initial release \ No newline at end of file diff --git a/thirdparty/jquery-layout/example.html b/thirdparty/jquery-layout/example.html new file mode 100755 index 00000000..b4ffd66e --- /dev/null +++ b/thirdparty/jquery-layout/example.html @@ -0,0 +1,23 @@ + + +Layout Example + + + + + +
    Center +

    Go to the Demos page

    +

    * Pane-resizing is disabled because ui.draggable.js is not linked

    +

    * Pane-animation is disabled because ui.effects.js is not linked

    +
    +
    North
    +
    South
    +
    East
    +
    West
    + + \ No newline at end of file diff --git a/thirdparty/jquery-layout/jquery.js b/thirdparty/jquery-layout/jquery.js new file mode 100755 index 00000000..92635743 --- /dev/null +++ b/thirdparty/jquery-layout/jquery.js @@ -0,0 +1,4376 @@ +/*! + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){ + +var + // Will speed up references to window, and allows munging its name. + window = this, + // Will speed up references to undefined, and allows munging its name. + undefined, + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + // Map over the $ in case of overwrite + _$ = window.$, + + jQuery = window.jQuery = window.$ = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this[0] = selector; + this.length = 1; + this.context = selector; + return this; + } + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + var match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) + selector = jQuery.clean( [ match[1] ], context ); + + // HANDLE: $("#id") + else { + var elem = document.getElementById( match[3] ); + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem && elem.id != match[3] ) + return jQuery().find( selector ); + + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery( elem || [] ); + ret.context = document; + ret.selector = selector; + return ret; + } + + // HANDLE: $(expr, [context]) + // (which is just equivalent to: $(content).find(expr) + } else + return jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) + return jQuery( document ).ready( selector ); + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return this.setArray(jQuery.isArray( selector ) ? + selector : + jQuery.makeArray(selector)); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.3.2", + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num === undefined ? + + // Return a 'clean' array + Array.prototype.slice.call( this ) : + + // Return just the object + this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = jQuery( elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) + ret.selector = this.selector + (this.selector ? " " : "") + selector; + else if ( name ) + ret.selector = this.selector + "." + name + "(" + selector + ")"; + + // Return the newly-formed element set + return ret; + }, + + // Force the current matched set of elements to become + // the specified array of elements (destroying the stack in the process) + // You should use pushStack() in order to do this, but maintain the stack + setArray: function( elems ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + this.length = 0; + Array.prototype.push.apply( this, elems ); + + return this; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem && elem.jquery ? elem[0] : elem + , this ); + }, + + attr: function( name, value, type ) { + var options = name; + + // Look for the case where we're accessing a style value + if ( typeof name === "string" ) + if ( value === undefined ) + return this[0] && jQuery[ type || "attr" ]( this[0], name ); + + else { + options = {}; + options[ name ] = value; + } + + // Check to see if we're setting style values + return this.each(function(i){ + // Set all the styles + for ( name in options ) + jQuery.attr( + type ? + this.style : + this, + name, jQuery.prop( this, options[ name ], type, i, name ) + ); + }); + }, + + css: function( key, value ) { + // ignore negative width and height values + if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) + value = undefined; + return this.attr( key, value, "curCSS" ); + }, + + text: function( text ) { + if ( typeof text !== "object" && text != null ) + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + + var ret = ""; + + jQuery.each( text || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + ret += this.nodeType != 1 ? + this.nodeValue : + jQuery.fn.text( [ this ] ); + }); + }); + + return ret; + }, + + wrapAll: function( html ) { + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).clone(); + + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); + + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } + + return this; + }, + + wrapInner: function( html ) { + return this.each(function(){ + jQuery( this ).contents().wrapAll( html ); + }); + }, + + wrap: function( html ) { + return this.each(function(){ + jQuery( this ).wrapAll( html ); + }); + }, + + append: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.appendChild( elem ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery( [] ); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: [].push, + sort: [].sort, + splice: [].splice, + + find: function( selector ) { + if ( this.length === 1 ) { + var ret = this.pushStack( [], "find", selector ); + ret.length = 0; + jQuery.find( selector, this[0], ret ); + return ret; + } else { + return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + })), "find", selector ); + } + }, + + clone: function( events ) { + // Do the clone + var ret = this.map(function(){ + if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // attributes in IE that are actually only stored + // as properties will not be copied (such as the + // the name attribute on an input). + var html = this.outerHTML; + if ( !html ) { + var div = this.ownerDocument.createElement("div"); + div.appendChild( this.cloneNode(true) ); + html = div.innerHTML; + } + + return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; + } else + return this.cloneNode(true); + }); + + // Copy the events from the original to the clone + if ( events === true ) { + var orig = this.find("*").andSelf(), i = 0; + + ret.find("*").andSelf().each(function(){ + if ( this.nodeName !== orig[i].nodeName ) + return; + + var events = jQuery.data( orig[i], "events" ); + + for ( var type in events ) { + for ( var handler in events[ type ] ) { + jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + } + } + + i++; + }); + } + + // Return the cloned set + return ret; + }, + + filter: function( selector ) { + return this.pushStack( + jQuery.isFunction( selector ) && + jQuery.grep(this, function(elem, i){ + return selector.call( elem, i ); + }) || + + jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ + return elem.nodeType === 1; + }) ), "filter", selector ); + }, + + closest: function( selector ) { + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, + closer = 0; + + return this.map(function(){ + var cur = this; + while ( cur && cur.ownerDocument ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { + jQuery.data(cur, "closest", closer); + return cur; + } + cur = cur.parentNode; + closer++; + } + }); + }, + + not: function( selector ) { + if ( typeof selector === "string" ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); + }, + + add: function( selector ) { + return this.pushStack( jQuery.unique( jQuery.merge( + this.get(), + typeof selector === "string" ? + jQuery( selector ) : + jQuery.makeArray( selector ) + ))); + }, + + is: function( selector ) { + return !!selector && jQuery.multiFilter( selector, this ).length > 0; + }, + + hasClass: function( selector ) { + return !!selector && this.is( "." + selector ); + }, + + val: function( value ) { + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if( jQuery.nodeName( elem, 'option' ) ) + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type == "select-one"; + + // Nothing was selected + if ( index < 0 ) + return null; + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) + return value; + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(/\r/g, ""); + + } + + return undefined; + } + + if ( typeof value === "number" ) + value += ''; + + return this.each(function(){ + if ( this.nodeType != 1 ) + return; + + if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); + + else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(value); + + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); + }, + + html: function( value ) { + return value === undefined ? + (this[0] ? + this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : + null) : + this.empty().append( value ); + }, + + replaceWith: function( value ) { + return this.after( value ).remove(); + }, + + eq: function( i ) { + return this.slice( i, +i + 1 ); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ), + "slice", Array.prototype.slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function(elem, i){ + return callback.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + domManip: function( args, table, callback ) { + if ( this[0] ) { + var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), + scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), + first = fragment.firstChild; + + if ( first ) + for ( var i = 0, l = this.length; i < l; i++ ) + callback.call( root(this[i], first), this.length > 1 || i > 0 ? + fragment.cloneNode(true) : fragment ); + + if ( scripts ) + jQuery.each( scripts, evalScript ); + } + + return this; + + function root( elem, cur ) { + return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; + } + } +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { + if ( elem.src ) + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + + else + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + + if ( elem.parentNode ) + elem.parentNode.removeChild( elem ); +} + +function now(){ + return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) + target = {}; + + // extend jQuery itself if only one argument is passed + if ( length == i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + // Recurse if we're merging object values + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) + target[ name ] = jQuery.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + + // Don't bring in undefined values + else if ( copy !== undefined ) + target[ name ] = copy; + + } + + // Return the modified object + return target; +}; + +// exclude the following css properties to add px +var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, + // cache defaultView + defaultView = document.defaultView || {}, + toString = Object.prototype.toString; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) + window.jQuery = _jQuery; + + return jQuery; + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + // check if an element is in a (or is an) XML document + isXMLDoc: function( elem ) { + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); + }, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && /\S/.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + if ( jQuery.support.scriptEval ) + script.appendChild( document.createTextNode( data ) ); + else + script.text = data; + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, length = object.length; + + if ( args ) { + if ( length === undefined ) { + for ( name in object ) + if ( callback.apply( object[ name ], args ) === false ) + break; + } else + for ( ; i < length; ) + if ( callback.apply( object[ i++ ], args ) === false ) + break; + + // A special, fast, case for the most common use of each + } else { + if ( length === undefined ) { + for ( name in object ) + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + } + + return object; + }, + + prop: function( elem, value, type, i, name ) { + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, i ); + + // Handle passing in a number to a CSS property + return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, classNames ) { + jQuery.each((classNames || "").split(/\s+/), function(i, className){ + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) + elem.className += (elem.className ? " " : "") + className; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, classNames ) { + if (elem.nodeType == 1) + elem.className = classNames !== undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; + }, + + // internal only, use hasClass("class") + has: function( elem, className ) { + return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var old = {}; + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( var name in options ) + elem.style[ name ] = old[ name ]; + }, + + css: function( elem, name, force, extra ) { + if ( name == "width" || name == "height" ) { + var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + + function getWH() { + val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + + if ( extra === "border" ) + return; + + jQuery.each( which, function() { + if ( !extra ) + val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + if ( extra === "margin" ) + val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; + else + val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + } + + if ( elem.offsetWidth !== 0 ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, Math.round(val)); + } + + return jQuery.curCSS( elem, name, force ); + }, + + curCSS: function( elem, name, force ) { + var ret, style = elem.style; + + // We need to handle opacity special in IE + if ( name == "opacity" && !jQuery.support.opacity ) { + ret = jQuery.attr( style, "opacity" ); + + return ret == "" ? + "1" : + ret; + } + + // Make sure we're using the right name for getting the float value + if ( name.match( /float/i ) ) + name = styleFloat; + + if ( !force && style && style[ name ] ) + ret = style[ name ]; + + else if ( defaultView.getComputedStyle ) { + + // Only "float" is needed here + if ( name.match( /float/i ) ) + name = "float"; + + name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + + var computedStyle = defaultView.getComputedStyle( elem, null ); + + if ( computedStyle ) + ret = computedStyle.getPropertyValue( name ); + + // We should always get a number back from opacity + if ( name == "opacity" && ret == "" ) + ret = "1"; + + } else if ( elem.currentStyle ) { + var camelCase = name.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + + ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { + // Remember the original values + var left = style.left, rsLeft = elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + elem.runtimeStyle.left = elem.currentStyle.left; + style.left = ret || 0; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + elem.runtimeStyle.left = rsLeft; + } + } + + return ret; + }, + + clean: function( elems, context, fragment ) { + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { + var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); + if ( match ) + return [ context.createElement( match[1] ) ]; + } + + var ret = [], scripts = [], div = context.createElement("div"); + + jQuery.each(elems, function(i, elem){ + if ( typeof elem === "number" ) + elem += ''; + + if ( !elem ) + return; + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? + all : + front + ">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); + + var wrap = + // option or optgroup + !tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
    " ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and + + + + + + + + + + +
    + +
    + +
    Inner Center
    +
    Inner West
    +
    Inner East
    +
    Inner North
    +
    Inner South
    + +
    +
    Middle West
    +
    Middle East
    + +
    + +
    Outer West
    +
    Outer East
    + +
    Outer North
    +
    Outer South
    + + + + diff --git a/thirdparty/jquery-layout/simple.html b/thirdparty/jquery-layout/simple.html new file mode 100755 index 00000000..c450190d --- /dev/null +++ b/thirdparty/jquery-layout/simple.html @@ -0,0 +1,197 @@ + + + + + Simple Layout Demo + + + + + + + + + + + + + + + + + +
    + This is the north pane, closable, slidable and resizable + +
      +
    • +
        +
      • one
      • +
      • two
      • +
      • three
      • +
      • four
      • +
      • five
      • +
      + Drop-Down +
    • +
    + +
    + + +
    + This is the west pane, closable, slidable and resizable + +
      +
    • +
        +
      • one
      • +
      • two
      • +
      • three
      • +
      • four
      • +
      • five
      • +
      + Pop-Up +
    • +
    + +

    + +
    + +
    + This is the south pane, closable, slidable and resizable   + +
    + +
    + This is the east pane, closable, slidable and resizable + + +
      +
    • +
        +
      • one
      • +
      • two
      • +
      • three
      • +
      • four
      • +
      • five
      • +
      + Pop-Up +
    • +
    + +

    + +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +
    + +
    + This center pane auto-sizes to fit the space between the 'border-panes' +

    This layout was created with only default options - no customization

    +

    Only the applyDefaultStyles option was enabled for basic formatting

    +

    The Pop-Up and Drop-Down lists demonstrate the allowOverflow() utility

    +

    The Close and Toggle buttons are examples of custom buttons

    +

    Go to the Demos page

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    +
    + + + \ No newline at end of file diff --git a/thirdparty/swfupload/.piston.yml b/thirdparty/swfupload/.piston.yml new file mode 100644 index 00000000..59ea6e46 --- /dev/null +++ b/thirdparty/swfupload/.piston.yml @@ -0,0 +1,8 @@ +--- +format: 1 +handler: + piston:remote-revision: 854 + piston:uuid: 46bd1c57-1c41-0410-9cc9-652e43039c97 +lock: false +repository_url: http://swfupload.googlecode.com/svn/swfupload/tags/swfupload_v2.0.1/FlashDevelop +repository_class: Piston::Svn::Repository diff --git a/thirdparty/swfupload/Changelog.txt b/thirdparty/swfupload/Changelog.txt new file mode 100644 index 00000000..4c0e754e --- /dev/null +++ b/thirdparty/swfupload/Changelog.txt @@ -0,0 +1,193 @@ + * SWFUpload v2.0 by Jacob Roberts, Nov 2007, www.swfupload.org, linebyline.blogspot.com + + * --------- 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 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/thirdparty/swfupload/Flash8/Delegate.as b/thirdparty/swfupload/Flash8/Delegate.as new file mode 100644 index 00000000..ba4526c7 --- /dev/null +++ b/thirdparty/swfupload/Flash8/Delegate.as @@ -0,0 +1,8 @@ +class Delegate +{ + static function create(obj:Object, func:Function):Function + { + return function() { return func.apply(obj, arguments); }; + } + +} \ No newline at end of file diff --git a/thirdparty/swfupload/Flash8/ExternalCall.as b/thirdparty/swfupload/Flash8/ExternalCall.as new file mode 100644 index 00000000..395381ec --- /dev/null +++ b/thirdparty/swfupload/Flash8/ExternalCall.as @@ -0,0 +1,115 @@ +import flash.external.ExternalInterface; + +class ExternalCall +{ + + /*public function ExternalCall() + { + + } + */ + + public static function Simple(callback:String):Void { + ExternalInterface.call(callback); + } + 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):Void { + + ExternalInterface.call(callback, EscapeMessage(num_files_selected)); + + } + + 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:Number, bytes_total:Number):Void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(bytes_loaded), EscapeMessage(bytes_total)); + + } + public static function UploadSuccess(callback:String, file_object:Object):Void { + + ExternalInterface.call(callback, EscapeMessage(file_object)); + + } + 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)); + + } + + /* 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 (typeof message == "string") { + message = EscapeString(message); + } + else if (typeof message == "array") { + message = EscapeArray(message); + } + else if (typeof message == "object") { + message = EscapeObject(message); + } + + return message; + } + + private static function EscapeString(message:String):String { + //var replacePattern:RegExp = /\\/g; //new RegExp("/\\/", "g"); + return message.split("\\").join("\\\\").split("\n").join("\\n").split("\r").join("\\r").split("\f").join("\\f").split("\b").join("\\b"); + } + private static function EscapeArray(message_array:Array):Array { + var length:Number = message_array.length; + var i:Number = 0; + for (i=0; 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/thirdparty/swfupload/Flash8/FileItem.as b/thirdparty/swfupload/Flash8/FileItem.as new file mode 100644 index 00000000..15eebb0a --- /dev/null +++ b/thirdparty/swfupload/Flash8/FileItem.as @@ -0,0 +1,65 @@ +import flash.net.FileReference; + +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:Number = 0; + private var js_object:Object; + + public static var FILE_STATUS_QUEUED:Number = -1; + public static var FILE_STATUS_IN_PROGRESS:Number = -2; + public static var FILE_STATUS_ERROR:Number = -3; + public static var FILE_STATUS_SUCCESS:Number = -4; + public static var FILE_STATUS_CANCELLED:Number = -5; + public static var FILE_STATUS_NEW:Number = -6; // This file status should never be sent to JavaScript + + public function FileItem(file_reference:FileReference, control_id:String) + { + this.postObject = {}; + this.file_reference = file_reference; + this.id = control_id + "_" + (FileItem.file_id_sequence++); + this.file_status = FileItem.FILE_STATUS_QUEUED; + + this.js_object = { + id: this.id, + index: this.index, + name: this.file_reference.name, + size: this.file_reference.size, + type: this.file_reference.type, + creationdate: this.file_reference.creationDate, + modificationdate: this.file_reference.modificationDate, + filestatus: this.file_status, + post: this.GetPostObject() + }; + + } + + 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():Object { + return this.postObject; + } + + // Update the js_object and return it. + public function ToJavaScriptObject():Object { + this.js_object.filestatus = this.file_status; + this.js_object.post = this.GetPostObject(); + + return this.js_object; + } + + public function toString():String { + return "FileItem - ID: " + this.id; + } +} \ No newline at end of file diff --git a/thirdparty/swfupload/Flash8/SWFUpload v2.as2proj b/thirdparty/swfupload/Flash8/SWFUpload v2.as2proj new file mode 100644 index 00000000..54be9bc9 --- /dev/null +++ b/thirdparty/swfupload/Flash8/SWFUpload v2.as2proj @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deploy.bat + + + + \ No newline at end of file diff --git a/thirdparty/swfupload/Flash8/SWFUpload.as b/thirdparty/swfupload/Flash8/SWFUpload.as new file mode 100644 index 00000000..f62fac31 --- /dev/null +++ b/thirdparty/swfupload/Flash8/SWFUpload.as @@ -0,0 +1,914 @@ +/* +* Todo: +* In SWFUpload v3 the file_queue and file_index should be merged. This probably means we'll remove FileItem.id and just +* use indexes everywhere. +* */ + +import flash.net.FileReferenceList; +import flash.net.FileReference; +import flash.external.ExternalInterface; + +import FileItem; +import ExternalCall; +import Delegate; + +class SWFUpload { + // Cause SWFUpload to start as soon as the movie starts + public static function main():Void + { + // Code to attempt to fix "flash script running slowly" error messages + var counter:Number = 0; + _root.onEnterFrame = function () { + if (++counter > 100) counter = 0; + }; + + // Start SWFUpload + var SWFUpload:SWFUpload = new SWFUpload(); + } + + private var build_number:String = "SWFUPLOAD 2.0.1 FP8 2007-12-05 0001"; + + // 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 file_reference_listener:Object; + + // 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; + + // 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 debugEnabled: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; + + public function SWFUpload() { + System.security.allowDomain("*"); // Allow uploading to any domain + + // Setup file FileReferenceList events + var fbmListener:Object = { + onSelect : Delegate.create(this, this.Select_Many_Handler), + onCancel : Delegate.create(this, this.DialogCancelled_Handler) + } + this.fileBrowserMany.addListener(fbmListener); + + // Setup the events listner + this.file_reference_listener = { + onProgress : Delegate.create(this, this.FileProgress_Handler), + onIOError : Delegate.create(this, this.IOError_Handler), + onSecurityError : Delegate.create(this, this.SecurityError_Handler), + onHTTPError : Delegate.create(this, this.HTTPError_Handler), + onComplete : Delegate.create(this, this.FileComplete_Handler) + }; + + // Get the move name + this.movieName = _root.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"; + + // Get the Flash Vars + this.uploadURL = _root.uploadURL; + this.filePostName = _root.filePostName; + this.fileTypes = _root.fileTypes; + this.fileTypesDescription = _root.fileTypesDescription + " (" + this.fileTypes + ")"; + this.loadPostParams(_root.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 = _root.debugEnabled == "true" ? true : false; + } catch (ex:Object) { + this.debugEnabled = false; + } + + try { + this.fileSizeLimit = Number(_root.fileSizeLimit); + if (this.fileSizeLimit < 0) this.fileSizeLimit = 0; + } catch (ex:Object) { + this.fileSizeLimit = 0; + } + + try { + this.fileUploadLimit = Number(_root.fileUploadLimit); + if (this.fileUploadLimit < 0) this.fileUploadLimit = 0; + } catch (ex:Object) { + this.fileUploadLimit = 0; + } + + try { + this.fileQueueLimit = Number(_root.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 { + ExternalInterface.addCallback("SelectFile", this, this.SelectFile); + ExternalInterface.addCallback("SelectFiles", this, this.SelectFiles); + ExternalInterface.addCallback("StartUpload", this, this.StartUpload); + ExternalInterface.addCallback("ReturnUploadStart", this, this.ReturnUploadStart); + ExternalInterface.addCallback("StopUpload", this, this.StopUpload); + ExternalInterface.addCallback("CancelUpload", this, this.CancelUpload); + + ExternalInterface.addCallback("GetStats", this, this.GetStats); + ExternalInterface.addCallback("SetStats", this, this.SetStats); + ExternalInterface.addCallback("GetFile", this, this.GetFile); + ExternalInterface.addCallback("GetFileByIndex", this, this.GetFileByIndex); + + ExternalInterface.addCallback("AddFileParam", this, this.AddFileParam); + ExternalInterface.addCallback("RemoveFileParam", this, this.RemoveFileParam); + + ExternalInterface.addCallback("SetUploadURL", this, this.SetUploadURL); + ExternalInterface.addCallback("SetPostParams", this, this.SetPostParams); + ExternalInterface.addCallback("SetFileTypes", this, this.SetFileTypes); + ExternalInterface.addCallback("SetFileSizeLimit", this, this.SetFileSizeLimit); + ExternalInterface.addCallback("SetFileUploadLimit", this, this.SetFileUploadLimit); + ExternalInterface.addCallback("SetFileQueueLimit", this, this.SetFileQueueLimit); + ExternalInterface.addCallback("SetFilePostName", this, this.SetFilePostName); + ExternalInterface.addCallback("SetDebugEnabled", this, this.SetDebugEnabled); + } catch (ex:Error) { + this.Debug("Callbacks where not set."); + } + + this.Debug("SWFUpload Init Complete"); + this.PrintDebugInfo(); + + // Do some feature detection + if (flash.net.FileReferenceList && flash.net.FileReference && flash.external.ExternalInterface && flash.external.ExternalInterface.available) { + ExternalCall.Simple(this.flashReady_Callback); + } else { + this.Debug("Feature Detection Failed"); + } + } + + /* ***************************************** + * FileReference Event Handlers + * *************************************** */ + private function DialogCancelled_Handler():Void { + this.Debug("Event: fileDialogComplete: File Dialog window cancelled."); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0); + } + + private function FileProgress_Handler(file:FileReference, bytesLoaded:Number, bytesTotal:Number):Void { + 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 FileComplete_Handler():Void { + this.successful_uploads++; + this.current_file_item.file_status = FileItem.FILE_STATUS_SUCCESS; + + this.Debug("Event: uploadSuccess: File ID: " + this.current_file_item.id + " Data: n/a"); + ExternalCall.UploadSuccess(this.uploadSuccess_Callback, this.current_file_item.ToJavaScriptObject()); + + this.UploadComplete(); + + } + + private function HTTPError_Handler(file:FileReference, httpError:Number):Void { + 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: " + httpError + "."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), httpError.toString()); + this.UploadComplete(); + } + + // 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 the crash the browser (FireFox/Opera) + private function IOError_Handler(file:FileReference):Void { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + if(!this.uploadURL.length) { + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, this.current_file_item.ToJavaScriptObject(), "IO Error"); + } else { + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), "IO Error"); + } + + this.UploadComplete(); + } + + private function SecurityError_Handler(file:FileReference, errorString:String):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:" + errorString); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), errorString); + + this.UploadComplete(); + } + + private function Select_Many_Handler(frl:FileReferenceList):Void { + this.Select_Handler(frl.fileList); + } + private function Select_One_Handler(file:FileReference):Void { + var fileArray:Array = new Array(1); + fileArray[0] = file; + this.Select_Handler(fileArray); + } + + private function Select_Handler(file_reference_list:Array):Void { + this.Debug("Select Handler: Files Selected from Dialog. Processing file list"); + this.Debug("Type: " + typeof(this.fileQueueLimit) + " Value:" + this.fileQueueLimit); + // 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; + } + } + + // 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); + + // Add the file to the index + file_item.index = this.file_index.length; + this.file_index[file_item.index] = file_item; + + // The the file to see if it is acceptable + 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) { + this.Debug("Event: fileQueued : File ID: " + file_item.id); + this.file_queue.push(file_item); + this.queued_uploads++; + ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject()); + } + else if (!is_valid_filetype) { + file_item.file_reference = null; // Cleanup the object + this.Debug("Event: fileQueueError : File not of a valid type."); + this.queue_errors++; + 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 + this.Debug("Event: fileQueueError : File exceeds size limit."); + this.queue_errors++; + 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 + this.Debug("Event: fileQueueError : File is zero bytes."); + this.queue_errors++; + 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 + this.Debug("Entered an unexpected state checking the file in Select_Handler"); + } + } + } + + this.Debug("Event: fileDialogComplete : Finished adding files"); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length); + } + + + /* **************************************************************** + Externally exposed functions + ****************************************************************** */ + // Opens a file browser dialog that allows one file to be selected. + private function SelectFile():Void { + this.fileBrowserOne = new FileReference(); + var fbo_listener:Object = { + onSelect : Delegate.create(this, this.Select_One_Handler), + onCancel : Delegate.create(this, this.DialogCancelled_Handler) + } + this.fileBrowserOne.addListener(fbo_listener); + + // 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); + + this.fileBrowserOne.browse([{ description : allowed_file_types_description, extension : allowed_file_types }]); + + } + + // 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); + this.fileBrowserMany.browse([{ description : allowed_file_types_description, extension : allowed_file_types }]); + } + + + // Starts uploading. Checks to see if a file is currently uploading and, if not, starts the upload. + private function StartUpload(file_id:String):Void { + if (file_id == undefined) file_id = ""; + + if (this.current_file_item == null) { + this.Debug("StartUpload(): Starting Upload: " + (file_id ? "File ID:" + file_id : "First file in queue")); + this.StartFile(file_id); + } else { + this.Debug("StartUpload(): Upload run already in progress"); + } + } + + // 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(); + + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + + // Remove the event handlers + this.current_file_item.file_reference.removeListener(this.file_reference_listener); + + this.file_queue.unshift(this.current_file_item); + var js_object:Object = this.current_file_item.ToJavaScriptObject(); + this.current_file_item = null; + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped"); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + this.Debug("StopUpload(): upload stopped."); + } else { + this.Debug("StopUpload(): Upload run not in progress"); + } + } + + /* 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):Void { + var file_item:FileItem = null; + if (file_id == undefined) file_id = ""; + + // 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.upload_cancelled++; + + this.Debug("Event: fileCancelled: File ID: " + this.current_file_item.id + ". Cancelling current upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled."); + + this.UploadComplete(); // <-- this advanced the upload to the next file + } 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[file_index] = null; + this.queued_uploads--; + this.upload_cancelled++; + + + // Cancel the file (just for good measure) and make the callback + file_item.file_reference.cancel(); + file_item.file_reference.removeListener(this.file_reference_listener); + file_item.file_reference = null; + + this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload"); + this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + + // 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(); + file_item.file_reference.removeListener(this.file_reference_listener); + file_item.file_reference = null; + + this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload"); + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + + // Get rid of the file object + file_item = null; + } + + } + + } + + 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:FileItem = null; + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + file = 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 AddFileParam(file_id:String, name:String, value:String):Boolean { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + var file_item:FileItem = FileItem(this.file_queue[file_index]); + + file_item.AddParam(name, value); + return true; + } else { + return false; + } + } + private function RemoveFileParam(file_id:String, name:String):Boolean { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + var file_item:FileItem = FileItem(this.file_queue[file_index]); + file_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); + } + + private function SetFileSizeLimit(bytes:Number):Void { + if (bytes < 0) bytes = 0; + this.fileSizeLimit = bytes; + } + + 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 SetDebugEnabled(debug_enabled:Boolean):Void { + this.debugEnabled = debug_enabled; + } + + /* ************************************************************* + File processing and handling functions + *************************************************************** */ + // + private function StartFile(file_id:String):Void { + if (file_id == undefined) file_id = ""; + + // Only upload a file uploads are being processed. + // startFile could be called by a file cancellation even when we aren't currently uploading + if (this.current_file_item != null) { + this.Debug("StartFile(): Upload already in progress. Not starting another upload."); + } + + this.Debug("StartFile: " + (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) { + // Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop) + this.current_file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem + if (typeof(this.current_file_item) == "undefined") { + this.current_file_item = null; + continue; + } + } + } 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[file_index] = null; + } 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 queued."); + } + } + + + // Start the upload if we found an item to upload + if (this.current_file_item != null) { + // Begin the upload + this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id); + 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("startFile(): No File Reference found. There are no files left to upload.\nstartFile(): Ending upload run."); + } + } + + // 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 file was no longer queued. This is okay if the file was stopped or cancelled."); + return; + } + + if (start_upload) { + try { + // Set the event handlers + this.current_file_item.file_reference.addListener(this.file_reference_listener); + + // Upload the file + var url:String = this.BuildRequest(); + + this.Debug("startFile(): File Reference found. File accepted by startUpload event. Starting upload to " + this.uploadURL + " for File ID: " + this.current_file_item.id); + this.current_file_item.file_reference.upload(url, this.filePostName, false); + } catch (ex:Error) { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + var message:String = ex.name + "\n" + ex.message; + this.Debug("Event: uploadError(): Unhandled exception: " + message); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message); + + this.UploadComplete(); + } + this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS; + + } else { + this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading file."); + + // Remove the event handlers + this.current_file_item.file_reference.removeListener(this.file_reference_listener); + + // Re-queue the FileItem + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + var js_object:Object = this.current_file_item.ToJavaScriptObject(); + this.file_queue.unshift(this.current_file_item); + this.current_file_item = null; + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file."); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + this.Debug("startFile(): upload rejected by startUpload event. File re-queued."); + } + } + + // Completes the file upload by deleting it's reference, advancing the pointer. + // Once this event files a new upload can be started. + private function UploadComplete():Void { + var jsFileObj:Object = this.current_file_item.ToJavaScriptObject(); + + this.current_file_item.file_reference.removeListener(this.file_reference_listener); + this.current_file_item.file_reference = null; + + this.current_file_item = null; + this.queued_uploads--; + + 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. + 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 * 1000)) { + 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():String { + // Create the request object + var file_post:Object = this.current_file_item.GetPostObject(); + var key:String; + + var url:String = this.uploadURL; + + 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(key + "=" + this.uploadPostObject[key]); + } + } + + for (key in file_post) { + this.Debug("File Post Item: " + key + "=" + this.uploadPostObject[key]); + if (file_post.hasOwnProperty(key)) { + pairs.push(escape(key) + "=" + escape(file_post[key])); + } + } + + url = this.uploadURL + (this.uploadURL.indexOf("?") > -1 ? "&" : "?") + pairs.join("&"); + + return url; + } + + private function Debug(msg:String):Void { + if (this.debugEnabled) { + var lines:Array = msg.split("\n"); + for (var i:Number=0; i < lines.length; i++) { + lines[i] = "SWF DEBUG: " + lines[i]; + } + try { + ExternalCall.Debug(this.debug_Callback, lines.join("\n")); + } catch (ex:Error) { + // pretend nothing happened + } + } + } + + private function PrintDebugInfo():Void { + var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n" + + "Build Number: " + this.build_number + "\n" + + "movieName: " + this.movieName + "\n" + + "Upload URL: " + this.uploadURL + "\n" + + "File Types String: " + this.fileTypes + "\n" + + "Parsed File Types: " + this.valid_file_extensions.toString() + "\n" + + "File Types Description: " + this.fileTypesDescription + "\n" + + "File Size Limit: " + this.fileSizeLimit + "\n" + + "File Upload Limit: " + this.fileUploadLimit + "\n" + + "File Queue Limit: " + this.fileQueueLimit + "\n" + + "Post Params:\n"; + for (var key:String in this.uploadPostObject) { + 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= 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[unescape(name_value.substring(0, index_of_equals))] = unescape(name_value.substr(index_of_equals + 1)); + } + } + } + this.uploadPostObject = post_object; + } + +} \ No newline at end of file diff --git a/thirdparty/swfupload/Flash8/deploy.bat b/thirdparty/swfupload/Flash8/deploy.bat new file mode 100644 index 00000000..99512893 --- /dev/null +++ b/thirdparty/swfupload/Flash8/deploy.bat @@ -0,0 +1,3 @@ +@echo off +copy ..\swfupload.js ..\..\demos\swfupload +copy swfupload_f8.swf ..\..\demos\swfupload \ No newline at end of file diff --git a/thirdparty/swfupload/Flash8/swfupload_f8.swf b/thirdparty/swfupload/Flash8/swfupload_f8.swf new file mode 100644 index 00000000..b0251286 Binary files /dev/null and b/thirdparty/swfupload/Flash8/swfupload_f8.swf differ diff --git a/thirdparty/swfupload/Flash9/ExternalCall.as b/thirdparty/swfupload/Flash9/ExternalCall.as new file mode 100644 index 00000000..e501ba32 --- /dev/null +++ b/thirdparty/swfupload/Flash9/ExternalCall.as @@ -0,0 +1,117 @@ +package { + import flash.external.ExternalInterface; + + internal class ExternalCall + { + + /*public function ExternalCall() + { + + } + */ + + public static function Simple(callback:String):void { + ExternalInterface.call(callback); + } + 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):void { + + ExternalInterface.call(callback, EscapeMessage(num_files_selected)); + + } + + 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):void { + + ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(server_data)); + + } + 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)); + + } + + /* 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 replacePattern:RegExp = /\\/g; //new RegExp("/\\/", "g"); + return message.replace(replacePattern, "$&$&"); + } + 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/thirdparty/swfupload/Flash9/FileItem.as b/thirdparty/swfupload/Flash9/FileItem.as new file mode 100644 index 00000000..cec92871 --- /dev/null +++ b/thirdparty/swfupload/Flash9/FileItem.as @@ -0,0 +1,74 @@ +package { + 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; + 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 function FileItem(file_reference:FileReference, control_id:String) + { + this.postObject = {}; + this.file_reference = file_reference; + this.id = control_id + "_" + (FileItem.file_id_sequence++); + this.file_status = FileItem.FILE_STATUS_NEW; + + this.js_object = { + id: this.id, + index: this.index, + 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; + this.js_object.modificationdate = this.file_reference.modificationDate; + } catch (ex:Error) { + this.file_status = FileItem.FILE_STATUS_ERROR; + } + + this.js_object.filestatus = this.file_status; + } + + 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():Object { + return this.postObject; + } + + // Create the simply file object that is passed to the browser + public function ToJavaScriptObject():Object { + this.js_object.file_status = this.file_status; + this.js_object.post = this.GetPostObject(); + + return this.js_object; + } + + public function toString():String { + return "FileItem - ID: " + this.id; + } + } +} \ No newline at end of file diff --git a/thirdparty/swfupload/Flash9/SWFUpload v2.as3proj b/thirdparty/swfupload/Flash9/SWFUpload v2.as3proj new file mode 100644 index 00000000..e904fbdb --- /dev/null +++ b/thirdparty/swfupload/Flash9/SWFUpload v2.as3proj @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + deploy.bat + + + + \ No newline at end of file diff --git a/thirdparty/swfupload/Flash9/SWFUpload.as b/thirdparty/swfupload/Flash9/SWFUpload.as new file mode 100644 index 00000000..d4bc27e0 --- /dev/null +++ b/thirdparty/swfupload/Flash9/SWFUpload.as @@ -0,0 +1,920 @@ +package { + /* + * Todo: + * I should look in to using array.splice to remove cancelled files from the array. + * Add GetFile(file_id) function that returns the FileItem js object for any file (defaults to current or first in queue). + * */ + + import flash.display.Stage; + import flash.display.Sprite; + 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 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 = "SWFUPLOAD 2.0.1 FP9 2007-12-05 0001"; + + // 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. + + // 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; + + // 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 debugEnabled: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; + + public function SWFUpload() { + Security.allowDomain("*"); // Allow uploading to any domain + + // 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 = 100; }); + + // Setup file FileReferenceList events + this.fileBrowserMany.addEventListener(Event.SELECT, this.Select_Many_Handler); + this.fileBrowserMany.addEventListener(Event.CANCEL, this.DialogCancelled_Handler); + + // Get the move name + this.movieName = 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"; + + + // Get the Flash Vars + this.uploadURL = root.loaderInfo.parameters.uploadURL; + this.filePostName = root.loaderInfo.parameters.filePostName; + this.fileTypes = root.loaderInfo.parameters.fileTypes; + this.fileTypesDescription = root.loaderInfo.parameters.fileTypesDescription + " (" + this.fileTypes + ")"; + this.loadPostParams(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 = root.loaderInfo.parameters.debugEnabled == "true" ? true : false; + } catch (ex:Object) { + this.debugEnabled = false; + } + + try { + this.fileSizeLimit = Number(root.loaderInfo.parameters.fileSizeLimit); + if (this.fileSizeLimit < 0) this.fileSizeLimit = 0; + } catch (ex:Object) { + this.fileSizeLimit = 0; + } + + try { + this.fileUploadLimit = Number(root.loaderInfo.parameters.fileUploadLimit); + if (this.fileUploadLimit < 0) this.fileUploadLimit = 0; + } catch (ex:Object) { + this.fileUploadLimit = 0; + } + + try { + this.fileQueueLimit = Number(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 { + 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("GetStats", this.GetStats); + ExternalInterface.addCallback("SetStats", this.SetStats); + ExternalInterface.addCallback("GetFile", this.GetFile); + ExternalInterface.addCallback("GetFileByIndex", this.GetFileByIndex); + + 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("SetDebugEnabled", this.SetDebugEnabled); + } catch (ex:Error) { + this.Debug("Callbacks where not set."); + } + + this.Debug("SWFUpload Init Complete"); + this.PrintDebugInfo(); + + // Do some feature detection + if (flash.net.FileReferenceList && flash.net.FileReference && flash.net.URLRequest && flash.external.ExternalInterface && flash.external.ExternalInterface.available && DataEvent.UPLOAD_COMPLETE_DATA) { + ExternalCall.Simple(this.flashReady_Callback); + } else { + this.Debug("Feature Detection Failed"); + } + } + + /* ***************************************** + * FileReference Event Handlers + * *************************************** */ + private function DialogCancelled_Handler(event:Event):void { + this.Debug("Event: fileDialogComplete: File Dialog window cancelled."); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0); + } + + private function FileProgress_Handler(event:ProgressEvent):void { + this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + event.bytesLoaded + ". Total: " + event.bytesTotal); + ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), event.bytesLoaded, event.bytesTotal); + } + + private function ServerData_Handler(event:DataEvent):void { + this.successful_uploads++; + this.current_file_item.file_status = FileItem.FILE_STATUS_SUCCESS; + + this.Debug("Event: uploadSuccess: File ID: " + this.current_file_item.id + " Data: " + event.data); + ExternalCall.UploadSuccess(this.uploadSuccess_Callback, this.current_file_item.ToJavaScriptObject(), event.data); + + this.UploadComplete(); + + } + + private function HTTPError_Handler(event:HTTPStatusEvent):void { + 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(); + } + + // 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 the crash the browser (FireFox/Opera) + private function IOError_Handler(event:IOErrorEvent):void { + this.upload_errors++; + this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR; + + if(!this.uploadURL.length) { + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, this.current_file_item.ToJavaScriptObject(), event.text); + } else { + this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error."); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), event.text); + } + + this.UploadComplete(); + } + + 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:" + event.text); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), event.text); + + this.UploadComplete(); + } + + 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: Files Selected from Dialog. Processing file list"); + + // 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; + } + } + + // 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); + + // Add the file to the index + file_item.index = 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) { + this.Debug("Event: fileQueued : File ID: " + file_item.id); + file_item.file_status = FileItem.FILE_STATUS_QUEUED; + this.file_queue.push(file_item); + this.queued_uploads++; + ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject()); + } + else if (!is_valid_filetype) { + file_item.file_reference = null; // Cleanup the object + this.Debug("Event: fileQueueError : File not of a valid type."); + this.queue_errors++; + 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 + this.Debug("Event: fileQueueError : File exceeds size limit."); + this.queue_errors++; + 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 + this.Debug("Event: fileQueueError : File is zero bytes."); + this.queue_errors++; + 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 + this.Debug("Event: fileQueueError : File is zero bytes or FileReference is invalid."); + this.queue_errors++; + 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 adding files"); + ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length); + } + + + /* **************************************************************** + 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); + + this.fileBrowserOne.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]); + + } + + // 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); + this.fileBrowserMany.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]); + } + + + // Starts uploading. Checks to see if a file is currently uploading and, if not, starts the upload. + private function StartUpload(file_id:String = ""):void { + if (this.current_file_item == null) { + this.Debug("StartUpload(): Starting Upload: " + (file_id ? "File ID:" + file_id : "First file in queue")); + this.StartFile(file_id); + } else { + this.Debug("StartUpload(): Upload run already in progress"); + } + } + + // 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(); + + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + + // Remove the event handlers + this.removeFileReferenceEventListeners(this.current_file_item); + + this.file_queue.unshift(this.current_file_item); + var js_object:Object = this.current_file_item.ToJavaScriptObject(); + this.current_file_item = null; + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped"); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + this.Debug("StopUpload(): upload stopped."); + } else { + this.Debug("StopUpload(): Upload run not in progress"); + } + } + + /* 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):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.upload_cancelled++; + + this.Debug("Event: fileCancelled: File ID: " + this.current_file_item.id + ". Cancelling current upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled."); + + this.UploadComplete(); // <-- this advanced the upload to the next file + } 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[file_index] = null; + this.queued_uploads--; + this.upload_cancelled++; + + + // Cancel the file (just for good measure) and make the callback + file_item.file_reference.cancel(); + this.removeFileReferenceEventListeners(file_item); + file_item.file_reference = null; + + + this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload"); + this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload"); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + + // 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.removeFileReferenceEventListeners(file_item); + file_item.file_reference = null; + + this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload"); + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled"); + + // Get rid of the file object + file_item = null; + } + + } + + } + + 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 AddFileParam(file_id:String, name:String, value:String):Boolean { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + var file_item:FileItem = FileItem(this.file_queue[file_index]); + + file_item.AddParam(name, value); + return true; + } else { + return false; + } + } + private function RemoveFileParam(file_id:String, name:String):Boolean { + var file_index:Number = this.FindIndexInFileQueue(file_id); + if (file_index >= 0) { + var file_item:FileItem = FileItem(this.file_queue[file_index]); + file_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); + } + + private function SetFileSizeLimit(bytes:Number):void { + if (bytes < 0) bytes = 0; + this.fileSizeLimit = bytes; + } + + 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 SetDebugEnabled(debug_enabled:Boolean):void { + this.debugEnabled = debug_enabled; + } + + /* ************************************************************* + File processing and handling functions + *************************************************************** */ + // + private function StartFile(file_id:String = ""):void { + // Only upload a file uploads are being processed. + // startFile could be called by a file cancellation even when we aren't currently uploading + if (this.current_file_item != null) { + this.Debug("StartFile(): Upload already in progress. Not starting another upload."); + } + + this.Debug("StartFile: " + (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) { + // Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop) + this.current_file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem + if (typeof(this.current_file_item) == "undefined") { + this.current_file_item = null; + continue; + } + } + } 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[file_index] = null; + } 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 queued."); + } + } + + + // Start the upload if we found an item to upload + if (this.current_file_item != null) { + // Begin the upload + this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id); + 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("startFile(): No File Reference found. There are no files left to upload.\nstartFile(): Ending upload run."); + } + } + + // 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 file was no longer queued. This is okay if the file was stopped or cancelled."); + return; + } + + if (start_upload) { + try { + // Set the event handlers + this.current_file_item.file_reference.addEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler); + this.current_file_item.file_reference.addEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler); + this.current_file_item.file_reference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler); + this.current_file_item.file_reference.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler); + this.current_file_item.file_reference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler); + + // Upload the file + var request:URLRequest = this.BuildRequest(); + + this.Debug("startFile(): File Reference found. File accepted by startUpload event. 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.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(): Unhandled exception: " + message); + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message); + + this.UploadComplete(); + } + this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS; + + } else { + this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading file."); + + // Remove the event handlers + this.removeFileReferenceEventListeners(this.current_file_item); + + // Re-queue the FileItem + this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED; + var js_object:Object = this.current_file_item.ToJavaScriptObject(); + this.file_queue.unshift(this.current_file_item); + this.current_file_item = null; + + ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file."); + ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object); + this.Debug("startFile(): upload rejected by startUpload event. File re-queued."); + } + } + + // Completes the file upload by deleting it's reference, advancing the pointer. + // Once this event files a new upload can be started. + private function UploadComplete():void { + var jsFileObj:Object = this.current_file_item.ToJavaScriptObject(); + + this.removeFileReferenceEventListeners(this.current_file_item); + this.current_file_item.file_reference = null; + this.current_file_item = null; + this.queued_uploads--; + + 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 * 1000)) { + 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; + request.url = this.uploadURL; + + var file_post:Object = this.current_file_item.GetPostObject(); + 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 + "=" + this.uploadPostObject[key]); + if (file_post.hasOwnProperty(key)) { + post[key] = file_post[key]; + } + } + + request.data = post; + + return request; + } + + private function Debug(msg:String):void { + if (this.debugEnabled) { + var lines:Array = msg.split("\n"); + for (var i:Number=0; i < lines.length; i++) { + lines[i] = "SWF DEBUG: " + lines[i]; + } + try { + ExternalCall.Debug(this.debug_Callback, lines.join("\n")); + } catch (ex:Error) { + // pretend nothing happened + } + } + } + + private function PrintDebugInfo():void { + var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n"; + debug_info += "Build Number: " + 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 += "File Types Description: " + this.fileTypesDescription + "\n"; + debug_info += "File Size Limit: " + this.fileSizeLimit + "\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) { + 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= 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 removeFileReferenceEventListeners(file_item:FileItem):void { + if (file_item != null && file_item.file_reference != null) { + file_item.file_reference.removeEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler); + file_item.file_reference.removeEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler); + file_item.file_reference.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler); + file_item.file_reference.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler); + file_item.file_reference.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler); + } + } + + } +} diff --git a/thirdparty/swfupload/Flash9/deploy.bat b/thirdparty/swfupload/Flash9/deploy.bat new file mode 100644 index 00000000..81a82c53 --- /dev/null +++ b/thirdparty/swfupload/Flash9/deploy.bat @@ -0,0 +1,3 @@ +@echo off +copy ..\swfupload.js ..\..\demos\swfupload +copy swfupload_f9.swf ..\..\demos\swfupload \ No newline at end of file diff --git a/thirdparty/swfupload/Flash9/swfupload_f9.swf b/thirdparty/swfupload/Flash9/swfupload_f9.swf new file mode 100644 index 00000000..7f714561 Binary files /dev/null and b/thirdparty/swfupload/Flash9/swfupload_f9.swf differ diff --git a/thirdparty/swfupload/license.txt b/thirdparty/swfupload/license.txt new file mode 100644 index 00000000..dd26171a --- /dev/null +++ b/thirdparty/swfupload/license.txt @@ -0,0 +1,11 @@ +/** + * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/ + * + * 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 Revision 7.0 is (c) 2007 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/thirdparty/swfupload/plugins/swfupload.cookies.js b/thirdparty/swfupload/plugins/swfupload.cookies.js new file mode 100644 index 00000000..7c6fcbbc --- /dev/null +++ b/thirdparty/swfupload/plugins/swfupload.cookies.js @@ -0,0 +1,50 @@ +/* + 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 (old_initSettings) { + return function (init_settings) { + if (typeof(old_initSettings) === "function") { + old_initSettings.call(this, init_settings); + } + + 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 send_to_flash parameters is optional and defaults to True + SWFUpload.prototype.refreshCookies = function (send_to_flash) { + if (send_to_flash !== false) send_to_flash = true; + + // Get the post_params object + var post_params = this.getSetting("post_params"); + + // Get the cookies + var i, cookie_array = document.cookie.split(';'), ca_length = cookie_array.length, c, eq_index, name, value; + for(i = 0; i < ca_length; i++) { + c = cookie_array[i]; + + // Left Trim spaces + while (c.charAt(0) == " ") { + c = c.substring(1, c.length); + } + eq_index = c.indexOf("="); + if (eq_index > 0) { + name = c.substring(0, eq_index); + value = c.substring(eq_index+1); + post_params[name] = value; + } + } + + if (send_to_flash) { + this.setPostParams(post_params); + } + }; + +} diff --git a/thirdparty/swfupload/plugins/swfupload.detectionkit.js b/thirdparty/swfupload/plugins/swfupload.detectionkit.js new file mode 100644 index 00000000..e69de29b diff --git a/thirdparty/swfupload/plugins/swfupload.documentready.js b/thirdparty/swfupload/plugins/swfupload.documentready.js new file mode 100644 index 00000000..dc8e35c3 --- /dev/null +++ b/thirdparty/swfupload/plugins/swfupload.documentready.js @@ -0,0 +1,102 @@ +/* + DocumentReady Plug-in + + This plugin loads SWFUpload as soon as the document is ready. You should not load SWFUpload inside window.onload using this plugin. + You can also chain other functions by calling SWFUpload.DocumentReady(your function). + + Warning: Embedded Ads or other scripts that overwrite window.onload or use their own document ready functions may interfer with this plugin. You + should not set window.onload when using this plugin. + + Usage Example: + + var swfu = new SWFUpload(your settings object); + SWFUpload.DocumentReady(function () { alert('Document Ready!'; }); + +*/ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + // Override iniSWFUpload so SWFUpload gets inited when the document is ready rather than immediately + SWFUpload.prototype.initSWFUpload = function (old_initSWFUpload) { + return function (init_settings) { + var self = this; + if (typeof(old_initSWFUpload) === "function") { + SWFUpload.DocumentReady(function () { + old_initSWFUpload.call(self, init_settings); + }); + } + } + + }(SWFUpload.prototype.initSWFUpload); + + + // The DocumentReady function adds the passed in function to + // the functions that will be executed when the document is ready/loaded + SWFUpload.DocumentReady = function (fn) { + // Add the function to the chain + SWFUpload.DocumentReady.InternalOnloadChain = function (previous_link_fn) { + return function () { + if (typeof(previous_link_fn) === "function") { + previous_link_fn(); + } + fn(); + }; + }(SWFUpload.DocumentReady.InternalOnloadChain); + }; + SWFUpload.DocumentReady.InternalOnloadChain = null; + SWFUpload.DocumentReady.Onload = function () { + // Execute the onload function chain + if (typeof(SWFUpload.DocumentReady.InternalOnloadChain) === "function") { + SWFUpload.DocumentReady.InternalOnloadChain(); + } + }; + SWFUpload.DocumentReady.SetupComplete = false; + + + /* ******************************************** + This portion of the code gets executed as soon it is loaded. + It binds the proper event for executing JavaScript is + early as possible. This is a per browser function and so + some browser sniffing is used. + + This solution still has the "exposed" issue (See the Global Delegation section at http://peter.michaux.ca/article/553 ) + + Base solution from http://dean.edwards.name/weblog/2006/06/again/ and http://dean.edwards.name/weblog/2005/09/busted/ + ******************************************** */ + if (!SWFUpload.DocumentReady.SetupComplete) { + // for Internet Explorer (using conditional comments) + /*@cc_on @*/ + /*@if (@_win32) + document.write("