$(function() {
/*
h1 is 0.5-12 times as large as h0
w1 is 0.5-12*ratio as large as w0
h1 = (h0 - 24) / 2
w1 = h1 * ratio
w0 = pageWidth - 8 - w1
*/
var $body = $("body"),
$document = $(document),
$window = $(window),
$editor,
$panel,
$player = [],
$players,
$timelines,
$timelineLarge,
$timelineSmall,
find = "cinema",
matches = [3, 22, 24, 55, 57, 61, 70, 105, 118, 122, 150, 152, 155],
pageWidth = $document.width() - 384 - 2,
posterFrame = 1515,
points = [2059, 2748],
videoId = document.location.hash.substring(1),
videoUrl = "/" + videoId + "/96p." + ($.support.video.webm ? "webm": "mp4"),
stripTimeline = false;
$.getJSON("/" + videoId + "/data/video.json", function(video) {
var duration = video.duration,
videoRatio = video.aspectRatio,
videoHeight = 96,
videoWidth = parseInt(video.aspectRatio*videoHeight),
position = duration/2;
videoWidth += videoWidth%2;
videoUrl = video.baseUrl + "/" + video.profiles[0] + "p." + ($.support.video.webm ? "webm": "mp4");
//resizeVideoPlayers(pageWidth);
Ox.Editor = function(options, self) {
var self = self || {},
that = new Ox.Element("div", self)
.defaults({
cuts: [],
duration: 0,
find: "",
largeTimeline: true,
matches: [],
points: [0, 0],
position: 0,
posterFrame: 0,
subtitles: [],
videoHeight: 0,
videoId: "",
videoWidth: 0,
videoSize: "large",
width: 0
})
.options(options || {})
.addClass("OxEditor");
$.extend(self, {
$player: [],
$timeline: [],
controlsHeight: 16,
margin: 8,
videoRatio: self.options.videoWidth / self.options.videoHeight
});
self.sizes = getSizes();
$.each(["play", "in", "out"], function(i, type) {
self.$player[i] = new Ox.VideoPlayer({
duration: self.options.duration,
find: self.options.find,
height: self.sizes.player[i].height,
id: "player" + Ox.toTitleCase(type),
points: self.options.points,
position: type == "play" ? self.options.position : self.options.points[type == "in" ? 0 : 1],
posterFrame: self.options.posterFrame,
subtitles: self.options.subtitles,
type: type,
url: videoUrl,
width: self.sizes.player[i].width
})
.css({
left: self.sizes.player[i].left + "px",
top: self.sizes.player[i].top + "px"
})
.appendTo(that);
if (type == "in" || type == "out") {
self.$player[i].bindEvent({
change: function() {
goToPoint(type);
},
set: function() {
setPoint(type);
}
})
}
});
self.$player[0].bindEvent("change", changePlayer);
self.$timeline[0] = new Ox.LargeTimeline({
cuts: self.options.cuts,
duration: self.options.duration,
find: self.options.find,
id: "timelineLarge",
matches: self.options.matches,
points: self.options.points,
position: self.options.position,
subtitles: self.options.subtitles,
videoId: self.options.videoId,
width: self.sizes.timeline[0].width
})
.css({
left: self.sizes.timeline[0].left + "px",
top: self.sizes.timeline[0].top + "px"
})
.bindEvent("change", changeTimelineLarge)
.bindEvent("changeEnd", changeTimelineLarge)
.appendTo(that);
self.$timeline[1] = new Ox.SmallTimeline({
cuts: self.options.cuts,
duration: self.options.duration,
find: self.options.find,
id: "timelineSmall",
matches: self.options.matches,
points: self.options.points,
position: self.options.position,
subtitles: self.options.subtitles,
videoId: self.options.videoId,
width: self.sizes.timeline[1].width
})
.css({
left: self.sizes.timeline[1].left + "px",
top: self.sizes.timeline[1].top + "px"
})
.bindEvent("change", changeTimelineSmall)
.appendTo(that);
that.addEvent({
key_alt_left: function() {
movePositionBy(-0.04);
},
key_alt_right: function() {
movePositionBy(0.04);
},
key_alt_shift_left: function() {
movePositionTo("cut", -1);
},
key_alt_shift_right: function() {
movePositionTo("cut", 1);
},
key_closebracket: function() {
goToPoint("out");
},
key_comma: function() {
movePositionTo("subtitle", -1);
},
key_dot: function() {
movePositionTo("subtitle", 1);
},
key_down: function() {
movePositionBy(self.options.width - 2 * self.margin);
},
key_i: function() {
setPoint("in");
},
key_left: function() {
movePositionBy(-1);
},
key_m: toggleMute,
key_o: function() {
setPoint("out");
},
key_openbracket: function() {
goToPoint("in");
},
key_p: playInToOut,
key_right: function() {
movePositionBy(1);
},
key_shift_comma: function() {
movePositionTo("match", -1)
},
key_shift_dot: function() {
movePositionTo("match", 1)
},
key_shift_down: function() {
movePositionBy(self.options.duration);
},
key_shift_left: function() {
movePositionBy(-60);
},
key_shift_right: function() {
movePositionBy(60);
},
key_shift_up: function() {
movePositionBy(-self.options.duration);
},
key_space: togglePlay,
key_up: function() {
movePositionBy(-(self.options.width - 2 * self.margin));
}
});
that.gainFocus();
function changePlayer(event, data) {
self.options.position = data.position;
self.$timeline[0].options({
position: data.position
});
self.$timeline[1].options({
position: data.position
});
}
function changeTimelineLarge(event, data) {
self.options.position = data.position;
self.$player[0].options({
position: data.position
});
self.$timeline[1].options({
position: data.position
});
}
function changeTimelineSmall(event, data) {
self.options.position = data.position;
self.$player[0].options({
position: data.position
});
self.$timeline[0].options({
position: data.position
});
}
function getNextPosition(type, direction) {
var found = false,
position = 0,
positions;
if (type == "cut") {
positions = self.options.cuts;
} else if (type == "match") {
positions = $.map(self.options.matches, function(v, i) {
return self.options.subtitles[v]["in"];
});
} else if (type == "subtitle") {
positions = $.map(self.options.subtitles, function(v, i) {
return v["in"];
});
}
direction == -1 && positions.reverse();
$.each(positions, function(i, v) {
if (direction == 1 ? v > self.options.position : v < self.options.position) {
position = v;
found = true;
return false;
}
});
direction == -1 && positions.reverse();
if (!found) {
position = positions[direction == 1 ? 0 : positions.length - 1];
}
return position;
}
function getSizes() {
var size = {
player: [],
timeline: []
};
size.player[0] = {
left: self.margin / 2,
top: self.margin / 2,
width: Math.round((self.options.width - 3 * self.margin + (self.controlsHeight + self.margin) / 2 * self.videoRatio) * 2/3),
}
size.player[0].height = Math.round(size.player[0].width / self.videoRatio);
size.player[1] = {
left: size.player[0].left + size.player[0].width + self.margin,
top: size.player[0].top,
width: self.options.width - 3 * self.margin - size.player[0].width
}
size.player[1].height = Math.ceil(size.player[1].width / self.videoRatio)
size.player[2] = {
left: size.player[1].left,
top: size.player[0].top + size.player[1].height + self.controlsHeight + self.margin,
width: size.player[1].width,
height: size.player[0].height - size.player[1].height - self.controlsHeight - self.margin
}
size.timeline[0] = {
left: self.margin / 2,
top: size.player[0].height + self.controlsHeight + 1.5 * self.margin,
width: self.options.width - 2 * self.margin,
height: 64
}
size.timeline[1] = {
left: size.timeline[0].left,
top: size.timeline[0].top + size.timeline[0].height + self.margin,
width: size.timeline[0].width
}
return size;
}
function goToPoint(point) {
self.options.position = self.options.points[point == "in" ? 0 : 1];
setPosition();
that.triggerEvent("change", {
position: self.options.position
});
}
function movePositionBy(sec) {
self.options.position = Ox.limit(self.options.position + sec, 0, self.options.duration);
setPosition();
that.triggerEvent("change", {
position: self.options.position
});
}
function movePositionTo(type, direction) {
self.options.position = getNextPosition(type, direction);
setPosition();
that.triggerEvent("change", {
position: self.options.position
});
}
function playInToOut() {
self.$player[0].playInToOut();
}
function resizeEditor(event, data) {
var width = data - 2 * margin + 100;
resizeVideoPlayers(width);
$timelineLarge.options({
width: width
});
$timelineSmall.options({
width: width
});
}
function resizePlayers() {
$.each(self.$player, function(i, v) {
v.options({
width: size[i].width,
height: size[i].height
})
.css({
left: size[i].left + "px",
top: size[i].top + "px",
});
});
}
function setPoint(point) {
self.options.points[point == "in" ? 0 : 1] = self.options.position;
self.$player[point == "in" ? 1 : 2].options({
position: self.options.position
});
if (self.options.points[1] < self.options.points[0]) {
self.options.points[point == "in" ? 1 : 0] = self.options.position;
self.$player[point == "in" ? 2 : 1].options({
position: self.options.position
});
}
$.each(self.$player, function(i, v) {
v.options({
points: self.options.points
});
});
$.each(self.$timeline, function(i, v) {
v.options({
points: self.options.points
});
});
}
function setPosition() {
self.$player[0].options({
position: self.options.position
});
$.each(self.$timeline, function(i, v) {
v.options({
position: self.options.position
});
});
}
function toggleMute() {
self.$player[0].toggleMute();
}
function togglePlay() {
self.$player[0].togglePlay();
}
self.onChange = function(key, value) {
if (key == "width") {
self.sizes = getSizes();
$.each(self.$player, function(i, v) {
v.options({
height: self.sizes.player[i].height,
width: self.sizes.player[i].width
})
.css({
left: self.sizes.player[i].left + "px",
top: self.sizes.player[i].top + "px"
});
});
$.each(self.$timeline, function(i, v) {
v.options({
width: self.sizes.timeline[i].width
})
.css({
left: self.sizes.timeline[i].left + "px",
top: self.sizes.timeline[i].top + "px"
});
});
}
};
return that;
};
Ox.LargeTimeline = function(options, self) {
var self = self || {},
that = new Ox.Element("div", self)
.defaults({
cuts: [],
duration: 0,
find: "",
matches: [],
points: [0, 0],
position: 0,
subtitles: [],
videoId: "",
width: 0
})
.options(options || {})
.addClass("OxTimelineLarge")
.mousedown(mousedown)
.mouseleave(mouseleave)
.mousemove(mousemove);
$.extend(self, {
$cuts: [],
$markerPoint: [],
$subtitles: [],
$tiles: {},
center: parseInt(self.options.width / 2),
element: that.$element[0],
fps: 25,
height: 64,
tileWidth: 1500
});
self.tiles = self.options.duration * self.fps / self.tileWidth;
self.$timeline = $("
")
.css({
left: self.center + "px"
})
.appendTo(that.$element)
$.each(self.options.subtitles, function(i, v) {
self.$subtitles[i] = $("
")
.addClass("OxSubtitle" + (self.options.matches.indexOf(i) > -1 ? " OxHighlight" : ""))
.css({
left: (v["in"] * self.fps) + "px",
width: (((v["out"] - v["in"]) * self.fps) - 4) + "px"
})
.html(highlight(v.text, self.options.find))
.appendTo(self.$timeline)
});
$.each(self.options.cuts, function(i, v) {
self.$cuts[i] = $("
")
.addClass("OxCut")
.attr({
src: "/static/oxjs/build/png/ox.ui/videoMarkerCut.png"
})
.css({
left: (v * self.fps) + "px"
})
.appendTo(self.$timeline)
});
self.$markerPosition = $("
")
.addClass("OxMarkerPosition")
.attr({
src: "/static/oxjs/build/png/ox.ui/videoMarkerPlay.png"
})
.appendTo(that.$element);
setMarker();
$.each(["In", "Out"], function(i, v) {
self.$markerPoint[i] = $("
")
.addClass("OxMarkerPoint" + v)
.attr({
src: "/static/oxjs/build/png/ox.ui/videoMarker" + v + ".png"
})
.appendTo(self.$timeline);
setMarkerPoint(i);
});
setWidth();
setPosition();
function mousedown(e) {
var mousemove = false,
x = e.clientX;
$window.mousemove(function(e) {
mousemove = true;
self.options.position = Ox.limit(
self.options.position + (x - e.clientX) / self.fps,
0, self.options.duration
);
x = e.clientX;
setPosition();
that.triggerEvent("change", {
position: self.options.position
});
});
$window.one("mouseup", function() {
$window.unbind("mousemove");
if (!mousemove) {
self.options.position = Ox.limit(
self.options.position + (e.clientX - that.$element.offset().left - self.center) / self.fps,
0, self.options.duration
);
setPosition();
}
that.triggerEvent("change", {
position: self.options.position
});
});
e.preventDefault();
}
function mouseleave(e) {
self.$tooltip.hide();
}
function mousemove(e) {
if (!self.$tooltip) {
self.$tooltip = Ox.Tooltip();
}
self.$tooltip
.options({
title: Ox.formatDuration(self.options.position + (e.clientX - that.offset().left - self.center) / self.fps, 3)
})
.show(e.clientX, e.clientY);
}
function setMarkerPoint(i) {
self.$markerPoint[i].css({
left: (self.options.points[i] * self.fps) + "px"
});
}
function setMarker() {
self.$markerPosition.css({
left: (self.center - 4) + "px",
});
}
function setPosition() {
self.tile = parseInt(self.options.position * self.fps / self.tileWidth);
self.$timeline.css({
marginLeft: (-self.options.position * self.fps) + "px"
});
$.each(Ox.range(Math.max(self.tile - 1, 0), Math.min(self.tile + 2, self.tiles)), function(i, v) {
if (!self.$tiles[v]) {
self.$tiles[v] = $("
")
.attr({
src: "/" + self.options.videoId + "/timelines/" + (stripTimeline ? "strip" : "timeline") + ".64." + v + ".png"
})
.css({
left: (v * self.tileWidth) + "px"
})
.appendTo(self.$timeline);
}
});
}
function setWidth() {
self.center = parseInt(self.options.width / 2);
that.css({
width: self.options.width + "px"
});
self.$timeline.css({
left: self.center + "px"
});
setMarker();
}
self.onChange = function(key, value) {
if (key == "points") {
setMarkerPoint(0);
setMarkerPoint(1);
} else if (key == "position") {
setPosition();
} else if (key == "width") {
setWidth();
}
};
return that;
};
Ox.SmallTimeline = function(options, self) {
var self = self || {},
that = new Ox.Element("div", self)
.defaults({
cuts: [],
duration: 0,
find: "",
matches: [],
points: [0, 0],
position: 0,
subtitles: [],
videoId: "",
width: 0
})
.options(options || {})
.addClass("OxTimelineSmall")
.mousedown(mousedown)
.mouseleave(mouseleave)
.mousemove(mousemove);
$.extend(self, {
$images: [],
$lines: [],
$markerPoint: [],
$subtitles: [],
height: 16,
lines: Math.ceil(self.options.duration / self.options.width),
margin: 8,
subtitlesImageURL: getSubtitlesImageURL()
});
$.each(Ox.range(0, self.lines), function(i) {
addLine(i);
});
self.$markerPosition = $("
")
.addClass("OxMarkerPosition")
.attr({
src: "/static/oxjs/build/png/ox.ui/videoMarkerPlay.png"
})
.css({
position: "absolute",
width: "9px",
height: "5px",
zIndex: 10
})
.appendTo(that.$element);
setPosition();
$.each(["in", "out"], function(i, v) {
var titleCase = Ox.toTitleCase(v);
self.$markerPoint[i] = $("
")
.addClass("OxMarkerPoint" + titleCase)
.attr({
src: "/static/oxjs/build/png/ox.ui/videoMarker" + titleCase + ".png"
})
.appendTo(that.$element);
setMarkerPoint(i);
});
function addLine(i) {
self.$lines[i] = new Ox.Element("div")
.css({
top: i * (self.height + self.margin) + "px",
width: self.options.width + "px"
})
.appendTo(that);
self.$images[i] = $("
")
.addClass("OxTimelineSmallImage")
.attr({
src: "/" + self.options.videoId + "/timelines/timeline.16." + 0 + ".png"
})
.css({
marginLeft: (-i * self.options.width) + "px"
})
.appendTo(self.$lines[i].$element);
self.$subtitles[i] = $("
")
.attr({
src: self.subtitlesImageURL
})
.css({
marginLeft: (-i * self.options.width) + "px"
})
.appendTo(self.$lines[i].$element);
}
function getSubtitle(position) {
var subtitle = null;
$.each(self.options.subtitles, function(i, v) {
if (v["in"] <= position && v["out"] >= position) {
subtitle = v;
return false;
}
});
return subtitle;
}
function getSubtitlesImageURL() {
var height = 18,
width = Math.ceil(self.options.duration),
$canvas = $("