Поправлена структура, разделы в подразделы до 3.3

This commit is contained in:
Юлия Болотина
2022-08-04 12:59:10 +03:00
parent c46d95f4cd
commit a014b62268
787 changed files with 162678 additions and 782 deletions

View File

@@ -0,0 +1,16 @@
# Library
This folder holds copies of external libraries.
The purpose of having static copies of external libraries is so that the examples work after simply cloning or downloading the repository.
## Aurora Solution
To enable the [Aurora](https://github.com/audiocogs) solution inside jPlayer, the following files were added to the library.
- [aurora.js](https://github.com/audiocogs/aurora.js)
- [aac.js](https://github.com/audiocogs/aac.js)
- [flac.js](https://github.com/audiocogs/flac.js)
- [mp3.js](https://github.com/audiocogs/mp3.js)
- [ogg.js](https://github.com/audiocogs/ogg.js)
- [opus.js](https://github.com/audiocogs/opus.js)
- [vorbis.js](https://github.com/audiocogs/vorbis.js)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,793 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
exports.FLACDemuxer = require('./src/demuxer');
exports.FLACDecoder = require('./src/decoder');
require('./src/ogg');
},{"./src/decoder":2,"./src/demuxer":3,"./src/ogg":4}],2:[function(require,module,exports){
/*
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
* Original C version from FFmpeg (c) 2003 Alex Beregszaszi
* JavaScript port by Devon Govett and Jens Nockert of Official.fm Labs
*
* Licensed under the same terms as the original. The original
* license follows.
*
* FLAC.js is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FLAC.js is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
var AV = (window.AV);
var FLACDecoder = AV.Decoder.extend(function() {
AV.Decoder.register('flac', this);
this.prototype.setCookie = function(cookie) {
this.cookie = cookie;
// initialize arrays
this.decoded = [];
for (var i = 0; i < this.format.channelsPerFrame; i++) {
this.decoded[i] = new Int32Array(cookie.maxBlockSize);
}
// for 24 bit lpc frames, this is used to simulate a 64 bit int
this.lpc_total = new Int32Array(2);
};
const BLOCK_SIZES = new Int16Array([
0, 192, 576 << 0, 576 << 1, 576 << 2, 576 << 3, 0, 0,
256 << 0, 256 << 1, 256 << 2, 256 << 3, 256 << 4, 256 << 5, 256 << 6, 256 << 7
]);
const SAMPLE_RATES = new Int32Array([
0, 88200, 176400, 192000,
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
0, 0, 0, 0
]);
const SAMPLE_SIZES = new Int8Array([
0, 8, 12, 0, 16, 20, 24, 0
]);
const MAX_CHANNELS = 8,
CHMODE_INDEPENDENT = 0,
CHMODE_LEFT_SIDE = 8,
CHMODE_RIGHT_SIDE = 9,
CHMODE_MID_SIDE = 10;
this.prototype.readChunk = function() {
var stream = this.bitstream;
if (!stream.available(32))
return;
// frame sync code
if ((stream.read(15) & 0x7FFF) !== 0x7FFC)
throw new Error('Invalid sync code');
var isVarSize = stream.read(1), // variable block size stream code
bsCode = stream.read(4), // block size
srCode = stream.read(4), // sample rate code
chMode = stream.read(4), // channel mode
bpsCode = stream.read(3); // bits per sample
stream.advance(1); // reserved bit
// channels
this.chMode = chMode;
var channels;
if (chMode < MAX_CHANNELS) {
channels = chMode + 1;
this.chMode = CHMODE_INDEPENDENT;
} else if (chMode <= CHMODE_MID_SIDE) {
channels = 2;
} else {
throw new Error('Invalid channel mode');
}
if (channels !== this.format.channelsPerFrame)
throw new Error('Switching channel layout mid-stream not supported.');
// bits per sample
if (bpsCode === 3 || bpsCode === 7)
throw new Error('Invalid sample size code');
this.bps = SAMPLE_SIZES[bpsCode];
if (this.bps !== this.format.bitsPerChannel)
throw new Error('Switching bits per sample mid-stream not supported.');
// sample number or frame number
// see http://www.hydrogenaudio.org/forums/index.php?s=ea7085ffe6d57132c36e6105c0d434c9&showtopic=88390&pid=754269&st=0&#entry754269
var ones = 0;
while (stream.read(1) === 1)
ones++;
var frame_or_sample_num = stream.read(7 - ones);
for (; ones > 1; ones--) {
stream.advance(2); // == 2
frame_or_sample_num = (frame_or_sample_num << 6) | stream.read(6);
}
// block size
if (bsCode === 0)
throw new Error('Reserved blocksize code');
else if (bsCode === 6)
this.blockSize = stream.read(8) + 1;
else if (bsCode === 7)
this.blockSize = stream.read(16) + 1;
else
this.blockSize = BLOCK_SIZES[bsCode];
// sample rate
var sampleRate;
if (srCode < 12)
sampleRate = SAMPLE_RATES[srCode];
else if (srCode === 12)
sampleRate = stream.read(8) * 1000;
else if (srCode === 13)
sampleRate = stream.read(16);
else if (srCode === 14)
sampleRate = stream.read(16) * 10;
else
throw new Error('Invalid sample rate code');
stream.advance(8); // skip CRC check
// subframes
for (var i = 0; i < channels; i++)
this.decodeSubframe(i);
stream.align();
stream.advance(16); // skip CRC frame footer
var is32 = this.bps > 16,
output = new ArrayBuffer(this.blockSize * channels * (is32 ? 4 : 2)),
buf = is32 ? new Int32Array(output) : new Int16Array(output),
blockSize = this.blockSize,
decoded = this.decoded,
j = 0;
switch (this.chMode) {
case CHMODE_INDEPENDENT:
for (var k = 0; k < blockSize; k++) {
for (var i = 0; i < channels; i++) {
buf[j++] = decoded[i][k];
}
}
break;
case CHMODE_LEFT_SIDE:
for (var i = 0; i < blockSize; i++) {
var left = decoded[0][i],
right = decoded[1][i];
buf[j++] = left;
buf[j++] = (left - right);
}
break;
case CHMODE_RIGHT_SIDE:
for (var i = 0; i < blockSize; i++) {
var left = decoded[0][i],
right = decoded[1][i];
buf[j++] = (left + right);
buf[j++] = right;
}
break;
case CHMODE_MID_SIDE:
for (var i = 0; i < blockSize; i++) {
var left = decoded[0][i],
right = decoded[1][i];
left -= right >> 1;
buf[j++] = (left + right);
buf[j++] = left;
}
break;
}
return buf;
};
this.prototype.decodeSubframe = function(channel) {
var wasted = 0,
stream = this.bitstream,
blockSize = this.blockSize,
decoded = this.decoded;
this.curr_bps = this.bps;
if (channel === 0) {
if (this.chMode === CHMODE_RIGHT_SIDE)
this.curr_bps++;
} else {
if (this.chMode === CHMODE_LEFT_SIDE || this.chMode === CHMODE_MID_SIDE)
this.curr_bps++;
}
if (stream.read(1))
throw new Error("Invalid subframe padding");
var type = stream.read(6);
if (stream.read(1)) {
wasted = 1;
while (!stream.read(1))
wasted++;
this.curr_bps -= wasted;
}
if (this.curr_bps > 32)
throw new Error("decorrelated bit depth > 32 (" + this.curr_bps + ")");
if (type === 0) {
var tmp = stream.read(this.curr_bps, true);
for (var i = 0; i < blockSize; i++)
decoded[channel][i] = tmp;
} else if (type === 1) {
var bps = this.curr_bps;
for (var i = 0; i < blockSize; i++)
decoded[channel][i] = stream.read(bps, true);
} else if ((type >= 8) && (type <= 12)) {
this.decode_subframe_fixed(channel, type & ~0x8);
} else if (type >= 32) {
this.decode_subframe_lpc(channel, (type & ~0x20) + 1);
} else {
throw new Error("Invalid coding type");
}
if (wasted) {
for (var i = 0; i < blockSize; i++)
decoded[channel][i] <<= wasted;
}
};
this.prototype.decode_subframe_fixed = function(channel, predictor_order) {
var decoded = this.decoded[channel],
stream = this.bitstream,
bps = this.curr_bps;
// warm up samples
for (var i = 0; i < predictor_order; i++)
decoded[i] = stream.read(bps, true);
this.decode_residuals(channel, predictor_order);
var a = 0, b = 0, c = 0, d = 0;
if (predictor_order > 0)
a = decoded[predictor_order - 1];
if (predictor_order > 1)
b = a - decoded[predictor_order - 2];
if (predictor_order > 2)
c = b - decoded[predictor_order - 2] + decoded[predictor_order - 3];
if (predictor_order > 3)
d = c - decoded[predictor_order - 2] + 2 * decoded[predictor_order - 3] - decoded[predictor_order - 4];
switch (predictor_order) {
case 0:
break;
case 1:
case 2:
case 3:
case 4:
var abcd = new Int32Array([a, b, c, d]),
blockSize = this.blockSize;
for (var i = predictor_order; i < blockSize; i++) {
abcd[predictor_order - 1] += decoded[i];
for (var j = predictor_order - 2; j >= 0; j--) {
abcd[j] += abcd[j + 1];
}
decoded[i] = abcd[0];
}
break;
default:
throw new Error("Invalid Predictor Order " + predictor_order);
}
};
this.prototype.decode_subframe_lpc = function(channel, predictor_order) {
var stream = this.bitstream,
decoded = this.decoded[channel],
bps = this.curr_bps,
blockSize = this.blockSize;
// warm up samples
for (var i = 0; i < predictor_order; i++) {
decoded[i] = stream.read(bps, true);
}
var coeff_prec = stream.read(4) + 1;
if (coeff_prec === 16)
throw new Error("Invalid coefficient precision");
var qlevel = stream.read(5, true);
if (qlevel < 0)
throw new Error("Negative qlevel, maybe buggy stream");
var coeffs = new Int32Array(32);
for (var i = 0; i < predictor_order; i++) {
coeffs[i] = stream.read(coeff_prec, true);
}
this.decode_residuals(channel, predictor_order);
if (this.bps <= 16) {
for (var i = predictor_order; i < blockSize - 1; i += 2) {
var d = decoded[i - predictor_order],
s0 = 0, s1 = 0, c = 0;
for (var j = predictor_order - 1; j > 0; j--) {
c = coeffs[j];
s0 += c * d;
d = decoded[i - j];
s1 += c * d;
}
c = coeffs[0];
s0 += c * d;
d = decoded[i] += (s0 >> qlevel);
s1 += c * d;
decoded[i + 1] += (s1 >> qlevel);
}
if (i < blockSize) {
var sum = 0;
for (var j = 0; j < predictor_order; j++)
sum += coeffs[j] * decoded[i - j - 1];
decoded[i] += (sum >> qlevel);
}
} else {
// simulate 64 bit integer using an array of two 32 bit ints
var total = this.lpc_total;
for (var i = predictor_order; i < blockSize; i++) {
// reset total to 0
total[0] = 0;
total[1] = 0;
for (j = 0; j < predictor_order; j++) {
// simulate `total += coeffs[j] * decoded[i - j - 1]`
multiply_add(total, coeffs[j], decoded[i - j - 1]);
}
// simulate `decoded[i] += total >> qlevel`
// we know that qlevel < 32 since it is a 5 bit field (see above)
decoded[i] += (total[0] >>> qlevel) | (total[1] << (32 - qlevel));
}
}
};
const TWO_PWR_32_DBL = Math.pow(2, 32);
// performs `total += a * b` on a simulated 64 bit int
// total is an Int32Array(2)
// a and b are JS numbers (32 bit ints)
function multiply_add(total, a, b) {
// multiply a * b (we can use normal JS multiplication for this)
var r = a * b;
var n = r < 0;
if (n)
r = -r;
var r_low = (r % TWO_PWR_32_DBL) | 0;
var r_high = (r / TWO_PWR_32_DBL) | 0;
if (n) {
r_low = ~r_low + 1;
r_high = ~r_high;
}
// add result to total
var a48 = total[1] >>> 16;
var a32 = total[1] & 0xFFFF;
var a16 = total[0] >>> 16;
var a00 = total[0] & 0xFFFF;
var b48 = r_high >>> 16;
var b32 = r_high & 0xFFFF;
var b16 = r_low >>> 16;
var b00 = r_low & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
// store result back in total
total[0] = (c16 << 16) | c00;
total[1] = (c48 << 16) | c32;
}
const INT_MAX = 32767;
this.prototype.decode_residuals = function(channel, predictor_order) {
var stream = this.bitstream,
method_type = stream.read(2);
if (method_type > 1)
throw new Error('Illegal residual coding method ' + method_type);
var rice_order = stream.read(4),
samples = (this.blockSize >>> rice_order);
if (predictor_order > samples)
throw new Error('Invalid predictor order ' + predictor_order + ' > ' + samples);
var decoded = this.decoded[channel],
sample = predictor_order,
i = predictor_order;
for (var partition = 0; partition < (1 << rice_order); partition++) {
var tmp = stream.read(method_type === 0 ? 4 : 5);
if (tmp === (method_type === 0 ? 15 : 31)) {
tmp = stream.read(5);
for (; i < samples; i++)
decoded[sample++] = stream.read(tmp, true);
} else {
for (; i < samples; i++)
decoded[sample++] = this.golomb(tmp, INT_MAX, 0);
}
i = 0;
}
};
const MIN_CACHE_BITS = 25;
this.prototype.golomb = function(k, limit, esc_len) {
var data = this.bitstream,
offset = data.bitPosition,
buf = data.peek(32 - offset) << offset,
v = 0;
var log = 31 - clz(buf | 1); // log2(buf)
if (log - k >= 32 - MIN_CACHE_BITS && 32 - log < limit) {
buf >>>= log - k;
buf += (30 - log) << k;
data.advance(32 + k - log);
v = buf;
} else {
for (var i = 0; data.read(1) === 0; i++)
buf = data.peek(32 - offset) << offset;
if (i < limit - 1) {
if (k)
buf = data.read(k);
else
buf = 0;
v = buf + (i << k);
} else if (i === limit - 1) {
buf = data.read(esc_len);
v = buf + 1;
} else {
v = -1;
}
}
return (v >> 1) ^ -(v & 1);
};
// Should be in the damned standard library...
function clz(input) {
var output = 0,
curbyte = 0;
while(true) { // emulate goto in JS using the break statement :D
curbyte = input >>> 24;
if (curbyte) break;
output += 8;
curbyte = input >>> 16;
if (curbyte & 0xff) break;
output += 8;
curbyte = input >>> 8;
if (curbyte & 0xff) break;
output += 8;
curbyte = input;
if (curbyte & 0xff) break;
output += 8;
return output;
}
if (!(curbyte & 0xf0))
output += 4;
else
curbyte >>>= 4;
if (curbyte & 0x8)
return output;
if (curbyte & 0x4)
return output + 1;
if (curbyte & 0x2)
return output + 2;
if (curbyte & 0x1)
return output + 3;
// shouldn't get here
return output + 4;
}
});
module.exports = FLACDecoder;
},{}],3:[function(require,module,exports){
/*
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
* By Devon Govett and Jens Nockert of Official.fm Labs
*
* FLAC.js is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FLAC.js is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
var AV = (window.AV);
var FLACDemuxer = AV.Demuxer.extend(function() {
AV.Demuxer.register(this);
this.probe = function(buffer) {
return buffer.peekString(0, 4) === 'fLaC';
}
const STREAMINFO = 0,
PADDING = 1,
APPLICATION = 2,
SEEKTABLE = 3,
VORBIS_COMMENT = 4,
CUESHEET = 5,
PICTURE = 6,
INVALID = 127,
STREAMINFO_SIZE = 34;
this.prototype.readChunk = function() {
var stream = this.stream;
if (!this.readHeader && stream.available(4)) {
if (stream.readString(4) !== 'fLaC')
return this.emit('error', 'Invalid FLAC file.');
this.readHeader = true;
}
while (stream.available(1) && !this.last) {
if (!this.readBlockHeaders) {
var tmp = stream.readUInt8();
this.last = (tmp & 0x80) === 0x80,
this.type = tmp & 0x7F,
this.size = stream.readUInt24();
}
if (!this.foundStreamInfo && this.type !== STREAMINFO)
return this.emit('error', 'STREAMINFO must be the first block');
if (!stream.available(this.size))
return;
switch (this.type) {
case STREAMINFO:
if (this.foundStreamInfo)
return this.emit('error', 'STREAMINFO can only occur once.');
if (this.size !== STREAMINFO_SIZE)
return this.emit('error', 'STREAMINFO size is wrong.');
this.foundStreamInfo = true;
var bitstream = new AV.Bitstream(stream);
var cookie = {
minBlockSize: bitstream.read(16),
maxBlockSize: bitstream.read(16),
minFrameSize: bitstream.read(24),
maxFrameSize: bitstream.read(24)
};
this.format = {
formatID: 'flac',
sampleRate: bitstream.read(20),
channelsPerFrame: bitstream.read(3) + 1,
bitsPerChannel: bitstream.read(5) + 1
};
this.emit('format', this.format);
this.emit('cookie', cookie);
var sampleCount = bitstream.read(36);
this.emit('duration', sampleCount / this.format.sampleRate * 1000 | 0);
stream.advance(16); // skip MD5 hashes
this.readBlockHeaders = false;
break;
/*
I am only looking at the least significant 32 bits of sample number and offset data
This is more than sufficient for the longest flac file I have (~50 mins 2-channel 16-bit 44.1k which uses about 7.5% of the UInt32 space for the largest offset)
Can certainly be improved by storing sample numbers and offests as doubles, but would require additional overriding of the searchTimestamp and seek functions (possibly more?)
Also the flac faq suggests it would be possible to find frame lengths and thus create seek points on the fly via decoding but I assume this would be slow
I may look into these thigns though as my project progresses
*/
case SEEKTABLE:
for(var s=0; s<this.size/18; s++)
{
if(stream.peekUInt32(0) == 0xFFFFFFFF && stream.peekUInt32(1) == 0xFFFFFFFF)
{
//placeholder, ignore
stream.advance(18);
} else {
if(stream.readUInt32() > 0)
{
this.emit('error', 'Seek points with sample number >UInt32 not supported');
}
var samplenum = stream.readUInt32();
if(stream.readUInt32() > 0)
{
this.emit('error', 'Seek points with stream offset >UInt32 not supported');
}
var offset = stream.readUInt32();
stream.advance(2);
this.addSeekPoint(offset, samplenum);
}
}
break;
case VORBIS_COMMENT:
// see http://www.xiph.org/vorbis/doc/v-comment.html
this.metadata || (this.metadata = {});
var len = stream.readUInt32(true);
this.metadata.vendor = stream.readString(len);
var length = stream.readUInt32(true);
for (var i = 0; i < length; i++) {
len = stream.readUInt32(true);
var str = stream.readString(len, 'utf8'),
idx = str.indexOf('=');
this.metadata[str.slice(0, idx).toLowerCase()] = str.slice(idx + 1);
}
// TODO: standardize field names across formats
break;
case PICTURE:
var type = stream.readUInt32();
if (type !== 3) { // make sure this is album art (type 3)
stream.advance(this.size - 4);
} else {
var mimeLen = stream.readUInt32(),
mime = stream.readString(mimeLen),
descLen = stream.readUInt32(),
description = stream.readString(descLen),
width = stream.readUInt32(),
height = stream.readUInt32(),
depth = stream.readUInt32(),
colors = stream.readUInt32(),
length = stream.readUInt32(),
picture = stream.readBuffer(length);
this.metadata || (this.metadata = {});
this.metadata.coverArt = picture;
}
// does anyone want the rest of the info?
break;
default:
stream.advance(this.size);
this.readBlockHeaders = false;
}
if (this.last && this.metadata)
this.emit('metadata', this.metadata);
}
while (stream.available(1) && this.last) {
var buffer = stream.readSingleBuffer(stream.remainingBytes());
this.emit('data', buffer);
}
}
});
module.exports = FLACDemuxer;
},{}],4:[function(require,module,exports){
var AV = (window.AV);
// if ogg.js exists, register a plugin
try {
var OggDemuxer = (window.AV.OggDemuxer);
} catch (e) {};
if (!OggDemuxer) return;
OggDemuxer.plugins.push({
magic: "\177FLAC",
init: function() {
this.list = new AV.BufferList();
this.stream = new AV.Stream(this.list);
},
readHeaders: function(packet) {
var stream = this.stream;
this.list.append(new AV.Buffer(packet));
stream.advance(5); // magic
if (stream.readUInt8() != 1)
throw new Error('Unsupported FLAC version');
stream.advance(3);
if (stream.peekString(0, 4) != 'fLaC')
throw new Error('Not flac');
this.flac = AV.Demuxer.find(stream.peekSingleBuffer(0, stream.remainingBytes()));
if (!this.flac)
throw new Error('Flac demuxer not found');
this.flac.prototype.readChunk.call(this);
return true;
},
readPacket: function(packet) {
this.list.append(new AV.Buffer(packet));
this.flac.prototype.readChunk.call(this);
}
});
},{}]},{},[1])
//# sourceMappingURL=flac.js.map

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,243 @@
/*
* CirclePlayer for the jPlayer Plugin (jQuery)
* http://www.jplayer.org
*
* Copyright (c) 2009 - 2012 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
*
* Version: 1.0.1 (jPlayer 2.1.0+)
* Date: 30th May 2011
*
* Author: Mark J Panaghiston @thepag
*
* CirclePlayer prototype developed by:
* Mark Boas @maboa
* Silvia Benvenuti @aulentina
* Jussi Kalliokoski @quinirill
*
* Inspired by :
* Neway @imneway http://imneway.net/ http://forrst.com/posts/Untitled-CPt
* and
* Liam McKay @liammckay http://dribbble.com/shots/50882-Purple-Play-Pause
*
* Standing on the shoulders of :
* John Resig @jresig
* Mark Panaghiston @thepag
* Louis-Rémi Babé @Louis_Remi
*/
var CirclePlayer = function(jPlayerSelector, media, options) {
var self = this,
defaults = {
// solution: "flash, html", // For testing Flash with CSS3
supplied: "m4a, oga",
// Android 2.3 corrupts media element if preload:"none" is used.
// preload: "none", // No point preloading metadata since no times are displayed. It helps keep the buffer state correct too.
cssSelectorAncestor: "#cp_container_1",
cssSelector: {
play: ".cp-play",
pause: ".cp-pause"
}
},
cssSelector = {
bufferHolder: ".cp-buffer-holder",
buffer1: ".cp-buffer-1",
buffer2: ".cp-buffer-2",
progressHolder: ".cp-progress-holder",
progress1: ".cp-progress-1",
progress2: ".cp-progress-2",
circleControl: ".cp-circle-control"
};
this.cssClass = {
gt50: "cp-gt50",
fallback: "cp-fallback"
};
this.spritePitch = 104;
this.spriteRatio = 0.24; // Number of steps / 100
this.player = $(jPlayerSelector);
this.media = $.extend({}, media);
this.options = $.extend(true, {}, defaults, options); // Deep copy
this.cssTransforms = Modernizr.csstransforms;
this.audio = {};
this.dragging = false; // Indicates if the progressbar is being 'dragged'.
this.eventNamespace = ".CirclePlayer"; // So the events can easily be removed in destroy.
this.jq = {};
$.each(cssSelector, function(entity, cssSel) {
self.jq[entity] = $(self.options.cssSelectorAncestor + " " + cssSel);
});
this._initSolution();
this._initPlayer();
};
CirclePlayer.prototype = {
_createHtml: function() {
},
_initPlayer: function() {
var self = this;
this.player.jPlayer(this.options);
this.player.bind($.jPlayer.event.ready + this.eventNamespace, function(event) {
if(event.jPlayer.html.used && event.jPlayer.html.audio.available) {
self.audio = $(this).data("jPlayer").htmlElement.audio;
}
$(this).jPlayer("setMedia", self.media);
self._initCircleControl();
});
this.player.bind($.jPlayer.event.play + this.eventNamespace, function(event) {
$(this).jPlayer("pauseOthers");
});
// This event fired as play time increments
this.player.bind($.jPlayer.event.timeupdate + this.eventNamespace, function(event) {
if (!self.dragging) {
self._timeupdate(event.jPlayer.status.currentPercentAbsolute);
}
});
// This event fired as buffered time increments
this.player.bind($.jPlayer.event.progress + this.eventNamespace, function(event) {
var percent = 0;
if((typeof self.audio.buffered === "object") && (self.audio.buffered.length > 0)) {
if(self.audio.duration > 0) {
var bufferTime = 0;
for(var i = 0; i < self.audio.buffered.length; i++) {
bufferTime += self.audio.buffered.end(i) - self.audio.buffered.start(i);
// console.log(i + " | start = " + self.audio.buffered.start(i) + " | end = " + self.audio.buffered.end(i) + " | bufferTime = " + bufferTime + " | duration = " + self.audio.duration);
}
percent = 100 * bufferTime / self.audio.duration;
} // else the Metadata has not been read yet.
// console.log("percent = " + percent);
} else { // Fallback if buffered not supported
// percent = event.jPlayer.status.seekPercent;
percent = 0; // Cleans up the inital conditions on all browsers, since seekPercent defaults to 100 when object is undefined.
}
self._progress(percent); // Problem here at initial condition. Due to the Opera clause above of buffered.length > 0 above... Removing it means Opera's white buffer ring never shows like with polyfill.
// Firefox 4 does not always give the final progress event when buffered = 100%
});
this.player.bind($.jPlayer.event.ended + this.eventNamespace, function(event) {
self._resetSolution();
});
},
_initSolution: function() {
if (this.cssTransforms) {
this.jq.progressHolder.show();
this.jq.bufferHolder.show();
}
else {
this.jq.progressHolder.addClass(this.cssClass.gt50).show();
this.jq.progress1.addClass(this.cssClass.fallback);
this.jq.progress2.hide();
this.jq.bufferHolder.hide();
}
this._resetSolution();
},
_resetSolution: function() {
if (this.cssTransforms) {
this.jq.progressHolder.removeClass(this.cssClass.gt50);
this.jq.progress1.css({'transform': 'rotate(0deg)'});
this.jq.progress2.css({'transform': 'rotate(0deg)'}).hide();
}
else {
this.jq.progress1.css('background-position', '0 ' + this.spritePitch + 'px');
}
},
_initCircleControl: function() {
var self = this;
this.jq.circleControl.grab({
onstart: function(){
self.dragging = true;
}, onmove: function(event){
var pc = self._getArcPercent(event.position.x, event.position.y);
self.player.jPlayer("playHead", pc).jPlayer("play");
self._timeupdate(pc);
}, onfinish: function(event){
self.dragging = false;
var pc = self._getArcPercent(event.position.x, event.position.y);
self.player.jPlayer("playHead", pc).jPlayer("play");
}
});
},
_timeupdate: function(percent) {
var degs = percent * 3.6+"deg";
var spriteOffset = (Math.floor((Math.round(percent))*this.spriteRatio)-1)*-this.spritePitch;
if (percent <= 50) {
if (this.cssTransforms) {
this.jq.progressHolder.removeClass(this.cssClass.gt50);
this.jq.progress1.css({'transform': 'rotate(' + degs + ')'});
this.jq.progress2.hide();
} else { // fall back
this.jq.progress1.css('background-position', '0 '+spriteOffset+'px');
}
} else if (percent <= 100) {
if (this.cssTransforms) {
this.jq.progressHolder.addClass(this.cssClass.gt50);
this.jq.progress1.css({'transform': 'rotate(180deg)'});
this.jq.progress2.css({'transform': 'rotate(' + degs + ')'});
this.jq.progress2.show();
} else { // fall back
this.jq.progress1.css('background-position', '0 '+spriteOffset+'px');
}
}
},
_progress: function(percent) {
var degs = percent * 3.6+"deg";
if (this.cssTransforms) {
if (percent <= 50) {
this.jq.bufferHolder.removeClass(this.cssClass.gt50);
this.jq.buffer1.css({'transform': 'rotate(' + degs + ')'});
this.jq.buffer2.hide();
} else if (percent <= 100) {
this.jq.bufferHolder.addClass(this.cssClass.gt50);
this.jq.buffer1.css({'transform': 'rotate(180deg)'});
this.jq.buffer2.show();
this.jq.buffer2.css({'transform': 'rotate(' + degs + ')'});
}
}
},
_getArcPercent: function(pageX, pageY) {
var offset = this.jq.circleControl.offset(),
x = pageX - offset.left - this.jq.circleControl.width()/2,
y = pageY - offset.top - this.jq.circleControl.height()/2,
theta = Math.atan2(y,x);
if (theta > -1 * Math.PI && theta < -0.5 * Math.PI) {
theta = 2 * Math.PI + theta;
}
// theta is now value between -0.5PI and 1.5PI
// ready to be normalized and applied
return (theta + Math.PI / 2) / 2 * Math.PI * 10;
},
setMedia: function(media) {
this.media = $.extend({}, media);
this.player.jPlayer("setMedia", this.media);
},
play: function(time) {
this.player.jPlayer("play", time);
},
pause: function(time) {
this.player.jPlayer("pause", time);
},
destroy: function() {
this.player.unbind(this.eventNamespace);
this.player.jPlayer("destroy");
}
};

