This commit is contained in:
magnolia-fan 2011-10-05 21:15:57 +04:00
parent 03ca130524
commit 49147fd80a
22 changed files with 5558 additions and 0 deletions

View File

@ -0,0 +1,31 @@
jPlayer : HTML5 Audio & Video for jQuery
http://www.jplayer.org/
What is jPlayer?
jPlayer is a jQuery plugin that allows you to:
* play and control media files in your webpage
* create and style a media player using just HTML and CSS
* add audio and video to your jQuery projects
* support more devices using HTML5
* support older browsers using a Flash Fallback
* control media on your website using a JavaScript API
jPlayer supports:
* HTML5: mp3, m4a (AAC), m4v (H.264), ogv*, oga*, wav*, webm*
* Flash: mp3, m4a (AAC), m4v (H.264)
(*) Optional counterpart formats to increase HTML5 x-browser support.
Dual licensed under the MIT and GPL licenses.
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/copyleft/gpl.html
Quick Start Guide:
http://www.jplayer.org/latest/quick-start-guide/
Developer Guide and API Reference:
http://www.jplayer.org/latest/developer-guide/
Author: Mark J Panaghiston

View File

@ -0,0 +1,415 @@
/*
* jPlayer Plugin for jQuery JavaScript Library
* http://www.happyworm.com/jquery/jplayer
*
* Copyright (c) 2009 - 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Mark J Panaghiston
* Version: 2.1.0
* Date: 1st September 2011
*
* FlashVars expected: (AS3 property of: loaderInfo.parameters)
* id: (URL Encoded: String) Id of jPlayer instance
* vol: (Number) Sets the initial volume
* muted: (Boolean in a String) Sets the initial muted state
* jQuery: (URL Encoded: String) Sets the jQuery var name. Used with: someVar = jQuery.noConflict(true);
*
* Compiled using: Adobe Flex Compiler (mxmlc) Version 4.5.1 build 21328
*/
package {
import flash.system.Security;
import flash.external.ExternalInterface;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.events.KeyboardEvent;
import flash.display.Sprite;
import happyworm.jPlayer.*;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;
import flash.net.URLRequest;
import flash.net.navigateToURL;
public class Jplayer extends Sprite {
private var jQuery:String;
private var sentNumberFractionDigits:uint = 2;
public var commonStatus:JplayerStatus = new JplayerStatus(); // Used for inital ready event so volume is correct.
private var myInitTimer:Timer = new Timer(100, 0);
private var myMp3Player:JplayerMp3;
private var myMp4Player:JplayerMp4;
private var isMp3:Boolean = false;
private var isVideo:Boolean = false;
private var txLog:TextField;
private var debug:Boolean = false; // Set debug to false for release compile!
public function Jplayer() {
flash.system.Security.allowDomain("*");
jQuery = loaderInfo.parameters.jQuery + "('#" + loaderInfo.parameters.id + "').jPlayer";
commonStatus.volume = Number(loaderInfo.parameters.vol);
commonStatus.muted = loaderInfo.parameters.muted == "true";
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, resizeHandler);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
var initialVolume:Number = commonStatus.volume;
if(commonStatus.muted) {
initialVolume = 0;
}
myMp3Player = new JplayerMp3(initialVolume);
addChild(myMp3Player);
myMp4Player = new JplayerMp4(initialVolume);
addChild(myMp4Player);
setupListeners(!isMp3, isMp3); // Set up the listeners to the default isMp3 state.
// The ContextMenu only partially works. The menu select events never occur.
// Investigated and it is something to do with the way jPlayer inserts the Flash on the page.
// A simple test inserting the Jplayer.swf on a page using: 1) SWFObject 2.2 works. 2) AC_FL_RunContent() works.
// jPlayer Flash insertion is based on SWFObject 2.2 and the resaon behind this failure is not clear. The Flash insertion HTML on the page looks similar.
var myContextMenu:ContextMenu = new ContextMenu();
myContextMenu.hideBuiltInItems();
var menuItem_jPlayer:ContextMenuItem = new ContextMenuItem("jPlayer " + JplayerStatus.VERSION);
var menuItem_happyworm:ContextMenuItem = new ContextMenuItem("© 2009-2011 Happyworm Ltd", true);
menuItem_jPlayer.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuSelectHandler_jPlayer);
menuItem_happyworm.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuSelectHandler_happyworm);
myContextMenu.customItems.push(menuItem_jPlayer, menuItem_happyworm);
contextMenu = myContextMenu;
// Log console for dev compile option: debug
if(debug) {
txLog = new TextField();
txLog.x = 5;
txLog.y = 5;
txLog.width = 540;
txLog.height = 390;
txLog.border = true;
txLog.background = true;
txLog.backgroundColor = 0xEEEEFF;
txLog.multiline = true;
txLog.text = "jPlayer " + JplayerStatus.VERSION;
txLog.visible = false;
this.addChild(txLog);
this.stage.addEventListener(KeyboardEvent.KEY_UP, keyboardHandler);
myMp3Player.addEventListener(JplayerEvent.DEBUG_MSG, debugMsgHandler);
myMp4Player.addEventListener(JplayerEvent.DEBUG_MSG, debugMsgHandler);
}
// Delay init() because Firefox 3.5.7+ developed a bug with local testing in Firebug.
myInitTimer.addEventListener(TimerEvent.TIMER, init);
myInitTimer.start();
}
private function init(e:TimerEvent):void {
myInitTimer.stop();
if(ExternalInterface.available) {
ExternalInterface.addCallback("fl_setAudio_mp3", fl_setAudio_mp3);
ExternalInterface.addCallback("fl_setAudio_m4a", fl_setAudio_m4a);
ExternalInterface.addCallback("fl_setVideo_m4v", fl_setVideo_m4v);
ExternalInterface.addCallback("fl_clearMedia", fl_clearMedia);
ExternalInterface.addCallback("fl_load", fl_load);
ExternalInterface.addCallback("fl_play", fl_play);
ExternalInterface.addCallback("fl_pause", fl_pause);
ExternalInterface.addCallback("fl_play_head", fl_play_head);
ExternalInterface.addCallback("fl_volume", fl_volume);
ExternalInterface.addCallback("fl_mute", fl_mute);
ExternalInterface.call(jQuery, "jPlayerFlashEvent", JplayerEvent.JPLAYER_READY, extractStatusData(commonStatus)); // See JplayerStatus() class for version number.
}
}
private function setupListeners(oldMP3:Boolean, newMP3:Boolean):void {
if(oldMP3 != newMP3) {
if(newMP3) {
listenToMp3(true);
listenToMp4(false);
} else {
listenToMp3(false);
listenToMp4(true);
}
}
}
private function listenToMp3(active:Boolean):void {
if(active) {
myMp3Player.addEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
myMp3Player.addEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
} else {
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
}
}
private function listenToMp4(active:Boolean):void {
if(active) {
myMp4Player.addEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
myMp4Player.addEventListener(JplayerEvent.JPLAYER_LOADEDMETADATA, jPlayerMetaDataHandler); // Note the unique handler
} else {
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_LOADEDMETADATA, jPlayerMetaDataHandler); // Note the unique handler
}
}
private function fl_setAudio_mp3(src:String):Boolean {
if (src != null) {
log("fl_setAudio_mp3: "+src);
setupListeners(isMp3, true);
isMp3 = true;
isVideo = false;
myMp4Player.clearFile();
myMp3Player.setFile(src);
return true;
} else {
log("fl_setAudio_mp3: null");
return false;
}
}
private function fl_setAudio_m4a(src:String):Boolean {
if (src != null) {
log("fl_setAudio_m4a: "+src);
setupListeners(isMp3, false);
isMp3 = false;
isVideo = false;
myMp3Player.clearFile();
myMp4Player.setFile(src);
return true;
} else {
log("fl_setAudio_m4a: null");
return false;
}
}
private function fl_setVideo_m4v(src:String):Boolean {
if (src != null) {
log("fl_setVideo_m4v: "+src);
setupListeners(isMp3, false);
isMp3 = false;
isVideo = true;
myMp3Player.clearFile();
myMp4Player.setFile(src);
return true;
} else {
log("fl_setVideo_m4v: null");
return false;
}
}
private function fl_clearMedia():void {
log("clearMedia.");
myMp3Player.clearFile();
myMp4Player.clearFile();
}
private function fl_load():Boolean {
log("load.");
if(isMp3) {
return myMp3Player.load();
} else {
return myMp4Player.load();
}
}
private function fl_play(time:Number = NaN):Boolean {
log("play: time = " + time);
if(isMp3) {
return myMp3Player.play(time * 1000); // Flash uses milliseconds
} else {
return myMp4Player.play(time * 1000); // Flash uses milliseconds
}
}
private function fl_pause(time:Number = NaN):Boolean {
log("pause: time = " + time);
if(isMp3) {
return myMp3Player.pause(time * 1000); // Flash uses milliseconds
} else {
return myMp4Player.pause(time * 1000); // Flash uses milliseconds
}
}
private function fl_play_head(percent:Number):Boolean {
log("play_head: "+percent+"%");
if(isMp3) {
return myMp3Player.playHead(percent);
} else {
return myMp4Player.playHead(percent);
}
}
private function fl_volume(v:Number):void {
log("volume: "+v);
commonStatus.volume = v;
if(!commonStatus.muted) {
myMp3Player.setVolume(v);
myMp4Player.setVolume(v);
}
}
private function fl_mute(mute:Boolean):void {
log("mute: "+mute);
commonStatus.muted = mute;
if(mute) {
myMp3Player.setVolume(0);
myMp4Player.setVolume(0);
} else {
myMp3Player.setVolume(commonStatus.volume);
myMp4Player.setVolume(commonStatus.volume);
}
}
private function jPlayerFlashEvent(e:JplayerEvent):void {
log("jPlayer Flash Event: " + e.type + ": " + e.target);
if(ExternalInterface.available) {
ExternalInterface.call(jQuery, "jPlayerFlashEvent", e.type, extractStatusData(e.data));
}
}
private function extractStatusData(data:JplayerStatus):Object {
var myStatus:Object = {
version: JplayerStatus.VERSION,
src: data.src,
paused: !data.isPlaying, // Changing this name requires inverting all assignments and conditional statements.
srcSet: data.srcSet,
seekPercent: data.seekPercent,
currentPercentRelative: data.currentPercentRelative,
currentPercentAbsolute: data.currentPercentAbsolute,
currentTime: data.currentTime / 1000, // JavaScript uses seconds
duration: data.duration / 1000, // JavaScript uses seconds
volume: commonStatus.volume,
muted: commonStatus.muted
};
log("extractStatusData: sp="+myStatus.seekPercent+" cpr="+myStatus.currentPercentRelative+" cpa="+myStatus.currentPercentAbsolute+" ct="+myStatus.currentTime+" d="+myStatus.duration);
return myStatus;
}
private function jPlayerMetaDataHandler(e:JplayerEvent):void {
log("jPlayerMetaDataHandler:" + e.target);
if(ExternalInterface.available) {
resizeHandler(new Event(Event.RESIZE));
ExternalInterface.call(jQuery, "jPlayerFlashEvent", e.type, extractStatusData(e.data));
}
}
private function resizeHandler(e:Event):void {
log("resizeHandler: stageWidth = " + stage.stageWidth + " | stageHeight = " + stage.stageHeight);
var mediaX:Number = 0;
var mediaY:Number = 0;
var mediaWidth:Number = 0;
var mediaHeight:Number = 0;
if(stage.stageWidth > 0 && stage.stageHeight > 0 && myMp4Player.myVideo.width > 0 && myMp4Player.myVideo.height > 0) {
var aspectRatioStage:Number = stage.stageWidth / stage.stageHeight;
var aspectRatioVideo:Number = myMp4Player.myVideo.width / myMp4Player.myVideo.height;
if(aspectRatioStage < aspectRatioVideo) {
mediaWidth = stage.stageWidth;
mediaHeight = stage.stageWidth / aspectRatioVideo;
mediaX = 0;
mediaY = (stage.stageHeight - mediaHeight) / 2;
} else {
mediaWidth = stage.stageHeight * aspectRatioVideo;
mediaHeight = stage.stageHeight;
mediaX = (stage.stageWidth - mediaWidth) / 2;
mediaY = 0;
}
resizeEntity(myMp4Player, mediaX, mediaY, mediaWidth, mediaHeight);
}
if(debug && stage.stageWidth > 20 && stage.stageHeight > 20) {
txLog.width = stage.stageWidth - 10;
txLog.height = stage.stageHeight - 10;
}
}
private function resizeEntity(entity:Sprite, mediaX:Number, mediaY:Number, mediaWidth:Number, mediaHeight:Number):void {
entity.x = mediaX;
entity.y = mediaY;
entity.width = mediaWidth;
entity.height = mediaHeight;
}
private function clickHandler(e:MouseEvent):void {
if(isMp3) {
jPlayerFlashEvent(new JplayerEvent(JplayerEvent.JPLAYER_CLICK, myMp3Player.myStatus, "click"))
} else {
jPlayerFlashEvent(new JplayerEvent(JplayerEvent.JPLAYER_CLICK, myMp4Player.myStatus, "click"))
}
}
// This event is never called. See comments in class constructor.
private function menuSelectHandler_jPlayer(e:ContextMenuEvent):void {
navigateToURL(new URLRequest("http://jplayer.org/"), "_blank");
}
// This event is never called. See comments in class constructor.
private function menuSelectHandler_happyworm(e:ContextMenuEvent):void {
navigateToURL(new URLRequest("http://happyworm.com/"), "_blank");
}
private function log(t:String):void {
if(debug) {
txLog.text = t + "\n" + txLog.text;
}
}
private function debugMsgHandler(e:JplayerEvent):void {
log(e.msg);
}
private function keyboardHandler(e:KeyboardEvent):void {
log("keyboardHandler: e.keyCode = " + e.keyCode);
switch(e.keyCode) {
case 68 : // d
txLog.visible = !txLog.visible;
log("Toggled log display: " + txLog.visible);
break;
case 76 : // l
if(e.ctrlKey && e.shiftKey) {
txLog.text = "Cleared log.";
}
break;
}
}
}
}

