├── images └── download.png ├── media └── downloadify.swf ├── LICENSE.txt ├── src ├── com │ └── dynamicflash │ │ └── util │ │ ├── tests │ │ └── Base64Test.as │ │ └── Base64.as ├── Downloadify.as └── downloadify.js ├── test.html ├── js ├── downloadify.min.js └── swfobject.js └── README.textile /images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcneiner/Downloadify/HEAD/images/download.png -------------------------------------------------------------------------------- /media/downloadify.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcneiner/Downloadify/HEAD/media/downloadify.swf -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Downloadify: Client Side File Creation 2 | JavaScript + Flash Library 3 | 4 | Copyright (c) 2009 Douglas C. Neiner 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/com/dynamicflash/util/tests/Base64Test.as: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006 Steve Webster 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.dynamicflash.util.tests { 23 | 24 | import flexunit.framework.TestCase; 25 | import flexunit.framework.TestSuite; 26 | import flash.utils.ByteArray; 27 | 28 | import com.dynamicflash.util.Base64; 29 | 30 | public class Base64Test extends TestCase { 31 | 32 | public function Base64Test(methodName:String = null) { 33 | super(methodName); 34 | } 35 | 36 | public function testEncode():void { 37 | assertEquals("VGhpcyBpcyBhIHRlc3Q=",Base64.encode("This is a test")); 38 | } 39 | 40 | public function testEncodeDecodeBytes():void { 41 | var obj:Object = {name:"Dynamic Flash", url:"http://dynamicflash.com"}; 42 | var source:ByteArray = new ByteArray(); 43 | source.writeObject(obj); 44 | var encoded:String = Base64.encodeByteArray(source); 45 | var decoded:ByteArray = Base64.decodeToByteArray(encoded); 46 | var obj2:Object = decoded.readObject(); 47 | assertEquals(obj.name, obj2.name); 48 | assertEquals(obj.url, obj2.url); 49 | } 50 | 51 | public function testDecode():void { 52 | assertEquals("This is a test",Base64.decode("VGhpcyBpcyBhIHRlc3Q=")); 53 | } 54 | 55 | public function testEncodeDecode():void { 56 | var string:String = "The quick brown fox jumped over the lazy dogs"; 57 | assertEquals(string, Base64.decode(Base64.encode(string))); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Downloadify 5 | 14 | 15 | 16 | 17 | 18 |

Downloadify Example

19 |

More info available at the Github Project Page

20 |
21 |

22 |
23 | 24 |

25 |

26 |
27 | 29 |

30 |

31 | You must have Flash 10 installed to download this file. 32 |

33 |
34 | 35 | 56 |

Downloadify Invoke Script For This Page

57 |
58 | Downloadify.create('downloadify',{
59 |   filename: function(){
60 |     return document.getElementById('filename').value;
61 |   },
62 |   data: function(){ 
63 |     return document.getElementById('data').value;
64 |   },
65 |   onComplete: function(){ 
66 |     alert('Your File Has Been Saved!'); 
67 |   },
68 |   onCancel: function(){ 
69 |     alert('You have cancelled the saving of this file.');
70 |   },
71 |   onError: function(){ 
72 |     alert('You must put something in the File Contents or there will be nothing to save!'); 
73 |   },
74 |   swf: 'media/downloadify.swf',
75 |   downloadImage: 'images/download.png',
76 |   width: 100,
77 |   height: 30,
78 |   transparent: true,
79 |   append: false
80 | });
81 | 
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /js/downloadify.min.js: -------------------------------------------------------------------------------- 1 | /* Downloadify 0.2 (c) 2009 by Douglas Neiner. Licensed under the MIT license */ 2 | /* See http://github.com/dcneiner/Downloadify for license and more info */ 3 | (function(){Downloadify=window.Downloadify={queue:{},uid:new Date().getTime(),getTextForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getData();return""},getFileNameForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getFilename();return""},getDataTypeForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getDataType();return""},saveComplete:function(a){var b=Downloadify.queue[a];if(b)b.complete();return true},saveCancel:function(a){var b=Downloadify.queue[a];if(b)b.cancel();return true},saveError:function(a){var b=Downloadify.queue[a];if(b)b.error();return true},addToQueue:function(a){Downloadify.queue[a.queue_name]=a},getUID:function(a){if(a.id=="")a.id='downloadify_'+Downloadify.uid++;return a.id}};Downloadify.create=function(a,b){var c=(typeof(a)=="string"?document.getElementById(a):a);return new Downloadify.Container(c,b)};Downloadify.Container=function(d,e){var f=this;f.el=d;f.enabled=true;f.dataCallback=null;f.filenameCallback=null;f.data=null;f.filename=null;var g=function(){f.options=e;if(!f.options.append)f.el.innerHTML="";f.flashContainer=document.createElement('span');f.el.appendChild(f.flashContainer);f.queue_name=Downloadify.getUID(f.flashContainer);if(typeof(f.options.filename)==="function")f.filenameCallback=f.options.filename;else if(f.options.filename)f.filename=f.options.filename;if(typeof(f.options.data)==="function")f.dataCallback=f.options.data;else if(f.options.data)f.data=f.options.data;var a={queue_name:f.queue_name,width:f.options.width,height:f.options.height};var b={allowScriptAccess:'always'};var c={id:f.flashContainer.id,name:f.flashContainer.id};if(f.options.enabled===false)f.enabled=false;if(f.options.transparent===true)b.wmode="transparent";if(f.options.downloadImage)a.downloadImage=f.options.downloadImage;swfobject.embedSWF(f.options.swf,f.flashContainer.id,f.options.width,f.options.height,"10",null,a,b,c);Downloadify.addToQueue(f)};f.enable=function(){var a=document.getElementById(f.flashContainer.id);a.setEnabled(true);f.enabled=true};f.disable=function(){var a=document.getElementById(f.flashContainer.id);a.setEnabled(false);f.enabled=false};f.getData=function(){if(!f.enabled)return"";if(f.dataCallback)return f.dataCallback();else if(f.data)return f.data;else return""};f.getFilename=function(){if(f.filenameCallback)return f.filenameCallback();else if(f.filename)return f.filename;else return""};f.getDataType=function(){if(f.options.dataType)return f.options.dataType;return"string"};f.complete=function(){if(typeof(f.options.onComplete)==="function")f.options.onComplete()};f.cancel=function(){if(typeof(f.options.onCancel)==="function")f.options.onCancel()};f.error=function(){if(typeof(f.options.onError)==="function")f.options.onError()};g()};Downloadify.defaultOptions={swf:'media/downloadify.swf',downloadImage:'images/download.png',width:100,height:30,transparent:true,append:false,dataType:"string"}})();if(typeof(jQuery)!="undefined"){(function($){$.fn.downloadify=function(b){return this.each(function(){b=$.extend({},Downloadify.defaultOptions,b);var a=Downloadify.create(this,b);$(this).data('Downloadify',a)})}})(jQuery)};if(typeof(MooTools)!='undefined'){Element.implement({downloadify:function(a){a=$merge(Downloadify.defaultOptions,a);return this.store('Downloadify',Downloadify.create(this,a))}})}; -------------------------------------------------------------------------------- /src/com/dynamicflash/util/Base64.as: -------------------------------------------------------------------------------- 1 | /* 2 | Base64 - 1.1.0 3 | 4 | Copyright (c) 2006 Steve Webster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.dynamicflash.util { 25 | 26 | import flash.utils.ByteArray; 27 | 28 | public class Base64 { 29 | 30 | private static const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 31 | 32 | public static const version:String = "1.1.0"; 33 | 34 | public static function encode(data:String):String { 35 | // Convert string to ByteArray 36 | var bytes:ByteArray = new ByteArray(); 37 | bytes.writeUTFBytes(data); 38 | 39 | // Return encoded ByteArray 40 | return encodeByteArray(bytes); 41 | } 42 | 43 | public static function encodeByteArray(data:ByteArray):String { 44 | // Initialise output 45 | var output:String = ""; 46 | 47 | // Create data and output buffers 48 | var dataBuffer:Array; 49 | var outputBuffer:Array = new Array(4); 50 | 51 | // Rewind ByteArray 52 | data.position = 0; 53 | 54 | // while there are still bytes to be processed 55 | while (data.bytesAvailable > 0) { 56 | // Create new data buffer and populate next 3 bytes from data 57 | dataBuffer = new Array(); 58 | for (var i:uint = 0; i < 3 && data.bytesAvailable > 0; i++) { 59 | dataBuffer[i] = data.readUnsignedByte(); 60 | } 61 | 62 | // Convert to data buffer Base64 character positions and 63 | // store in output buffer 64 | outputBuffer[0] = (dataBuffer[0] & 0xfc) >> 2; 65 | outputBuffer[1] = ((dataBuffer[0] & 0x03) << 4) | ((dataBuffer[1]) >> 4); 66 | outputBuffer[2] = ((dataBuffer[1] & 0x0f) << 2) | ((dataBuffer[2]) >> 6); 67 | outputBuffer[3] = dataBuffer[2] & 0x3f; 68 | 69 | // If data buffer was short (i.e not 3 characters) then set 70 | // end character indexes in data buffer to index of '=' symbol. 71 | // This is necessary because Base64 data is always a multiple of 72 | // 4 bytes and is basses with '=' symbols. 73 | for (var j:uint = dataBuffer.length; j < 3; j++) { 74 | outputBuffer[j + 1] = 64; 75 | } 76 | 77 | // Loop through output buffer and add Base64 characters to 78 | // encoded data string for each character. 79 | for (var k:uint = 0; k < outputBuffer.length; k++) { 80 | output += BASE64_CHARS.charAt(outputBuffer[k]); 81 | } 82 | } 83 | 84 | // Return encoded data 85 | return output; 86 | } 87 | 88 | public static function decode(data:String):String { 89 | // Decode data to ByteArray 90 | var bytes:ByteArray = decodeToByteArray(data); 91 | 92 | // Convert to string and return 93 | return bytes.readUTFBytes(bytes.length); 94 | } 95 | 96 | public static function decodeToByteArray(data:String):ByteArray { 97 | // Initialise output ByteArray for decoded data 98 | var output:ByteArray = new ByteArray(); 99 | 100 | // Create data and output buffers 101 | var dataBuffer:Array = new Array(4); 102 | var outputBuffer:Array = new Array(3); 103 | 104 | // While there are data bytes left to be processed 105 | for (var i:uint = 0; i < data.length; i += 4) { 106 | // Populate data buffer with position of Base64 characters for 107 | // next 4 bytes from encoded data 108 | for (var j:uint = 0; j < 4 && i + j < data.length; j++) { 109 | dataBuffer[j] = BASE64_CHARS.indexOf(data.charAt(i + j)); 110 | } 111 | 112 | // Decode data buffer back into bytes 113 | outputBuffer[0] = (dataBuffer[0] << 2) + ((dataBuffer[1] & 0x30) >> 4); 114 | outputBuffer[1] = ((dataBuffer[1] & 0x0f) << 4) + ((dataBuffer[2] & 0x3c) >> 2); 115 | outputBuffer[2] = ((dataBuffer[2] & 0x03) << 6) + dataBuffer[3]; 116 | 117 | // Add all non-padded bytes in output buffer to decoded data 118 | for (var k:uint = 0; k < outputBuffer.length; k++) { 119 | if (dataBuffer[k+1] == 64) break; 120 | output.writeByte(outputBuffer[k]); 121 | } 122 | } 123 | 124 | // Rewind decoded data ByteArray 125 | output.position = 0; 126 | 127 | // Return decoded data 128 | return output; 129 | } 130 | 131 | public function Base64() { 132 | throw new Error("Base64 class is static container only"); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /src/Downloadify.as: -------------------------------------------------------------------------------- 1 | /* 2 | Downloadify: Client Side File Creation 3 | JavaScript + Flash Library 4 | 5 | Version: 0.2 6 | 7 | Copyright (c) 2009 Douglas C. Neiner 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | */ 27 | package { 28 | import flash.system.Security; 29 | import flash.events.Event; 30 | import flash.events.MouseEvent; 31 | import flash.net.FileReference; 32 | import flash.net.FileFilter; 33 | import flash.net.URLRequest; 34 | import flash.display.*; 35 | import flash.utils.ByteArray; 36 | import flash.external.ExternalInterface; 37 | import com.dynamicflash.util.Base64; 38 | 39 | [SWF(backgroundColor="#CCCCCC")] 40 | [SWF(backgroundAlpha=0)] 41 | public class Downloadify extends Sprite { 42 | 43 | private var options:Object, 44 | file:FileReference = new FileReference(), 45 | queue_name:String = "", 46 | 47 | _width:Number = 0, 48 | _height:Number = 0, 49 | 50 | enabled:Boolean = true, 51 | over:Boolean = false, 52 | down:Boolean = false, 53 | 54 | buttonImage:String = "images/download.png", 55 | 56 | button:Loader; 57 | 58 | public function Downloadify() { 59 | Security.allowDomain('*'); 60 | 61 | stage.align = StageAlign.TOP_LEFT; 62 | stage.scaleMode = StageScaleMode.NO_SCALE; 63 | 64 | options = this.root.loaderInfo.parameters; 65 | 66 | queue_name = options.queue_name.toString(); 67 | 68 | _width = options.width; 69 | _height = options.height; 70 | 71 | if(options.downloadImage){ 72 | buttonImage = options.downloadImage; 73 | } 74 | 75 | setupDefaultButton(); 76 | addChild(button); 77 | 78 | this.buttonMode = true; 79 | 80 | this.addEventListener(MouseEvent.CLICK, onMouseClickEvent); 81 | this.addEventListener(MouseEvent.ROLL_OVER , onMouseEnter); 82 | this.addEventListener(MouseEvent.ROLL_OUT , onMouseLeave); 83 | this.addEventListener(MouseEvent.MOUSE_DOWN , onMouseDown); 84 | this.addEventListener(MouseEvent.MOUSE_UP , onMouseUp); 85 | 86 | ExternalInterface.addCallback('setEnabled', setEnabled); 87 | 88 | file.addEventListener(Event.COMPLETE, onSaveComplete); 89 | file.addEventListener(Event.CANCEL, onSaveCancel); 90 | } 91 | 92 | private function setEnabled(isEnabled:Boolean):Boolean { 93 | enabled = isEnabled; 94 | if(enabled === true){ 95 | button.y = 0; 96 | this.buttonMode = true; 97 | } else { 98 | button.y = (-3 * _height); 99 | this.buttonMode = false; 100 | } 101 | return enabled; 102 | } 103 | 104 | private function setupDefaultButton():void { 105 | button = new Loader(); 106 | var urlReq:URLRequest = new URLRequest(buttonImage); 107 | button.load(urlReq); 108 | button.x = 0; 109 | button.y = 0; 110 | } 111 | 112 | 113 | 114 | protected function onMouseEnter(event:Event):void { 115 | if(enabled === true){ 116 | if(down === false) button.y = (-1 * _height); 117 | over = true; 118 | } 119 | } 120 | protected function onMouseLeave(event:Event):void { 121 | if(enabled === true){ 122 | if(down === false) button.y = 0; 123 | over = false; 124 | } 125 | } 126 | protected function onMouseDown(event:Event):void { 127 | if(enabled === true){ 128 | button.y = button.y = (-2 * _height); 129 | down = true; 130 | } 131 | } 132 | protected function onMouseUp(event:Event):void { 133 | if(enabled === true){ 134 | if(over === false){ 135 | button.y = 0; 136 | } else { 137 | button.y = (-1 * _height); 138 | } 139 | down = false; 140 | } 141 | } 142 | 143 | protected function onMouseClickEvent(event:Event):void{ 144 | var theData:String = ExternalInterface.call('Downloadify.getTextForSave',queue_name), 145 | filename:String = ExternalInterface.call('Downloadify.getFileNameForSave',queue_name), 146 | dataType:String = ExternalInterface.call('Downloadify.getDataTypeForSave',queue_name); 147 | 148 | if (dataType == "string" && theData != "") { 149 | file.save(theData, filename); 150 | } else if (dataType == "base64" && theData){ 151 | file.save(Base64.decodeToByteArray(theData), filename); 152 | } else { 153 | onSaveError(); 154 | } 155 | } 156 | 157 | protected function onSaveComplete(event:Event):void{ 158 | trace('Save Complete'); 159 | ExternalInterface.call('Downloadify.saveComplete',queue_name); 160 | } 161 | 162 | protected function onSaveCancel(event:Event):void{ 163 | trace('Save Cancel'); 164 | ExternalInterface.call('Downloadify.saveCancel',queue_name); 165 | } 166 | 167 | protected function onSaveError():void{ 168 | trace('Save Error'); 169 | ExternalInterface.call('Downloadify.saveError',queue_name); 170 | } 171 | 172 | } 173 | } -------------------------------------------------------------------------------- /src/downloadify.js: -------------------------------------------------------------------------------- 1 | /* 2 | Downloadify: Client Side File Creation 3 | JavaScript + Flash Library 4 | 5 | Version: 0.2 6 | 7 | Copyright (c) 2009 Douglas C. Neiner 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | */ 27 | 28 | (function(){ 29 | Downloadify = window.Downloadify = { 30 | queue: {}, 31 | uid: new Date().getTime(), 32 | getTextForSave: function(queue){ 33 | var obj = Downloadify.queue[queue]; 34 | if(obj) return obj.getData(); 35 | return ""; 36 | }, 37 | getFileNameForSave: function(queue){ 38 | var obj = Downloadify.queue[queue]; 39 | if(obj) return obj.getFilename(); 40 | return ""; 41 | }, 42 | getDataTypeForSave: function(queue){ 43 | var obj = Downloadify.queue[queue]; 44 | if(obj) return obj.getDataType(); 45 | return ""; 46 | }, 47 | saveComplete: function(queue){ 48 | var obj = Downloadify.queue[queue]; 49 | if(obj) obj.complete(); 50 | return true; 51 | }, 52 | saveCancel: function(queue){ 53 | var obj = Downloadify.queue[queue]; 54 | if(obj) obj.cancel(); 55 | return true; 56 | }, 57 | saveError: function(queue){ 58 | var obj = Downloadify.queue[queue]; 59 | if(obj) obj.error(); 60 | return true; 61 | }, 62 | addToQueue: function(container){ 63 | Downloadify.queue[container.queue_name] = container; 64 | }, 65 | // Concept adapted from: http://tinyurl.com/yzsyfto 66 | // SWF object runs off of ID's, so this is the good way to get a unique ID 67 | getUID: function(el){ 68 | if(el.id == "") el.id = 'downloadify_' + Downloadify.uid++; 69 | return el.id; 70 | } 71 | }; 72 | 73 | Downloadify.create = function( idOrDOM, options ){ 74 | var el = (typeof(idOrDOM) == "string" ? document.getElementById(idOrDOM) : idOrDOM ); 75 | return new Downloadify.Container(el, options); 76 | }; 77 | 78 | Downloadify.Container = function(el, options){ 79 | var base = this; 80 | 81 | base.el = el; 82 | base.enabled = true; 83 | base.dataCallback = null; 84 | base.filenameCallback = null; 85 | base.data = null; 86 | base.filename = null; 87 | 88 | var init = function(){ 89 | base.options = options; 90 | 91 | if( !base.options.append ) base.el.innerHTML = ""; 92 | 93 | base.flashContainer = document.createElement('span'); 94 | base.el.appendChild(base.flashContainer); 95 | 96 | base.queue_name = Downloadify.getUID( base.flashContainer ); 97 | 98 | if( typeof(base.options.filename) === "function" ) 99 | base.filenameCallback = base.options.filename; 100 | else if (base.options.filename) 101 | base.filename = base.options.filename; 102 | 103 | if( typeof(base.options.data) === "function" ) 104 | base.dataCallback = base.options.data; 105 | else if (base.options.data) 106 | base.data = base.options.data; 107 | 108 | 109 | var flashVars = { 110 | queue_name: base.queue_name, 111 | width: base.options.width, 112 | height: base.options.height 113 | }; 114 | 115 | var params = { 116 | allowScriptAccess: 'always' 117 | }; 118 | 119 | var attributes = { 120 | id: base.flashContainer.id, 121 | name: base.flashContainer.id 122 | }; 123 | 124 | if(base.options.enabled === false) base.enabled = false; 125 | 126 | if(base.options.transparent === true) params.wmode = "transparent"; 127 | 128 | if(base.options.downloadImage) flashVars.downloadImage = base.options.downloadImage; 129 | 130 | swfobject.embedSWF(base.options.swf, base.flashContainer.id, base.options.width, base.options.height, "10", null, flashVars, params, attributes ); 131 | 132 | Downloadify.addToQueue( base ); 133 | }; 134 | 135 | base.enable = function(){ 136 | var swf = document.getElementById(base.flashContainer.id); 137 | swf.setEnabled(true); 138 | base.enabled = true; 139 | }; 140 | 141 | base.disable = function(){ 142 | var swf = document.getElementById(base.flashContainer.id); 143 | swf.setEnabled(false); 144 | base.enabled = false; 145 | }; 146 | 147 | base.getData = function(){ 148 | if( !base.enabled ) return ""; 149 | if( base.dataCallback ) return base.dataCallback(); 150 | else if (base.data) return base.data; 151 | else return ""; 152 | }; 153 | 154 | base.getFilename = function(){ 155 | if( base.filenameCallback ) return base.filenameCallback(); 156 | else if (base.filename) return base.filename; 157 | else return ""; 158 | }; 159 | 160 | base.getDataType = function(){ 161 | if (base.options.dataType) return base.options.dataType; 162 | return "string"; 163 | }; 164 | 165 | base.complete = function(){ 166 | if( typeof(base.options.onComplete) === "function" ) base.options.onComplete(); 167 | }; 168 | 169 | base.cancel = function(){ 170 | if( typeof(base.options.onCancel) === "function" ) base.options.onCancel(); 171 | }; 172 | 173 | base.error = function(){ 174 | if( typeof(base.options.onError) === "function" ) base.options.onError(); 175 | }; 176 | 177 | init(); 178 | }; 179 | 180 | Downloadify.defaultOptions = { 181 | swf: 'media/downloadify.swf', 182 | downloadImage: 'images/download.png', 183 | width: 100, 184 | height: 30, 185 | transparent: true, 186 | append: false, 187 | dataType: "string" 188 | }; 189 | })(); 190 | 191 | // Support for jQuery 192 | if(typeof(jQuery) != "undefined"){ 193 | (function($){ 194 | $.fn.downloadify = function(options){ 195 | return this.each(function(){ 196 | options = $.extend({}, Downloadify.defaultOptions, options); 197 | var dl = Downloadify.create( this, options); 198 | $(this).data('Downloadify', dl); 199 | }); 200 | }; 201 | })(jQuery); 202 | }; 203 | 204 | /* mootools helper */ 205 | if(typeof(MooTools) != 'undefined'){ 206 | Element.implement({ 207 | downloadify: function(options) { 208 | options = $merge(Downloadify.defaultOptions,options); 209 | return this.store('Downloadify',Downloadify.create(this,options)); 210 | } 211 | }); 212 | }; 213 | 214 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h2. Downloadify: Client Side File Creation 2 | 3 | *_Important! The swf has been compiled for online use only. Testing from the file path (i.e. file:// ) will not work as it will violate the security sandbox._* 4 | 5 | h3. Overview 6 | 7 | This library is a tiny JavaScript + Flash library that allows you to generate files on the fly, in the browser, without server interaction. Web applications that allow you to generate vCards, color palettes, custom code, etc would benefit from using this library. In addition to increasing speed (no round trip to the server) this solution can reduce the database and server load of existing web applications. _This is not a library to 'force download' a file from a server. It does not interact with a server at all._ 8 | 9 | h3. Demo 10 | 11 | A "very simple demo is available":http://pixelgraphics.us/downloadify/test.html that lets you supply your own content and filename and test out saving, canceling, and the error functionality when the file is blank. 12 | 13 | For a real world usage, see "Starter for jQuery":http://starter.pixelgraphics.us . To quickly demo the usage, just click "Load Example Data" then click Download. After the initial page load, no further contact is made with the server. Everything happens on the client side. 14 | 15 | h3. Download 16 | 17 | Please download from the "Downloads":http://github.com/dcneiner/Downloadify/downloads section of this project. 18 | 19 | h3. Support 20 | 21 | For support, please use the "Issues":http://github.com/dcneiner/Downloadify/issues section of this project. You can also try to hit me up on "Twitter at @dougneiner":http://twitter.com/dougneiner but I might just ask you to file an issue. :) 22 | 23 | h3. Dependencies 24 | 25 | _The end user must have Flash 10 or higher installed for this plugin to work. As of September 2009, it was at a 93% saturation, so most users should already have it installed._ 26 | 27 | Downloadify is only dependent on "SWFObject 2.0":http://code.google.com/p/swfobject/ which is included with the download. It is compatible with any JavaScript framework but has a helper for both jQuery and MooTools. Neither helper will be activatd if Downloadify is inserted prior to including the framework library. 28 | 29 | h3. Usage 30 | 31 | *Any JavaScript framework*: 32 | 33 |
Downloadify.create( id_or_DOM_element, options );
34 | 35 | *jQuery*: 36 | 37 |
$("#element").downloadify( options );
38 | 39 | *MooTools:* 40 | 41 |
$("elementid").downloadify( options );
42 | 43 | h3. Options 44 | 45 | Unless you are using the jQuery plugin included with Downloadify, you must supply all required options with your call to the Downloadify.create function. 46 | 47 | *Defaults and Required Options* 48 | 49 | * swf: 'media/downloadify.swf' *Required*
_Path to the SWF File. Can be relative from the page, or an absolute path._ 50 | * downloadImage: 'images/download.png' *Required*
_Path to the Button Image. Can be relative from the page or an absolute path._ 51 | * width: 175 *Required*
_Width of the button (and the flash movie)_ 52 | * height: 55 *Required*
_Height of the button. This will be 1/4 the height of the image._ 53 | * filename: *Required*
_Can be a String or a function callback. If a function, the return value of the function will be used as the filename._ 54 | * data: *Required*
_Can be a normal String, base64 encoded String, or a function callback. If a function, the return value of the function will be used as the file data._ 55 | * dataType: 'string'
_Must be a String with the value string or base64. Data paired with the dataType of base64 will be decoded into a ByteArray prior to saving._ 56 | * transparent: false
_Set this to true to use wmode=transparent on the flash movie._ 57 | * append: false
_By default the contents of the targeted DOM element are removed. Set this to true to keep the contents and append the button._ 58 | 59 | *Event Callbacks* 60 | 61 | No data is passed into these functions, they are simply called. 62 | 63 | * onError: _Called when the Download button is clicked but your data callback returns ""._ 64 | * onCancel: _Called when the Download button is clicked but the user then cancels without saving._ 65 | * onComplete: _Called when the Download button is clicked and the file is saved to the user's computer._ 66 | 67 | h3. Setting Up the Image 68 | 69 | Downloadify supports (i.e. requires) three button states with limited support for a fourth. The states are: 70 | 71 | * *Normal* 72 | * *Over* ( When the mouse hovers over the button ) 73 | * *Down* ( When the button is pressed ) 74 | * *Disabled* ( Limited support, when .disable() is called on the Downloadify.Container object ) 75 | 76 | In trying to keep this plugin as simple as possible, all four states are always assumed to be present. You should prepare your button image as a single image the width you want your button, and four times the height of the button. All four states should then live in that one image in the same order as the previous list from top to bottom. 77 | 78 | h3. Potential Issues 79 | 80 | When working with different button images, you may find Flash has cached your image. This is the desired behavior on a live site, but not during development. To get around this, simply supply a ?rev=1 or ?rev=2 etc on the end of your downloadImage url. 81 | 82 | h3. Compiling Notes 83 | 84 | I develop locally using Xcode and the Flex 4 SDK Beta 2. Please do not submit request on how to setup a local testing environment. If you are interested in my Xcode project files, send me a message. 85 | 86 | h3. Developers 87 | 88 | *Core Developer:* "Doug Neiner":http://dougneiner.com 89 | 90 | *Contributors:* 91 | 92 | * "David Walsh":http://davidwalsh.name -- Contributed the MooTools helper 93 | 94 | h3. Change Log 95 | 96 | * *Version 0.2*: 97 | ** Added support for base64 via the "as3base64 Library":http://github.com/spjwebster/as3base64 98 | ** Added dataType option 99 | ** Added MooTools helper (Thanks David!) 100 | ** Upgraded SWFObject to v2.2 101 | * *Original Release:* Version 0.1 102 | 103 | h3. License Information: MIT 104 | 105 | as3base64: "Copyright (c) 2006 Steve Webster":http://github.com/spjwebster/as3base64 106 | 107 | All Downloadify Code: Copyright (c) 2009 Douglas C. Neiner 108 | 109 | 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: 110 | 111 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 112 | 113 | 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. -------------------------------------------------------------------------------- /js/swfobject.js: -------------------------------------------------------------------------------- 1 | /* SWFObject v2.2 2 | is released under the MIT License 3 | */ 4 | var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab