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 {
|
||||
|
||||
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;
|
||||
|
||||
@@ -72,19 +73,36 @@ public class PlaceViewUtil
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayUtil.applyToHierarchy(root as DisplayObject,
|
||||
function (disp :DisplayObject) :void {
|
||||
if (disp is PlaceView) {
|
||||
try {
|
||||
(root as PlaceView)[funct](plobj);
|
||||
} catch (e :Error) {
|
||||
var log :Log = Log.getLog(PlaceViewUtil);
|
||||
log.warning("Component choked on " + funct + "() " +
|
||||
"[component=" + root + ", plobj=" + plobj + "].");
|
||||
log.logStackTrace(e);
|
||||
}
|
||||
// much of the code in here is adapted from
|
||||
// com.threerings.flash.DisplayUtil, which we cannot access because
|
||||
// it's in the nenya package. So sad, too bad.
|
||||
|
||||
if (root is PlaceView) {
|
||||
try {
|
||||
(root as PlaceView)[funct](plobj);
|
||||
} catch (e :Error) {
|
||||
var log :Log = Log.getLog(PlaceViewUtil);
|
||||
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