View File

@ -0,0 +1,69 @@
/*
* jPlayer Plugin for jQuery JavaScript Library
* http://www.happyworm.com/jquery/jplayer
*
* Copyright (c) 2009 - 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Mark J Panaghiston
* Date: 8th August 2011
*/
package happyworm.jPlayer {
import flash.events.Event;
public class JplayerEvent extends Event {
// The event strings must match those in the JavaScript's $.jPlayer.event object
public static const JPLAYER_READY:String = "jPlayer_ready";
public static const JPLAYER_FLASHRESET:String = "jPlayer_flashreset"; // Handled in JavaScript
public static const JPLAYER_RESIZE:String = "jPlayer_resize"; // Handled in JavaScript
public static const JPLAYER_REPEAT:String = "jPlayer_repeat"; // Handled in JavaScript
public static const JPLAYER_CLICK:String = "jPlayer_click";
public static const JPLAYER_ERROR:String = "jPlayer_error";
public static const JPLAYER_WARNING:String = "jPlayer_warning"; // Currently not used by the flash solution
public static const JPLAYER_LOADSTART:String = "jPlayer_loadstart";
public static const JPLAYER_PROGRESS:String = "jPlayer_progress";
public static const JPLAYER_SUSPEND:String = "jPlayer_suspend"; // Not implemented
public static const JPLAYER_ABORT:String = "jPlayer_abort"; // Not implemented
public static const JPLAYER_EMPTIED:String = "jPlayer_emptied"; // Not implemented
public static const JPLAYER_STALLED:String = "jPlayer_stalled"; // Not implemented
public static const JPLAYER_PLAY:String = "jPlayer_play";
public static const JPLAYER_PAUSE:String = "jPlayer_pause";
public static const JPLAYER_LOADEDMETADATA:String = "jPlayer_loadedmetadata"; // MP3 has no equivilent
public static const JPLAYER_LOADEDDATA:String = "jPlayer_loadeddata"; // Not implemented
public static const JPLAYER_WAITING:String = "jPlayer_waiting"; // Not implemented
public static const JPLAYER_PLAYING:String = "jPlayer_playing"; // Not implemented
public static const JPLAYER_CANPLAY:String = "jPlayer_canplay"; // Not implemented
public static const JPLAYER_CANPLAYTHROUGH:String = "jPlayer_canplaythrough"; // Not implemented
public static const JPLAYER_SEEKING:String = "jPlayer_seeking";
public static const JPLAYER_SEEKED:String = "jPlayer_seeked";
public static const JPLAYER_TIMEUPDATE:String = "jPlayer_timeupdate";
public static const JPLAYER_ENDED:String = "jPlayer_ended";
public static const JPLAYER_RATECHANGE:String = "jPlayer_ratechange"; // Not implemented
public static const JPLAYER_DURATIONCHANGE:String = "jPlayer_durationchange"; // Not implemented
public static const JPLAYER_VOLUMECHANGE:String = "jPlayer_volumechange"; // See JavaScript
// Events used internal to jPlayer's Flash.
public static const DEBUG_MSG:String = "debug_msg";
public var data:JplayerStatus;
public var msg:String = ""
public function JplayerEvent(type:String, data:JplayerStatus, msg:String = "", bubbles:Boolean = false, cancelable:Boolean = false) {
super(type, bubbles, cancelable);
this.data = data;
this.msg = msg;
}
public override function clone():Event {
return new JplayerEvent(type, data, msg, bubbles, cancelable);
}
public override function toString():String {
return formatToString("JplayerEvent", "type", "bubbles", "cancelable", "eventPhase", "data", "msg");
}
}
}

View File

@ -0,0 +1,328 @@
/*
* jPlayer Plugin for jQuery JavaScript Library
* http://www.happyworm.com/jquery/jplayer
*
* Copyright (c) 2009 - 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Mark J Panaghiston
* Date: 1st September 2011
*/
package happyworm.jPlayer {
import flash.display.Sprite;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundLoaderContext;
import flash.media.SoundTransform;
import flash.net.URLRequest;
import flash.utils.Timer;
import flash.errors.IOError;
import flash.events.*;
public class JplayerMp3 extends Sprite {
private var mySound:Sound = new Sound();
private var myChannel:SoundChannel = new SoundChannel();
private var myContext:SoundLoaderContext = new SoundLoaderContext(3000, false);
private var myTransform:SoundTransform = new SoundTransform();
private var myRequest:URLRequest = new URLRequest();
private var timeUpdateTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
private var progressTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
private var seekingTimer:Timer = new Timer(100, 0); // Internal: How often seeking is checked to see if it is over.
public var myStatus:JplayerStatus = new JplayerStatus();
public function JplayerMp3(volume:Number) {
timeUpdateTimer.addEventListener(TimerEvent.TIMER, timeUpdateHandler);
progressTimer.addEventListener(TimerEvent.TIMER, progressHandler);
seekingTimer.addEventListener(TimerEvent.TIMER, seekingHandler);
setVolume(volume);
}
public function setFile(src:String):void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "setFile: " + src));
if(myStatus.isPlaying) {
myChannel.stop();
progressUpdates(false);
timeUpdates(false);
}
try {
mySound.close();
} catch (err:IOError) {
// Occurs if the file is either yet to be opened or has finished downloading.
}
mySound = null;
mySound = new Sound();
mySound.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
mySound.addEventListener(Event.OPEN, loadOpen);
mySound.addEventListener(Event.COMPLETE, loadComplete);
myRequest = new URLRequest(src);
myStatus.reset();
myStatus.src = src;
myStatus.srcSet = true;
timeUpdateEvent();
}
public function clearFile():void {
setFile("");
myStatus.srcSet = false;
}
private function errorHandler(err:IOErrorEvent):void {
// MP3 player needs to stop progress and timeupdate events as they are started before the error occurs.
// NB: The MP4 player works differently and the error occurs before they are started.
progressUpdates(false);
timeUpdates(false);
myStatus.error(); // Resets status except the src, and it sets srcError property.
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ERROR, myStatus));
}
private function loadOpen(e:Event):void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "loadOpen:"));
myStatus.loading();
if(myStatus.playOnLoad) {
myStatus.playOnLoad = false; // Capture the flag
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus)); // So loadstart event happens before play event occurs.
play();
} else {
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus));
pause();
}
progressUpdates(true);
}
private function loadComplete(e:Event):void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "loadComplete:"));
myStatus.loaded();
progressUpdates(false);
progressEvent();
}
private function soundCompleteHandler(e:Event):void {
myStatus.pausePosition = 0;
myStatus.isPlaying = false;
timeUpdates(false);
timeUpdateEvent();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED, myStatus));
}
private function progressUpdates(active:Boolean):void {
// Using a timer rather than Flash's load progress event, because that event gave data at about 200Hz. The 10Hz timer is closer to HTML5 norm.
if(active) {
progressTimer.start();
} else {
progressTimer.stop();
}
}
private function progressHandler(e:TimerEvent):void {
progressEvent();
}
private function progressEvent():void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressEvent:"));
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PROGRESS, myStatus));
}
private function timeUpdates(active:Boolean):void {
if(active) {
timeUpdateTimer.start();
} else {
timeUpdateTimer.stop();
}
}
private function timeUpdateHandler(e:TimerEvent):void {
timeUpdateEvent();
}
private function timeUpdateEvent():void {
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_TIMEUPDATE, myStatus));
}
private function seeking(active:Boolean):void {
if(active) {
if(!myStatus.isSeeking) {
seekingEvent();
seekingTimer.start();
}
} else {
seekingTimer.stop();
}
}
private function seekingHandler(e:TimerEvent):void {
if(myStatus.pausePosition <= getDuration()) {
seekedEvent();
seeking(false);
if(myStatus.playOnSeek) {
myStatus.playOnSeek = false; // Capture the flag.
play();
}
} else if(myStatus.isLoaded && (myStatus.pausePosition > getDuration())) {
// Illegal seek time
seeking(false);
seekedEvent();
pause(0);
}
}
private function seekingEvent():void {
myStatus.isSeeking = true;
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKING, myStatus));
}
private function seekedEvent():void {
myStatus.isSeeking = false;
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKED, myStatus));
}
public function load():Boolean {
if(myStatus.loadRequired()) {
myStatus.startingDownload();
mySound.load(myRequest, myContext);
return true;
} else {
return false;
}
}
public function play(time:Number = NaN):Boolean {
var wasPlaying:Boolean = myStatus.isPlaying;
if(!isNaN(time) && myStatus.srcSet) {
if(myStatus.isPlaying) {
myChannel.stop();
myStatus.isPlaying = false;
}
myStatus.pausePosition = time;
}
if(myStatus.isStartingDownload) {
myStatus.playOnLoad = true; // Raise flag, captured in loadOpen()
return true;
} else if(myStatus.loadRequired()) {
myStatus.playOnLoad = true; // Raise flag, captured in loadOpen()
return load();
} else if((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
if(myStatus.isLoaded && myStatus.pausePosition > getDuration()) { // The time is invalid, ie., past the end.
myStatus.pausePosition = 0;
timeUpdates(false);
timeUpdateEvent();
if(wasPlaying) { // For when playing and then get a play(huge)
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
}
} else if(myStatus.pausePosition > getDuration()) {
myStatus.playOnSeek = true;
seeking(true);
} else {
myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
myChannel = mySound.play(myStatus.pausePosition);
myChannel.soundTransform = myTransform;
myChannel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
timeUpdates(true);
if(!wasPlaying) {
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
}
}
return true;
} else {
return false;
}
}
public function pause(time:Number = NaN):Boolean {
myStatus.playOnLoad = false; // Reset flag in case load/play issued immediately before this command, ie., before loadOpen() event.
myStatus.playOnSeek = false; // Reset flag in case play(time) issued before the command and is still seeking to time set.
var wasPlaying:Boolean = myStatus.isPlaying;
// To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
var alreadyPausedAtTime:Boolean = false;
if(!isNaN(time) && myStatus.pausePosition == time) {
alreadyPausedAtTime = true;
}
if(myStatus.isPlaying) {
myStatus.isPlaying = false;
myChannel.stop();
if(myChannel.position > 0) { // Required otherwise a fast play then pause causes myChannel.position to equal zero and not the correct value. ie., When it happens leave pausePosition alone.
myStatus.pausePosition = myChannel.position;
}
}
if(!isNaN(time) && myStatus.srcSet) {
myStatus.pausePosition = time;
}
if(wasPlaying) {
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
}
if(myStatus.isStartingDownload) {
return true;
} else if(myStatus.loadRequired()) {
if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
return load();
} else {
return true; // Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
}
} else if(myStatus.isLoading || myStatus.isLoaded) {
if(myStatus.isLoaded && myStatus.pausePosition > getDuration()) { // The time is invalid, ie., past the end.
myStatus.pausePosition = 0;
} else if(myStatus.pausePosition > getDuration()) {
seeking(true);
}
timeUpdates(false);
// Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
// Neither pause() nor pause(time) will cause a timeupdate loop.
if(wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
timeUpdateEvent();
}
return true;
} else {
return false;
}
}
public function playHead(percent:Number):Boolean {
var time:Number = percent * getDuration() / 100;
if(myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek) {
return play(time);
} else {
return pause(time);
}
}
public function setVolume(v:Number):void {
myStatus.volume = v;
myTransform.volume = v;
myChannel.soundTransform = myTransform;
}
private function updateStatusValues():void {
myStatus.seekPercent = 100 * getLoadRatio();
myStatus.currentTime = getCurrentTime();
myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
myStatus.duration = getDuration();
}
public function getLoadRatio():Number {
if((myStatus.isLoading || myStatus.isLoaded) && mySound.bytesTotal > 0) {
return mySound.bytesLoaded / mySound.bytesTotal;
} else {
return 0;
}
}
public function getDuration():Number {
if(mySound.length > 0) {
return mySound.length;
} else {
return 0;
}
}
public function getCurrentTime():Number {
if(myStatus.isPlaying) {
return myChannel.position;
} else {
return myStatus.pausePosition;
}
}
public function getCurrentRatioRel():Number {
if((getDuration() > 0) && (getCurrentTime() <= getDuration())) {
return getCurrentTime() / getDuration();
} else {
return 0;
}
}
public function getCurrentRatioAbs():Number {
return getCurrentRatioRel() * getLoadRatio();
}
}
}

View File

@ -0,0 +1,413 @@
/*
* jPlayer Plugin for jQuery JavaScript Library
* http://www.happyworm.com/jquery/jplayer
*
* Copyright (c) 2009 - 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Mark J Panaghiston
* Date: 7th August 2011
*/
package happyworm.jPlayer {
import flash.display.Sprite;
import flash.media.Video;
import flash.media.SoundTransform;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.utils.Timer;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
public class JplayerMp4 extends Sprite {
public var myVideo:Video = new Video();
private var myConnection:NetConnection;
private var myStream:NetStream;
private var myTransform:SoundTransform = new SoundTransform();
public var myStatus:JplayerStatus = new JplayerStatus();
private var timeUpdateTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
private var progressTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
private var seekingTimer:Timer = new Timer(100, 0); // Internal: How often seeking is checked to see if it is over.
public function JplayerMp4(volume:Number) {
myConnection = new NetConnection();
myConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
myConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
myVideo.smoothing = true;
this.addChild(myVideo);
timeUpdateTimer.addEventListener(TimerEvent.TIMER, timeUpdateHandler);
progressTimer.addEventListener(TimerEvent.TIMER, progressHandler);
seekingTimer.addEventListener(TimerEvent.TIMER, seekingHandler);
myStatus.volume = volume;
}
private function progressUpdates(active:Boolean):void {
if(active) {
progressTimer.start();
} else {
progressTimer.stop();
}
}
private function progressHandler(e:TimerEvent):void {
if(myStatus.isLoading) {
if(getLoadRatio() == 1) { // Close as can get to a loadComplete event since client.onPlayStatus only works with FMS
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressHandler: loadComplete"));
myStatus.loaded();
progressUpdates(false);
}
}
progressEvent();
}
private function progressEvent():void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressEvent:"));
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PROGRESS, myStatus));
}
private function timeUpdates(active:Boolean):void {
if(active) {
timeUpdateTimer.start();
} else {
timeUpdateTimer.stop();
}
}
private function timeUpdateHandler(e:TimerEvent):void {
timeUpdateEvent();
}
private function timeUpdateEvent():void {
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_TIMEUPDATE, myStatus));
}
private function seeking(active:Boolean):void {
if(active) {
if(!myStatus.isSeeking) {
seekingEvent();
}
seekingTimer.start();
} else {
if(myStatus.isSeeking) {
seekedEvent();
}
seekingTimer.stop();
}
}
private function seekingHandler(e:TimerEvent):void {
if(getSeekTimeRatio() <= getLoadRatio()) {
seeking(false);
if(myStatus.playOnSeek) {
myStatus.playOnSeek = false; // Capture the flag.
play(myStatus.pausePosition); // Must pass time or the seek time is never set.
} else {
pause(myStatus.pausePosition); // Must pass time or the stream.time is read.
}
} else if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) {
// Illegal seek time
seeking(false);
pause(0);
}
}
private function seekingEvent():void {
myStatus.isSeeking = true;
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKING, myStatus));
}
private function seekedEvent():void {
myStatus.isSeeking = false;
updateStatusValues();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKED, myStatus));
}
private function netStatusHandler(e:NetStatusEvent):void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "netStatusHandler: '" + e.info.code + "'"));
switch(e.info.code) {
case "NetConnection.Connect.Success":
connectStream();
break;
case "NetStream.Play.Start":
// This event code occurs once, when the media is opened. Equiv to loadOpen() in mp3 player.
myStatus.loading();
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus));
progressUpdates(true);
// See onMetaDataHandler() for other condition, since duration is vital.
break;
case "NetStream.Play.Stop":
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "NetStream.Play.Stop: getDuration() - getCurrentTime() = " + (getDuration() - getCurrentTime())));
// Check if media is at the end (or close) otherwise this was due to download bandwidth stopping playback. ie., Download is not fast enough.
if(Math.abs(getDuration() - getCurrentTime()) < 150) { // Testing found 150ms worked best for M4A files, where playHead(99.9) caused a stuck state due to firing with ~116ms left to play.
endedEvent();
}
break;
case "NetStream.Seek.InvalidTime":
// Used for capturing invalid set times and clicks on the end of the progress bar.
endedEvent();
break;
case "NetStream.Play.StreamNotFound":
myStatus.error(); // Resets status except the src, and it sets srcError property.
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ERROR, myStatus));
break;
}
// "NetStream.Seek.Notify" event code is not very useful. It occurs after every seek(t) command issued and does not appear to wait for the media to be ready.
}
private function endedEvent():void {
var wasPlaying:Boolean = myStatus.isPlaying;
pause(0);
timeUpdates(false);
timeUpdateEvent();
if(wasPlaying) {
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED, myStatus));
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "securityErrorHandler."));
}
private function connectStream():void {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "connectStream."));
var customClient:Object = new Object();
customClient.onMetaData = onMetaDataHandler;
// customClient.onPlayStatus = onPlayStatusHandler; // According to the forums and my tests, onPlayStatus only works with FMS (Flash Media Server).
myStream = null;
myStream = new NetStream(myConnection);
myStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
myStream.client = customClient;
myVideo.attachNetStream(myStream);
setVolume(myStatus.volume);
myStream.play(myStatus.src);
}
public function setFile(src:String):void {
if(myStream != null) {
myStream.close();
}
myVideo.clear();
progressUpdates(false);
timeUpdates(false);
myStatus.reset();
myStatus.src = src;
myStatus.srcSet = true;
timeUpdateEvent();
}
public function clearFile():void {
setFile("");
myStatus.srcSet = false;
}
public function load():Boolean {
if(myStatus.loadRequired()) {
myStatus.startingDownload();
myConnection.connect(null);
return true;
} else {
return false;
}
}
public function play(time:Number = NaN):Boolean {
var wasPlaying:Boolean = myStatus.isPlaying;
if(!isNaN(time) && myStatus.srcSet) {
if(myStatus.isPlaying) {
myStream.pause();
myStatus.isPlaying = false;
}
myStatus.pausePosition = time;
}
if(myStatus.isStartingDownload) {
myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
return true;
} else if(myStatus.loadRequired()) {
myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
return load();
} else if((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) { // The time is invalid, ie., past the end.
myStream.pause(); // Since it is playing by default at this point.
myStatus.pausePosition = 0;
myStream.seek(0);
timeUpdates(false);
timeUpdateEvent();
if(wasPlaying) { // For when playing and then get a play(huge)
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
}
} else if(getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
myStatus.playOnSeek = true;
seeking(true);
myStream.pause(); // Since it is playing by default at this point.
} else {
if(!isNaN(time)) { // Avoid using seek() when it is already correct.
myStream.seek(myStatus.pausePosition/1000); // Since time is in ms and seek() takes seconds
}
myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
myStream.resume();
timeUpdates(true);
if(!wasPlaying) {
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
}
}
return true;
} else {
return false;
}
}
public function pause(time:Number = NaN):Boolean {
myStatus.playOnLoad = false; // Reset flag in case load/play issued immediately before this command, ie., before onMetadata() event.
myStatus.playOnSeek = false; // Reset flag in case play(time) issued before the command and is still seeking to time set.
var wasPlaying:Boolean = myStatus.isPlaying;
// To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
var alreadyPausedAtTime:Boolean = false;
if(!isNaN(time) && myStatus.pausePosition == time) {
alreadyPausedAtTime = true;
}
// Need to wait for metadata to load before ever issuing a pause. The metadata handler will call this function if needed, when ready.
if(myStream != null && myStatus.metaDataReady) { // myStream is a null until the 1st media is loaded. ie., The 1st ever setMedia being followed by a pause() or pause(t).
myStream.pause();
}
if(myStatus.isPlaying) {
myStatus.isPlaying = false;
myStatus.pausePosition = myStream.time * 1000;
}
if(!isNaN(time) && myStatus.srcSet) {
myStatus.pausePosition = time;
}
if(wasPlaying) {
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
}
if(myStatus.isStartingDownload) {
return true;
} else if(myStatus.loadRequired()) {
if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
return load();
} else {
return true; // Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
}
} else if(myStatus.isLoading || myStatus.isLoaded) {
if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) { // The time is invalid, ie., past the end.
myStatus.pausePosition = 0;
myStream.seek(0);
seekedEvent(); // Deals with seeking effect when using setMedia() then pause(huge). NB: There is no preceeding seeking event.
} else if(!isNaN(time)) {
if(getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
seeking(true);
} else {
if(myStatus.metaDataReady) { // Otherwise seek(0) will stop the metadata loading.
myStream.seek(myStatus.pausePosition/1000);
}
}
}
timeUpdates(false);
// Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
// Neither pause() nor pause(time) will cause a timeupdate loop.
if(wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
timeUpdateEvent();
}
return true;
} else {
return false;
}
}
public function playHead(percent:Number):Boolean {
var time:Number = percent * getDuration() * getLoadRatio() / 100;
if(myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek) {
return play(time);
} else {
return pause(time);
}
}
public function setVolume(v:Number):void {
myStatus.volume = v;
myTransform.volume = v;
if(myStream != null) {
myStream.soundTransform = myTransform;
}
}
private function updateStatusValues():void {
myStatus.seekPercent = 100 * getLoadRatio();
myStatus.currentTime = getCurrentTime();
myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
myStatus.duration = getDuration();
}
public function getLoadRatio():Number {
if((myStatus.isLoading || myStatus.isLoaded) && myStream.bytesTotal > 0) {
return myStream.bytesLoaded / myStream.bytesTotal;
} else {
return 0;
}
}
public function getDuration():Number {
return myStatus.duration; // Set from meta data.
}
public function getCurrentTime():Number {
if(myStatus.isPlaying) {
return myStream.time * 1000;
} else {
return myStatus.pausePosition;
}
}
public function getCurrentRatioRel():Number {
if((getLoadRatio() > 0) && (getCurrentRatioAbs() <= getLoadRatio())) {
return getCurrentRatioAbs() / getLoadRatio();
} else {
return 0;
}
}
public function getCurrentRatioAbs():Number {
if(getDuration() > 0) {
return getCurrentTime() / getDuration();
} else {
return 0;
}
}
public function getSeekTimeRatio():Number {
if(getDuration() > 0) {
return myStatus.pausePosition / getDuration();
} else {
return 1;
}
}
public function onMetaDataHandler(info:Object):void { // Used in connectStream() in myStream.client object.
// This event occurs when jumping to the start of static files! ie., seek(0) will cause this event to occur.
if(!myStatus.metaDataReady) {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "onMetaDataHandler: " + info.duration + " | " + info.width + "x" + info.height));
myStatus.metaDataReady = true; // Set flag so that this event only effects jPlayer the 1st time.
myStatus.metaData = info;
myStatus.duration = info.duration * 1000; // Only available via Meta Data.
if(info.width != undefined) {
myVideo.width = info.width;
}
if(info.height != undefined) {
myVideo.height = info.height;
}
if(myStatus.playOnLoad) {
myStatus.playOnLoad = false; // Capture the flag
if(myStatus.pausePosition > 0 ) { // Important for setMedia followed by play(time).
play(myStatus.pausePosition);
} else {
play(); // Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
}
} else {
pause(myStatus.pausePosition); // Always send the pausePosition. Important for setMedia() followed by pause(time). Deals with not reading stream.time with setMedia() and play() immediately followed by stop() or pause(0)
}
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA, myStatus));
} else {
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "onMetaDataHandler: Already read (NO EFFECT)"));
}
}
}
}

