Skip to content

Commit

Permalink
Merge branch 'release/0.9.8'
Browse files Browse the repository at this point in the history
* release/0.9.8:
  New swc
  Cleanup flexLibProperties
  New version with Audio support
  Adding support for HTML5 Audio
  Sync the video event loop with the ENTER_FRAME
  Fixes unbindUnPress where for touch screens the events weren't properly removed
  Added fix for ie, that sometimes does not know the size of images. Now we use naturalWidth when supported (all modern browsers) and we use append/remove element for older browsers.
  - Added support in the Loader for the event FAR_ENOUGH, which corresponds to   the moment a loaded asset is ready to use but not yet fully loaded. Fully   loaded still corresponds to COMPLETE. - Fixed the duration in the  animator, the duration is now independent of the actual rendered frame rate.
  • Loading branch information
humboldtjs committed Apr 24, 2014
2 parents 1b349e2 + 666541a commit f928185
Show file tree
Hide file tree
Showing 10 changed files with 783 additions and 389 deletions.
1 change: 0 additions & 1 deletion .flexLibProperties
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexLibProperties includeAllClasses="true" useMultiPlatformConfig="false" version="3">
<includeClasses>
<classEntry path="com.humboldtjs.system.OperatingSystem"/>
</includeClasses>
<includeResources/>
<namespaceManifests/>
Expand Down
Binary file modified bin/HumboldtJSLibrary.swc
Binary file not shown.
291 changes: 291 additions & 0 deletions src/com/humboldtjs/audio/Audio.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
/*
* HumboldtJSLibrary
* http://humboldtjs.com/
*
* Copyright (c) 2012 Daniël Haveman
* Licensed under the MIT license
* http://humboldtjs.com/license.html
*/
package com.humboldtjs.audio
{
import com.humboldtjs.display.DisplayObject;
import com.humboldtjs.display.ISrcDisplayObject;
import com.humboldtjs.display.Stage;
import com.humboldtjs.events.DataEvent;
import com.humboldtjs.events.HJSEvent;
import com.humboldtjs.system.Capabilities;
import com.humboldtjs.system.HtmlUtils;
import com.humboldtjs.system.OperatingSystem;

import dom.document;
import dom.domobjects.Event;
import dom.window;

/**
* A simple Audio class which deals with some of the weird issues with
* HTML5 Audio. This already supports a few different platforms and situations
* however you still have to determine whether to load an MP4 or WEBM
* using the Capabilities class first.
*
* Note there are still numerous issues with various mobile browsers which
* have varying degrees of support for HTML5.
*/
public class Audio extends DisplayObject implements ISrcDisplayObject
{
public static const AUDIO_MP3:String = "mp3";

public static const EVENT_ENDED:String = "ended";
public static const EVENT_TIME_CHANGED:String = "timechanged";

protected var mHasAudio:Boolean = false;
protected var mLoopRunning:Boolean = false;
protected var mSrc:String = "";

protected var mEnded:Boolean = false;
protected var mPaused:Boolean = true;
protected var mCurrentTime:Number = 0;
protected var mTimer:int = -1;
protected var mTries:int = 0;

/**
* The duration
*/
public function getDuration():Number { return mElement.duration; }

/**
* The current playhead time
*/
public function getCurrentTime():Number { return mElement.currentTime; }
/**
* The current playhead time
*/
public function setCurrentTime(value:Number):void
{
// Work around a bug on iPad where setting currentTime to 0 makes
// later calls to currentTime stop working. This also prevents the
// ended event being triggered.
// @see http://stackoverflow.com/questions/3874070/missing-html5-video-ended-event-on-ipad
if (value == 0)
value = 0.01;
mElement.currentTime = value;
}

/**
* The source URL of the audio.
*/
public function getSrc():String { return mSrc; }
/**
* The source URL of the audio.
*/
public function setSrc(value:String):void
{
if (mSrc != value) {
mSrc = value;
mHasAudio = false;
mElement.preload = true;
mElement.autoplay = false;
mElement.controls = false;
pause();
mElement.src = value;

mTries = 4;
HtmlUtils.addHtmlEventListener(mElement, "load", onLoadedFarEnough);

if (Capabilities.getOs() == OperatingSystem.IOS || Capabilities.getOs() == OperatingSystem.ANDROID) {
play();
window.setTimeout(pause, 1);
}

clearTimer();

onLoadComplete();
}
}

/**
* Whether the audio is loaded
*/
public function getHasAudio():Boolean { return mHasAudio; }

/**
* @constructor
*/
public function Audio()
{
super();

document.body.appendChild(mElement);
}

override protected function initializeElement():void
{
mElement = document.createElement("audio");
}

public function dispose():void
{
mHasAudio = false;

// Clear the src attribute to make sure the audio is garbage collected.
if (mElement != null) mElement.src = "";
clearTimer();
removeHtmlEventListeners();
}

/**
* Return a copy of the Audio with the same contents
*/
public function clone():ISrcDisplayObject
{
var theAudio:Audio = new Audio();
theAudio.setSrc(getSrc());

return theAudio;
}

/**
* Start video playback
*/
public function play():void
{
mElement.play();
mPaused = false;
}

/**
* Pause video playback
*/
public function pause():void
{
mElement.pause();
mPaused = true;
}

/**
* Removes html eventlisteners.
*/
protected function removeHtmlEventListeners():void
{
HtmlUtils.removeHtmlEventListener(mElement, "load", onLoadedFarEnough);
}

/**
* Removes html eventlisteners.
*/
protected function clearTimer():void
{
if (mTimer != -1) {
window.clearTimeout(mTimer);
mTimer = -1;
}
}

/**
* Handle when loading of the video is complete
*/
protected function onLoadComplete():void
{
mTimer = -1;

// stop trying to load if we don't have a source
if (mSrc == "")
return;

// If NETWORK_NO_SOURCE it means loading failed
if (mElement.error != null) {
onLoadError();
return;
}

// Also allow networkState 2; 'loading', as '1' may not always be set e.g. in chrome.
// TODO: see if there is another way to signal that all data has been loaded.
// Note: in principle we should also dispatch 'LoadedFarEnough' which is when readyState == 4.
if (mElement.duration == 0 ||
isNaN(mElement.duration) ||
!((mElement.readyState == 4 ||
mElement.readyState == 3) &&
(mElement.networkState == 1 ||
mElement.networkState == 2))) {
mTimer = window.setTimeout(onLoadComplete, 100);
return;
}

if (mElement.readyState !== 4 && mTries > 0) {
mTries--;
mTimer = window.setTimeout(onLoadComplete, 50);
return;
}

removeHtmlEventListeners();

// Allready set complete
if (mHasAudio) return;

// Set the playhead time to the start of the video
setCurrentTime(0);

// Video has been loaded
mHasAudio = true;

// When the video has loaded we'll startup an event loop that will
// notify any listeners of TIME_CHANGED events. This can then be
// used for example to create custom player controls
if (!mLoopRunning) {
mLoopRunning = true;
Stage.getInstance().addEventListener(HJSEvent.ENTER_FRAME, onEventLoop);
}

// And we're done!
dispatchEvent(new HJSEvent(HJSEvent.COMPLETE));
}

/**
* onLoadedFarEnough events on iOS devices, wait for these events
* before setting complete in order to not show QT logo
* @param aEvent
*
*/
protected function onLoadedFarEnough(aEvent:Event):void
{
removeHtmlEventListeners();

onLoadComplete();
}

/**
* Called when loading threw an error or was aborted for some reason
*/
protected function onLoadError():void
{
dispatchEvent(new HJSEvent(HJSEvent.IO_ERROR));
}

/**
* An eventloop which notifies listeners of changes in the timestamp of
* the video or when playback has ended (when the end of the video has
* been reached).
*/
protected function onEventLoop(aEvent:HJSEvent):void
{
// If we don't have a video anymore then stop the loop
if (mSrc == "" || mSrc == null || !mHasAudio) {
Stage.getInstance().removeEventListener(HJSEvent.ENTER_FRAME, onEventLoop);
mLoopRunning = false;
return;
}

// If the playback has ended, send an event
if (mEnded != mElement.ended) {
mEnded = mElement.ended;
if (mEnded)
dispatchEvent(new HJSEvent(EVENT_ENDED));
}

// If the currentTime has changed then send an event
if (mCurrentTime != mElement.currentTime) {
mCurrentTime = Math.round(mElement.currentTime * 25) / 25;
dispatchEvent(new DataEvent(EVENT_TIME_CHANGED, mCurrentTime));
}
}
}
}
31 changes: 24 additions & 7 deletions src/com/humboldtjs/display/Bitmap.as
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ package com.humboldtjs.display
public function clone():ISrcDisplayObject
{
var theBitmap:Bitmap = new Bitmap();
theBitmap.setSrc(getSrc());
theBitmap.mUnscaledWidth = mUnscaledWidth;
theBitmap.mUnscaledHeight = mUnscaledHeight;
theBitmap.setSrc(getSrc());

return theBitmap;
}
Expand All @@ -83,12 +83,29 @@ package com.humboldtjs.display
*/
protected function onLoadComplete():void
{
document.body.appendChild(mElement);
if (mUnscaledWidth == 0)
mUnscaledWidth = Convert.toInt(mElement.width.toString());
if (mUnscaledHeight == 0)
mUnscaledHeight = Convert.toInt(mElement.height.toString());
document.body.removeChild(mElement);
if (mElement["naturalWidth"]) {

mUnscaledWidth = mElement.naturalWidth;
mUnscaledHeight = mElement.naturalHeight;

} else {

// When IE has failed loading this image once before it
// will use a 28x30 image-not-found-placeholder. This is
// workaround for a bug where when requesting unscaled
// size it will use this 28x30 from the cache instead.
// Setting style to auto forces recalculation.
mElement.style.width = "auto";
mElement.style.height = "auto";

document.body.appendChild(mElement);
if (mUnscaledWidth == 0)
mUnscaledWidth = Convert.toInt(mElement.width.toString());
if (mUnscaledHeight == 0)
mUnscaledHeight = Convert.toInt(mElement.height.toString());

document.body.removeChild(mElement);
}

if (mParent)
mParent["mElement"].appendChild(mElement);
Expand Down
Loading

0 comments on commit f928185

Please sign in to comment.