Flash components we've made, display-related utils.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@161 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color utility methods.
|
||||||
|
*
|
||||||
|
* See also mx.utils.ColorUtil.
|
||||||
|
*/
|
||||||
|
public class ColorUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Blend the two colors, either 50-50 or according to the ratio specified.
|
||||||
|
*/
|
||||||
|
public static function blend (
|
||||||
|
first :uint, second :uint, firstPerc :Number = 0.5) :uint
|
||||||
|
{
|
||||||
|
var secondPerc :Number = 1 - firstPerc;
|
||||||
|
|
||||||
|
var result :uint = 0;
|
||||||
|
for (var shift :int = 0; shift <= 16; shift += 8) {
|
||||||
|
var c1 :uint = (first >> shift) & 0xFF;
|
||||||
|
var c2 :uint = (second >> shift) & 0xFF;
|
||||||
|
result |= uint(Math.max(0, Math.min(255,
|
||||||
|
(c1 * firstPerc) + (c2 * secondPerc)))) << shift;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
import flash.display.DisplayObject;
|
||||||
|
import flash.display.DisplayObjectContainer;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
import flash.geom.Rectangle;
|
||||||
|
|
||||||
|
import mx.core.IRawChildrenContainer;
|
||||||
|
|
||||||
|
public class DisplayUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Call the specified function for the display object and all descendants.
|
||||||
|
*
|
||||||
|
* This is nearly exactly like mx.utils.DisplayUtil.walkDisplayObjects,
|
||||||
|
* except this method copes with security errors when examining a child.
|
||||||
|
*/
|
||||||
|
public static function applyToHierarchy (
|
||||||
|
disp :DisplayObject, callbackFunction :Function) :void
|
||||||
|
{
|
||||||
|
callbackFunction(disp);
|
||||||
|
|
||||||
|
if (disp is DisplayObjectContainer) {
|
||||||
|
// a little type-unsafety so that we don't have to write two blocks
|
||||||
|
var o :Object = (disp is IRawChildrenContainer) ?
|
||||||
|
IRawChildrenContainer(disp).rawChildren : disp;
|
||||||
|
var nn :int = int(o.numChildren);
|
||||||
|
for (var ii :int = 0; ii < nn; ii++) {
|
||||||
|
try {
|
||||||
|
disp = DisplayObject(o.getChildAt(ii));
|
||||||
|
} catch (err :SecurityError) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// and then we apply outside of the try/catch block so that
|
||||||
|
// we don't hide errors thrown by the callbackFunction.
|
||||||
|
applyToHierarchy(disp, callbackFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most reasonable position for the specified rectangle to
|
||||||
|
* be placed at so as to maximize its containment by the specified
|
||||||
|
* bounding rectangle while still placing it as near its original
|
||||||
|
* coordinates as possible.
|
||||||
|
*
|
||||||
|
* @param rect the rectangle to be positioned.
|
||||||
|
* @param bounds the containing rectangle.
|
||||||
|
*/
|
||||||
|
public static function fitRectInRect (
|
||||||
|
rect :Rectangle , bounds :Rectangle) :Point
|
||||||
|
{
|
||||||
|
// Guarantee that the right and bottom edges will be contained
|
||||||
|
// and do our best for the top and left edges.
|
||||||
|
var br :Point = bounds.bottomRight;
|
||||||
|
return new Point(
|
||||||
|
Math.min(br.x - rect.width, Math.max(rect.x, bounds.x)),
|
||||||
|
Math.min(br.y - rect.height, Math.max(rect.y, bounds.y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Position the specified rectangle within the bounds, avoiding
|
||||||
|
* any of the Rectangles in the avoid array, which may be destructively
|
||||||
|
* modified.
|
||||||
|
*
|
||||||
|
* @return true if the rectangle was successfully placed, given the
|
||||||
|
* constraints, or false if the positioning failed (the rectangle will
|
||||||
|
* be left at its original location.
|
||||||
|
*/
|
||||||
|
public static function positionRect (
|
||||||
|
r :Rectangle, bounds :Rectangle, avoid :Array) :Boolean
|
||||||
|
{
|
||||||
|
var origPos :Point = r.topLeft;
|
||||||
|
var pointSorter :Function = createPointSorter(origPos);
|
||||||
|
var possibles :Array = new Array();
|
||||||
|
// start things off with the passed-in point (adjusted to
|
||||||
|
// be inside the bounds, if needed)
|
||||||
|
possibles.push(fitRectInRect(r, bounds));
|
||||||
|
|
||||||
|
// keep track of area that doesn't generate new possibles
|
||||||
|
var dead :Array = new Array();
|
||||||
|
|
||||||
|
// Note: labeled breaks and continues are supposed to be legal,
|
||||||
|
// but they throw wacky runtime exceptions for me. So instead
|
||||||
|
// I'm throwing a boolean and using that to continue the while
|
||||||
|
/* CHECKPOSSIBLES: */ while (possibles.length > 0) {
|
||||||
|
try {
|
||||||
|
var p :Point = (possibles.shift() as Point);
|
||||||
|
r.x = p.x;
|
||||||
|
r.y = p.y;
|
||||||
|
|
||||||
|
// make sure the rectangle is in the view
|
||||||
|
if (!bounds.containsRect(r)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and not over a dead area
|
||||||
|
for each (var deadRect :Rectangle in dead) {
|
||||||
|
if (deadRect.intersects(r)) {
|
||||||
|
//continue CHECKPOSSIBLES;
|
||||||
|
throw true; // continue outer loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if it hits any rects we're trying to avoid
|
||||||
|
for (var ii :int = 0; ii < avoid.length; ii++) {
|
||||||
|
var avoidRect :Rectangle = (avoid[ii] as Rectangle);
|
||||||
|
if (avoidRect.intersects(r)) {
|
||||||
|
// remove it from the avoid list
|
||||||
|
avoid.splice(ii, 1);
|
||||||
|
// but add it to the dead list
|
||||||
|
dead.push(avoidRect);
|
||||||
|
|
||||||
|
// add 4 new possible points, each pushed in
|
||||||
|
// one direction
|
||||||
|
possibles.push(
|
||||||
|
new Point(avoidRect.x - r.width, r.y),
|
||||||
|
new Point(r.x, avoidRect.y - r.height),
|
||||||
|
new Point(avoidRect.x + avoidRect.width, r.y),
|
||||||
|
new Point(r.x, avoidRect.y + avoidRect.height));
|
||||||
|
|
||||||
|
// re-sort the list
|
||||||
|
possibles.sort(pointSorter);
|
||||||
|
//continue CHECKPOSSIBLES;
|
||||||
|
throw true; // continue outer loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hey! if we got here, then it worked!
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (continueWhile :Boolean) {
|
||||||
|
// simply catch the boolean and use it to continue inner loops
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we never found a match, move the rectangle back
|
||||||
|
r.x = origPos.x;
|
||||||
|
r.y = origPos.y;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a sort Function that can be used to compare Points in an
|
||||||
|
* Array according to their distance from the specified Point.
|
||||||
|
*
|
||||||
|
* Note: The function will always sort according to distance from the
|
||||||
|
* passed-in point, even if that point's coordinates change after
|
||||||
|
* the function is created.
|
||||||
|
*/
|
||||||
|
public static function createPointSorter (origin :Point) :Function
|
||||||
|
{
|
||||||
|
return function (p1 :Point, p2 :Point) :Number {
|
||||||
|
var dist1 :Number = Point.distance(origin, p1);
|
||||||
|
var dist2 :Number = Point.distance(origin, p2);
|
||||||
|
|
||||||
|
return (dist1 > dist2) ? 1 : ((dist1 < dist2) ? -1 : 0); // signum
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
import flash.events.Event;
|
||||||
|
|
||||||
|
import flash.text.TextField;
|
||||||
|
|
||||||
|
import flash.utils.getTimer; // function import
|
||||||
|
|
||||||
|
public class FPSDisplay extends TextField
|
||||||
|
{
|
||||||
|
public function FPSDisplay (framesToTrack :int = 150)
|
||||||
|
{
|
||||||
|
background = true;
|
||||||
|
text = String(Number.MIN_VALUE);
|
||||||
|
height = textHeight + 4;
|
||||||
|
|
||||||
|
_framesToTrack = framesToTrack;
|
||||||
|
|
||||||
|
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleEnterFrame (event :Event) :void
|
||||||
|
{
|
||||||
|
var curStamp :Number = getTimer();
|
||||||
|
_frameStamps.push(curStamp);
|
||||||
|
if (_frameStamps.length > _framesToTrack) {
|
||||||
|
_frameStamps.shift(); // forget the oldest timestamps
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstStamp :Number = Number(_frameStamps[0]);
|
||||||
|
var seconds :Number = (curStamp - firstStamp) / 1000;
|
||||||
|
// subtract one from the frames, since we're measuring the time
|
||||||
|
// elapsed over the frames (when there are two timestamps, that's
|
||||||
|
// the difference between 1 frame)
|
||||||
|
var frames :Number = _frameStamps.length - 1;
|
||||||
|
|
||||||
|
this.text = String(frames / seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Timestamps of past ENTER_FRAME events. */
|
||||||
|
protected var _frameStamps :Array = [];
|
||||||
|
|
||||||
|
/** The number of running frames we track. */
|
||||||
|
protected var _framesToTrack :int;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.display.DisplayObject;
|
||||||
|
import flash.display.Shape;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
import flash.geom.Rectangle;
|
||||||
|
|
||||||
|
import flash.text.TextField;
|
||||||
|
import flash.text.TextLineMetrics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image and Bitmap related utility functions.
|
||||||
|
*/
|
||||||
|
public class ImageUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a DisplayObject that will display an error message
|
||||||
|
* of the specified dimensions.
|
||||||
|
*/
|
||||||
|
public static function createErrorImage (width :int, height :int)
|
||||||
|
:DisplayObject
|
||||||
|
{
|
||||||
|
var shape :Shape = new Shape();
|
||||||
|
shape.graphics.beginBitmapFill(createErrorBitmap());
|
||||||
|
shape.graphics.drawRect(0, 0, width, height);
|
||||||
|
shape.graphics.endFill();
|
||||||
|
return shape;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Alternate implementation that creates an actual Bitmap object
|
||||||
|
var src :BitmapData = createErrorBitmap();
|
||||||
|
var data :BitmapData = new BitmapData(width, height, false);
|
||||||
|
data.draw(src);
|
||||||
|
var rect :Rectangle = new Rectangle(0, 0, src.width, src.height);
|
||||||
|
for (var xx :int = 0; xx < width; xx += src.width) {
|
||||||
|
for (var yy :int = 0; yy < height; yy += src.height) {
|
||||||
|
if (xx != 0 || yy != 0) {
|
||||||
|
data.copyPixels(data, rect, new Point(xx, yy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Bitmap(data);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a minimally-sized "error" BitmapData.
|
||||||
|
*/
|
||||||
|
public static function createErrorBitmap () :BitmapData
|
||||||
|
{
|
||||||
|
var txt :TextField = new TextField();
|
||||||
|
txt.text = "Error";
|
||||||
|
var metrics :TextLineMetrics = txt.getLineMetrics(0);
|
||||||
|
var data :BitmapData = new BitmapData(
|
||||||
|
metrics.width + ERROR_PADDING, metrics.height + ERROR_PADDING,
|
||||||
|
false, 0xFF4040);
|
||||||
|
data.draw(txt);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The amount to pad error messages by. */
|
||||||
|
private static const ERROR_PADDING :int = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,517 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
//import flash.display.Bitmap;
|
||||||
|
import flash.display.DisplayObject;
|
||||||
|
import flash.display.DisplayObjectContainer;
|
||||||
|
import flash.display.Loader;
|
||||||
|
import flash.display.LoaderInfo;
|
||||||
|
import flash.display.Shape;
|
||||||
|
import flash.display.Sprite;
|
||||||
|
|
||||||
|
import flash.errors.IOError;
|
||||||
|
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.events.EventDispatcher;
|
||||||
|
import flash.events.IEventDispatcher;
|
||||||
|
import flash.events.IOErrorEvent;
|
||||||
|
import flash.events.MouseEvent;
|
||||||
|
import flash.events.NetStatusEvent;
|
||||||
|
import flash.events.ProgressEvent;
|
||||||
|
import flash.events.SecurityErrorEvent;
|
||||||
|
import flash.events.StatusEvent;
|
||||||
|
import flash.events.TextEvent;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
import flash.text.TextField;
|
||||||
|
import flash.text.TextFieldAutoSize;
|
||||||
|
|
||||||
|
import flash.system.ApplicationDomain;
|
||||||
|
import flash.system.LoaderContext;
|
||||||
|
import flash.system.SecurityDomain;
|
||||||
|
|
||||||
|
import flash.net.URLRequest;
|
||||||
|
|
||||||
|
import com.threerings.util.StringUtil;
|
||||||
|
import com.threerings.util.ValueEvent;
|
||||||
|
import com.threerings.util.Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper class for all media that will be placed on the screen.
|
||||||
|
* Subject to change.
|
||||||
|
*/
|
||||||
|
public class MediaContainer extends Sprite
|
||||||
|
{
|
||||||
|
/** A ValueEvent we dispatch when our size is known.
|
||||||
|
* Value: [ width, height ]. */
|
||||||
|
public static const SIZE_KNOWN :String = "mediaSizeKnown";
|
||||||
|
|
||||||
|
/** A log instance that can be shared by sprites. */
|
||||||
|
protected static const log :Log = Log.getLog(MediaContainer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function MediaContainer (url :String = null)
|
||||||
|
{
|
||||||
|
if (url != null) {
|
||||||
|
setMedia(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnabled = false;
|
||||||
|
mouseChildren = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the media. If the media was loaded using a URL, this will
|
||||||
|
* likely be the Loader object holding the real media.
|
||||||
|
*/
|
||||||
|
public function getMedia () :DisplayObject
|
||||||
|
{
|
||||||
|
return _media;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the media to display.
|
||||||
|
*/
|
||||||
|
public function setMedia (url :String) :void
|
||||||
|
{
|
||||||
|
if (Util.equals(_url, url)) {
|
||||||
|
return; // no change
|
||||||
|
}
|
||||||
|
_url = url;
|
||||||
|
|
||||||
|
// shutdown any previous media
|
||||||
|
if (_media != null) {
|
||||||
|
shutdown(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the new media
|
||||||
|
if (StringUtil.endsWith(url.toLowerCase(), ".flv")) {
|
||||||
|
setupVideo(url);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setupSwfOrImage(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure our media as an instance of the specified class.
|
||||||
|
*/
|
||||||
|
public function setMediaClass (clazz :Class) :void
|
||||||
|
{
|
||||||
|
setMediaObject(new clazz() as DisplayObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure an already-instantiated DisplayObject as our media.
|
||||||
|
*/
|
||||||
|
public function setMediaObject (disp :DisplayObject) :void
|
||||||
|
{
|
||||||
|
_url = null;
|
||||||
|
if (_media != null) {
|
||||||
|
shutdown(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(disp);
|
||||||
|
_media = disp;
|
||||||
|
updateContentDimensions(disp.width, disp.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure this sprite to show a video.
|
||||||
|
*/
|
||||||
|
protected function setupVideo (url :String) :void
|
||||||
|
{
|
||||||
|
var vid :VideoDisplayer = new VideoDisplayer();
|
||||||
|
vid.addEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
|
||||||
|
vid.addEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
|
||||||
|
_media = vid;
|
||||||
|
addChild(vid);
|
||||||
|
vid.setup(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure this sprite to show an image or flash movie.
|
||||||
|
*/
|
||||||
|
protected function setupSwfOrImage (url :String) :void
|
||||||
|
{
|
||||||
|
startedLoading();
|
||||||
|
|
||||||
|
// create our loader and set up some event listeners
|
||||||
|
var loader :Loader = new Loader();
|
||||||
|
_media = loader;
|
||||||
|
var info :LoaderInfo = loader.contentLoaderInfo;
|
||||||
|
info.addEventListener(Event.COMPLETE, loadingComplete);
|
||||||
|
info.addEventListener(IOErrorEvent.IO_ERROR, loadError);
|
||||||
|
info.addEventListener(ProgressEvent.PROGRESS, loadProgress);
|
||||||
|
|
||||||
|
// create a mask to prevent the media from drawing out of bounds
|
||||||
|
if (getMaxContentWidth() < int.MAX_VALUE &&
|
||||||
|
getMaxContentHeight() < int.MAX_VALUE) {
|
||||||
|
configureMask(getMaxContentWidth(), getMaxContentHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
// start it loading, add it as a child
|
||||||
|
loader.load(new URLRequest(url), getContext(url));
|
||||||
|
addChild(loader);
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateContentDimensions(info.width, info.height);
|
||||||
|
} catch (err :Error) {
|
||||||
|
// an error is thrown trying to access these props before they're
|
||||||
|
// ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a 'broken image' to indicate there were troubles with
|
||||||
|
* loading the media.
|
||||||
|
*/
|
||||||
|
protected function setupBrokenImage (w :int = -1, h :int = -1) :void
|
||||||
|
{
|
||||||
|
if (w == -1) {
|
||||||
|
w = 100;
|
||||||
|
}
|
||||||
|
if (h == -1) {
|
||||||
|
h = 100;
|
||||||
|
}
|
||||||
|
setMediaObject(ImageUtil.createErrorImage(w, h));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the application domain being used by this media, or null if
|
||||||
|
* none or not applicable.
|
||||||
|
*/
|
||||||
|
public function getApplicationDomain () :ApplicationDomain
|
||||||
|
{
|
||||||
|
return (_media is Loader)
|
||||||
|
? (_media as Loader).contentLoaderInfo.applicationDomain
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unload the media we're displaying, clean up any resources.
|
||||||
|
*
|
||||||
|
* @param completely if true, we're going away and should stop
|
||||||
|
* everything. Otherwise, we're just loading up new media.
|
||||||
|
*/
|
||||||
|
public function shutdown (completely :Boolean = true) :void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// remove the mask
|
||||||
|
if (_media != null && _media.mask != null) {
|
||||||
|
removeChild(_media.mask);
|
||||||
|
_media.mask = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_media is Loader) {
|
||||||
|
var loader :Loader = (_media as Loader);
|
||||||
|
// remove any listeners
|
||||||
|
removeListeners(loader.contentLoaderInfo);
|
||||||
|
|
||||||
|
// dispose of media
|
||||||
|
try {
|
||||||
|
loader.close();
|
||||||
|
} catch (ioe :IOError) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
loader.unload();
|
||||||
|
|
||||||
|
removeChild(loader);
|
||||||
|
|
||||||
|
} else if (_media is VideoDisplayer) {
|
||||||
|
var vid :VideoDisplayer = (_media as VideoDisplayer);
|
||||||
|
vid.removeEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
|
||||||
|
vid.removeEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
|
||||||
|
vid.shutdown();
|
||||||
|
removeChild(vid);
|
||||||
|
|
||||||
|
} else if (_media != null) {
|
||||||
|
removeChild(_media);
|
||||||
|
}
|
||||||
|
} catch (ioe :IOError) {
|
||||||
|
log.warning("Error shutting down media: " + ioe);
|
||||||
|
log.logStackTrace(ioe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean everything up
|
||||||
|
_w = 0;
|
||||||
|
_h = 0;
|
||||||
|
_media = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the width of the content, bounded by the maximum.
|
||||||
|
*/
|
||||||
|
public function getContentWidth () :int
|
||||||
|
{
|
||||||
|
return Math.min(Math.abs(_w * getMediaScaleX()), getMaxContentWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the height of the content, bounded by the maximum.
|
||||||
|
*/
|
||||||
|
public function getContentHeight () :int
|
||||||
|
{
|
||||||
|
return Math.min(Math.abs(_h * getMediaScaleY()), getMaxContentHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum allowable width for our content.
|
||||||
|
*/
|
||||||
|
public function getMaxContentWidth () :int
|
||||||
|
{
|
||||||
|
return int.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum allowable height for our content.
|
||||||
|
*/
|
||||||
|
public function getMaxContentHeight () :int
|
||||||
|
{
|
||||||
|
return int.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the X scaling factor to use on the actual media.
|
||||||
|
*/
|
||||||
|
public function getMediaScaleX () :Number
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Y scaling factor to use on the actual media.
|
||||||
|
*/
|
||||||
|
public function getMediaScaleY () :Number
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by MediaWrapper as notification that its size has changed.
|
||||||
|
*/
|
||||||
|
public function containerDimensionsUpdated (newWidth :Number, newHeight :Number) :void
|
||||||
|
{
|
||||||
|
// do nothing in base MediaContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function toString () :String
|
||||||
|
{
|
||||||
|
return "MediaContainer[url=" + _url + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the LoaderContext that should be used to load the media
|
||||||
|
* at the specified url.
|
||||||
|
*/
|
||||||
|
protected function getContext (url :String) :LoaderContext
|
||||||
|
{
|
||||||
|
if (isImage(url)) {
|
||||||
|
// load images into our domain so that we can view their pixels
|
||||||
|
return new LoaderContext(true,
|
||||||
|
new ApplicationDomain(ApplicationDomain.currentDomain),
|
||||||
|
SecurityDomain.currentDomain);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// share nothing, trust nothing
|
||||||
|
return new LoaderContext(false, new ApplicationDomain(null), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the specified url represent an image?
|
||||||
|
*/
|
||||||
|
protected function isImage (url :String) :Boolean
|
||||||
|
{
|
||||||
|
// look at the last 4 characters in the lowercased url
|
||||||
|
switch (url.toLowerCase().slice(-4)) {
|
||||||
|
case ".png":
|
||||||
|
case ".jpg":
|
||||||
|
case ".gif":
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove our listeners from the LoaderInfo object.
|
||||||
|
*/
|
||||||
|
protected function removeListeners (info :LoaderInfo) :void
|
||||||
|
{
|
||||||
|
info.removeEventListener(Event.COMPLETE, loadingComplete);
|
||||||
|
info.removeEventListener(IOErrorEvent.IO_ERROR, loadError);
|
||||||
|
info.removeEventListener(ProgressEvent.PROGRESS, loadProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback to receive IO_ERROR events.
|
||||||
|
*/
|
||||||
|
protected function loadError (event :IOErrorEvent) :void
|
||||||
|
{
|
||||||
|
stoppedLoading();
|
||||||
|
setupBrokenImage(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback to receive PROGRESS events.
|
||||||
|
*/
|
||||||
|
protected function loadProgress (event :ProgressEvent) :void
|
||||||
|
{
|
||||||
|
updateLoadingProgress(event.bytesLoaded, event.bytesTotal);
|
||||||
|
var info :LoaderInfo = (event.target as LoaderInfo);
|
||||||
|
try {
|
||||||
|
updateContentDimensions(info.width, info.height);
|
||||||
|
} catch (err :Error) {
|
||||||
|
// an error is thrown trying to access these props before they're
|
||||||
|
// ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles video size known.
|
||||||
|
*/
|
||||||
|
protected function handleVideoSizeKnown (event :ValueEvent) :void
|
||||||
|
{
|
||||||
|
var args :Array = (event.value as Array);
|
||||||
|
updateContentDimensions(int(args[0]), int(args[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an error loading a video.
|
||||||
|
*/
|
||||||
|
protected function handleVideoError (event :ValueEvent) :void
|
||||||
|
{
|
||||||
|
log.warning("Error loading video [cause=" + event.value + "].");
|
||||||
|
stoppedLoading();
|
||||||
|
setupBrokenImage(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to receive COMPLETE events for swfs or images.
|
||||||
|
*/
|
||||||
|
protected function loadingComplete (event :Event) :void
|
||||||
|
{
|
||||||
|
var info :LoaderInfo = (event.target as LoaderInfo);
|
||||||
|
removeListeners(info);
|
||||||
|
|
||||||
|
// trace("Loading complete: " + info.url +
|
||||||
|
// ", childAllowsParent=" + info.childAllowsParent +
|
||||||
|
// ", parentAllowsChild=" + info.parentAllowsChild +
|
||||||
|
// ", sameDomain=" + info.sameDomain);
|
||||||
|
|
||||||
|
updateContentDimensions(info.width, info.height);
|
||||||
|
updateLoadingProgress(1, 1);
|
||||||
|
stoppedLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the mask for this object.
|
||||||
|
*/
|
||||||
|
protected function configureMask (ww :int, hh :int) :void
|
||||||
|
{
|
||||||
|
var mask :Shape;
|
||||||
|
if (_media.mask != null) {
|
||||||
|
mask = (_media.mask as Shape);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mask = new Shape();
|
||||||
|
// the mask must be added to the display list (which is wacky)
|
||||||
|
addChild(mask);
|
||||||
|
_media.mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask.graphics.clear();
|
||||||
|
mask.graphics.beginFill(0xFFFFFF);
|
||||||
|
mask.graphics.drawRect(0, 0, ww, hh);
|
||||||
|
mask.graphics.endFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during loading as we figure out how big the content we're
|
||||||
|
* loading is.
|
||||||
|
*/
|
||||||
|
protected function updateContentDimensions (ww :int, hh :int) :void
|
||||||
|
{
|
||||||
|
// update our saved size, and possibly notify our container
|
||||||
|
if (_w != ww || _h != hh) {
|
||||||
|
_w = ww;
|
||||||
|
_h = hh;
|
||||||
|
contentDimensionsUpdated();
|
||||||
|
// dispatch an event to any listeners
|
||||||
|
dispatchEvent(new ValueEvent(SIZE_KNOWN, [ ww, hh ]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we know the true size of the content.
|
||||||
|
*/
|
||||||
|
protected function contentDimensionsUpdated () :void
|
||||||
|
{
|
||||||
|
// nada, by default
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the graphics to indicate how much is loaded.
|
||||||
|
*/
|
||||||
|
protected function updateLoadingProgress (
|
||||||
|
soFar :Number, total :Number) :void
|
||||||
|
{
|
||||||
|
// nada, by default
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we've started loading new media. Will not be called
|
||||||
|
* for new media that does not require loading.
|
||||||
|
*/
|
||||||
|
protected function startedLoading () :void
|
||||||
|
{
|
||||||
|
// nada
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we've stopped loading, which may be as a result of
|
||||||
|
* completion, an error while loading, or early termination.
|
||||||
|
*/
|
||||||
|
protected function stoppedLoading () :void
|
||||||
|
{
|
||||||
|
// nada
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The unaltered URL of the content we're displaying. */
|
||||||
|
protected var _url :String;
|
||||||
|
|
||||||
|
/** The unscaled width of our content. */
|
||||||
|
protected var _w :int;
|
||||||
|
|
||||||
|
/** The unscaled height of our content. */
|
||||||
|
protected var _h :int;
|
||||||
|
|
||||||
|
/** Either a Loader or a VideoDisplay. */
|
||||||
|
protected var _media :DisplayObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
import flash.ui.ContextMenuItem;
|
||||||
|
import flash.events.ContextMenuEvent;
|
||||||
|
|
||||||
|
import com.threerings.util.CommandEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public class MenuUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a menu item that will submit a controller command when selected.
|
||||||
|
*/
|
||||||
|
public static function createControllerMenuItem (
|
||||||
|
caption :String, cmd :String, arg :Object = null,
|
||||||
|
separatorBefore :Boolean = false, enabled :Boolean = true,
|
||||||
|
visible :Boolean = true) :ContextMenuItem
|
||||||
|
{
|
||||||
|
var item :ContextMenuItem =
|
||||||
|
new ContextMenuItem(caption, separatorBefore, enabled, visible);
|
||||||
|
item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
|
||||||
|
function (event :ContextMenuEvent) :void {
|
||||||
|
CommandEvent.dispatch(event.mouseTarget, cmd, arg);
|
||||||
|
});
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.flash {
|
||||||
|
|
||||||
|
import flash.display.Sprite;
|
||||||
|
|
||||||
|
import flash.events.AsyncErrorEvent;
|
||||||
|
import flash.events.IOErrorEvent;
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.events.MouseEvent;
|
||||||
|
import flash.events.NetStatusEvent;
|
||||||
|
import flash.events.SecurityErrorEvent;
|
||||||
|
import flash.events.TimerEvent;
|
||||||
|
|
||||||
|
import flash.media.Video;
|
||||||
|
|
||||||
|
import flash.net.NetConnection;
|
||||||
|
import flash.net.NetStream;
|
||||||
|
|
||||||
|
import flash.utils.Timer;
|
||||||
|
|
||||||
|
import com.threerings.util.ValueEvent;
|
||||||
|
|
||||||
|
public class VideoDisplayer extends Sprite
|
||||||
|
{
|
||||||
|
/** A value event dispatched when the size of the video is known.
|
||||||
|
* Value: [ width, height ]. */
|
||||||
|
public static const SIZE_KNOWN :String = "videoDisplayerSizeKnown";
|
||||||
|
|
||||||
|
/** A value event sent when there's an error loading the video.
|
||||||
|
* Value: original error event. */
|
||||||
|
public static const VIDEO_ERROR :String = "videoDisplayerError";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a video displayer.
|
||||||
|
*/
|
||||||
|
public function VideoDisplayer ()
|
||||||
|
{
|
||||||
|
_vid = new Video();
|
||||||
|
addChild(_vid);
|
||||||
|
|
||||||
|
addEventListener(MouseEvent.ROLL_OVER, handleRollOver);
|
||||||
|
addEventListener(MouseEvent.ROLL_OUT, handleRollOut);
|
||||||
|
|
||||||
|
_videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck);
|
||||||
|
|
||||||
|
_pauser = new Sprite();
|
||||||
|
_pauser.addEventListener(MouseEvent.CLICK, handleClick);
|
||||||
|
redrawPauser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start playing a video!
|
||||||
|
*/
|
||||||
|
public function setup (url :String) :void
|
||||||
|
{
|
||||||
|
_netCon = new NetConnection();
|
||||||
|
_netCon.addEventListener(NetStatusEvent.NET_STATUS, handleNetStatus);
|
||||||
|
|
||||||
|
// error handlers
|
||||||
|
_netCon.addEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError);
|
||||||
|
_netCon.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
|
||||||
|
_netCon.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
|
||||||
|
|
||||||
|
_netCon.connect(null);
|
||||||
|
_netStream = new NetStream(_netCon);
|
||||||
|
// pass in refs to some of our protected methods
|
||||||
|
_netStream.client = {
|
||||||
|
onMetaData: metaDataReceived
|
||||||
|
};
|
||||||
|
_netStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
|
||||||
|
|
||||||
|
_vid.attachNetStream(_netStream);
|
||||||
|
_videoChecker.start();
|
||||||
|
_netStream.play(url);
|
||||||
|
_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop playing our video.
|
||||||
|
*/
|
||||||
|
public function shutdown () :void
|
||||||
|
{
|
||||||
|
_videoChecker.reset();
|
||||||
|
_vid.attachNetStream(null);
|
||||||
|
|
||||||
|
if (_netStream != null) {
|
||||||
|
_netStream.close();
|
||||||
|
_netStream.removeEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
|
||||||
|
_netStream = null;
|
||||||
|
}
|
||||||
|
if (_netCon != null) {
|
||||||
|
_netCon.close();
|
||||||
|
_netCon.removeEventListener(NetStatusEvent.NET_STATUS, handleNetStatus);
|
||||||
|
_netCon.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError);
|
||||||
|
_netCon.removeEventListener(IOErrorEvent.IO_ERROR, handleIOError);
|
||||||
|
_netCon.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
|
||||||
|
_netCon = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if we now know the dimensions of the video.
|
||||||
|
*/
|
||||||
|
protected function handleVideoCheck (event :TimerEvent) :void
|
||||||
|
{
|
||||||
|
if (_vid.videoWidth == 0 || _vid.videoHeight == 0) {
|
||||||
|
return; // not known yet!
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the checker timer
|
||||||
|
_videoChecker.stop();
|
||||||
|
|
||||||
|
// set up the width/height
|
||||||
|
_vid.width = _vid.videoWidth;
|
||||||
|
_vid.height = _vid.videoHeight;
|
||||||
|
redrawPauser();
|
||||||
|
|
||||||
|
// tell any interested parties
|
||||||
|
dispatchEvent(new ValueEvent(SIZE_KNOWN, [ _vid.videoWidth, _vid.videoHeight ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleRollOver (event :MouseEvent) :void
|
||||||
|
{
|
||||||
|
addChild(_pauser);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleRollOut (event :MouseEvent) :void
|
||||||
|
{
|
||||||
|
if (_pauser.parent) {
|
||||||
|
removeChild(_pauser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleClick (event :MouseEvent) :void
|
||||||
|
{
|
||||||
|
// the buck stops here!
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (_paused) {
|
||||||
|
_netStream.resume();
|
||||||
|
_paused = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_netStream.pause();
|
||||||
|
_paused = true;
|
||||||
|
}
|
||||||
|
redrawPauser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the pauser sprite, update it's location.
|
||||||
|
* This will become something artistic, etc.
|
||||||
|
*/
|
||||||
|
protected function redrawPauser () :void
|
||||||
|
{
|
||||||
|
with (_pauser.graphics) {
|
||||||
|
clear();
|
||||||
|
// draw a nice circle
|
||||||
|
beginFill(0x333333);
|
||||||
|
drawCircle(0, 0, 20);
|
||||||
|
endFill();
|
||||||
|
lineStyle(2, 0);
|
||||||
|
drawCircle(0, 0, 20);
|
||||||
|
|
||||||
|
if (_paused) {
|
||||||
|
lineStyle(0, 0, 0);
|
||||||
|
beginFill(0x00FF00);
|
||||||
|
moveTo(-4, -10);
|
||||||
|
lineTo(4, 0);
|
||||||
|
lineTo(-4, 10);
|
||||||
|
lineTo(-4, -10);
|
||||||
|
endFill();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lineStyle(2, 0x00FF00);
|
||||||
|
moveTo(-4, -10);
|
||||||
|
lineTo(-4, 10);
|
||||||
|
moveTo(4, -10);
|
||||||
|
lineTo(4, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and update the location
|
||||||
|
_pauser.x = _vid.width/2;
|
||||||
|
_pauser.y = _vid.height/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleNetStatus (event :NetStatusEvent) :void
|
||||||
|
{
|
||||||
|
var info :Object = event.info;
|
||||||
|
if ("error" == info.level) {
|
||||||
|
trace("NetStatus error: " + info.code);
|
||||||
|
redispatchError(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// else info.level == "status"
|
||||||
|
switch (info.code) {
|
||||||
|
case "NetConnection.Connect.Success":
|
||||||
|
case "NetConnection.Connect.Closed":
|
||||||
|
// these status events we ignore
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
trace("NetStatus status: " + info.code);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleStreamNetStatus (event :NetStatusEvent) :void
|
||||||
|
{
|
||||||
|
if (event.info.code == "NetStream.Play.Stop") {
|
||||||
|
_netStream.seek(0);
|
||||||
|
_netStream.pause();
|
||||||
|
_paused = true;
|
||||||
|
redrawPauser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleAsyncError (event :AsyncErrorEvent) :void
|
||||||
|
{
|
||||||
|
trace("AsyncError: " + event);
|
||||||
|
redispatchError(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleIOError (event :IOErrorEvent) :void
|
||||||
|
{
|
||||||
|
trace("IOError: " + event);
|
||||||
|
redispatchError(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleSecurityError (event :SecurityErrorEvent) :void
|
||||||
|
{
|
||||||
|
trace("SecurityError: " + event);
|
||||||
|
redispatchError(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redispatch some error we received to our listeners.
|
||||||
|
*/
|
||||||
|
protected function redispatchError (event :Event) :void
|
||||||
|
{
|
||||||
|
dispatchEvent(new ValueEvent(VIDEO_ERROR, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when metadata (if any) is found in the video stream.
|
||||||
|
*/
|
||||||
|
protected function metaDataReceived (obj :Object) :void
|
||||||
|
{
|
||||||
|
trace("Got video metadata:");
|
||||||
|
for (var n :String in obj) {
|
||||||
|
trace(" " + n + ": " + obj[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected var _vid :Video;
|
||||||
|
|
||||||
|
protected var _paused :Boolean = false;
|
||||||
|
|
||||||
|
protected var _netCon :NetConnection;
|
||||||
|
|
||||||
|
protected var _netStream :NetStream;
|
||||||
|
|
||||||
|
protected var _pauser :Sprite;
|
||||||
|
|
||||||
|
/** Checks the video every 100ms to see if the dimensions are now know.
|
||||||
|
* Yes, this is how to do it. We could trigger on ENTER_FRAME, but then
|
||||||
|
* we may not know the dimensions unless we're added on the display list,
|
||||||
|
* and we want this to work in the general case. */
|
||||||
|
protected var _videoChecker :Timer = new Timer(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user