View File

@ -0,0 +1,101 @@
/*
* jPlayer Plugin for jQuery JavaScript Library
* http://www.happyworm.com/jquery/jplayer
*
* Copyright (c) 2009 - 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Mark J Panaghiston
* Date: 1st September 2011
*/
package happyworm.jPlayer {
public class JplayerStatus {
public static const VERSION:String = "2.1.0"; // The version of the Flash jPlayer entity.
public var volume:Number = 0.5; // Not affected by reset()
public var muted:Boolean = false; // Not affected by reset()
public var src:String;
public var srcError:Boolean;
public var srcSet:Boolean;
public var isPlaying:Boolean;
public var isSeeking:Boolean;
public var playOnLoad:Boolean;
public var playOnSeek:Boolean;
public var isStartingDownload:Boolean;
public var isLoading:Boolean;
public var isLoaded:Boolean;
public var pausePosition:Number;
public var seekPercent:Number;
public var currentTime:Number;
public var currentPercentRelative:Number;
public var currentPercentAbsolute:Number;
public var duration:Number;
public var metaDataReady:Boolean;
public var metaData:Object;
public function JplayerStatus() {
reset();
}
public function reset():void {
src = "";
srcError = false;
srcSet = false;
isPlaying = false;
isSeeking = false;
playOnLoad = false;
playOnSeek = false;
isStartingDownload = false;
isLoading = false;
isLoaded = false;
pausePosition = 0;
seekPercent = 0;
currentTime = 0;
currentPercentRelative = 0;
currentPercentAbsolute = 0;
duration = 0;
metaDataReady = false;
metaData = {};
}
public function error():void {
var srcSaved:String = src;
reset();
src = srcSaved;
srcError = true;
}
public function loadRequired():Boolean {
return (srcSet && !isStartingDownload && !isLoading && !isLoaded);
}
public function startingDownload():void {
isStartingDownload = true;
isLoading = false;
isLoaded = false;
}
public function loading():void {
isStartingDownload = false;
isLoading = true;
isLoaded = false;
}
public function loaded():void {
isStartingDownload = false;
isLoading = false;
isLoaded = true;
}
}
}

View File

