Moved flash display-specific stuff to nenya: com.threerings.flash
(to match the com.threerings.flex). Let's still put general-purpose flash stuff here in com.threerings.util. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4613 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -22,8 +22,9 @@
|
|||||||
package com.threerings.crowd.client {
|
package com.threerings.crowd.client {
|
||||||
|
|
||||||
import flash.display.DisplayObject;
|
import flash.display.DisplayObject;
|
||||||
|
import flash.display.DisplayObjectContainer;
|
||||||
|
|
||||||
import com.threerings.util.DisplayUtil;
|
import mx.core.IRawChildrenContainer; // a simple interface from flex
|
||||||
|
|
||||||
import com.threerings.crowd.data.PlaceObject;
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
|
|
||||||
@@ -72,19 +73,36 @@ public class PlaceViewUtil
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayUtil.applyToHierarchy(root as DisplayObject,
|
// much of the code in here is adapted from
|
||||||
function (disp :DisplayObject) :void {
|
// com.threerings.flash.DisplayUtil, which we cannot access because
|
||||||
if (disp is PlaceView) {
|
// it's in the nenya package. So sad, too bad.
|
||||||
try {
|
|
||||||
(root as PlaceView)[funct](plobj);
|
if (root is PlaceView) {
|
||||||
} catch (e :Error) {
|
try {
|
||||||
var log :Log = Log.getLog(PlaceViewUtil);
|
(root as PlaceView)[funct](plobj);
|
||||||
log.warning("Component choked on " + funct + "() " +
|
} catch (e :Error) {
|
||||||
"[component=" + root + ", plobj=" + plobj + "].");
|
var log :Log = Log.getLog(PlaceViewUtil);
|
||||||
log.logStackTrace(e);
|
log.warning("Component choked on " + funct + "() " +
|
||||||
}
|
"[component=" + root + ", plobj=" + plobj + "].");
|
||||||
|
log.logStackTrace(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root is DisplayObjectContainer) {
|
||||||
|
// a little type-unsafety so that we don't have to write two blocks
|
||||||
|
var o :Object= (root is IRawChildrenContainer) ?
|
||||||
|
IRawChildrenContainer(root).rawChildren : root;
|
||||||
|
var nn :int = int(o.numChildren);
|
||||||
|
for (var ii :int = 0; ii < nn; ii++) {
|
||||||
|
try {
|
||||||
|
root = o.getChildAt(ii);
|
||||||
|
} catch (err :SecurityError) {
|
||||||
|
// don't try dispatching to children we cannot access!
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
});
|
dispatch(root, plobj, funct);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
||||||
// http://www.threerings.net/code/narya/
|
|
||||||
//
|
|
||||||
// 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.media.image {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
||||||
// http://www.threerings.net/code/narya/
|
|
||||||
//
|
|
||||||
// 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.util {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
||||||
// http://www.threerings.net/code/narya/
|
|
||||||
//
|
|
||||||
// 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.util {
|
|
||||||
|
|
||||||
import flash.display.DisplayObject;
|
|
||||||
import flash.display.DisplayObjectContainer;
|
|
||||||
|
|
||||||
import flash.geom.Point;
|
|
||||||
import flash.geom.Rectangle;
|
|
||||||
|
|
||||||
import mx.core.IChildList;
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
||||||
// http://www.threerings.net/code/narya/
|
|
||||||
//
|
|
||||||
// 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.util {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,517 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
||||||
// http://www.threerings.net/code/narya/
|
|
||||||
//
|
|
||||||
// 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.util {
|
|
||||||
|
|
||||||
//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.media.image.ImageUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
||||||
// http://www.threerings.net/code/narya/
|
|
||||||
//
|
|
||||||
// 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.util {
|
|
||||||
|
|
||||||
import flash.ui.ContextMenuItem;
|
|
||||||
import flash.events.ContextMenuEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
package com.threerings.util { // TODO: Move.
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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