summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortimb <timb@mb.local>2010-02-11 03:54:41 -0800
committertimb <timb@mb.local>2010-02-11 03:54:41 -0800
commitc6c120b9aa3b081fc9724f083b72bfc7f83ebce7 (patch)
tree7f32faf9425a2b23da92240436da903eadc96df3
parent3352b2dfdf82955d58fca2dd08e4a4d5fc289c93 (diff)
enabled webcam upload\!
-rwxr-xr-xstatic/js/pichat.js19
-rwxr-xr-xstatic/pichat.css47
-rw-r--r--static/webcam/close.gifbin0 -> 102 bytes
-rw-r--r--static/webcam/shutter.mp3bin0 -> 13624 bytes
-rw-r--r--static/webcam/webcam.js231
-rw-r--r--static/webcam/webcam.swfbin0 -> 6932 bytes
-rwxr-xr-xtemplate/chat.st4
-rw-r--r--webcam/Webcam.as2
-rw-r--r--webcam/com/marstonstudio/UploadPostHelper.as21
-rw-r--r--webcam/ru/inspirit/net/MultipartURLLoader.as582
-rw-r--r--webcam/ru/inspirit/net/events/MultipartURLLoaderEvent.as27
11 files changed, 311 insertions, 622 deletions
diff --git a/static/js/pichat.js b/static/js/pichat.js
index 48b11ae..de01f53 100755
--- a/static/js/pichat.js
+++ b/static/js/pichat.js
@@ -213,11 +213,14 @@ function initChat() {
scrollToEnd()
scrollWatcher()
+
+ // see /static/webcam/webcam.js
+ webcam.init()
+
setTimeout(refresh, 1000);
}
-
function initProfile() {
$('.logged-dump .content').each(function() {
var t = $(this);
@@ -307,4 +310,18 @@ function scrollToEnd(){
function scrollWatcher(){
scrollIfPossible()
setTimeout(scrollWatcher, 500)
+}
+
+// well fuck webkit for not supporting {text-decoration: blink}
+
+function blinkStart(){
+ blinkTimer = setInterval(function(){
+ $(".blink").removeClass("blink").addClass("blink-turning-off")
+ $(".blink-off").removeClass("blink-off").addClass("blink")
+ $(".blink-turning-off").removeClass("blink-turning-off").addClass("blink-off")
+ },500);
+}
+
+function blinkStop(){
+ clearInterval(blinkTimer);
} \ No newline at end of file
diff --git a/static/pichat.css b/static/pichat.css
index 9ccb725..1e4a4f3 100755
--- a/static/pichat.css
+++ b/static/pichat.css
@@ -146,7 +146,7 @@ top:15px;
color:#fff;
text-shadow:1px 1px 3px rgba(0,0,0,1);
}
-#webcam{
+#webcam-button-upload{
border-top-right-radius:10px;
-webkit-border-top-right-radius:5px;
-moz-border-radius-topright:5px;
@@ -173,6 +173,51 @@ background-image:url(/static/cambutton.png);
text-shadow:1px 1px 3px rgba(0,0,0,1);
}
+#webcam-button-snap{
+ border-top-right-radius:10px;
+ -webkit-border-top-right-radius:5px;
+ -moz-border-radius-topright:5px;
+ border-bottom-right-radius:5px;
+ -webkit-border-bottom-right-radius:5px;
+ -moz-border-radius-bottomright:5px;
+ border-radius:5px;
+ position:absolute;
+ display:inline-block;
+ width:120px;
+height:35px;
+background-position:center;
+right:0;
+top:15px;
+padding-bottom:1;
+ text-align:center;
+ z-index:100;
+ cursor:pointer;
+background-color:yellow;
+ font-size:16px;
+ color:#fff;
+background-image:url(/static/bg-btn-red.png);
+
+ text-shadow:1px 1px 3px rgba(0,0,0,1);
+}
+#webcam-button-snap.blink{
+ color:#4f4;
+}
+
+
+#webcam-button-close { /* 16 x 14 */
+ position: fixed;
+ bottom: 300px; /* 240 + 68 - (16 / 2) */
+ right: 313px; /* 320 - (14 / 2) */
+ z-index: 5001;
+ cursor: pointer;
+}
+
+#webcam-preview {
+ position:fixed;
+ bottom: 68px;
+ right:0;
+ z-index:5000
+}
#upload {
border-top-right-radius:10px;
-webkit-border-top-right-radius:5px;
diff --git a/static/webcam/close.gif b/static/webcam/close.gif
new file mode 100644
index 0000000..679ca2a
--- /dev/null
+++ b/static/webcam/close.gif
Binary files differ
diff --git a/static/webcam/shutter.mp3 b/static/webcam/shutter.mp3
new file mode 100644
index 0000000..16df2b3
--- /dev/null
+++ b/static/webcam/shutter.mp3
Binary files differ
diff --git a/static/webcam/webcam.js b/static/webcam/webcam.js
new file mode 100644
index 0000000..9829fd7
--- /dev/null
+++ b/static/webcam/webcam.js
@@ -0,0 +1,231 @@
+/* JPEGCam v1.0.8 */
+/* Webcam library for capturing JPEG images and submitting to a server */
+/* Copyright (c) 2008 - 2009 Joseph Huckaby <jhuckaby@goldcartridge.com> */
+/* Licensed under the GNU Lesser Public License */
+/* http://www.gnu.org/licenses/lgpl.html */
+
+/* Usage:
+ <script language="JavaScript">
+ document.write( webcam.get_html(320, 240) );
+ webcam.set_api_url( 'test.php' );
+ webcam.set_hook( 'onComplete', 'my_callback_function' );
+ function my_callback_function(response) {
+ alert("Success! PHP returned: " + response);
+ }
+ </script>
+ <a href="javascript:void(webcam.snap())">Take Snapshot</a>
+*/
+
+// Everything is under a 'webcam' Namespace
+window.webcam = {
+ version: '1.0.8',
+
+ // globals
+ ie: !!navigator.userAgent.match(/MSIE/),
+ protocol: location.protocol.match(/https/i) ? 'https' : 'http',
+ callback: null, // user callback for completed uploads
+ swf_url: 'webcam.swf', // URI to webcam.swf movie (defaults to cwd)
+ shutter_url: 'shutter.mp3', // URI to shutter.mp3 sound
+ api_url: '', // URL to upload script
+ loaded: false, // true when webcam movie finishes loading
+ quality: 90, // JPEG quality (1 - 100)
+ shutter_sound: true, // shutter sound effect on/off
+ hooks: {
+ onLoad: null,
+ onComplete: null,
+ onError: null
+ }, // callback hook functions
+
+ init: function(){
+ webcam.set_api_url( '/upload' );
+ webcam.set_swf_url('/static/webcam/webcam.swf')
+ webcam.set_quality( 90 );
+ webcam.set_shutter_sound(false);
+ webcam.set_hook('onComplete', webcam.uploadCompleted );
+ $("#webcam-button-upload").click(webcam.show);
+ $("#webcam-button-snap").click(webcam.takePicture)
+ },
+
+ uploadCompleted: function(){
+ $("#webcam-button-snap").attr("value", "Send Pic")
+ webcam.reset();
+ },
+
+ takePicture: function(){
+ webcam.set_form_data({"room": Room})
+ $("#webcam-button-snap").attr("value", "Sending...")
+ webcam.snap()
+ },
+
+ show: function() {
+ var closeHtml = '<img src="/static/webcam/close.gif" onclick="webcam.hide()" id="webcam-button-close" />'
+ var camHtml = '<div id="webcam-preview">' + webcam.get_html(320, 240) + closeHtml + "</div>"
+ $("body").append(camHtml)
+ $("#webcam-button-upload").addClass("invisible")
+ $("#webcam-button-snap").removeClass("invisible")
+ blinkStart()
+ },
+
+ hide: function() {
+ $("#webcam-button-snap").addClass("invisible")
+ $("#webcam-button-upload").removeClass("invisible")
+ blinkStop()
+ $("#webcam-preview").remove()
+ },
+
+ set_hook: function(name, callback) {
+ // set callback hook
+ // supported hooks: onLoad, onComplete, onError
+ if (typeof(this.hooks[name]) == 'undefined')
+ return alert("Hook type not supported: " + name);
+
+ this.hooks[name] = callback;
+ },
+
+ fire_hook: function(name, value) {
+ // fire hook callback, passing optional value to it
+ if (this.hooks[name]) {
+ if (typeof(this.hooks[name]) == 'function') {
+ // callback is function reference, call directly
+ this.hooks[name](value);
+ }
+ else if (typeof(this.hooks[name]) == 'array') {
+ // callback is PHP-style object instance method
+ this.hooks[name][0][this.hooks[name][1]](value);
+ }
+ else if (window[this.hooks[name]]) {
+ // callback is global function name
+ window[ this.hooks[name] ](value);
+ }
+ return true;
+ }
+ return false; // no hook defined
+ },
+
+ set_api_url: function(url) {
+ // set location of upload API script
+ this.api_url = url;
+ },
+
+ set_swf_url: function(url) {
+ // set location of SWF movie (defaults to webcam.swf in cwd)
+ this.swf_url = url;
+ },
+
+ get_html: function(width, height, server_width, server_height) {
+ // Return HTML for embedding webcam capture movie
+ // Specify pixel width and height (640x480, 320x240, etc.)
+ // Server width and height are optional, and default to movie width/height
+ if (!server_width) server_width = width;
+ if (!server_height) server_height = height;
+
+ var html = '';
+ var flashvars = 'shutter_enabled=' + (this.shutter_sound ? 1 : 0) +
+ '&shutter_url=' + escape(this.shutter_url) +
+ '&width=' + width +
+ '&height=' + height +
+ '&server_width=' + server_width +
+ '&server_height=' + server_height;
+
+ if (this.ie) {
+ html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+this.protocol+'://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="webcam_movie" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+this.swf_url+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/></object>';
+ }
+ else {
+ html += '<embed id="webcam_movie" src="'+this.swf_url+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="webcam_movie" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" />';
+ }
+
+ this.loaded = false;
+ return html;
+ },
+
+ get_movie: function() {
+ // get reference to movie object/embed in DOM
+ if (!this.loaded) return alert("ERROR: Movie is not loaded yet");
+ var movie = document.getElementById('webcam_movie');
+ if (!movie) alert("ERROR: Cannot locate movie 'webcam_movie' in DOM");
+ return movie;
+ },
+
+ snap: function(url, callback) {
+ // take snapshot and send to server
+ // specify fully-qualified URL to server API script
+ // and callback function (string or function object)
+ if (callback) this.set_hook('onComplete', callback);
+ if (url) this.set_api_url(url);
+
+ this.get_movie()._snap( this.api_url, this.quality, this.shutter_sound ? 1 : 0 );
+ },
+
+ set_form_data: function(form_data) {
+ this.get_movie()._set_form_data(form_data);
+ },
+
+ freeze: function() {
+ // freeze webcam image (capture but do not upload)
+ this.get_movie()._snap('', this.quality, this.shutter_sound ? 1 : 0 );
+ },
+
+ upload: function(url, callback) {
+ // upload image to server after taking snapshot
+ // specify fully-qualified URL to server API script
+ // and callback function (string or function object)
+ if (callback) this.set_hook('onComplete', callback);
+ if (url) this.set_api_url(url);
+
+ this.get_movie()._upload( this.api_url );
+ },
+
+ reset: function() {
+ // reset movie after taking snapshot
+ this.get_movie()._reset();
+ },
+
+ configure: function(panel) {
+ // open flash configuration panel -- specify tab name:
+ // "camera", "privacy", "default", "localStorage", "microphone", "settingsManager"
+ if (!panel) panel = "camera";
+ this.get_movie()._configure(panel);
+ },
+
+ set_quality: function(new_quality) {
+ // set the JPEG quality (1 - 100)
+ // default is 90
+ this.quality = new_quality;
+ },
+
+ set_shutter_sound: function(enabled, url) {
+ // enable or disable the shutter sound effect
+ // defaults to enabled
+ this.shutter_sound = enabled;
+ this.shutter_url = url ? url : 'shutter.mp3';
+ },
+
+ flash_notify: function(type, msg) {
+ // receive notification from flash about event
+ switch (type) {
+ case 'flashLoadComplete':
+ // movie loaded successfully
+ this.loaded = true;
+ this.fire_hook('onLoad');
+ break;
+
+ case 'error':
+ // HTTP POST error most likely
+ if (!this.fire_hook('onError', msg)) {
+ alert("JPEGCam Flash Error: " + msg);
+ }
+ break;
+
+ case 'success':
+ // upload complete, execute user callback function
+ // and pass raw API script results to function
+ this.fire_hook('onComplete', msg.toString());
+ break;
+
+ default:
+ // catch-all, just in case
+ alert("jpegcam flash_notify: " + type + ": " + msg);
+ break;
+ }
+ }
+};
diff --git a/static/webcam/webcam.swf b/static/webcam/webcam.swf
new file mode 100644
index 0000000..9285dc9
--- /dev/null
+++ b/static/webcam/webcam.swf
Binary files differ
diff --git a/template/chat.st b/template/chat.st
index cf724fd..90e2602 100755
--- a/template/chat.st
+++ b/template/chat.st
@@ -3,6 +3,7 @@
<title>dump.fm</title>
$head()$
<link rel="stylesheet" type="text/css" href="/static/pichat.css">
+ <script type="text/javascript" src="/static/webcam/webcam.js"></script>
<script>
jQuery(document).ready(initChat);
var Nick = $json_user_nick$;
@@ -102,7 +103,8 @@ function MM_swapImage() { //v3.0
<input id="msgSubmit" type="submit" value="Send"
/>
<input id="upload" value="Upload" type="submit">
- <input id="webcam" value="Webcam" type="submit">
+ <input id="webcam-button-upload" value="Webcam" type="submit">
+ <input id="webcam-button-snap" value="Send Pic" type="submit" class="invisible blink">
</div>
$endif$
</div>
diff --git a/webcam/Webcam.as b/webcam/Webcam.as
index c8be6ca..7a55373 100644
--- a/webcam/Webcam.as
+++ b/webcam/Webcam.as
@@ -1 +1 @@
-package { /* JPEGCam v1.0.8 */ /* Webcam library for capturing JPEG images and submitting to a server */ /* Copyright (c) 2008 - 2009 Joseph Huckaby <jhuckaby@goldcartridge.com> */ /* Licensed under the GNU Lesser Public License */ /* http://www.gnu.org/licenses/lgpl.html */ import flash.display.LoaderInfo; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.*; import flash.utils.*; import flash.media.Camera; import flash.media.Video; import flash.external.ExternalInterface; import flash.net.*; import flash.system.Security; import flash.system.SecurityPanel; import flash.media.Sound; import flash.media.SoundChannel; import flash.geom.Matrix; import com.adobe.images.JPGEncoder; public class Webcam extends Sprite { private var video:Video; private var encoder:JPGEncoder; private var snd:Sound; private var channel:SoundChannel = new SoundChannel(); private var jpeg_quality:int; private var video_width:int; private var video_height:int; private var server_width:int; private var server_height:int; private var camera:Camera; private var bmp:Bitmap; private var bmpdata:BitmapData; private var url:String; public function Webcam() { // class constructor flash.system.Security.allowDomain("*"); var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters; video_width = Math.floor( flashvars.width ); video_height = Math.floor( flashvars.height ); server_width = Math.floor( flashvars.server_width ); server_height = Math.floor( flashvars.server_height ); stage.scaleMode = StageScaleMode.NO_SCALE; // stage.scaleMode = StageScaleMode.EXACT_FIT; stage.align = StageAlign.TOP_LEFT; stage.stageWidth = Math.max(video_width, server_width); stage.stageHeight = Math.max(video_height, server_height); // Hack to auto-select iSight camera on Mac (JPEGCam Issue #5, submitted by manuel.gonzalez.noriega) // From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/ var cameraIdx:int = -1; for (var idx = 0, len = Camera.names.length; idx < len; idx++) { if (Camera.names[idx] == "USB Video Class Video") { cameraIdx = idx; idx = len; } } if (cameraIdx > -1) camera = Camera.getCamera( String(cameraIdx) ); else camera = Camera.getCamera(); if (camera != null) { camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); video = new Video( Math.max(video_width, server_width), Math.max(video_height, server_height) ); video.attachCamera(camera); addChild(video); if ((video_width < server_width) && (video_height < server_height)) { video.scaleX = video_width / server_width; video.scaleY = video_height / server_height; } camera.setQuality(0, 100); camera.setKeyFrameInterval(10); camera.setMode( Math.max(video_width, server_width), Math.max(video_height, server_height), 30); // do not detect motion (may help reduce CPU usage) camera.setMotionLevel( 100 ); ExternalInterface.addCallback('_snap', snap); ExternalInterface.addCallback('_configure', configure); ExternalInterface.addCallback('_upload', upload); ExternalInterface.addCallback('_reset', reset); if (flashvars.shutter_enabled == 1) { snd = new Sound(); snd.load( new URLRequest( flashvars.shutter_url ) ); } jpeg_quality = 90; ExternalInterface.call('webcam.flash_notify', 'flashLoadComplete', true); } else { trace("You need a camera."); ExternalInterface.call('webcam.flash_notify', "error", "No camera was detected."); } } public function set_quality(new_quality:int) { // set JPEG image quality if (new_quality < 0) new_quality = 0; if (new_quality > 100) new_quality = 100; jpeg_quality = new_quality; } public function configure(panel:String = SecurityPanel.CAMERA) { // show configure dialog inside flash movie Security.showSettings(panel); } private function activityHandler(event:ActivityEvent):void { trace("activityHandler: " + event); } public function snap(url, new_quality, shutter) { // take snapshot from camera, and upload if URL was provided if (new_quality) set_quality(new_quality); trace("in snap(), drawing to bitmap"); if (shutter) { channel = snd.play(); setTimeout( snap2, 10, url ); } else snap2(url); } public function snap2(url) { // take snapshot, convert to jpeg, submit to server bmpdata = new BitmapData( Math.max(video_width, server_width), Math.max(video_height, server_height) ); bmpdata.draw( video ); // draw snapshot on stage bmp = new Bitmap( bmpdata ); addChild( bmp ); // stop capturing video video.attachCamera( null ); removeChild( video ); // if URL was provided, upload now if (url) upload( url ); } public function upload(url) { if (bmpdata) { if ((video_width > server_width) && (video_height > server_height)) { // resize image downward before submitting var tmpdata = new BitmapData(server_width, server_height); var matrix = new Matrix(); matrix.scale( server_width / video_width, server_height / video_height ); tmpdata.draw( bmpdata, matrix, null, null, null, true ); // smoothing bmpdata = tmpdata; } // need resize trace("converting to jpeg"); var ba:ByteArray; encoder = new JPGEncoder( jpeg_quality ); ba = encoder.encode( bmpdata ); trace("jpeg length: " + ba.length); var head:URLRequestHeader = new URLRequestHeader("Accept","text/*"); var req:URLRequest = new URLRequest( url ); req.requestHeaders.push(head); req.data = ba; req.method = URLRequestMethod.POST; req.contentType = "image/jpeg"; var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, onLoaded); trace("sending post to: " + url); try { loader.load(req); } catch (error:Error) { trace("Unable to load requested document."); ExternalInterface.call('webcam.flash_notify', "error", "Unable to post data: " + error); } } else { ExternalInterface.call('webcam.flash_notify', "error", "Nothing to upload, must capture an image first."); } } public function onLoaded(evt:Event):void { // image upload complete var msg = "unknown"; if (evt && evt.target && evt.target.data) msg = evt.target.data; ExternalInterface.call('webcam.flash_notify', "success", msg); } public function reset() { // reset video after taking snapshot if (bmp) { removeChild( bmp ); bmp = null; bmpdata = null; video.attachCamera(camera); addChild(video); } } } } \ No newline at end of file
+package { /* JPEGCam v1.0.8 */ /* Webcam library for capturing JPEG images and submitting to a server */ /* Copyright (c) 2008 - 2009 Joseph Huckaby <jhuckaby@goldcartridge.com> */ /* Licensed under the GNU Lesser Public License */ /* http://www.gnu.org/licenses/lgpl.html */ import flash.display.LoaderInfo; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.*; import flash.utils.*; import flash.media.Camera; import flash.media.Video; import flash.external.ExternalInterface; import flash.net.*; import flash.system.Security; import flash.system.SecurityPanel; import flash.media.Sound; import flash.media.SoundChannel; import flash.geom.Matrix; import com.adobe.images.JPGEncoder; import com.marstonstudio.UploadPostHelper; public class Webcam extends Sprite { private var video:Video; private var encoder:JPGEncoder; private var snd:Sound; private var channel:SoundChannel = new SoundChannel(); private var jpeg_quality:int; private var video_width:int; private var video_height:int; private var server_width:int; private var server_height:int; private var camera:Camera; private var bmp:Bitmap; private var bmpdata:BitmapData; private var url:String; private var form_data:Object; public function Webcam() { // class constructor flash.system.Security.allowDomain("*"); var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters; video_width = Math.floor( flashvars.width ); video_height = Math.floor( flashvars.height ); server_width = Math.floor( flashvars.server_width ); server_height = Math.floor( flashvars.server_height ); form_data = {}; stage.scaleMode = StageScaleMode.NO_SCALE; // stage.scaleMode = StageScaleMode.EXACT_FIT; stage.align = StageAlign.TOP_LEFT; stage.stageWidth = Math.max(video_width, server_width); stage.stageHeight = Math.max(video_height, server_height); // Hack to auto-select iSight camera on Mac (JPEGCam Issue #5, submitted by manuel.gonzalez.noriega) // From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/ var cameraIdx:int = -1; for (var idx = 0, len = Camera.names.length; idx < len; idx++) { if (Camera.names[idx] == "USB Video Class Video") { cameraIdx = idx; idx = len; } } if (cameraIdx > -1) camera = Camera.getCamera( String(cameraIdx) ); else camera = Camera.getCamera(); if (camera != null) { camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); video = new Video( Math.max(video_width, server_width), Math.max(video_height, server_height) ); video.attachCamera(camera); addChild(video); if ((video_width < server_width) && (video_height < server_height)) { video.scaleX = video_width / server_width; video.scaleY = video_height / server_height; } camera.setQuality(0, 100); camera.setKeyFrameInterval(10); camera.setMode( Math.max(video_width, server_width), Math.max(video_height, server_height), 30); // do not detect motion (may help reduce CPU usage) camera.setMotionLevel( 100 ); ExternalInterface.addCallback('_snap', snap); ExternalInterface.addCallback('_configure', configure); ExternalInterface.addCallback('_upload', upload); ExternalInterface.addCallback('_reset', reset); ExternalInterface.addCallback('_set_form_data', set_form_data); if (flashvars.shutter_enabled == 1) { snd = new Sound(); snd.load( new URLRequest( flashvars.shutter_url ) ); } jpeg_quality = 90; ExternalInterface.call('webcam.flash_notify', 'flashLoadComplete', true); } else { trace("You need a camera."); ExternalInterface.call('webcam.flash_notify', "error", "No camera was detected."); } } public function set_quality(new_quality:int) { // set JPEG image quality if (new_quality < 0) new_quality = 0; if (new_quality > 100) new_quality = 100; jpeg_quality = new_quality; } public function set_form_data(new_form_data:Object) { form_data = new_form_data; } public function configure(panel:String = SecurityPanel.CAMERA) { // show configure dialog inside flash movie Security.showSettings(panel); } private function activityHandler(event:ActivityEvent):void { trace("activityHandler: " + event); } public function snap(url, new_quality, shutter) { // take snapshot from camera, and upload if URL was provided if (new_quality) set_quality(new_quality); trace("in snap(), drawing to bitmap"); if (shutter) { channel = snd.play(); setTimeout( snap2, 10, url ); } else snap2(url); } public function snap2(url) { // take snapshot, convert to jpeg, submit to server bmpdata = new BitmapData( Math.max(video_width, server_width), Math.max(video_height, server_height) ); bmpdata.draw( video ); // draw snapshot on stage bmp = new Bitmap( bmpdata ); addChild( bmp ); // stop capturing video video.attachCamera( null ); removeChild( video ); // if URL was provided, upload now if (url) upload( url ); } public function upload(url) { if (bmpdata) { if ((video_width > server_width) && (video_height > server_height)) { // resize image downward before submitting var tmpdata = new BitmapData(server_width, server_height); var matrix = new Matrix(); matrix.scale( server_width / video_width, server_height / video_height ); tmpdata.draw( bmpdata, matrix, null, null, null, true ); // smoothing bmpdata = tmpdata; } // need resize trace("converting to jpeg"); var ba:ByteArray; encoder = new JPGEncoder( jpeg_quality ); ba = encoder.encode( bmpdata ); trace("jpeg length: " + ba.length); //URLRequest containing the form fields and the attached image var req : URLRequest = new URLRequest(url); req.method = URLRequestMethod.POST; req.data = UploadPostHelper.getPostData( 'dump.fm.webcam.test.jpg', ba, form_data ); req.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache') ); req.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary())); //URLLoader to load the request var loader : URLLoader = new URLLoader(); loader.dataFormat = URLLoaderDataFormat.BINARY; loader.addEventListener(Event.COMPLETE, onLoaded); trace("sending post to: " + url); try { loader.load(req); } catch (error:Error) { trace("Unable to load requested document."); ExternalInterface.call('webcam.flash_notify', "error", "Unable to post data: " + error); } } else { ExternalInterface.call('webcam.flash_notify', "error", "Nothing to upload, must capture an image first."); } } public function onLoaded(evt:Event):void { // image upload complete var msg = "unknown"; if (evt && evt.target && evt.target.data) msg = evt.target.data; ExternalInterface.call('webcam.flash_notify', "success", msg); } public function reset() { // reset video after taking snapshot if (bmp) { removeChild( bmp ); bmp = null; bmpdata = null; video.attachCamera(camera); addChild(video); } } } } \ No newline at end of file
diff --git a/webcam/com/marstonstudio/UploadPostHelper.as b/webcam/com/marstonstudio/UploadPostHelper.as
index 857bc42..43eb429 100644
--- a/webcam/com/marstonstudio/UploadPostHelper.as
+++ b/webcam/com/marstonstudio/UploadPostHelper.as
@@ -36,7 +36,7 @@ package com.marstonstudio
public static function getBoundary():String {
if(_boundary.length == 0) {
- for (var i:int = 0; i &lt; 0x20; i++ ) {
+ for (var i:int = 0; i < 0x20; i++ ) {
_boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) );
}
}
@@ -66,7 +66,7 @@ package com.marstonstudio
postData = BOUNDARY(postData);
postData = LINEBREAK(postData);
bytes = 'Content-Disposition: form-data; name="' + name + '"';
- for ( i = 0; i &lt; bytes.length; i++ ) {
+ for ( i = 0; i < bytes.length; i++ ) {
postData.writeByte( bytes.charCodeAt(i) );
}
postData = LINEBREAK(postData);
@@ -78,15 +78,15 @@ package com.marstonstudio
//add Filedata to postData
postData = BOUNDARY(postData);
postData = LINEBREAK(postData);
- bytes = 'Content-Disposition: form-data; name="Filedata"; filename="';
- for ( i = 0; i &lt; bytes.length; i++ ) {
+ bytes = 'Content-Disposition: form-data; name="image"; filename="';
+ for ( i = 0; i < bytes.length; i++ ) {
postData.writeByte( bytes.charCodeAt(i) );
}
postData.writeUTFBytes(fileName);
postData = QUOTATIONMARK(postData);
postData = LINEBREAK(postData);
- bytes = 'Content-Type: application/octet-stream';
- for ( i = 0; i &lt; bytes.length; i++ ) {
+ bytes = 'Content-Type: image/jpeg';
+ for ( i = 0; i < bytes.length; i++ ) {
postData.writeByte( bytes.charCodeAt(i) );
}
postData = LINEBREAK(postData);
@@ -94,21 +94,24 @@ package com.marstonstudio
postData.writeBytes(byteArray, 0, byteArray.length);
postData = LINEBREAK(postData);
+ // timb: i commented this shit out because i don't think it's needed
//add upload filed to postData
+ /*
postData = LINEBREAK(postData);
postData = BOUNDARY(postData);
postData = LINEBREAK(postData);
bytes = 'Content-Disposition: form-data; name="Upload"';
- for ( i = 0; i &lt; bytes.length; i++ ) {
+ for ( i = 0; i < bytes.length; i++ ) {
postData.writeByte( bytes.charCodeAt(i) );
}
postData = LINEBREAK(postData);
postData = LINEBREAK(postData);
bytes = 'Submit Query';
- for ( i = 0; i &lt; bytes.length; i++ ) {
+ for ( i = 0; i < bytes.length; i++ ) {
postData.writeByte( bytes.charCodeAt(i) );
}
postData = LINEBREAK(postData);
+ */
//closing boundary
postData = BOUNDARY(postData);
@@ -124,7 +127,7 @@ package com.marstonstudio
var l:int = UploadPostHelper.getBoundary().length;
p = DOUBLEDASH(p);
- for (var i:int = 0; i &lt; l; i++ ) {
+ for (var i:int = 0; i < l; i++ ) {
p.writeByte( _boundary.charCodeAt( i ) );
}
return p;
diff --git a/webcam/ru/inspirit/net/MultipartURLLoader.as b/webcam/ru/inspirit/net/MultipartURLLoader.as
deleted file mode 100644
index bfd3db2..0000000
--- a/webcam/ru/inspirit/net/MultipartURLLoader.as
+++ /dev/null
@@ -1,582 +0,0 @@
-package ru.inspirit.net
-{
- import flash.errors.IllegalOperationError;
- import flash.events.Event;
- import flash.events.EventDispatcher;
- import flash.events.HTTPStatusEvent;
- import flash.events.IOErrorEvent;
- import flash.events.ProgressEvent;
- import flash.events.SecurityErrorEvent;
- import flash.net.URLLoader;
- import flash.net.URLLoaderDataFormat;
- import flash.net.URLRequest;
- import flash.net.URLRequestHeader;
- import flash.net.URLRequestMethod;
- import flash.utils.ByteArray;
- import flash.utils.Dictionary;
- import flash.utils.Endian;
- import flash.utils.setTimeout;
- import flash.utils.clearInterval;
- import ru.inspirit.net.events.MultipartURLLoaderEvent;
-
- /**
- * Multipart URL Loader
- *
- * Original idea by Marston Development Studio - http://marstonstudio.com/?p=36
- *
- * History
- * 2009.01.15 version 1.0
- * Initial release
- *
- * 2009.01.19 version 1.1
- * Added options for MIME-types (default is application/octet-stream)
- *
- * 2009.01.20 version 1.2
- * Added clearVariables and clearFiles methods
- * Small code refactoring
- * Public methods documentaion
- *
- * 2009.02.09 version 1.2.1
- * Changed 'useWeakReference' to false (thanx to zlatko)
- * It appears that on some servers setting 'useWeakReference' to true
- * completely disables this event
- *
- * 2009.03.05 version 1.3
- * Added Async property. Now you can prepare data asynchronous before sending it.
- * It will prevent flash player from freezing while constructing request data.
- * You can specify the amount of bytes to write per iteration through BLOCK_SIZE static property.
- * Added events for asynchronous method.
- * Added dataFormat property for returned server data.
- * Removed 'Cache-Control' from headers and added custom requestHeaders array property.
- * Added getter for the URLLoader class used to send data.
- *
- * @author Eugene Zatepyakin
- * @version 1.3
- * @link http://blog.inspirit.ru/
- */
- public class MultipartURLLoader extends EventDispatcher
- {
- public static var BLOCK_SIZE:uint = 64 * 1024;
-
- private var _loader:URLLoader;
- private var _boundary:String;
- private var _variableNames:Array;
- private var _fileNames:Array;
- private var _variables:Dictionary;
- private var _files:Dictionary;
-
- private var _async:Boolean = false;
- private var _path:String;
- private var _data:ByteArray;
-
- private var _prepared:Boolean = false;
- private var asyncWriteTimeoutId:Number;
- private var asyncFilePointer:uint = 0;
- private var totalFilesSize:uint = 0;
- private var writtenBytes:uint = 0;
-
- public var requestHeaders:Array;
-
- public function MultipartURLLoader()
- {
- _fileNames = new Array();
- _files = new Dictionary();
- _variableNames = new Array();
- _variables = new Dictionary();
- _loader = new URLLoader();
- requestHeaders = new Array();
- }
-
- /**
- * Start uploading data to specified path
- *
- * @param path The server script path
- * @param async Set to true if you are uploading huge amount of data
- */
- public function load(path:String, async:Boolean = false):void
- {
- if (path == null || path == '') throw new IllegalOperationError('You cant load without specifing PATH');
-
- _path = path;
- _async = async;
-
- if (_async) {
- if(!_prepared){
- constructPostDataAsync();
- } else {
- doSend();
- }
- } else {
- _data = constructPostData();
- doSend();
- }
- }
-
- /**
- * Start uploading data after async prepare
- */
- public function startLoad():void
- {
- if ( _path == null || _path == '' || _async == false ) throw new IllegalOperationError('You can use this method only if loading asynchronous.');
- if ( !_prepared && _async ) throw new IllegalOperationError('You should prepare data before sending when using asynchronous.');
-
- doSend();
- }
-
- /**
- * Prepare data before sending (only if you use asynchronous)
- */
- public function prepareData():void
- {
- constructPostDataAsync();
- }
-
- /**
- * Stop loader action
- */
- public function close():void
- {
- try {
- _loader.close();
- } catch( e:Error ) { }
- }
-
- /**
- * Add string variable to loader
- * If you have already added variable with the same name it will be overwritten
- *
- * @param name Variable name
- * @param value Variable value
- */
- public function addVariable(name:String, value:Object = ''):void
- {
- if (_variableNames.indexOf(name) == -1) {
- _variableNames.push(name);
- }
- _variables[name] = value;
- _prepared = false;
- }
-
- /**
- * Add file part to loader
- * If you have already added file with the same fileName it will be overwritten
- *
- * @param fileContent File content encoded to ByteArray
- * @param fileName Name of the file
- * @param dataField Name of the field containg file data
- * @param contentType MIME type of the uploading file
- */
- public function addFile(fileContent:ByteArray, fileName:String, dataField:String = 'Filedata', contentType:String = 'application/octet-stream'):void
- {
- if (_fileNames.indexOf(fileName) == -1) {
- _fileNames.push(fileName);
- _files[fileName] = new FilePart(fileContent, fileName, dataField, contentType);
- totalFilesSize += fileContent.length;
- } else {
- var f:FilePart = _files[fileName] as FilePart;
- totalFilesSize -= f.fileContent.length;
- f.fileContent = fileContent;
- f.fileName = fileName;
- f.dataField = dataField;
- f.contentType = contentType;
- totalFilesSize += fileContent.length;
- }
-
- _prepared = false;
- }
-
- /**
- * Remove all variable parts
- */
- public function clearVariables():void
- {
- _variableNames = new Array();
- _variables = new Dictionary();
- _prepared = false;
- }
-
- /**
- * Remove all file parts
- */
- public function clearFiles():void
- {
- for each(var name:String in _fileNames)
- {
- (_files[name] as FilePart).dispose();
- }
- _fileNames = new Array();
- _files = new Dictionary();
- totalFilesSize = 0;
- _prepared = false;
- }
-
- /**
- * Dispose all class instance objects
- */
- public function dispose(): void
- {
- clearInterval(asyncWriteTimeoutId);
- removeListener();
- close();
-
- _loader = null;
- _boundary = null;
- _variableNames = null;
- _variables = null;
- _fileNames = null;
- _files = null;
- requestHeaders = null;
- _data = null;
- }
-
- /**
- * Generate random boundary
- * @return Random boundary
- */
- public function getBoundary():String
- {
- if (_boundary == null) {
- _boundary = '';
- for (var i:int = 0; i < 0x20; i++ ) {
- _boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) );
- }
- }
- return _boundary;
- }
-
- public function get ASYNC():Boolean
- {
- return _async;
- }
-
- public function get PREPARED():Boolean
- {
- return _prepared;
- }
-
- public function get dataFormat():String
- {
- return _loader.dataFormat;
- }
-
- public function set dataFormat(format:String):void
- {
- if (format != URLLoaderDataFormat.BINARY && format != URLLoaderDataFormat.TEXT && format != URLLoaderDataFormat.VARIABLES) {
- throw new IllegalOperationError('Illegal URLLoader Data Format');
- }
- _loader.dataFormat = format;
- }
-
- public function get loader():URLLoader
- {
- return _loader;
- }
-
- private function doSend():void
- {
- var urlRequest:URLRequest = new URLRequest();
- urlRequest.url = _path;
- urlRequest.contentType = 'multipart/form-data; boundary=' + getBoundary();
- urlRequest.method = URLRequestMethod.POST;
- urlRequest.data = _data;
-
- if (requestHeaders.length && requestHeaders != null){
- urlRequest.requestHeaders = requestHeaders.concat();
- }
-
- addListener();
-
- _loader.load(urlRequest);
- }
-
- private function constructPostDataAsync():void
- {
- clearInterval(asyncWriteTimeoutId);
-
- _data = new ByteArray();
- _data.endian = Endian.BIG_ENDIAN;
-
- _data = constructVariablesPart(_data);
-
- asyncFilePointer = 0;
- writtenBytes = 0;
- _prepared = false;
- if (_fileNames.length) {
- nextAsyncLoop();
- } else {
- _data = closeDataObject(_data);
- _prepared = true;
- dispatchEvent( new MultipartURLLoaderEvent(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE) );
- }
- }
-
- private function constructPostData():ByteArray
- {
- var postData:ByteArray = new ByteArray();
- postData.endian = Endian.BIG_ENDIAN;
-
- postData = constructVariablesPart(postData);
- postData = constructFilesPart(postData);
-
- postData = closeDataObject(postData);
-
- return postData;
- }
-
- private function closeDataObject(postData:ByteArray):ByteArray
- {
- postData = BOUNDARY(postData);
- postData = DOUBLEDASH(postData);
- return postData;
- }
-
- private function constructVariablesPart(postData:ByteArray):ByteArray
- {
- var i:uint;
- var bytes:String;
-
- for each(var name:String in _variableNames)
- {
- postData = BOUNDARY(postData);
- postData = LINEBREAK(postData);
- bytes = 'Content-Disposition: form-data; name="' + name + '"';
- for ( i = 0; i < bytes.length; i++ ) {
- postData.writeByte( bytes.charCodeAt(i) );
- }
- postData = LINEBREAK(postData);
- postData = LINEBREAK(postData);
- postData.writeUTFBytes(_variables[name]);
- postData = LINEBREAK(postData);
- }
-
- return postData;
- }
-
- private function constructFilesPart(postData:ByteArray):ByteArray
- {
- var i:uint;
- var bytes:String;
-
- if(_fileNames.length){
- for each(var name:String in _fileNames)
- {
- postData = getFilePartHeader(postData, _files[name] as FilePart);
- postData = getFilePartData(postData, _files[name] as FilePart);
- postData = LINEBREAK(postData);
- }
- postData = closeFilePartsData(postData);
- }
-
- return postData;
- }
-
- private function closeFilePartsData(postData:ByteArray):ByteArray
- {
- var i:uint;
- var bytes:String;
-
- postData = LINEBREAK(postData);
- postData = BOUNDARY(postData);
- postData = LINEBREAK(postData);
- bytes = 'Content-Disposition: form-data; name="Upload"';
- for ( i = 0; i < bytes.length; i++ ) {
- postData.writeByte( bytes.charCodeAt(i) );
- }
- postData = LINEBREAK(postData);
- postData = LINEBREAK(postData);
- bytes = 'Submit Query';
- for ( i = 0; i < bytes.length; i++ ) {
- postData.writeByte( bytes.charCodeAt(i) );
- }
- postData = LINEBREAK(postData);
-
- return postData;
- }
-
- private function getFilePartHeader(postData:ByteArray, part:FilePart):ByteArray
- {
- var i:uint;
- var bytes:String;
-
- postData = BOUNDARY(postData);
- postData = LINEBREAK(postData);
- bytes = 'Content-Disposition: form-data; name="Filename"';
- for ( i = 0; i < bytes.length; i++ ) {
- postData.writeByte( bytes.charCodeAt(i) );
- }
- postData = LINEBREAK(postData);
- postData = LINEBREAK(postData);
- postData.writeUTFBytes(part.fileName);
- postData = LINEBREAK(postData);
-
- postData = BOUNDARY(postData);
- postData = LINEBREAK(postData);
- bytes = 'Content-Disposition: form-data; name="' + part.dataField + '"; filename="';
- for ( i = 0; i < bytes.length; i++ ) {
- postData.writeByte( bytes.charCodeAt(i) );
- }
- postData.writeUTFBytes(part.fileName);
- postData = QUOTATIONMARK(postData);
- postData = LINEBREAK(postData);
- bytes = 'Content-Type: ' + part.contentType;
- for ( i = 0; i < bytes.length; i++ ) {
- postData.writeByte( bytes.charCodeAt(i) );
- }
- postData = LINEBREAK(postData);
- postData = LINEBREAK(postData);
-
- return postData;
- }
-
- private function getFilePartData(postData:ByteArray, part:FilePart):ByteArray
- {
- postData.writeBytes(part.fileContent, 0, part.fileContent.length);
-
- return postData;
- }
-
- private function onProgress( event: ProgressEvent ): void
- {
- dispatchEvent( event );
- }
-
- private function onComplete( event: Event ): void
- {
- removeListener();
- dispatchEvent( event );
- }
-
- private function onIOError( event: IOErrorEvent ): void
- {
- removeListener();
- dispatchEvent( event );
- }
-
- private function onSecurityError( event: SecurityErrorEvent ): void
- {
- removeListener();
- dispatchEvent( event );
- }
-
- private function onHTTPStatus( event: HTTPStatusEvent ): void
- {
- dispatchEvent( event );
- }
-
- private function addListener(): void
- {
- _loader.addEventListener( Event.COMPLETE, onComplete, false, 0, false );
- _loader.addEventListener( ProgressEvent.PROGRESS, onProgress, false, 0, false );
- _loader.addEventListener( IOErrorEvent.IO_ERROR, onIOError, false, 0, false );
- _loader.addEventListener( HTTPStatusEvent.HTTP_STATUS, onHTTPStatus, false, 0, false );
- _loader.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, false );
- }
-
- private function removeListener(): void
- {
- _loader.removeEventListener( Event.COMPLETE, onComplete );
- _loader.removeEventListener( ProgressEvent.PROGRESS, onProgress );
- _loader.removeEventListener( IOErrorEvent.IO_ERROR, onIOError );
- _loader.removeEventListener( HTTPStatusEvent.HTTP_STATUS, onHTTPStatus );
- _loader.removeEventListener( SecurityErrorEvent.SECURITY_ERROR, onSecurityError );
- }
-
- private function BOUNDARY(p:ByteArray):ByteArray
- {
- var l:int = getBoundary().length;
- p = DOUBLEDASH(p);
- for (var i:int = 0; i < l; i++ ) {
- p.writeByte( _boundary.charCodeAt( i ) );
- }
- return p;
- }
-
- private function LINEBREAK(p:ByteArray):ByteArray
- {
- p.writeShort(0x0d0a);
- return p;
- }
-
- private function QUOTATIONMARK(p:ByteArray):ByteArray
- {
- p.writeByte(0x22);
- return p;
- }
-
- private function DOUBLEDASH(p:ByteArray):ByteArray
- {
- p.writeShort(0x2d2d);
- return p;
- }
-
- private function nextAsyncLoop():void
- {
- var fp:FilePart;
-
- if (asyncFilePointer < _fileNames.length) {
-
- fp = _files[_fileNames[asyncFilePointer]] as FilePart;
- _data = getFilePartHeader(_data, fp);
-
- asyncWriteTimeoutId = setTimeout(writeChunkLoop, 10, _data, fp.fileContent, 0);
-
- asyncFilePointer ++;
- } else {
- _data = closeFilePartsData(_data);
- _data = closeDataObject(_data);
-
- _prepared = true;
-
- dispatchEvent( new MultipartURLLoaderEvent(MultipartURLLoaderEvent.DATA_PREPARE_PROGRESS, totalFilesSize, totalFilesSize) );
- dispatchEvent( new MultipartURLLoaderEvent(MultipartURLLoaderEvent.DATA_PREPARE_COMPLETE) );
- }
- }
-
- private function writeChunkLoop(dest:ByteArray, data:ByteArray, p:uint = 0):void
- {
- var len:uint = Math.min(BLOCK_SIZE, data.length - p);
- dest.writeBytes(data, p, len);
-
- if (len < BLOCK_SIZE || p + len >= data.length) {
- // Finished writing file bytearray
- dest = LINEBREAK(dest);
- nextAsyncLoop();
- return;
- }
-
- p += len;
- writtenBytes += len;
- if ( writtenBytes % BLOCK_SIZE * 2 == 0 ) {
- dispatchEvent( new MultipartURLLoaderEvent(MultipartURLLoaderEvent.DATA_PREPARE_PROGRESS, writtenBytes, totalFilesSize) );
- }
-
- asyncWriteTimeoutId = setTimeout(writeChunkLoop, 10, dest, data, p);
- }
-
- }
-}
-
-internal class FilePart
-{
-
- public var fileContent:flash.utils.ByteArray;
- public var fileName:String;
- public var dataField:String;
- public var contentType:String;
-
- public function FilePart(fileContent:flash.utils.ByteArray, fileName:String, dataField:String = 'Filedata', contentType:String = 'application/octet-stream')
- {
- this.fileContent = fileContent;
- this.fileName = fileName;
- this.dataField = dataField;
- this.contentType = contentType;
- }
-
- public function dispose():void
- {
- fileContent = null;
- fileName = null;
- dataField = null;
- contentType = null;
- }
-} \ No newline at end of file
diff --git a/webcam/ru/inspirit/net/events/MultipartURLLoaderEvent.as b/webcam/ru/inspirit/net/events/MultipartURLLoaderEvent.as
deleted file mode 100644
index 2b90855..0000000
--- a/webcam/ru/inspirit/net/events/MultipartURLLoaderEvent.as
+++ /dev/null
@@ -1,27 +0,0 @@
-package ru.inspirit.net.events
-{
- import flash.events.Event;
-
- /**
- * MultipartURLLoader Event for async data prepare tracking
- * @author Eugene Zatepyakin
- */
- public class MultipartURLLoaderEvent extends Event
- {
- public static const DATA_PREPARE_PROGRESS:String = 'dataPrepareProgress';
- public static const DATA_PREPARE_COMPLETE:String = 'dataPrepareComplete';
-
- public var bytesWritten:uint = 0;
- public var bytesTotal:uint = 0;
-
- public function MultipartURLLoaderEvent(type:String, w:uint = 0, t:uint = 0)
- {
- super(type);
-
- bytesTotal = t;
- bytesWritten = w;
- }
-
- }
-
-} \ No newline at end of file