// This is the source code for SWFHttpRequest Flash/Ajax Utility
Unless otherwise specified, this haxe code is released under The MIT License. If you choose to install it, you do so at your own risk and discretion.
// SWFHttpRequest.hx // version: 0.3 // Copyright (c) 2007, Jim R. Wilson (wilson.jim.r at gmail) // Released under The MIT License (http://www.opensource.org/licenses/mit-license.php) // // Purpose: // This haxe source file builds a SWF which adds a JavaScript class called // SWFHttpRequest to the browser environment in which it's run. This new // class behaves in many ways similar to the native XMLHttpRequest object, // except it uses Flash as an intermediary for requesting data. // // The benefit of using Flash for this purpose is its security model which // uses an opt-in model controlled by crossdomain.xml files for determining // third-party access rather than the much more restrictive same-origin // policy employed by browsers for native XHRs. // // This is NOT a perfect replacement for XHRs, however, since it lacks the // following features: // * Capacity to read HTTP response headers // * Capability to provide credentials or respond to HTTP auth challenges // // The failures mentioned above may be possible to implement by using a lower- // level API. For example, instead of using the haxe.Http class, one could use // an appropriately featured ActionScript library, or even (if necessary) drop // down to making TCP connections using the flash.net.Socket class. // // To build: // 1. Acquire the latest stable version of haxe from http://haxe.org/download // Note: To run haxe, you may also need Neko (http://nekovm.org/download) // 2. Create a file called SWFHttpRequest.hx with these contents. // 3. Create a file called compile.hxml with these contents: // -swf-version 9 // -swf swfhttprequest.swf // -main SWFHttpRequest // 4. From the command line, execute: // haxe compile.hxml // // To activate: // 1. Take swfhttprequest.swf generated by haxe and deploy it to your webserver. // 2. Embed it into your HTML file with code like the following: // <object // id="swfhttprequest" class="hidden" // classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" // codebase="http://fpdownload.adobe.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0"> // <param name="movie" value="swfhttprequest.swf" /> // <param name="allowScriptAccess" value="always" /> // <embed // class="hidden" // src="swfhttprequest.swf" // allowScriptAccess="always" // type="application/x-shockwave-flash" // pluginspage="http://www.adobe.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"> // </embed> // </object> // WARNING: Make sure this is NOT inside a pair of <form> tags. This will // cause EXTREMELY DIFFICULT TO DEBUG issues in Internet Explorer. // NOTE: The id attribute of the <object> tag is NECESSARY for IE to function. // It can be anything you like, as long as it contains ONLY alphanumeric // characters and underscores (so-called "word" characters). Hyphens will kill it. // // To hide: // Note that you'll probably want to hide the flash object by shifting it // way offscreen via position:absolute with negative top and left coordinates. // For example, you could add class="hidden" to both the <object> and <embed> // tags, then add this to your site-wide CSS: // embed.hidden, // object.hidden { // height: 1px; // width: 1px; // position: absolute; // top: -1000px; // left: -1000px; // } // -------------------------------------------------------------------- // Copyright (c) 2007 Jim R. Wilson // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // -------------------------------------------------------------------- // import flash.external.ExternalInterface; import flash.Lib; import haxe.Http; class SWFHttpRequest { public static var ver:String = '0.3'; public static var transports:Hash<Transport> = new Hash(); public static function abort( instance:Int ) { transports.get( instance + '' ).abort(); } public static function open( instance:Int, method:String, url:String ) { transports.set( instance + '', new Transport( instance, method, url ) ); } public static function send( instance:Int, data:String ) { transports.get( instance + '' ).send( data ); } public static function setRequestHeader( instance:Int, header:String, value:String ) { transports.get( instance + '' ).setRequestHeader( header, value ); } public static function version() { return ver; } public static function main() { ExternalInterface.addCallback("abort",abort); ExternalInterface.addCallback("open",open); ExternalInterface.addCallback("send",send); ExternalInterface.addCallback("setRequestHeader",setRequestHeader); ExternalInterface.addCallback("SWFHttpRequestVersion",version); ExternalInterface.call( [ "(function(){", "if (window.SWFHttpRequest) return;", "var Class = function(properties){", "var klass = function(){ return this.abort.apply(this); };", "klass.prototype = properties;", "klass.constructor = arguments.callee;", "return klass;", "};", "window.SWFHttpRequest = new Class({", "abort: function(){", "if (typeof this.instance != 'undefined') {", "window.SWFHttpRequest.instances[this.instance] = null;", "window.SWFHttpRequest.engine.abort( this.instance );", "}", "this.readyState = 0;", "this.responseText = '';", "this.responseXML = null;", "this.status = 0;", "this.statusText = '';", "this.instance = window.SWFHttpRequest.instances.length;", "window.SWFHttpRequest.instances.push( this );", "},", "getAllResponseHeaders: function(){ return null; },", "getResponseHeader: function(){ return null; },", "onreadystatechange: null,", "open: function(method, url, async, user, password){", "this.status = 0;", "this.readyState = 1;", "this.statusText = this.responseText = '';", "this.responseXML = null;", "window.SWFHttpRequest.engine.open( this.instance, method, url );", "},", "send: function(data){", // TODO: Once haxe.Http supports setPostData() for flash9 targets, this function // should be updated to allow for a Document as the data. When that's the case, // it should be serialized as XML prior to sending over to the Flash executor. "window.SWFHttpRequest.engine.send( this.instance, data );", "},", "setRequestHeader: function(header, value){", "if (this.readyState != 1 || !header || !value) return;", "window.SWFHttpRequest.engine.setRequestHeader( this.instance, header, value );", "}", "});", "window.SWFHttpRequest.instances = [];", "window.SWFHttpRequest.version = '" + ver + "';", "var f = function(tag){", "var elems = document.getElementsByTagName(tag);", "for (var i=0; i<elems.length; i++) if (elems[i].SWFHttpRequestVersion) return elems[i];", "};", "window.SWFHttpRequest.engine = f('embed') || f('object');", "})" ].join('') ); var params = Lib.current.loaderInfo.parameters; if (Reflect.hasField(params,'onload')) ExternalInterface.call( Reflect.field(params,'onload') ); } } class Transport { var active:Bool; var instance:Int; var method:String; var url:String; var http:Http; public function new( instance:Int, method:String, url:String ) { this.active = true; this.instance = instance; this.method = method.toUpperCase(); this.url = url; this.http = new Http( this.url ); this.http.onData = this.onData; this.http.onError = this.onError; this.http.onStatus = this.onStatus; } public function send( ?data:String ) { if ( data==null ) return this.http.request( this.method=='POST' ); // NOTE: Once haxe.Http supports the setPostData() method for flash9 targets, // all the following should be replaced by: this.http.setPostData( data ); var re = ~/^(.*?)=(.*)$/; var pairs:Array<String> = data.split('&'); var ud = function(s){ try { return StringTools.urlDecode(s); } catch ( e:Dynamic ) { } return s; }; for( i in 0...pairs.length ) { if (re.match(pairs[i])) this.http.setParameter( ud(re.matched(1)), ud(re.matched(2)) ); else this.http.setParameter( ud(pairs[i]), '' ); } return this.http.request( this.method=='POST' ); } public function setRequestHeader( header:String, value:String ) { this.http.setHeader( header, value ); } public function onData( data:String ) { if (!this.active) return; ExternalInterface.call( [ "(function(instance, data){", "var shr = window.SWFHttpRequest.instances[instance];", "if (!shr) return;", "shr.status = 200;", "shr.statusText = 'OK';", "shr.readyState = 4;", "shr.responseText = data;", "try {", "if (window.DOMParser) {", "var dp = new DOMParser();", "shr.responseXML = dp.parseFromString( data, 'text/xml' );", "} else {", "shr.responseXML = new ActiveXObject('Microsoft.XMLDOM');", "shr.responseXML.async = 'false';", "shr.responseXML.loadXML(data);", "}", "} catch(error) { shr.responseXML = null; }", "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();", "})" ].join(''), this.instance, data); } public function onError( msg:String ) { if (!this.active) return; ExternalInterface.call( [ "(function(instance){", "var shr = window.SWFHttpRequest.instances[instance];", "if (!shr) return;", "shr.status = 404;", "shr.statusText = 'Not Found';", "shr.readyState = 4;", "shr.responseText = null;", "shr.responseXML = null;", "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();", "})" ].join(''), this.instance ); } public function onStatus( status:Int ) { if (!this.active || status==200) return; ExternalInterface.call( [ "(function(instance, status){", "var shr = window.SWFHttpRequest.instances[instance];", "if (!shr) return;", "shr.status = status;", "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();", "})" ].join(''), this.instance, status ); } public function abort() { this.active = false; } } //