View File

@@ -0,0 +1,201 @@
/*
jQuery grab
https://github.com/jussi-kalliokoski/jQuery.grab
Ported from Jin.js::gestures
https://github.com/jussi-kalliokoski/jin.js/
Created by Jussi Kalliokoski
Licensed under MIT License.
Includes fix for IE
*/
(function($){
var extend = $.extend,
mousedown = 'mousedown',
mousemove = 'mousemove',
mouseup = 'mouseup',
touchstart = 'touchstart',
touchmove = 'touchmove',
touchend = 'touchend',
touchcancel = 'touchcancel';
function unbind(elem, type, func){
if (type.substr(0,5) !== 'touch'){ // A temporary fix for IE8 data passing problem in Jin.
return $(elem).unbind(type, func);
}
var fnc, i;
for (i=0; i<bind._binds.length; i++){
if (bind._binds[i].elem === elem && bind._binds[i].type === type && bind._binds[i].func === func){
if (document.addEventListener){
elem.removeEventListener(type, bind._binds[i].fnc, false);
} else {
elem.detachEvent('on'+type, bind._binds[i].fnc);
}
bind._binds.splice(i--, 1);
}
}
}
function bind(elem, type, func, pass){
if (type.substr(0,5) !== 'touch'){ // A temporary fix for IE8 data passing problem in Jin.
return $(elem).bind(type, pass, func);
}
var fnc, i;
if (bind[type]){
return bind[type].bind(elem, type, func, pass);
}
fnc = function(e){
if (!e){ // Fix some ie bugs...
e = window.event;
}
if (!e.stopPropagation){
e.stopPropagation = function(){ this.cancelBubble = true; };
}
e.data = pass;
func.call(elem, e);
};
if (document.addEventListener){
elem.addEventListener(type, fnc, false);
} else {
elem.attachEvent('on' + type, fnc);
}
bind._binds.push({elem: elem, type: type, func: func, fnc: fnc});
}
function grab(elem, options)
{
var data = {
move: {x: 0, y: 0},
offset: {x: 0, y: 0},
position: {x: 0, y: 0},
start: {x: 0, y: 0},
affects: document.documentElement,
stopPropagation: false,
preventDefault: true,
touch: true // Implementation unfinished, and doesn't support multitouch
};
extend(data, options);
data.element = elem;
bind(elem, mousedown, mouseDown, data);
if (data.touch){
bind(elem, touchstart, touchStart, data);
}
}
function ungrab(elem){
unbind(elem, mousedown, mousedown);
}
function mouseDown(e){
e.data.position.x = e.pageX;
e.data.position.y = e.pageY;
e.data.start.x = e.pageX;
e.data.start.y = e.pageY;
e.data.event = e;
if (e.data.onstart && e.data.onstart.call(e.data.element, e.data)){
return;
}
if (e.preventDefault && e.data.preventDefault){
e.preventDefault();
}
if (e.stopPropagation && e.data.stopPropagation){
e.stopPropagation();
}
bind(e.data.affects, mousemove, mouseMove, e.data);
bind(e.data.affects, mouseup, mouseUp, e.data);
}
function mouseMove(e){
if (e.preventDefault && e.data.preventDefault){
e.preventDefault();
}
if (e.stopPropagation && e.data.preventDefault){
e.stopPropagation();
}
e.data.move.x = e.pageX - e.data.position.x;
e.data.move.y = e.pageY - e.data.position.y;
e.data.position.x = e.pageX;
e.data.position.y = e.pageY;
e.data.offset.x = e.pageX - e.data.start.x;
e.data.offset.y = e.pageY - e.data.start.y;
e.data.event = e;
if (e.data.onmove){
e.data.onmove.call(e.data.element, e.data);
}
}
function mouseUp(e){
if (e.preventDefault && e.data.preventDefault){
e.preventDefault();
}
if (e.stopPropagation && e.data.stopPropagation){
e.stopPropagation();
}
unbind(e.data.affects, mousemove, mouseMove);
unbind(e.data.affects, mouseup, mouseUp);
e.data.event = e;
if (e.data.onfinish){
e.data.onfinish.call(e.data.element, e.data);
}
}
function touchStart(e){
e.data.position.x = e.touches[0].pageX;
e.data.position.y = e.touches[0].pageY;
e.data.start.x = e.touches[0].pageX;
e.data.start.y = e.touches[0].pageY;
e.data.event = e;
if (e.data.onstart && e.data.onstart.call(e.data.element, e.data)){
return;
}
if (e.preventDefault && e.data.preventDefault){
e.preventDefault();
}
if (e.stopPropagation && e.data.stopPropagation){
e.stopPropagation();
}
bind(e.data.affects, touchmove, touchMove, e.data);
bind(e.data.affects, touchend, touchEnd, e.data);
}
function touchMove(e){
if (e.preventDefault && e.data.preventDefault){
e.preventDefault();
}
if (e.stopPropagation && e.data.stopPropagation){
e.stopPropagation();
}
e.data.move.x = e.touches[0].pageX - e.data.position.x;
e.data.move.y = e.touches[0].pageY - e.data.position.y;
e.data.position.x = e.touches[0].pageX;
e.data.position.y = e.touches[0].pageY;
e.data.offset.x = e.touches[0].pageX - e.data.start.x;
e.data.offset.y = e.touches[0].pageY - e.data.start.y;
e.data.event = e;
if (e.data.onmove){
e.data.onmove.call(e.data.elem, e.data);
}
}
function touchEnd(e){
if (e.preventDefault && e.data.preventDefault){
e.preventDefault();
}
if (e.stopPropagation && e.data.stopPropagation){
e.stopPropagation();
}
unbind(e.data.affects, touchmove, touchMove);
unbind(e.data.affects, touchend, touchEnd);
e.data.event = e;
if (e.data.onfinish){
e.data.onfinish.call(e.data.element, e.data);
}
}
bind._binds = [];
$.fn.grab = function(a, b){
return this.each(function(){
return grab(this, a, b);
});
};
$.fn.ungrab = function(a){
return this.each(function(){
return ungrab(this, a);
});
};
})(jQuery);