@ -0,0 +1,452 @@
/*
* Playlist Object for the jPlayer Plugin
* http://www.jplayer.org
*
* Copyright (c) 2009 - 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Mark J Panaghiston
* Version: 2.1.0 (jPlayer 2.1.0)
* Date: 1st September 2011
*/
/* Code verified using http://www.jshint.com/ */
/*jshint asi:false, bitwise:false, boss:false, browser:true, curly:true, debug:false, eqeqeq:true, eqnull:false, evil:false, forin:false, immed:false, jquery:true, laxbreak:false, newcap:true, noarg:true, noempty:true, nonew:true, nomem:false, onevar:false, passfail:false, plusplus:false, regexp:false, undef:true, sub:false, strict:false, white:false */
/*global jPlayerPlaylist: true, jQuery:false, alert:false */
(function($, undefined) {
jPlayerPlaylist = function(cssSelector, playlist, options) {
var self = this;
this.current = 0;
this.loop = false; // Flag used with the jPlayer repeat event
this.shuffled = false;
this.removing = false; // Flag is true during remove animation, disabling the remove() method until complete.
this.cssSelector = $.extend({}, this._cssSelector, cssSelector); // Object: Containing the css selectors for jPlayer and its cssSelectorAncestor
this.options = $.extend(true, {}, this._options, options); // Object: The jPlayer constructor options for this playlist and the playlist options
this.playlist = []; // Array of Objects: The current playlist displayed (Un-shuffled or Shuffled)
this.original = []; // Array of Objects: The original playlist
this._initPlaylist(playlist); // Copies playlist to this.original. Then mirrors this.original to this.playlist. Creating two arrays, where the element pointers match. (Enables pointer comparison.)
// Setup the css selectors for the extra interface items used by the playlist.
this.cssSelector.title = this.cssSelector.cssSelectorAncestor + " .jp-title"; // Note that the text is written to the decendant li node.
this.cssSelector.playlist = this.cssSelector.cssSelectorAncestor + " .jp-playlist";
this.cssSelector.next = this.cssSelector.cssSelectorAncestor + " .jp-next";
this.cssSelector.previous = this.cssSelector.cssSelectorAncestor + " .jp-previous";
this.cssSelector.shuffle = this.cssSelector.cssSelectorAncestor + " .jp-shuffle";
this.cssSelector.shuffleOff = this.cssSelector.cssSelectorAncestor + " .jp-shuffle-off";
// Override the cssSelectorAncestor given in options
this.options.cssSelectorAncestor = this.cssSelector.cssSelectorAncestor;
// Override the default repeat event handler
this.options.repeat = function(event) {
self.loop = event.jPlayer.options.loop;
};
// Create a ready event handler to initialize the playlist
$(this.cssSelector.jPlayer).bind($.jPlayer.event.ready, function(event) {
self._init();
});
// Create an ended event handler to move to the next item
$(this.cssSelector.jPlayer).bind($.jPlayer.event.ended, function(event) {
self.next();
});
// Create a play event handler to pause other instances
$(this.cssSelector.jPlayer).bind($.jPlayer.event.play, function(event) {
$(this).jPlayer("pauseOthers");
});
// Create a resize event handler to show the title in full screen mode.
$(this.cssSelector.jPlayer).bind($.jPlayer.event.resize, function(event) {
if(event.jPlayer.options.fullScreen) {
$(self.cssSelector.title).show();
} else {
$(self.cssSelector.title).hide();
}
});
// Create click handlers for the extra buttons that do playlist functions.
$(this.cssSelector.previous).click(function() {
self.previous();
$(this).blur();
return false;
});
$(this.cssSelector.next).click(function() {
self.next();
$(this).blur();
return false;
});
$(this.cssSelector.shuffle).click(function() {
self.shuffle(true);
return false;
});
$(this.cssSelector.shuffleOff).click(function() {
self.shuffle(false);
return false;
}).hide();
// Put the title in its initial display state
if(!this.options.fullScreen) {
$(this.cssSelector.title).hide();
}
// Remove the empty <li> from the page HTML. Allows page to be valid HTML, while not interfereing with display animations
$(this.cssSelector.playlist + " ul").empty();
// Create .live() handlers for the playlist items along with the free media and remove controls.
this._createItemHandlers();
// Instance jPlayer
$(this.cssSelector.jPlayer).jPlayer(this.options);
};
jPlayerPlaylist.prototype = {
_cssSelector: { // static object, instanced in constructor
jPlayer: "#jquery_jplayer_1",
cssSelectorAncestor: "#jp_container_1"
},
_options: { // static object, instanced in constructor
playlistOptions: {
autoPlay: false,
loopOnPrevious: false,
shuffleOnLoop: true,
enableRemoveControls: false,
displayTime: 'slow',
addTime: 'fast',
removeTime: 'fast',
shuffleTime: 'slow',
itemClass: "jp-playlist-item",
freeGroupClass: "jp-free-media",
freeItemClass: "jp-playlist-item-free",
removeItemClass: "jp-playlist-item-remove"
}
},
option: function(option, value) { // For changing playlist options only
if(value === undefined) {
return this.options.playlistOptions[option];
}
this.options.playlistOptions[option] = value;
switch(option) {
case "enableRemoveControls":
this._updateControls();
break;
case "itemClass":
case "freeGroupClass":
case "freeItemClass":
case "removeItemClass":
this._refresh(true); // Instant
this._createItemHandlers();
break;
}
return this;
},
_init: function() {
var self = this;
this._refresh(function() {
if(self.options.playlistOptions.autoPlay) {
self.play(self.current);
} else {
self.select(self.current);
}
});
},
_initPlaylist: function(playlist) {
this.current = 0;
this.shuffled = false;
this.removing = false;
this.original = $.extend(true, [], playlist); // Copy the Array of Objects
this._originalPlaylist();
},
_originalPlaylist: function() {
var self = this;
this.playlist = [];
// Make both arrays point to the same object elements. Gives us 2 different arrays, each pointing to the same actual object. ie., Not copies of the object.
$.each(this.original, function(i,v) {
self.playlist[i] = self.original[i];
});
},
_refresh: function(instant) {
/* instant: Can be undefined, true or a function.
* undefined -> use animation timings
* true -> no animation
* function -> use animation timings and excute function at half way point.
*/
var self = this;
if(instant && !$.isFunction(instant)) {
$(this.cssSelector.playlist + " ul").empty();
$.each(this.playlist, function(i,v) {
$(self.cssSelector.playlist + " ul").append(self._createListItem(self.playlist[i]));
});
this._updateControls();
} else {
var displayTime = $(this.cssSelector.playlist + " ul").children().length ? this.options.playlistOptions.displayTime : 0;
$(this.cssSelector.playlist + " ul").slideUp(displayTime, function() {
var $this = $(this);
$(this).empty();
$.each(self.playlist, function(i,v) {
$this.append(self._createListItem(self.playlist[i]));
});
self._updateControls();
if($.isFunction(instant)) {
instant();
}
if(self.playlist.length) {
$(this).slideDown(self.options.playlistOptions.displayTime);
} else {
$(this).show();
}
});
}
},
_createListItem: function(media) {
var self = this;
// Wrap the <li> contents in a <div>
var listItem = "<li><div>";
// Create remove control
listItem += "<a href='javascript:;' class='" + this.options.playlistOptions.removeItemClass + "'>&times;</a>";
// Create links to free media
if(media.free) {
var first = true;
listItem += "<span class='" + this.options.playlistOptions.freeGroupClass + "'>(";
$.each(media, function(property,value) {
if($.jPlayer.prototype.format[property]) { // Check property is a media format.
if(first) {
first = false;
} else {
listItem += " | ";
}
listItem += "<a class='" + self.options.playlistOptions.freeItemClass + "' href='" + value + "' tabindex='1'>" + property + "</a>";
}
});
listItem += ")</span>";
}
// The title is given next in the HTML otherwise the float:right on the free media corrupts in IE6/7
listItem += "<a href='javascript:;' class='" + this.options.playlistOptions.itemClass + "' tabindex='1'>" + media.title + (media.artist ? " <span class='jp-artist'>by " + media.artist + "</span>" : "") + "</a>";
listItem += "</div></li>";
return listItem;
},
_createItemHandlers: function() {
var self = this;
// Create .live() handlers for the playlist items
$(this.cssSelector.playlist + " a." + this.options.playlistOptions.itemClass).die("click").live("click", function() {
var index = $(this).parent().parent().index();
if(self.current !== index) {
self.play(index);
} else {
$(self.cssSelector.jPlayer).jPlayer("play");
}
$(this).blur();
return false;
});
// Create .live() handlers that disable free media links to force access via right click
$(self.cssSelector.playlist + " a." + this.options.playlistOptions.freeItemClass).die("click").live("click", function() {
$(this).parent().parent().find("." + self.options.playlistOptions.itemClass).click();
$(this).blur();
return false;
});
// Create .live() handlers for the remove controls
$(self.cssSelector.playlist + " a." + this.options.playlistOptions.removeItemClass).die("click").live("click", function() {
var index = $(this).parent().parent().index();
self.remove(index);
$(this).blur();
return false;
});
},
_updateControls: function() {
if(this.options.playlistOptions.enableRemoveControls) {
$(this.cssSelector.playlist + " ." + this.options.playlistOptions.removeItemClass).show();
} else {
$(this.cssSelector.playlist + " ." + this.options.playlistOptions.removeItemClass).hide();
}
if(this.shuffled) {
$(this.cssSelector.shuffleOff).show();
$(this.cssSelector.shuffle).hide();
} else {
$(this.cssSelector.shuffleOff).hide();
$(this.cssSelector.shuffle).show();
}
},
_highlight: function(index) {
if(this.playlist.length && index !== undefined) {
$(this.cssSelector.playlist + " .jp-playlist-current").removeClass("jp-playlist-current");
$(this.cssSelector.playlist + " li:nth-child(" + (index + 1) + ")").addClass("jp-playlist-current").find(".jp-playlist-item").addClass("jp-playlist-current");
$(this.cssSelector.title + " li").html(this.playlist[index].title + (this.playlist[index].artist ? " <span class='jp-artist'>by " + this.playlist[index].artist + "</span>" : ""));
}
},
setPlaylist: function(playlist) {
this._initPlaylist(playlist);
this._init();
},
add: function(media, playNow) {
$(this.cssSelector.playlist + " ul").append(this._createListItem(media)).find("li:last-child").hide().slideDown(this.options.playlistOptions.addTime);
this._updateControls();
this.original.push(media);
this.playlist.push(media); // Both array elements share the same object pointer. Comforms with _initPlaylist(p) system.
if(playNow) {
this.play(this.playlist.length - 1);
} else {
if(this.original.length === 1) {
this.select(0);
}
}
},
remove: function(index) {
var self = this;
if(index === undefined) {
this._initPlaylist([]);
this._refresh(function() {
$(self.cssSelector.jPlayer).jPlayer("clearMedia");
});
return true;
} else {
if(this.removing) {
return false;
} else {
index = (index < 0) ? self.original.length + index : index; // Negative index relates to end of array.
if(0 <= index && index < this.playlist.length) {
this.removing = true;
$(this.cssSelector.playlist + " li:nth-child(" + (index + 1) + ")").slideUp(this.options.playlistOptions.removeTime, function() {
$(this).remove();
if(self.shuffled) {
var item = self.playlist[index];
$.each(self.original, function(i,v) {
if(self.original[i] === item) {
self.original.splice(i, 1);
return false; // Exit $.each
}
});
self.playlist.splice(index, 1);
} else {
self.original.splice(index, 1);
self.playlist.splice(index, 1);
}
if(self.original.length) {
if(index === self.current) {
self.current = (index < self.original.length) ? self.current : self.original.length - 1; // To cope when last element being selected when it was removed
self.select(self.current);
} else if(index < self.current) {
self.current--;
}
} else {
$(self.cssSelector.jPlayer).jPlayer("clearMedia");
self.current = 0;
self.shuffled = false;
self._updateControls();
}
self.removing = false;
});
}
return true;
}
}
},
select: function(index) {
index = (index < 0) ? this.original.length + index : index; // Negative index relates to end of array.
if(0 <= index && index < this.playlist.length) {
this.current = index;
this._highlight(index);
$(this.cssSelector.jPlayer).jPlayer("setMedia", this.playlist[this.current]);
} else {
this.current = 0;
}
},
play: function(index) {
index = (index < 0) ? this.original.length + index : index; // Negative index relates to end of array.
if(0 <= index && index < this.playlist.length) {
if(this.playlist.length) {
this.select(index);
$(this.cssSelector.jPlayer).jPlayer("play");
}
} else if(index === undefined) {
$(this.cssSelector.jPlayer).jPlayer("play");
}
},
pause: function() {
$(this.cssSelector.jPlayer).jPlayer("pause");
},
next: function() {
var index = (this.current + 1 < this.playlist.length) ? this.current + 1 : 0;
if(this.loop) {
// See if we need to shuffle before looping to start, and only shuffle if more than 1 item.
if(index === 0 && this.shuffled && this.options.playlistOptions.shuffleOnLoop && this.playlist.length > 1) {
this.shuffle(true, true); // playNow
} else {
this.play(index);
}
} else {
// The index will be zero if it just looped round
if(index > 0) {
this.play(index);
}
}
},
previous: function() {
var index = (this.current - 1 >= 0) ? this.current - 1 : this.playlist.length - 1;
if(this.loop && this.options.playlistOptions.loopOnPrevious || index < this.playlist.length - 1) {
this.play(index);
}
},
shuffle: function(shuffled, playNow) {
var self = this;
if(shuffled === undefined) {
shuffled = !this.shuffled;
}
if(shuffled || shuffled !== this.shuffled) {
$(this.cssSelector.playlist + " ul").slideUp(this.options.playlistOptions.shuffleTime, function() {
self.shuffled = shuffled;
if(shuffled) {
self.playlist.sort(function() {
return 0.5 - Math.random();
});
} else {
self._originalPlaylist();
}
self._refresh(true); // Instant
if(playNow || !$(self.cssSelector.jPlayer).data("jPlayer").status.paused) {
self.play(0);
} else {
self.select(0);
}
$(this).slideDown(self.options.playlistOptions.shuffleTime);
});
}
}
};
})(jQuery);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,623 @@
/*
* Skin for jPlayer Plugin (jQuery JavaScript Library)
* http://www.happyworm.com/jquery/jplayer
*
* Skin Name: Blue Monday
*
* Copyright (c) 2010-2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Silvia Benvenuti
* Skin Version: 4.0 (jPlayer 2.1.0)
* Date: 1st September 2011
*/
div.jp-audio,
div.jp-video {
/* Edit the font-size to counteract inherited font sizing.
* Eg. 1.25em = 1 / 0.8em
*/
font-size:1.25em; /* 1.25em for testing in site pages */ /* No parent CSS that can effect the size in the demos ZIP */
font-family:Verdana, Arial, sans-serif;
line-height:1.6;
color: #666;
border:1px solid #009be3;
background-color:#eee;
position:relative;
}
div.jp-audio {
width:420px;
}
div.jp-video-270p {
width:480px;
}
div.jp-video-360p {
width:640px;
}
div.jp-video-full {
/* Rules for IE6 (full-screen) */
width:480px;
height:270px;
/* Rules for IE7 (full-screen) - Otherwise the relative container causes other page items that are not position:static (default) to appear over the video/gui. */
position:static !important; position:relative
}
div.jp-video-full div.jp-jplayer {
top: 0;
left: 0;
position: fixed !important; position: relative; /* Rules for IE6 (full-screen) */
overflow: hidden;
z-index:1000;
}
div.jp-video-full div.jp-gui {
position: fixed !important; position: static; /* Rules for IE6 (full-screen) */
top: 0;
left: 0;
width:100%;
height:100%;
z-index:1000;
}
div.jp-video-full div.jp-interface {
position: absolute !important; position: relative; /* Rules for IE6 (full-screen) */
bottom: 0;
left: 0;
z-index:1000;
}
div.jp-interface {
position: relative;
background-color:#eee;
width:100%;
}
div.jp-audio div.jp-type-single div.jp-interface {
height:80px;
}
div.jp-audio div.jp-type-playlist div.jp-interface {
height:80px;
}
div.jp-video div.jp-interface {
border-top:1px solid #009be3;
}
/* @group CONTROLS */
div.jp-controls-holder {
clear: both;
width:440px;
margin:0 auto;
position: relative;
overflow:hidden;
top:-8px; /* This negative value depends on the size of the text in jp-currentTime and jp-duration */
}
div.jp-interface ul.jp-controls {
list-style-type:none;
margin:0;
padding: 0;
overflow:hidden;
}
div.jp-audio ul.jp-controls {
width: 380px;
padding:20px 20px 0 20px;
}
div.jp-video div.jp-type-single ul.jp-controls {
width: 78px;
margin-left: 200px;
}
div.jp-video div.jp-type-playlist ul.jp-controls {
width: 134px;
margin-left: 172px;
}
div.jp-video ul.jp-controls,
div.jp-interface ul.jp-controls li {
display:inline;
float: left;
}
div.jp-interface ul.jp-controls a {
display:block;
overflow:hidden;
text-indent:-9999px;
}
a.jp-play,
a.jp-pause {
width:40px;
height:40px;
}
a.jp-play {
background: url("jplayer.blue.monday.jpg") 0 0 no-repeat;
}
a.jp-play:hover {
background: url("jplayer.blue.monday.jpg") -41px 0 no-repeat;
}
a.jp-pause {
background: url("jplayer.blue.monday.jpg") 0 -42px no-repeat;
display: none;
}
a.jp-pause:hover {
background: url("jplayer.blue.monday.jpg") -41px -42px no-repeat;
}
a.jp-stop, a.jp-previous, a.jp-next {
width:28px;
height:28px;
margin-top:6px;
}
a.jp-stop {
background: url("jplayer.blue.monday.jpg") 0 -83px no-repeat;
margin-left:10px;
}
a.jp-stop:hover {
background: url("jplayer.blue.monday.jpg") -29px -83px no-repeat;
}
a.jp-previous {
background: url("jplayer.blue.monday.jpg") 0 -112px no-repeat;
}
a.jp-previous:hover {
background: url("jplayer.blue.monday.jpg") -29px -112px no-repeat;
}
a.jp-next {
background: url("jplayer.blue.monday.jpg") 0 -141px no-repeat;
}
a.jp-next:hover {
background: url("jplayer.blue.monday.jpg") -29px -141px no-repeat;
}
/* @end */
/* @group progress bar */
div.jp-progress {
overflow:hidden;
background-color: #ddd;
}
div.jp-audio div.jp-progress {
position: absolute;
top:32px;
height:15px;
}
div.jp-audio div.jp-type-single div.jp-progress {
left:110px;
width:186px;
}
div.jp-audio div.jp-type-playlist div.jp-progress {
left:166px;
width:130px;
}
div.jp-video div.jp-progress {
top:0px;
left:0px;
width:100%;
height:10px;
}
div.jp-seek-bar {
background: url("jplayer.blue.monday.jpg") 0 -202px repeat-x;
width:0px;
height:100%;
cursor: pointer;
}
div.jp-play-bar {
background: url("jplayer.blue.monday.jpg") 0 -218px repeat-x ;
width:0px;
height:100%;
}
/* The seeking class is added/removed inside jPlayer */
div.jp-seeking-bg {
background: url("jplayer.blue.monday.seeking.gif");
}
/* @end */
/* @group volume controls */
a.jp-mute,
a.jp-unmute,
a.jp-volume-max {
width:18px;
height:15px;
margin-top:12px;
}
div.jp-audio div.jp-type-single a.jp-mute,
div.jp-audio div.jp-type-single a.jp-unmute {
margin-left: 210px;
}
div.jp-audio div.jp-type-playlist a.jp-mute,
div.jp-audio div.jp-type-playlist a.jp-unmute {
margin-left: 154px;
}
div.jp-audio a.jp-volume-max {
margin-left: 56px;
}
div.jp-video a.jp-mute,
div.jp-video a.jp-unmute,
div.jp-video a.jp-volume-max {
position: absolute;
top:12px;
margin-top:0;
}
div.jp-video a.jp-mute,
div.jp-video a.jp-unmute {
left: 50px;
}
div.jp-video a.jp-volume-max {
left: 134px;
}
a.jp-mute {
background: url("jplayer.blue.monday.jpg") 0 -170px no-repeat;
}
a.jp-mute:hover {
background: url("jplayer.blue.monday.jpg") -19px -170px no-repeat;
}
a.jp-unmute {
background: url("jplayer.blue.monday.jpg") -60px -170px no-repeat;
display: none;
}
a.jp-unmute:hover {
background: url("jplayer.blue.monday.jpg") -79px -170px no-repeat;
}
a.jp-volume-max {
background: url("jplayer.blue.monday.jpg") 0 -186px no-repeat;
}
a.jp-volume-max:hover {
background: url("jplayer.blue.monday.jpg") -19px -186px no-repeat;
}
div.jp-volume-bar {
position: absolute;
overflow:hidden;
background: url("jplayer.blue.monday.jpg") 0 -250px repeat-x;
width:46px;
height:5px;
cursor: pointer;
}
div.jp-audio div.jp-volume-bar {
top:37px;
left:330px;
}
div.jp-video div.jp-volume-bar {
top:17px;
left:72px;
}
div.jp-volume-bar-value {
background: url("jplayer.blue.monday.jpg") 0 -256px repeat-x;
width:0px;
height:5px;
}
/* @end */
/* @group current time and duration */
div.jp-audio div.jp-time-holder {
position:absolute;
top:50px;
}
div.jp-audio div.jp-type-single div.jp-time-holder {
left:110px;
width:186px;
}
div.jp-audio div.jp-type-playlist div.jp-time-holder {
left:166px;
width:130px;
}
div.jp-current-time,
div.jp-duration {
width:60px;
font-size:.64em;
font-style:oblique;
}
div.jp-current-time {
float: left;
display:inline;
}
div.jp-duration {
float: right;
display:inline;
text-align: right;
}
div.jp-video div.jp-current-time {
margin-left:20px;
}
div.jp-video div.jp-duration {
margin-right:20px;
}
/* @end */
/* @group playlist */
div.jp-title {
font-weight:bold;
text-align:center;
}
div.jp-title,
div.jp-playlist {
width:100%;
background-color:#ccc;
border-top:1px solid #009be3;
}
div.jp-type-single div.jp-title,
div.jp-type-playlist div.jp-title,
div.jp-type-single div.jp-playlist {
border-top:none;
}
div.jp-title ul,
div.jp-playlist ul {
list-style-type:none;
margin:0;
padding:0 20px;
font-size:.72em;
}
div.jp-title li {
padding:5px 0;
font-weight:bold;
}
div.jp-playlist li {
padding:5px 0 4px 20px;
border-bottom:1px solid #eee;
}
div.jp-playlist li div {
display:inline;
}
/* Note that the first-child (IE6) and last-child (IE6/7/8) selectors do not work on IE */
div.jp-type-playlist div.jp-playlist li:last-child {
padding:5px 0 5px 20px;
border-bottom:none;
}
div.jp-type-playlist div.jp-playlist li.jp-playlist-current {
list-style-type:square;
list-style-position:inside;
padding-left:7px;
}
div.jp-type-playlist div.jp-playlist a {
color: #333;
text-decoration: none;
}
div.jp-type-playlist div.jp-playlist a:hover {
color:#0d88c1;
}
div.jp-type-playlist div.jp-playlist a.jp-playlist-current {
color:#0d88c1;
}
div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove {
float:right;
display:inline;
text-align:right;
margin-right:10px;
font-weight:bold;
color:#666;
}
div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove:hover {
color:#0d88c1;
}
div.jp-type-playlist div.jp-playlist span.jp-free-media {
float:right;
display:inline;
text-align:right;
margin-right:10px;
}
div.jp-type-playlist div.jp-playlist span.jp-free-media a{
color:#666;
}
div.jp-type-playlist div.jp-playlist span.jp-free-media a:hover{
color:#0d88c1;
}
span.jp-artist {
font-size:.8em;
color:#666;
}
/* @end */
div.jp-video-play {
position:absolute;
top:0;
left:0;
width:100%;
cursor:pointer;
background-color:rgba(0,0,0,0); /* Makes IE9 work with the active area over the whole video area. IE6/7/8 only have the button as active area. */
}
div.jp-video-270p div.jp-video-play {
height:270px;
}
div.jp-video-360p div.jp-video-play {
height:360px;
}
div.jp-video-full div.jp-video-play {
height:100%;
z-index:1000;
}
a.jp-video-play-icon {
position:relative;
display:block;
width: 112px;
height: 100px;
margin-left:-56px;
margin-top:-50px;
left:50%;
top:50%;
background: url("jplayer.blue.monday.video.play.png") 0 0 no-repeat;
text-indent:-9999px;
}
div.jp-video-play:hover a.jp-video-play-icon {
background: url("jplayer.blue.monday.video.play.png") 0 -100px no-repeat;
}
div.jp-jplayer audio,
div.jp-jplayer {
width:0px;
height:0px;
}
div.jp-jplayer {
background-color: #000000;
}
/* @group TOGGLES */
/* The audio toggles are nested inside jp-time-holder */
ul.jp-toggles {
list-style-type:none;
padding:0;
margin:0 auto;
overflow:hidden;
}
div.jp-audio .jp-type-single ul.jp-toggles {
width:25px;
}
div.jp-audio .jp-type-playlist ul.jp-toggles {
width:55px;
margin: 0;
position: absolute;
left: 325px;
top: 50px;
}
div.jp-video ul.jp-toggles {
margin-top:10px;
width:100px;
}
ul.jp-toggles li {
display:block;
float:right;
}
ul.jp-toggles li a {
display:block;
width:25px;
height:18px;
text-indent:-9999px;
line-height:100%; /* need this for IE6 */
}
a.jp-full-screen {
background: url("jplayer.blue.monday.jpg") 0 -310px no-repeat;
margin-left: 20px;
}
a.jp-full-screen:hover {
background: url("jplayer.blue.monday.jpg") -30px -310px no-repeat;
}
a.jp-restore-screen {
background: url("jplayer.blue.monday.jpg") -60px -310px no-repeat;
margin-left: 20px;
}
a.jp-restore-screen:hover {
background: url("jplayer.blue.monday.jpg") -90px -310px no-repeat;
}
a.jp-repeat {
background: url("jplayer.blue.monday.jpg") 0 -290px no-repeat;
}
a.jp-repeat:hover {
background: url("jplayer.blue.monday.jpg") -30px -290px no-repeat;
}
a.jp-repeat-off {
background: url("jplayer.blue.monday.jpg") -60px -290px no-repeat;
}
a.jp-repeat-off:hover {
background: url("jplayer.blue.monday.jpg") -90px -290px no-repeat;
}
a.jp-shuffle {
background: url("jplayer.blue.monday.jpg") 0 -270px no-repeat;
margin-left: 5px;
}
a.jp-shuffle:hover {
background: url("jplayer.blue.monday.jpg") -30px -270px no-repeat;
}
a.jp-shuffle-off {
background: url("jplayer.blue.monday.jpg") -60px -270px no-repeat;
margin-left: 5px;
}
a.jp-shuffle-off:hover {
background: url("jplayer.blue.monday.jpg") -90px -270px no-repeat;
}
/* @end */
/* @group NO SOLUTION error feedback */
.jp-no-solution {
position:absolute;
width:390px;
margin-left:-202px;
left:50%;
top: 10px;
padding:5px;
font-size:.8em;
background-color:#eee;
border:2px solid #009be3;
color:#000;
display:none;
}
.jp-no-solution a {
color:#000;
}
.jp-no-solution span {
font-size:1em;
display:block;
text-align:center;
font-weight:bold;
}
/* @end */

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,650 @@
/*
* Skin for jPlayer Plugin (jQuery JavaScript Library)
* http://www.jplayer.org
*
* Skin Name: Pink Flag
*
* Copyright (c) 2011 Happyworm Ltd
* Dual licensed under the MIT and GPL licenses.
* - http://www.opensource.org/licenses/mit-license.php
* - http://www.gnu.org/copyleft/gpl.html
*
* Author: Silvia Benvenuti
* Skin Version: 1.0 (jPlayer 2.1.0)
* Date: 1st September 2011
*/
div.jp-audio,
div.jp-video {
/* Edit the font-size to counteract inherited font sizing.
* Eg. 1.25em = 1 / 0.8em
*/
font-size:1.25em; /* 1.25em for testing in site pages */ /* No parent CSS that can effect the size in the demos ZIP */
font-family:Verdana, Arial, sans-serif;
line-height:1.6;
color: #fff;
border-top:1px solid #554461;
border-left:1px solid #554461;
border-right:1px solid #180a1f;
border-bottom:1px solid #180a1f;
background-color:#3a2a45;
position:relative;
}
div.jp-audio {
width:201px;
padding:20px;
}
div.jp-video-270p {
width:480px;
}
div.jp-video-360p {
width:640px;
}
div.jp-video-full {
/* Rules for IE6 (full-screen) */
width:480px;
height:270px;
/* Rules for IE7 (full-screen) - Otherwise the relative container causes other page items that are not position:static (default) to appear over the video/gui. */
position:static !important; position:relative
}
div.jp-video-full div.jp-jplayer {
top: 0;
left: 0;
position: fixed !important; position: relative; /* Rules for IE6 (full-screen) */
overflow: hidden;
z-index:1000;
}
div.jp-video-full div.jp-gui {
position: fixed !important; position: static; /* Rules for IE6 (full-screen) */
top: 0;
left: 0;
width:100%;
height:100%;
z-index:1000;
}
div.jp-video-full div.jp-interface {
position: absolute !important; position: relative; /* Rules for IE6 (full-screen) */
bottom: 0;
left: 0;
z-index:1000;
}
div.jp-interface {
position: relative;
width:100%;
background-color:#3a2a45; /* Required for the full screen */
}
div.jp-audio .jp-interface {
height: 80px;
padding-top:30px;
}
/* @group CONTROLS */
div.jp-controls-holder {
clear: both;
width:440px;
margin:0 auto 10px auto;
position: relative;
overflow:hidden;
}
div.jp-interface ul.jp-controls {
background: url("jplayer.pink.flag.jpg") 0 0 no-repeat;
list-style-type:none;
padding: 1px 0 2px 1px;
overflow:hidden;
width: 201px;
height: 34px;
}
div.jp-audio ul.jp-controls {
margin:0 auto;
}
div.jp-video ul.jp-controls {
margin:0 0 0 115px;
float:left;
display:inline; /* need this to fix IE6 double margin */
}
div.jp-interface ul.jp-controls li {
display:inline;
float: left;
}
div.jp-interface ul.jp-controls a {
display:block;
overflow:hidden;
text-indent:-9999px;
height: 34px;
margin: 0 1px 2px 0;
padding: 0;
}
/* @group single player controls */
div.jp-type-single .jp-controls li a{
width: 99px;
}
div.jp-type-single .jp-play {
background: url("jplayer.pink.flag.jpg") 0px -40px no-repeat;
}
div.jp-type-single .jp-play:hover {
background: url("jplayer.pink.flag.jpg") -100px -40px no-repeat;
}
div.jp-type-single .jp-pause {
background: url("jplayer.pink.flag.jpg") 0px -120px no-repeat;
}
div.jp-type-single .jp-pause:hover {
background: url("jplayer.pink.flag.jpg") -100px -120px no-repeat;
}
div.jp-type-single .jp-stop {
background: url("jplayer.pink.flag.jpg") 0px -80px no-repeat;
}
div.jp-type-single .jp-stop:hover {
background: url("jplayer.pink.flag.jpg") -100px -80px no-repeat;
}
/* @end */
/* @group playlist player controls */
div.jp-type-playlist .jp-controls li a{
width: 49px;
}
div.jp-type-playlist .jp-play {
background: url("jplayer.pink.flag.jpg") -24px -40px no-repeat;
}
div.jp-type-playlist .jp-play:hover {
background: url("jplayer.pink.flag.jpg") -124px -40px no-repeat;
}
div.jp-type-playlist .jp-pause {
background: url("jplayer.pink.flag.jpg") -24px -120px no-repeat;
}
div.jp-type-playlist .jp-pause:hover {
background: url("jplayer.pink.flag.jpg") -124px -120px no-repeat;
}
div.jp-type-playlist .jp-stop {
background: url("jplayer.pink.flag.jpg") -24px -80px no-repeat;
}
div.jp-type-playlist .jp-stop:hover {
background: url("jplayer.pink.flag.jpg") -124px -80px no-repeat;
}
div.jp-type-playlist .jp-previous {
background: url("jplayer.pink.flag.jpg") -24px -200px no-repeat;
}
div.jp-type-playlist .jp-previous:hover {
background: url("jplayer.pink.flag.jpg") -124px -200px no-repeat;
}
div.jp-type-playlist .jp-next {
background: url("jplayer.pink.flag.jpg") -24px -160px no-repeat;
}
div.jp-type-playlist .jp-next:hover {
background: url("jplayer.pink.flag.jpg") -124px -160px no-repeat;
}
/* @end */
/* @end */
/* @group TOGGLES */
ul.jp-toggles {
list-style-type:none;
padding:0;
margin:0 auto;
z-index:20;
overflow:hidden;
}
div.jp-audio ul.jp-toggles {
width:55px;
}
div.jp-audio .jp-type-single ul.jp-toggles {
width:25px;
}
div.jp-video ul.jp-toggles {
width:100px;
margin-top: 10px;
}
ul.jp-toggles li{
display:block;
float:right;
}
ul.jp-toggles li a{
display:block;
width:25px;
height:18px;
text-indent:-9999px;
line-height:100%; /* need this for IE6 */
}
.jp-full-screen {
background: url("jplayer.pink.flag.jpg") 0 -420px no-repeat;
margin-left: 20px;
}
.jp-full-screen:hover {
background: url("jplayer.pink.flag.jpg") -30px -420px no-repeat;
}
.jp-restore-screen {
background: url("jplayer.pink.flag.jpg") -60px -420px no-repeat;
margin-left: 20px;
}
.jp-restore-screen:hover {
background: url("jplayer.pink.flag.jpg") -90px -420px no-repeat;
}
.jp-repeat {
background: url("jplayer.pink.flag.jpg") 0 -440px no-repeat;
}
.jp-repeat:hover {
background: url("jplayer.pink.flag.jpg") -30px -440px no-repeat;
}
.jp-repeat-off {
background: url("jplayer.pink.flag.jpg") -60px -440px no-repeat;
}
.jp-repeat-off:hover {
background: url("jplayer.pink.flag.jpg") -90px -440px no-repeat;
}
.jp-shuffle {
background: url("jplayer.pink.flag.jpg") 0 -460px no-repeat;
margin-left: 5px;
}
.jp-shuffle:hover {
background: url("jplayer.pink.flag.jpg") -30px -460px no-repeat;
}
.jp-shuffle-off {
background: url("jplayer.pink.flag.jpg") -60px -460px no-repeat;
margin-left: 5px;
}
.jp-shuffle-off:hover {
background: url("jplayer.pink.flag.jpg") -90px -460px no-repeat;
}
/* @end */
/* @group progress bar */
/* The seeking class is added/removed inside jPlayer */
div.jp-seeking-bg {
background: url("jplayer.pink.flag.seeking.gif");
}
.jp-progress {
background: url("jplayer.pink.flag.jpg") 0px -240px no-repeat;
width: 197px;
height: 13px;
padding: 0 2px 2px 2px;
margin-bottom: 4px;
overflow:hidden;
}
div.jp-video .jp-progress {
border-top:1px solid #180a1f;
border-bottom: 1px solid #554560;
width:100%;
background-image: none;
padding: 0;
}
.jp-seek-bar {
background: url("jplayer.pink.flag.jpg") 0px -260px repeat-x;
width:0px;
height: 100%;
overflow:hidden;
cursor:pointer;
}
.jp-play-bar {
background: url("jplayer.pink.flag.jpg") 0px -280px repeat-x;
width:0px;
height: 100%;
overflow:hidden;
}
/* @end */
/* @group volume controls */
div.jp-interface ul.jp-controls a.jp-mute,
div.jp-interface ul.jp-controls a.jp-unmute,
div.jp-interface ul.jp-controls a.jp-volume-max {
background: url("jplayer.pink.flag.jpg") 0px -330px no-repeat;
position: absolute;
width: 16px;
height: 11px;
}
div.jp-audio ul.jp-controls a.jp-mute,
div.jp-audio ul.jp-controls a.jp-unmute {
top:-6px;
left: 0;
}
div.jp-audio ul.jp-controls a.jp-volume-max {
top:-6px;
right: 0;
}
div.jp-video ul.jp-controls a.jp-mute,
div.jp-video ul.jp-controls a.jp-unmute {
left: 0;
top:14px;
}
div.jp-video ul.jp-controls a.jp-volume-max {
left: 84px;
top:14px;
}
div.jp-interface ul.jp-controls a.jp-mute:hover {
background: url("jplayer.pink.flag.jpg") -25px -330px no-repeat;
}
div.jp-interface ul.jp-controls a.jp-unmute {
background: url("jplayer.pink.flag.jpg") -60px -330px no-repeat;
}
div.jp-interface ul.jp-controls a.jp-unmute:hover {
background: url("jplayer.pink.flag.jpg") -85px -330px no-repeat;
}
div.jp-interface ul.jp-controls a.jp-volume-max {
background: url("jplayer.pink.flag.jpg") 0px -350px no-repeat;
}
div.jp-interface ul.jp-controls a.jp-volume-max:hover {
background: url("jplayer.pink.flag.jpg") -25px -350px no-repeat;
}
.jp-volume-bar {
background: url("jplayer.pink.flag.jpg") 0px -300px repeat-x;
position: absolute;
width: 197px;
height: 4px;
padding: 2px 2px 1px 2px;
overflow: hidden;
}
.jp-volume-bar:hover {
cursor: pointer;
}
div.jp-audio .jp-interface .jp-volume-bar {
top:10px;
left: 0;
}
div.jp-video .jp-volume-bar {
top: 0;
left: 0;
width:95px;
border-right:1px solid #000;
margin-top: 30px;
}
.jp-volume-bar-value {
background: url("jplayer.pink.flag.jpg") 0px -320px repeat-x;
height: 4px;
}
/* @end */
/* @group current time and duration */
.jp-current-time, .jp-duration {
width:70px;
font-size:.5em;
color: #8c7a99;
}
.jp-current-time {
float: left;
}
.jp-duration {
float: right;
text-align:right;
}
.jp-video .jp-current-time {
padding-left:20px;
}
.jp-video .jp-duration {
padding-right:20px;
}
/* @end */
/* @group playlist */
.jp-title ul,
.jp-playlist ul {
list-style-type:none;
font-size:.7em;
margin: 0;
padding: 0;
}
.jp-video .jp-title ul {
margin: 0 20px 10px;
}
.jp-video .jp-playlist ul {
margin: 0 20px;
}
.jp-title li,
.jp-playlist li {
position: relative;
padding: 2px 0;
border-top:1px solid #554461;
border-bottom:1px solid #180a1f;
overflow: hidden;
}
.jp-title li{
border-bottom:none;
border-top:none;
padding:0;
text-align:center;
}
/* Note that the first-child (IE6) and last-child (IE6/7/8) selectors do not work on IE */
div.jp-type-playlist div.jp-playlist li:first-child {
border-top:none;
padding-top:3px;
}
div.jp-type-playlist div.jp-playlist li:last-child {
border-bottom:none;
padding-bottom:3px;
}
div.jp-type-playlist div.jp-playlist a {
color: #fff;
text-decoration:none;
}
div.jp-type-playlist div.jp-playlist a:hover {
color: #e892e9;
}
div.jp-type-playlist div.jp-playlist li.jp-playlist-current {
background-color: #26102e;
margin: 0 -20px;
padding: 2px 20px;
border-top: 1px solid #26102e;
border-bottom: 1px solid #26102e;
}
div.jp-type-playlist div.jp-playlist li.jp-playlist-current a{
color: #e892e9;
}
div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove {
float:right;
display:inline;
text-align:right;
margin-left:10px;
font-weight:bold;
color:#8C7A99;
}
div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove:hover {
color:#E892E9;
}
div.jp-type-playlist div.jp-playlist span.jp-free-media {
float: right;
display:inline;
text-align:right;
color:#8C7A99;
}
div.jp-type-playlist div.jp-playlist span.jp-free-media a{
color:#8C7A99;
}
div.jp-type-playlist div.jp-playlist span.jp-free-media a:hover{
color:#E892E9;
}
span.jp-artist {
font-size:.8em;
color:#8C7A99;
}
/* @end */
div.jp-video div.jp-video-play {
position:absolute;
top:0;
left:0;
width:100%;
cursor:pointer;
background-color:rgba(0,0,0,0); /* Makes IE9 work with the active area over the whole video area. IE6/7/8 only have the button as active area. */
}
div.jp-video-270p div.jp-video-play {
height:270px;
}
div.jp-video-360p div.jp-video-play {
height:360px;
}
div.jp-video-full div.jp-video-play {
height:100%;
z-index:1000;
}
a.jp-video-play-icon {
position:relative;
display:block;
width: 112px;
height: 100px;
margin-left:-56px;
margin-top:-50px;
left:50%;
top:50%;
background: url("jplayer.pink.flag.video.play.png") 0 0 no-repeat;
text-indent:-9999px;
}
div.jp-video-play:hover a.jp-video-play-icon {
background: url("jplayer.pink.flag.video.play.png") 0 -100px no-repeat;
}
div.jp-jplayer audio,
div.jp-jplayer {
width:0px;
height:0px;
}
div.jp-jplayer {
background-color: #000000;
}
/* @group NO SOLUTION error feedback */
.jp-no-solution {
position:absolute;
width:390px;
margin-left:-202px;
left:50%;
top: 10px;
padding:5px;
font-size:.8em;
background-color:#3a2a45;
border-top:2px solid #554461;
border-left:2px solid #554461;
border-right:2px solid #180a1f;
border-bottom:2px solid #180a1f;
color:#FFF;
display:none;
}
.jp-no-solution a {
color:#FFF;
}
.jp-no-solution span {
font-size:1em;
display:block;
text-align:center;
font-weight:bold;
}
.jp-audio .jp-no-solution {
width:190px;
margin-left:-102px;
}
/* @end */

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,20 @@
Copyright (c) 2011 Gregory Eremin
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,28 @@
# Vkontakte (VK.com) API music search tool
Don't forget to log in to [vk.com API](http://vk.com/developers.php?oid=-17680044&p=Open_API)
### CoffeeScript example
```coffeescript
vk_music = new VkontakteMusic
vk_music.search "Kasabian", "L.S.F. (Lost Souls Forever)", "2:17", (url) ->
audio = document.createElement "audio"
audio.setAttribute "src", url
document.getElementsByTagName("body")[0].appendChild audio
audio.play()
false
```
### JavaScript example
```javascript
var vk_music;
vk_music = new VkontakteMusic;
vk_music.search("Kasabian", "L.S.F. (Lost Souls Forever)", "2:17", function(url) {
var audio;
audio = document.createElement("audio");
audio.setAttribute("src", url);
document.getElementsByTagName("body")[0].appendChild(audio);
audio.play();
return false;
});
```

View File

@ -0,0 +1,9 @@
/*
* Vkontakte (VK.com) API music search tool
* https://github.com/magnolia-fan/vkontakte_music_search
*
* Copyright 2011, Gregory Eremin
* Licensed under the MIT license.
* https://raw.github.com/magnolia-fan/vkontakte_music_search/master/LICENSE
*/
var VkontakteMusic;VkontakteMusic=function(){function a(){}return a.prototype.query_results={},a.prototype.search=function(a,b,c,d,e){var f,g;return e==null&&(e=!1),f=this.prepareQuery(a,b),this.query_results[f]!=null&&!e&&d(this.query_results[f]),g=this,VK.Api.call("audio.search",{q:f},function(h){var i,j;return i=g.range(h.response,a,b,c),j=null,i.length>0&&(j=i[0].url),g.query_results[f]=i,d(e?i:j)})},a.prototype.range=function(a,b,c,d){var e,f,g,h,i;typeof d=="string"&&(d=d.split(":"),d=parseInt(d[0],10)*60+parseInt(d[1],10));for(f=0,i=a.length;f<i;f++){g=a[f];if(typeof g!="object")continue;g.score=0,g.artist=this.trim(g.artist),g.title=this.trim(g.title),h=0,g.artist.length>0&&(g.artist===b?h+=10:g.artist.split(b).length===2?h+=5:g.title.split(b).length===2&&(h+=4)),g.artist.length>0&&(g.title===c?h+=10:g.title.split(c).length===2&&(h+=5)),parseInt(g.duration,10)===d?h+=15:(e=Math.abs(parseInt(g.duration,10)-d),e<10&&(h+=10-e)),a[f].score=h}return a.length>0&&typeof a[0]!="object"&&(a.splice(0,1),a.sort(function(a,b){return b.score-a.score})),a},a.prototype.prepareQuery=function(a,b){return a+" "+b.replace(/\(.*\)/i,"").split("/")[0]},a.prototype.trim=function(a){while(a.indexOf(" ")!==-1)a=a.replace(" "," ");return a.charAt(0)===" "&&(a=a.substring(1)),a.charAt(a.length-1)===" "&&(a=a.substring(0,a.length-1)),a},a}()