View File

@@ -0,0 +1,551 @@
/*
* transform: A jQuery cssHooks adding cross-browser 2d transform capabilities to $.fn.css() and $.fn.animate()
*
* limitations:
* - requires jQuery 1.4.3+
* - Should you use the *translate* property, then your elements need to be absolutely positionned in a relatively positionned wrapper **or it will fail in IE678**.
* - transformOrigin is not accessible
*
* latest version and complete README available on Github:
* https://github.com/louisremi/jquery.transform.js
*
* Copyright 2011 @louis_remi
* Licensed under the MIT license.
*
* This saved you an hour of work?
* Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON
*
*/
(function( $, window, document, Math, undefined ) {
/*
* Feature tests and global variables
*/
var div = document.createElement("div"),
divStyle = div.style,
suffix = "Transform",
testProperties = [
"O" + suffix,
"ms" + suffix,
"Webkit" + suffix,
"Moz" + suffix
],
i = testProperties.length,
supportProperty,
supportMatrixFilter,
supportFloat32Array = "Float32Array" in window,
propertyHook,
propertyGet,
rMatrix = /Matrix([^)]*)/,
rAffine = /^\s*matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*(?:,\s*0(?:px)?\s*){2}\)\s*$/,
_transform = "transform",
_transformOrigin = "transformOrigin",
_translate = "translate",
_rotate = "rotate",
_scale = "scale",
_skew = "skew",
_matrix = "matrix";
// test different vendor prefixes of these properties
while ( i-- ) {
if ( testProperties[i] in divStyle ) {
$.support[_transform] = supportProperty = testProperties[i];
$.support[_transformOrigin] = supportProperty + "Origin";
continue;
}
}
// IE678 alternative
if ( !supportProperty ) {
$.support.matrixFilter = supportMatrixFilter = divStyle.filter === "";
}
// px isn't the default unit of these properties
$.cssNumber[_transform] = $.cssNumber[_transformOrigin] = true;
/*
* fn.css() hooks
*/
if ( supportProperty && supportProperty != _transform ) {
// Modern browsers can use jQuery.cssProps as a basic hook
$.cssProps[_transform] = supportProperty;
$.cssProps[_transformOrigin] = supportProperty + "Origin";
// Firefox needs a complete hook because it stuffs matrix with "px"
if ( supportProperty == "Moz" + suffix ) {
propertyHook = {
get: function( elem, computed ) {
return (computed ?
// remove "px" from the computed matrix
$.css( elem, supportProperty ).split("px").join(""):
elem.style[supportProperty]
);
},
set: function( elem, value ) {
// add "px" to matrices
elem.style[supportProperty] = /matrix\([^)p]*\)/.test(value) ?
value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, _matrix+"$1$2px,$3px"):
value;
}
};
/* Fix two jQuery bugs still present in 1.5.1
* - rupper is incompatible with IE9, see http://jqbug.com/8346
* - jQuery.css is not really jQuery.cssProps aware, see http://jqbug.com/8402
*/
} else if ( /^1\.[0-5](?:\.|$)/.test($.fn.jquery) ) {
propertyHook = {
get: function( elem, computed ) {
return (computed ?
$.css( elem, supportProperty.replace(/^ms/, "Ms") ):
elem.style[supportProperty]
);
}
};
}
/* TODO: leverage hardware acceleration of 3d transform in Webkit only
else if ( supportProperty == "Webkit" + suffix && support3dTransform ) {
propertyHook = {
set: function( elem, value ) {
elem.style[supportProperty] =
value.replace();
}
}
}*/
} else if ( supportMatrixFilter ) {
propertyHook = {
get: function( elem, computed, asArray ) {
var elemStyle = ( computed && elem.currentStyle ? elem.currentStyle : elem.style ),
matrix, data;
if ( elemStyle && rMatrix.test( elemStyle.filter ) ) {
matrix = RegExp.$1.split(",");
matrix = [
matrix[0].split("=")[1],
matrix[2].split("=")[1],
matrix[1].split("=")[1],
matrix[3].split("=")[1]
];
} else {
matrix = [1,0,0,1];
}
if ( ! $.cssHooks[_transformOrigin] ) {
matrix[4] = elemStyle ? parseInt(elemStyle.left, 10) || 0 : 0;
matrix[5] = elemStyle ? parseInt(elemStyle.top, 10) || 0 : 0;
} else {
data = $._data( elem, "transformTranslate", undefined );
matrix[4] = data ? data[0] : 0;
matrix[5] = data ? data[1] : 0;
}
return asArray ? matrix : _matrix+"(" + matrix + ")";
},
set: function( elem, value, animate ) {
var elemStyle = elem.style,
currentStyle,
Matrix,
filter,
centerOrigin;
if ( !animate ) {
elemStyle.zoom = 1;
}
value = matrix(value);
// rotate, scale and skew
Matrix = [
"Matrix("+
"M11="+value[0],
"M12="+value[2],
"M21="+value[1],
"M22="+value[3],
"SizingMethod='auto expand'"
].join();
filter = ( currentStyle = elem.currentStyle ) && currentStyle.filter || elemStyle.filter || "";
elemStyle.filter = rMatrix.test(filter) ?
filter.replace(rMatrix, Matrix) :
filter + " progid:DXImageTransform.Microsoft." + Matrix + ")";
if ( ! $.cssHooks[_transformOrigin] ) {
// center the transform origin, from pbakaus's Transformie http://github.com/pbakaus/transformie
if ( (centerOrigin = $.transform.centerOrigin) ) {
elemStyle[centerOrigin == "margin" ? "marginLeft" : "left"] = -(elem.offsetWidth/2) + (elem.clientWidth/2) + "px";
elemStyle[centerOrigin == "margin" ? "marginTop" : "top"] = -(elem.offsetHeight/2) + (elem.clientHeight/2) + "px";
}
// translate
// We assume that the elements are absolute positionned inside a relative positionned wrapper
elemStyle.left = value[4] + "px";
elemStyle.top = value[5] + "px";
} else {
$.cssHooks[_transformOrigin].set( elem, value );
}
}
};
}
// populate jQuery.cssHooks with the appropriate hook if necessary
if ( propertyHook ) {
$.cssHooks[_transform] = propertyHook;
}
// we need a unique setter for the animation logic
propertyGet = propertyHook && propertyHook.get || $.css;
/*
* fn.animate() hooks
*/
$.fx.step.transform = function( fx ) {
var elem = fx.elem,
start = fx.start,
end = fx.end,
pos = fx.pos,
transform = "",
precision = 1E5,
i, startVal, endVal, unit;
// fx.end and fx.start need to be converted to interpolation lists
if ( !start || typeof start === "string" ) {
// the following block can be commented out with jQuery 1.5.1+, see #7912
if ( !start ) {
start = propertyGet( elem, supportProperty );
}
// force layout only once per animation
if ( supportMatrixFilter ) {
elem.style.zoom = 1;
}
// replace "+=" in relative animations (-= is meaningless with transforms)
end = end.split("+=").join(start);
// parse both transform to generate interpolation list of same length
$.extend( fx, interpolationList( start, end ) );
start = fx.start;
end = fx.end;
}
i = start.length;
// interpolate functions of the list one by one
while ( i-- ) {
startVal = start[i];
endVal = end[i];
unit = +false;
switch ( startVal[0] ) {
case _translate:
unit = "px";
case _scale:
unit || ( unit = "");
transform = startVal[0] + "(" +
Math.round( (startVal[1][0] + (endVal[1][0] - startVal[1][0]) * pos) * precision ) / precision + unit +","+
Math.round( (startVal[1][1] + (endVal[1][1] - startVal[1][1]) * pos) * precision ) / precision + unit + ")"+
transform;
break;
case _skew + "X":
case _skew + "Y":
case _rotate:
transform = startVal[0] + "(" +
Math.round( (startVal[1] + (endVal[1] - startVal[1]) * pos) * precision ) / precision +"rad)"+
transform;
break;
}
}
fx.origin && ( transform = fx.origin + transform );
propertyHook && propertyHook.set ?
propertyHook.set( elem, transform, +true ):
elem.style[supportProperty] = transform;
};
/*
* Utility functions
*/
// turns a transform string into its "matrix(A,B,C,D,X,Y)" form (as an array, though)
function matrix( transform ) {
transform = transform.split(")");
var
trim = $.trim
, i = -1
// last element of the array is an empty string, get rid of it
, l = transform.length -1
, split, prop, val
, prev = supportFloat32Array ? new Float32Array(6) : []
, curr = supportFloat32Array ? new Float32Array(6) : []
, rslt = supportFloat32Array ? new Float32Array(6) : [1,0,0,1,0,0]
;
prev[0] = prev[3] = rslt[0] = rslt[3] = 1;
prev[1] = prev[2] = prev[4] = prev[5] = 0;
// Loop through the transform properties, parse and multiply them
while ( ++i < l ) {
split = transform[i].split("(");
prop = trim(split[0]);
val = split[1];
curr[0] = curr[3] = 1;
curr[1] = curr[2] = curr[4] = curr[5] = 0;
switch (prop) {
case _translate+"X":
curr[4] = parseInt(val, 10);
break;
case _translate+"Y":
curr[5] = parseInt(val, 10);
break;
case _translate:
val = val.split(",");
curr[4] = parseInt(val[0], 10);
curr[5] = parseInt(val[1] || 0, 10);
break;
case _rotate:
val = toRadian(val);
curr[0] = Math.cos(val);
curr[1] = Math.sin(val);
curr[2] = -Math.sin(val);
curr[3] = Math.cos(val);
break;
case _scale+"X":
curr[0] = +val;
break;
case _scale+"Y":
curr[3] = val;
break;
case _scale:
val = val.split(",");
curr[0] = val[0];
curr[3] = val.length>1 ? val[1] : val[0];
break;
case _skew+"X":
curr[2] = Math.tan(toRadian(val));
break;
case _skew+"Y":
curr[1] = Math.tan(toRadian(val));
break;
case _matrix:
val = val.split(",");
curr[0] = val[0];
curr[1] = val[1];
curr[2] = val[2];
curr[3] = val[3];
curr[4] = parseInt(val[4], 10);
curr[5] = parseInt(val[5], 10);
break;
}
// Matrix product (array in column-major order)
rslt[0] = prev[0] * curr[0] + prev[2] * curr[1];
rslt[1] = prev[1] * curr[0] + prev[3] * curr[1];
rslt[2] = prev[0] * curr[2] + prev[2] * curr[3];
rslt[3] = prev[1] * curr[2] + prev[3] * curr[3];
rslt[4] = prev[0] * curr[4] + prev[2] * curr[5] + prev[4];
rslt[5] = prev[1] * curr[4] + prev[3] * curr[5] + prev[5];
prev = [rslt[0],rslt[1],rslt[2],rslt[3],rslt[4],rslt[5]];
}
return rslt;
}
// turns a matrix into its rotate, scale and skew components
// algorithm from http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp
function unmatrix(matrix) {
var
scaleX
, scaleY
, skew
, A = matrix[0]
, B = matrix[1]
, C = matrix[2]
, D = matrix[3]
;
// Make sure matrix is not singular
if ( A * D - B * C ) {
// step (3)
scaleX = Math.sqrt( A * A + B * B );
A /= scaleX;
B /= scaleX;
// step (4)
skew = A * C + B * D;
C -= A * skew;
D -= B * skew;
// step (5)
scaleY = Math.sqrt( C * C + D * D );
C /= scaleY;
D /= scaleY;
skew /= scaleY;
// step (6)
if ( A * D < B * C ) {
A = -A;
B = -B;
skew = -skew;
scaleX = -scaleX;
}
// matrix is singular and cannot be interpolated
} else {
// In this case the elem shouldn't be rendered, hence scale == 0
scaleX = scaleY = skew = 0;
}
// The recomposition order is very important
// see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971
return [
[_translate, [+matrix[4], +matrix[5]]],
[_rotate, Math.atan2(B, A)],
[_skew + "X", Math.atan(skew)],
[_scale, [scaleX, scaleY]]
];
}
// build the list of transform functions to interpolate
// use the algorithm described at http://dev.w3.org/csswg/css3-2d-transforms/#animation
function interpolationList( start, end ) {
var list = {
start: [],
end: []
},
i = -1, l,
currStart, currEnd, currType;
// get rid of affine transform matrix
( start == "none" || isAffine( start ) ) && ( start = "" );
( end == "none" || isAffine( end ) ) && ( end = "" );
// if end starts with the current computed style, this is a relative animation
// store computed style as the origin, remove it from start and end
if ( start && end && !end.indexOf("matrix") && toArray( start ).join() == toArray( end.split(")")[0] ).join() ) {
list.origin = start;
start = "";
end = end.slice( end.indexOf(")") +1 );
}
if ( !start && !end ) { return; }
// start or end are affine, or list of transform functions are identical
// => functions will be interpolated individually
if ( !start || !end || functionList(start) == functionList(end) ) {
start && ( start = start.split(")") ) && ( l = start.length );
end && ( end = end.split(")") ) && ( l = end.length );
while ( ++i < l-1 ) {
start[i] && ( currStart = start[i].split("(") );
end[i] && ( currEnd = end[i].split("(") );
currType = $.trim( ( currStart || currEnd )[0] );
append( list.start, parseFunction( currType, currStart ? currStart[1] : 0 ) );
append( list.end, parseFunction( currType, currEnd ? currEnd[1] : 0 ) );
}
// otherwise, functions will be composed to a single matrix
} else {
list.start = unmatrix(matrix(start));
list.end = unmatrix(matrix(end))
}
return list;
}
function parseFunction( type, value ) {
var
// default value is 1 for scale, 0 otherwise
defaultValue = +(!type.indexOf(_scale)),
scaleX,
// remove X/Y from scaleX/Y & translateX/Y, not from skew
cat = type.replace( /e[XY]/, "e" );
switch ( type ) {
case _translate+"Y":
case _scale+"Y":
value = [
defaultValue,
value ?
parseFloat( value ):
defaultValue
];
break;
case _translate+"X":
case _translate:
case _scale+"X":
scaleX = 1;
case _scale:
value = value ?
( value = value.split(",") ) && [
parseFloat( value[0] ),
parseFloat( value.length>1 ? value[1] : type == _scale ? scaleX || value[0] : defaultValue+"" )
]:
[defaultValue, defaultValue];
break;
case _skew+"X":
case _skew+"Y":
case _rotate:
value = value ? toRadian( value ) : 0;
break;
case _matrix:
return unmatrix( value ? toArray(value) : [1,0,0,1,0,0] );
break;
}
return [[ cat, value ]];
}
function isAffine( matrix ) {
return rAffine.test(matrix);
}
function functionList( transform ) {
return transform.replace(/(?:\([^)]*\))|\s/g, "");
}
function append( arr1, arr2, value ) {
while ( value = arr2.shift() ) {
arr1.push( value );
}
}
// converts an angle string in any unit to a radian Float
function toRadian(value) {
return ~value.indexOf("deg") ?
parseInt(value,10) * (Math.PI * 2 / 360):
~value.indexOf("grad") ?
parseInt(value,10) * (Math.PI/200):
parseFloat(value);
}
// Converts "matrix(A,B,C,D,X,Y)" to [A,B,C,D,X,Y]
function toArray(matrix) {
// remove the unit of X and Y for Firefox
matrix = /([^,]*),([^,]*),([^,]*),([^,]*),([^,p]*)(?:px)?,([^)p]*)(?:px)?/.exec(matrix);
return [matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]];
}
$.transform = {
centerOrigin: "margin"
};
})( jQuery, window, document, Math );