View File

@ -0,0 +1,70 @@
###
* Vkontakte (VK.com) API music search tool
* https://github.com/magnolia-fan/vkontakte_music_search
*
* Copyright 2011, Gregory Eremin
* Licensed under the MIT license.
* https://raw.github.com/magnolia-fan/vkontakte_music_search/master/LICENSE
###
class window.VkontakteMusic
query_results: {}
search: (artist, track, duration, callback, return_all = false) ->
query = this.prepareQuery artist, track
if @query_results[query]? and not return_all
callback @query_results[query]
that = this
VK.Api.call 'audio.search', q: query, (r) ->
results = that.range r.response, artist, track, duration
top_result = null
if results.length > 0
top_result = results[0].url
that.query_results[query] = results
callback if return_all then results else top_result
range: (data, artist, track, duration) ->
if typeof duration is 'string'
duration = duration.split ':'
duration = parseInt(duration[0], 10) * 60 + parseInt(duration[1], 10)
for item, i in data
if typeof item isnt 'object'
continue
item.score = 0;
item.artist = this.trim(item.artist);
item.title = this.trim(item.title);
score = 0
if item.artist.length > 0
if item.artist == artist
score += 10
else if item.artist.split(artist).length is 2
score += 5
else if item.title.split(artist).length is 2
score += 4
if item.artist.length > 0
if item.title == track
score += 10
else if item.title.split(track).length is 2
score += 5
if parseInt(item.duration, 10) == duration
score += 15
else
delta = Math.abs parseInt(item.duration, 10) - duration
score += (10 - delta) if delta < 10
data[i].score = score
if data.length > 0 and typeof data[0] isnt 'object'
data.splice 0, 1
data.sort (a, b) ->
b.score - a.score
data
prepareQuery: (artist, track) ->
artist+" "+track.replace(/\(.*\)/i, '').split('/')[0]
trim: (str) ->
while str.indexOf(' ') isnt -1
str = str.replace ' ', ' '
if str.charAt(0) is ' '
str = str.substring 1
if str.charAt(str.length - 1) is ' '
str = str.substring 0, str.length - 1
str