View File

@@ -0,0 +1,2 @@
/* Modernizr custom build of 1.7pre: csstransforms */
window.Modernizr=function(a,b,c){function G(){}function F(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+" "+p.join(c+" ")+c).split(" ");return!!E(d,b)}function E(a,b){for(var d in a)if(k[a[d]]!==c&&(!b||b(a[d],j)))return!0}function D(a,b){return(""+a).indexOf(b)!==-1}function C(a,b){return typeof a===b}function B(a,b){return A(o.join(a+";")+(b||""))}function A(a){k.cssText=a}var d="1.7pre",e={},f=!0,g=b.documentElement,h=b.head||b.getElementsByTagName("head")[0],i="modernizr",j=b.createElement(i),k=j.style,l=b.createElement("input"),m=":)",n=Object.prototype.toString,o=" -webkit- -moz- -o- -ms- -khtml- ".split(" "),p="Webkit Moz O ms Khtml".split(" "),q={svg:"http://www.w3.org/2000/svg"},r={},s={},t={},u=[],v,w=function(a){var c=b.createElement("style"),d=b.createElement("div"),e;c.textContent=a+"{#modernizr{height:3px}}",h.appendChild(c),d.id="modernizr",g.appendChild(d),e=d.offsetHeight===3,c.parentNode.removeChild(c),d.parentNode.removeChild(d);return!!e},x=function(){function d(d,e){e=e||b.createElement(a[d]||"div");var f=(d="on"+d)in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=C(e[d],"function"),C(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),y=({}).hasOwnProperty,z;C(y,c)||C(y.call,c)?z=function(a,b){return b in a&&C(a.constructor.prototype[b],c)}:z=function(a,b){return y.call(a,b)},r.csstransforms=function(){return!!E(["transformProperty","WebkitTransform","MozTransform","OTransform","msTransform"])};for(var H in r)z(r,H)&&(v=H.toLowerCase(),e[v]=r[H](),u.push((e[v]?"":"no-")+v));e.input||G(),e.crosswindowmessaging=e.postmessage,e.historymanagement=e.history,e.addTest=function(a,b){a=a.toLowerCase();if(!e[a]){b=!!b(),g.className+=" "+(b?"":"no-")+a,e[a]=b;return e}},A(""),j=l=null,e._enableHTML5=f,e._version=d,g.className=g.className.replace(/\bno-js\b/,"")+" js "+u.join(" ");return e}(this,this.document)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,133 @@
/*
* Project: CirclePlayer
* http://www.jplayer.org
*
* Copyright (c) 2012 Happyworm Ltd
*
* Author: Silvia Benvenuti
* Edited by: Mark J Panaghiston
* Date: 2nd October 2012
* Artwork inspired by: http://forrst.com/posts/Untitled-CJz
*/
.cp-container {
position:relative;
width:104px; /* 200 - (2 * 48) */
height:104px;
background:url("bgr.jpg") 0 0 no-repeat;
padding:48px;
-webkit-tap-highlight-color:rgba(0,0,0,0);
}
.cp-container :focus {
border:none;
outline:0;
}
.cp-buffer-1,
.cp-buffer-2,
.cp-progress-1,
.cp-progress-2 {
position:absolute;
top:0;
left:0;
width:104px;
height:104px;
clip:rect(0px,52px,104px,0px);
-moz-border-radius:52px;
-webkit-border-radius:52px;
border-radius:52px;
}
.cp-buffer-1,
.cp-buffer-2 {
background:url("buffer.png") 0 0 no-repeat;
}
/* FALLBACK for .progress
* (24 steps starting from 1hr filled progress, Decrease second value by 104px for next step)
* (It needs the container selector to work. Or use div)
*/
.cp-container .cp-fallback {
background:url("progress_sprite.jpg") no-repeat;
background-position:0 104px;
}
.cp-progress-1,
.cp-progress-2 {
background:url("progress.png") 0 0 no-repeat;
}
.cp-buffer-holder,
.cp-progress-holder,
.cp-circle-control {
position:absolute;
width:104px;
height:104px;
}
.cp-circle-control {
cursor:pointer;
}
.cp-buffer-holder,
.cp-progress-holder {
clip:rect(0px,104px,104px,52px);
display:none;
}
/* This is needed when progress is greater than 50% or for fallback */
.cp-buffer-holder.cp-gt50,
.cp-progress-holder.cp-gt50,
.cp-progress-1.cp-fallback{
clip:rect(auto, auto, auto, auto);
}
.cp-controls {
margin:0;
padding:26px;
}
.cp-controls li{
list-style-type:none;
display:block;
/*IE Fix*/
position:absolute;
}
.cp-controls li a{
position:relative;
display:block;
width:50px;
height:50px;
text-indent:-9999px;
z-index:1;
cursor:pointer;
}
.cp-controls .cp-play {
background:url("controls.jpg") 0 0 no-repeat;
}
.cp-controls .cp-play:hover {
background:url("controls.jpg") -50px 0 no-repeat;
}
.cp-controls .cp-pause {
background:url("controls.jpg") 0 -50px no-repeat;
}
.cp-controls .cp-pause:hover {
background:url("controls.jpg") -50px -50px no-repeat;
}
.cp-jplayer {
width:0;
height:0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,450 @@
(function() {
if ( !document.addEventListener && !document.removeEventListener && !document.dispatchEvent ) {
var events = {};
var addEventListener = function( eventName, callBack ) {
eventName = ( eventName === "DOMContentLoaded" ) ? "readystatechange" : eventName;
if ( Event[ eventName.toUpperCase() ] || eventName === "readystatechange" ) {
document.attachEvent( "on" + eventName, callBack );
return;
}
if ( !events[ eventName ] ) {
events[ eventName ] = {
events: [],
queue: [],
active: false
};
}
if ( events[ eventName ].active ) {
events[ eventName ].queue.push( callBack );
} else {
events[ eventName ].events.push( callBack );
}
};
var removeEventListener = function( eventName, callBack ) {
eventName = ( eventName === "DOMContentLoaded" ) ? "readystatechange" : eventName;
var i = 0,
listeners = events[ eventName ];
if ( Event[ eventName.toUpperCase() ] || eventName === "readystatechange" ) {
document.detachEvent( "on" + eventName, callBack );
return;
}
if ( !listeners ) {
return;
}
for ( i = listeners.events.length - 1; i >= 0; i-- ) {
if ( callBack === listeners.events[ i ] ) {
delete listeners.events[ i ];
}
}
for ( i = listeners.queue.length - 1; i >= 0; i-- ) {
if ( callBack === listeners.queue[ i ] ) {
delete listeners.queue[ i ];
}
}
};
var dispatchEvent = function( eventObject ) {
var evt,
self = this,
eventInterface,
listeners,
eventName = eventObject.type,
queuedListener;
// A string was passed, create event object
if ( !eventName ) {
eventName = eventObject;
eventInterface = Popcorn.events.getInterface( eventName );
if ( eventInterface ) {
evt = document.createEvent( eventInterface );
evt.initCustomEvent( eventName, true, true, window, 1 );
}
}
listeners = events[ eventName ];
if ( listeners ) {
listeners.active = true;
for ( var i = 0; i < listeners.events.length; i++ ) {
if ( listeners.events[ i ] ) {
listeners.events[ i ].call( self, evt, self );
}
}
if ( listeners.queue.length ) {
while ( listeners.queue.length ) {
queuedListener = listeners.queue.shift();
if ( queuedListener ) {
listeners.events.push( queuedListener );
}
}
}
listeners.active = false;
listeners.events.forEach(function( listener ) {
if ( !listener ) {
listeners.events.splice( listeners.events.indexOf( listener ), 1 );
}
});
listeners.queue.forEach(function( listener ) {
if ( !listener ) {
listeners.queue.splice( listeners.queue.indexOf( listener ), 1 );
}
});
}
};
document.addEventListener = addEventListener;
document.removeEventListener = removeEventListener;
document.dispatchEvent = dispatchEvent;
}
if ( !Event.prototype.preventDefault ) {
Event.prototype.preventDefault = function() {
this.returnValue = false;
};
}
if ( !Event.prototype.stopPropagation ) {
Event.prototype.stopPropagation = function() {
this.cancelBubble = true;
};
}
window.addEventListener = window.addEventListener || function( event, callBack ) {
event = "on" + event;
window.attachEvent( event, callBack );
};
window.removeEventListener = window.removeEventListener || function( event, callBack ) {
event = "on" + event;
window.detachEvent( event, callBack );
};
HTMLScriptElement.prototype.addEventListener = HTMLScriptElement.prototype.addEventListener || function( event, callBack ) {
event = ( event === "load" ) ? "onreadystatechange" : "on" + event;
if( event === "onreadystatechange" ){
callBack.readyStateCheck = callBack.readyStateCheck || function( e ){
if( self.readyState === "loaded" ){
callBack( e );
}
};
}
this.attachEvent( event, ( callBack.readyStateCheck || callBack ) );
};
HTMLScriptElement.prototype.removeEventListener = HTMLScriptElement.prototype.removeEventListener || function( event, callBack ) {
event = ( event === "load" ) ? "onreadystatechange" : "on" + event;
this.detachEvent( event, ( callBack.readyStateCheck || callBack ) );
};
document.createEvent = document.createEvent || function ( type ) {
return {
type : null,
target : null,
currentTarget : null,
cancelable : false,
detail: false,
bubbles : false,
initEvent : function (type, bubbles, cancelable) {
this.type = type;
},
initCustomEvent: function(type, bubbles, cancelable, detail) {
this.type = type;
this.detail = detail;
},
stopPropagation : function () {},
stopImmediatePropagation : function () {}
}
};
Array.prototype.forEach = Array.prototype.forEach || function( fn, context ) {
var obj = this,
hasOwn = Object.prototype.hasOwnProperty;
if ( !obj || !fn ) {
return {};
}
context = context || this;
var key, len;
for ( key in obj ) {
if ( hasOwn.call( obj, key ) ) {
fn.call( context, obj[ key ], key, obj );
}
}
return obj;
};
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if ( !Array.prototype.map ) {
Array.prototype.map = function( callback, thisArg ) {
var T, A, k;
if ( this == null ) {
throw new TypeError( "this is null or not defined" );
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object( this );
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if ( {}.toString.call( callback ) != "[object Function]" ) {
throw new TypeError( callback + " is not a function" );
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if ( thisArg ) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
// the standard built-in constructor with that name and len is the value of len.
A = new Array( len );
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while( k < len ) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if ( k in O ) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[ k ];
// ii. Let mappedValue be the result of calling the Call internal method of callback
// with T as the this value and argument list containing kValue, k, and O.
mappedValue = callback.call( T, kValue, k, O );
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true},
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
// For best browser support, use the following:
A[ k ] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
if ( !Array.prototype.indexOf ) {
Array.prototype.indexOf = function ( searchElement /*, fromIndex */ ) {
if ( this == null) {
throw new TypeError();
}
var t = Object( this ),
len = t.length >>> 0;
if ( len === 0 ) {
return -1;
}
var n = 0;
if ( arguments.length > 0 ) {
n = Number( arguments[ 1 ] );
if ( n != n ) { // shortcut for verifying if it's NaN
n = 0;
} else if ( n != 0 && n != Infinity && n != -Infinity ) {
n = ( n > 0 || -1 ) * Math.floor( Math.abs( n ) );
}
}
if ( n >= len ) {
return -1;
}
var k = n >= 0 ? n : Math.max( len - Math.abs( n ), 0 );
for (; k < len; k++ ) {
if ( k in t && t[ k ] === searchElement ) {
return k;
}
}
return -1;
}
}
if ( typeof String.prototype.trim !== "function" ) {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, "");
};
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
Object.keys = (function () {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
if ( !Object.defineProperties ) {
Object.defineProperties = function(obj, properties) {
function convertToDescriptor(desc) {
function hasProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function isCallable(v) {
// NB: modify as necessary if other values than functions are callable.
return typeof v === "function";
}
if (typeof desc !== "object" || desc === null)
throw new TypeError("bad desc");
var d = {};
if (hasProperty(desc, "enumerable"))
d.enumerable = !!obj.enumerable;
if (hasProperty(desc, "configurable"))
d.configurable = !!desc.configurable;
if (hasProperty(desc, "value"))
d.value = obj.value;
if (hasProperty(desc, "writable"))
d.writable = !!desc.writable;
if ( hasProperty(desc, "get") ) {
var g = desc.get;
if (!isCallable(g) && g !== "undefined")
throw new TypeError("bad get");
d.get = g;
}
if ( hasProperty(desc, "set") ) {
var s = desc.set;
if (!isCallable(s) && s !== "undefined")
throw new TypeError("bad set");
d.set = s;
}
if (("get" in d || "set" in d) && ("value" in d || "writable" in d))
throw new TypeError("identity-confused descriptor");
return d;
}
if (typeof obj !== "object" || obj === null)
throw new TypeError("bad obj");
properties = Object(properties);
var keys = Object.keys(properties);
var descs = [];
for (var i = 0; i < keys.length; i++)
descs.push([keys[i], convertToDescriptor(properties[keys[i]])]);
for (var i = 0; i < descs.length; i++)
Object.defineProperty(obj, descs[i][0], descs[i][1]);
return obj;
};
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,437 @@
(function( Popcorn ) {
// combines calls of two function calls into one
var combineFn = function( first, second ) {
first = first || Popcorn.nop;
second = second || Popcorn.nop;
return function() {
first.apply( this, arguments );
second.apply( this, arguments );
};
};
// ID string matching
var rIdExp = /^(#([\w\-\_\.]+))$/;
Popcorn.player = function( name, player ) {
// return early if a player already exists under this name
if ( Popcorn[ name ] ) {
return;
}
player = player || {};
var playerFn = function( target, src, options ) {
options = options || {};
// List of events
var date = new Date() / 1000,
baselineTime = date,
currentTime = 0,
readyState = 0,
volume = 1,
muted = false,
events = {},
// The container div of the resource
container = typeof target === "string" ? Popcorn.dom.find( target ) : target,
basePlayer = {},
timeout,
popcorn;
if ( !Object.prototype.__defineGetter__ ) {
basePlayer = container || document.createElement( "div" );
}
// copies a div into the media object
for( var val in container ) {
// don't copy properties if using container as baseplayer
if ( val in basePlayer ) {
continue;
}
if ( typeof container[ val ] === "object" ) {
basePlayer[ val ] = container[ val ];
} else if ( typeof container[ val ] === "function" ) {
basePlayer[ val ] = (function( value ) {
// this is a stupid ugly kludgy hack in honour of Safari
// in Safari a NodeList is a function, not an object
if ( "length" in container[ value ] && !container[ value ].call ) {
return container[ value ];
} else {
return function() {
return container[ value ].apply( container, arguments );
};
}
}( val ));
} else {
Popcorn.player.defineProperty( basePlayer, val, {
get: (function( value ) {
return function() {
return container[ value ];
};
}( val )),
set: Popcorn.nop,
configurable: true
});
}
}
var timeupdate = function() {
date = new Date() / 1000;
if ( !basePlayer.paused ) {
basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
basePlayer.dispatchEvent( "timeupdate" );
timeout = setTimeout( timeupdate, 10 );
}
baselineTime = date;
};
basePlayer.play = function() {
this.paused = false;
if ( basePlayer.readyState >= 4 ) {
baselineTime = new Date() / 1000;
basePlayer.dispatchEvent( "play" );
timeupdate();
}
};
basePlayer.pause = function() {
this.paused = true;
basePlayer.dispatchEvent( "pause" );
};
Popcorn.player.defineProperty( basePlayer, "currentTime", {
get: function() {
return currentTime;
},
set: function( val ) {
// make sure val is a number
currentTime = +val;
basePlayer.dispatchEvent( "timeupdate" );
return currentTime;
},
configurable: true
});
Popcorn.player.defineProperty( basePlayer, "volume", {
get: function() {
return volume;
},
set: function( val ) {
// make sure val is a number
volume = +val;
basePlayer.dispatchEvent( "volumechange" );
return volume;
},
configurable: true
});
Popcorn.player.defineProperty( basePlayer, "muted", {
get: function() {
return muted;
},
set: function( val ) {
// make sure val is a number
muted = +val;
basePlayer.dispatchEvent( "volumechange" );
return muted;
},
configurable: true
});
Popcorn.player.defineProperty( basePlayer, "readyState", {
get: function() {
return readyState;
},
set: function( val ) {
readyState = val;
return readyState;
},
configurable: true
});
// Adds an event listener to the object
basePlayer.addEventListener = function( evtName, fn ) {
if ( !events[ evtName ] ) {
events[ evtName ] = [];
}
events[ evtName ].push( fn );
return fn;
};
// Removes an event listener from the object
basePlayer.removeEventListener = function( evtName, fn ) {
var i,
listeners = events[ evtName ];
if ( !listeners ){
return;
}
// walk backwards so we can safely splice
for ( i = events[ evtName ].length - 1; i >= 0; i-- ) {
if( fn === listeners[ i ] ) {
listeners.splice(i, 1);
}
}
return fn;
};
// Can take event object or simple string
basePlayer.dispatchEvent = function( oEvent ) {
var evt,
self = this,
eventInterface,
eventName = oEvent.type;
// A string was passed, create event object
if ( !eventName ) {
eventName = oEvent;
eventInterface = Popcorn.events.getInterface( eventName );
if ( eventInterface ) {
evt = document.createEvent( eventInterface );
evt.initEvent( eventName, true, true, window, 1 );
}
}
if ( events[ eventName ] ) {
for ( var i = events[ eventName ].length - 1; i >= 0; i-- ) {
events[ eventName ][ i ].call( self, evt, self );
}
}
};
// Attempt to get src from playerFn parameter
basePlayer.src = src || "";
basePlayer.duration = 0;
basePlayer.paused = true;
basePlayer.ended = 0;
options && options.events && Popcorn.forEach( options.events, function( val, key ) {
basePlayer.addEventListener( key, val, false );
});
// true and undefined returns on canPlayType means we should attempt to use it,
// false means we cannot play this type
if ( player._canPlayType( container.nodeName, src ) !== false ) {
if ( player._setup ) {
player._setup.call( basePlayer, options );
} else {
// there is no setup, which means there is nothing to load
basePlayer.readyState = 4;
basePlayer.dispatchEvent( "loadedmetadata" );
basePlayer.dispatchEvent( "loadeddata" );
basePlayer.dispatchEvent( "canplaythrough" );
}
} else {
// Asynchronous so that users can catch this event
setTimeout( function() {
basePlayer.dispatchEvent( "error" );
}, 0 );
}
popcorn = new Popcorn.p.init( basePlayer, options );
if ( player._teardown ) {
popcorn.destroy = combineFn( popcorn.destroy, function() {
player._teardown.call( basePlayer, options );
});
}
return popcorn;
};
playerFn.canPlayType = player._canPlayType = player._canPlayType || Popcorn.nop;
Popcorn[ name ] = Popcorn.player.registry[ name ] = playerFn;
};
Popcorn.player.registry = {};
Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
object.__defineGetter__( description, options.get || Popcorn.nop );
object.__defineSetter__( description, options.set || Popcorn.nop );
};
// player queue is to help players queue things like play and pause
// HTML5 video's play and pause are asynch, but do fire in sequence
// play() should really mean "requestPlay()" or "queuePlay()" and
// stash a callback that will play the media resource when it's ready to be played
Popcorn.player.playerQueue = function() {
var _queue = [],
_running = false;
return {
next: function() {
_running = false;
_queue.shift();
_queue[ 0 ] && _queue[ 0 ]();
},
add: function( callback ) {
_queue.push(function() {
_running = true;
callback && callback();
});
// if there is only one item on the queue, start it
!_running && _queue[ 0 ]();
}
};
};
// Popcorn.smart will attempt to find you a wrapper or player. If it can't do that,
// it will default to using an HTML5 video in the target.
Popcorn.smart = function( target, src, options ) {
var node = typeof target === "string" ? Popcorn.dom.find( target ) : target,
i, srci, j, media, mediaWrapper, popcorn, srcLength,
// We leave HTMLVideoElement and HTMLAudioElement wrappers out
// of the mix, since we'll default to HTML5 video if nothing
// else works. Waiting on #1254 before we add YouTube to this.
wrappers = "HTMLYouTubeVideoElement HTMLVimeoVideoElement HTMLSoundCloudAudioElement HTMLNullVideoElement".split(" ");
if ( !node ) {
Popcorn.error( "Specified target `" + target + "` was not found." );
return;
}
// If our src is not an array, create an array of one.
src = typeof src === "string" ? [ src ] : src;
// Loop through each src, and find the first playable.
for ( i = 0, srcLength = src.length; i < srcLength; i++ ) {
srci = src[ i ];
// See if we can use a wrapper directly, if not, try players.
for ( j = 0; j < wrappers.length; j++ ) {
mediaWrapper = Popcorn[ wrappers[ j ] ];
if ( mediaWrapper && mediaWrapper._canPlaySrc( srci ) === "probably" ) {
media = mediaWrapper( node );
popcorn = Popcorn( media, options );
// Set src, but not until after we return the media so the caller
// can get error events, if any.
setTimeout( function() {
media.src = srci;
}, 0 );
return popcorn;
}
}
// No wrapper can play this, check players.
for ( var key in Popcorn.player.registry ) {
if ( Popcorn.player.registry.hasOwnProperty( key ) ) {
if ( Popcorn.player.registry[ key ].canPlayType( node.nodeName, srci ) ) {
// Popcorn.smart( player, src, /* options */ )
return Popcorn[ key ]( node, srci, options );
}
}
}
}
// If we don't have any players or wrappers that can handle this,
// Default to using HTML5 video. Similar to the HTMLVideoElement
// wrapper, we put a video in the div passed to us via:
// Popcorn.smart( div, src, options )
var videoHTML,
videoElement,
videoID = Popcorn.guid( "popcorn-video-" ),
videoHTMLContainer = document.createElement( "div" );
videoHTMLContainer.style.width = "100%";
videoHTMLContainer.style.height = "100%";
// If we only have one source, do not bother with source elements.
// This means we don't have the IE9 hack,
// and we can properly listen to error events.
// That way an error event can be told to backup to Flash if it fails.
if ( src.length === 1 ) {
videoElement = document.createElement( "video" );
videoElement.id = videoID;
node.appendChild( videoElement );
setTimeout( function() {
// Hack to decode html characters like &amp; to &
var decodeDiv = document.createElement( "div" );
decodeDiv.innerHTML = src[ 0 ];
videoElement.src = decodeDiv.firstChild.nodeValue;
}, 0 );
return Popcorn( '#' + videoID, options );
}
node.appendChild( videoHTMLContainer );
// IE9 doesn't like dynamic creation of source elements on <video>
// so we do it in one shot via innerHTML.
videoHTML = '<video id="' + videoID + '" preload=auto autobuffer>';
for ( i = 0, srcLength = src.length; i < srcLength; i++ ) {
videoHTML += '<source src="' + src[ i ] + '">';
}
videoHTML += "</video>";
videoHTMLContainer.innerHTML = videoHTML;
if ( options && options.events && options.events.error ) {
node.addEventListener( "error", options.events.error, false );
}
return Popcorn( '#' + videoID, options );
};
})( Popcorn );

View File

@@ -0,0 +1,143 @@
// PLUGIN: Subtitle
(function ( Popcorn ) {
var i = 0,
createDefaultContainer = function( context, id ) {
var ctxContainer = context.container = document.createElement( "div" ),
style = ctxContainer.style,
media = context.media;
var updatePosition = function() {
var position = context.position();
// the video element must have height and width defined
style.fontSize = "18px";
style.width = media.offsetWidth + "px";
style.top = position.top + media.offsetHeight - ctxContainer.offsetHeight - 40 + "px";
style.left = position.left + "px";
setTimeout( updatePosition, 10 );
};
ctxContainer.id = id || Popcorn.guid();
style.position = "absolute";
style.color = "white";
style.textShadow = "black 2px 2px 6px";
style.fontWeight = "bold";
style.textAlign = "center";
updatePosition();
context.media.parentNode.appendChild( ctxContainer );
return ctxContainer;
};
/**
* Subtitle popcorn plug-in
* Displays a subtitle over the video, or in the target div
* Options parameter will need a start, and end.
* Optional parameters are target and text.
* Start is the time that you want this plug-in to execute
* End is the time that you want this plug-in to stop executing
* Target is the id of the document element that the content is
* appended to, this target element must exist on the DOM
* Text is the text of the subtitle you want to display.
*
* @param {Object} options
*
* Example:
var p = Popcorn('#video')
.subtitle({
start: 5, // seconds, mandatory
end: 15, // seconds, mandatory
text: 'Hellow world', // optional
target: 'subtitlediv', // optional
} )
*
*/
Popcorn.plugin( "subtitle" , {
manifest: {
about: {
name: "Popcorn Subtitle Plugin",
version: "0.1",
author: "Scott Downe",
website: "http://scottdowne.wordpress.com/"
},
options: {
start: {
elem: "input",
type: "text",
label: "Start"
},
end: {
elem: "input",
type: "text",
label: "End"
},
target: "subtitle-container",
text: {
elem: "input",
type: "text",
label: "Text"
}
}
},
_setup: function( options ) {
var newdiv = document.createElement( "div" );
newdiv.id = "subtitle-" + i++;
newdiv.style.display = "none";
// Creates a div for all subtitles to use
( !this.container && ( !options.target || options.target === "subtitle-container" ) ) &&
createDefaultContainer( this );
// if a target is specified, use that
if ( options.target && options.target !== "subtitle-container" ) {
// In case the target doesn't exist in the DOM
options.container = document.getElementById( options.target ) || createDefaultContainer( this, options.target );
} else {
// use shared default container
options.container = this.container;
}
document.getElementById( options.container.id ) && document.getElementById( options.container.id ).appendChild( newdiv );
options.innerContainer = newdiv;
options.showSubtitle = function() {
options.innerContainer.innerHTML = options.text || "";
};
},
/**
* @member subtitle
* The start function will be executed when the currentTime
* of the video reaches the start time provided by the
* options variable
*/
start: function( event, options ){
options.innerContainer.style.display = "inline";
options.showSubtitle( options, options.text );
},
/**
* @member subtitle
* The end function will be executed when the currentTime
* of the video reaches the end time provided by the
* options variable
*/
end: function( event, options ) {
options.innerContainer.style.display = "none";
options.innerContainer.innerHTML = "";
},
_teardown: function ( options ) {
options.container.removeChild( options.innerContainer );
}
});
})( Popcorn );