pandora/static/js/editPanel.js

660 lines
25 KiB
JavaScript
Raw Normal View History

'use strict';
2014-02-09 08:07:33 +00:00
pandora.ui.editPanel = function(isEmbed) {
2013-07-13 23:08:01 +00:00
var ui = pandora.user.ui,
edit,
listSizes = [
2014-09-26 12:12:25 +00:00
144 + Ox.UI.SCROLLBAR_SIZE,
280 + Ox.UI.SCROLLBAR_SIZE,
416 + Ox.UI.SCROLLBAR_SIZE
2013-07-13 23:08:01 +00:00
],
listSize = listSizes[ui.clipColumns],
smallTimelineCanvas,
smallTimelineContext,
that = Ox.Element();
2014-02-09 08:07:33 +00:00
ui.edit ? getEdit(isEmbed ? renderEmbedEdit : renderEdit) : renderEdits();
2013-07-13 23:08:01 +00:00
2013-08-07 14:32:17 +00:00
function editsKey(key) {
return 'edits.' + ui.edit.replace(/\./g, '\\.') + '.' + key;
2013-07-13 23:08:01 +00:00
}
function enableDragAndDrop() {
pandora.enableDragAndDrop(
2014-09-25 16:58:38 +00:00
Ox.$elements[that.find('.OxIconList').data('oxid')],
edit.editable,
'edits',
function($list) {
return serializeClips($list.options('selected')).map(function(id) {
return id.split('/').slice(0, 2).join('/');
});
}
);
}
2013-08-08 17:04:21 +00:00
function getClips(ids) {
return ids.map(function(id) {
2013-08-09 12:56:07 +00:00
return Ox.getObjectById(edit.clips, id);
2013-08-08 17:04:21 +00:00
});
}
2013-08-09 13:45:51 +00:00
function getEdit(callback) {
pandora.api.getEdit({id: ui.edit}, function(result) {
edit = result.data;
2014-02-06 10:28:35 +00:00
sortClips(callback);
2013-08-09 13:45:51 +00:00
});
}
function getSmallTimelineCanvas() {
var fps = getSmallTimelineFPS(),
width = Math.ceil(edit.duration * fps),
height = fps == 1 ? 16 : 64;
return Ox.$('<canvas>').attr({width: width, height: height})[0];
}
function getSmallTimelineFPS() {
return Math.floor(edit.duration * 25) < 32768 ? 25 : 1;
}
2013-07-13 23:08:01 +00:00
function getSmallTimelineURL() {
smallTimelineCanvas = getSmallTimelineCanvas();
2013-07-13 23:08:01 +00:00
smallTimelineContext = smallTimelineCanvas.getContext('2d');
return smallTimelineCanvas.toDataURL();
}
function getVideos() {
2014-09-13 21:59:23 +00:00
var videos = [];
edit.clips.filter(function(clip) {
return clip.duration;
}).map(function(clip) {
pandora.site.video.resolutions.forEach(function(resolution) {
videos = videos.concat(pandora.getClipVideos(clip, resolution));
});
});
return videos;
2013-07-13 23:08:01 +00:00
}
2013-07-14 18:57:31 +00:00
function renderEdit() {
2014-02-09 08:07:33 +00:00
if (ui.section != 'edits' || ui.edit != edit.id) {
return;
}
2013-08-09 13:45:51 +00:00
that = pandora.$ui.editPanel = Ox.VideoEditPanel({
2014-01-22 18:24:21 +00:00
annotationsCalendarSize: ui.annotationsCalendarSize,
annotationsMapSize: ui.annotationsMapSize,
annotationsRange: ui.annotationsRange,
annotationsSize: ui.annotationsSize,
annotationsSort: ui.annotationsSort,
clickLink: pandora.clickLink,
2014-02-06 10:28:35 +00:00
clipRatio: pandora.site.video.previewRatio,
2013-08-09 13:45:51 +00:00
clips: Ox.clone(edit.clips),
clipSize: listSize,
2014-09-25 19:56:19 +00:00
clipTooltip: 'clips <span class="OxBright">' + Ox.SYMBOLS.shift + 'C</span>',
2013-08-09 13:45:51 +00:00
clipView: ui.edits[ui.edit].view,
controlsTooltips: {
open: Ox._('Open in {0} View', [Ox._(Ox.getObjectById(
pandora.site.itemViews, pandora.user.ui.videoView
).title)])
},
2013-08-09 13:45:51 +00:00
duration: edit.duration,
editable: edit.type == 'static' && edit.editable,
2013-08-09 13:45:51 +00:00
enableSubtitles: ui.videoSubtitles,
formatTitle: function() {
return pandora.getItemTitle(Ox.last(arguments), true);
},
2013-08-09 13:45:51 +00:00
fullscreen: false,
getClipImageURL: function(id, width, height) {
var clip = Ox.getObjectById(edit.clips, id);
return pandora.getMediaURL('/' + clip.item + '/' + height + 'p' + clip['in'] + '.jpg');
2013-08-09 13:45:51 +00:00
},
getLargeTimelineURL: function(type, i, callback) {
pandora.getLargeEditTimelineURL(edit, type, i, callback);
},
height: pandora.$ui.appPanel.size(1),
'in': ui.edits[ui.edit]['in'],
2014-01-22 18:24:21 +00:00
layers: getLayers(edit.clips),
2013-08-09 13:45:51 +00:00
loop: ui.videoLoop,
muted: ui.videoMuted,
out: ui.edits[ui.edit].out,
position: ui.edits[ui.edit].position,
resolution: ui.videoResolution,
scaleToFill: ui.videoScale == 'fill',
2013-08-11 07:43:53 +00:00
selected: ui.edits[ui.edit].selection,
2014-01-22 18:24:21 +00:00
showAnnotationsCalendar: ui.showAnnotationsCalendar,
showAnnotationsMap: ui.showAnnotationsMap,
2013-08-09 13:45:51 +00:00
showClips: ui.showClips,
2014-01-22 18:24:21 +00:00
showLayers: ui.showLayers,
2013-08-09 13:45:51 +00:00
showTimeline: ui.showTimeline,
2014-01-22 18:24:21 +00:00
showUsers: pandora.site.annotations.showUsers,
2013-08-09 13:45:51 +00:00
smallTimelineURL: getSmallTimelineURL(),
sort: ui.edits[ui.edit].sort,
2014-02-06 14:53:53 +00:00
sortOptions: (
edit.type == 'static'
? [{id: 'index', title: Ox._('Sort Manually'), operator: '+'}]
: []
)
2014-02-06 10:28:35 +00:00
.concat(
2013-08-09 13:45:51 +00:00
pandora.site.clipKeys.map(function(key) {
return Ox.extend(Ox.clone(key), {
title: Ox._(('Sort by Clip {0}'), [Ox._(key.title)])
2013-07-13 23:08:01 +00:00
});
2013-08-09 13:45:51 +00:00
})
).concat(
pandora.site.sortKeys.map(function(key) {
return Ox.extend(Ox.clone(key), {
title: Ox._('Sort by {0}', [Ox._(key.title)])
2013-08-09 12:56:07 +00:00
});
2013-08-09 13:45:51 +00:00
})
),
2014-02-05 07:35:30 +00:00
subtitles: getSubtitles(edit.clips),
2015-04-14 14:53:47 +00:00
subtitlesOffset: ui.videoSubtitlesOffset,
2013-08-09 13:45:51 +00:00
timeline: ui.videoTimeline,
2014-09-25 19:56:19 +00:00
timelineTooltip: Ox._('timeline') + ' <span class="OxBright">' + Ox.SYMBOLS.shift + 'T</span>',
2013-08-09 13:45:51 +00:00
video: getVideos(),
volume: ui.videoVolume,
width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1
})
.bindEvent({
copy: function(data) {
pandora.clipboard.copy(serializeClips(data.ids), 'clip');
},
copyadd: function(data) {
pandora.clipboard.add(serializeClips(data.ids), 'clip');
},
cut: function(data) {
var clips = serializeClips(data.ids);
pandora.clipboard.copy(clips, 'clip');
pandora.doHistory('cut', clips, ui.edit, function(result) {
Ox.Request.clearCache('getEdit');
Ox.Request.clearCache('sortClips');
2013-08-09 13:45:51 +00:00
updateClips(result.data.clips);
});
},
cutadd: function(data) {
var clips = serializeClips(data.ids);
pandora.clipboard.add(clips, 'clip');
pandora.doHistory('cut', clips, ui.edit, function(result) {
Ox.Request.clearCache('getEdit');
Ox.Request.clearCache('sortClips');
2013-08-09 13:45:51 +00:00
updateClips(result.data.clips);
});
},
'delete': function(data) {
var clips = serializeClips(data.ids);
pandora.doHistory('delete', clips, ui.edit, function(result) {
Ox.Request.clearCache('getEdit');
Ox.Request.clearCache('sortClips');
2015-04-02 11:56:00 +00:00
edit.clips = result.data.clips;
sortClips(updateClips);
2013-08-09 13:45:51 +00:00
});
},
edit: function(data) {
var args = {id: data.id},
index = Ox.getIndexById(edit.clips, data.id),
clip = edit.clips[index];
if (data.key == 'duration') {
data.key = 'out';
data.value += clip['in'];
}
pandora.api.get({id: clip.item, keys: ['duration']}, function(result) {
var clips;
if (data.key == 'out') {
data.value = Math.min(data.value, result.data.duration);
2013-08-09 13:45:51 +00:00
}
clips = serializeClips([data.id]).concat(serializeClips([{
id: data.id,
'in': data.key == 'in' ? data.value : clip['in'],
item: clip.item,
out: data.key == 'out' ? data.value : clip.out
}]));
pandora.doHistory('edit', clips, ui.edit, function(result) {
edit.clips[Ox.getIndexById(edit.clips, data.id)] = result.data;
Ox.Request.clearCache('sortClips');
sortClips(updateClips);
2013-08-09 13:45:51 +00:00
});
});
},
join: function(data) {
2013-08-10 11:12:09 +00:00
var clips = [serializeClips(data.ids), serializeClips(data.join)];
pandora.doHistory('join', clips, ui.edit, function(result) {
2015-04-02 11:56:00 +00:00
edit.clips = edit.clips.filter(function(clip) {
2013-08-10 11:12:09 +00:00
return !Ox.contains(data.ids, clip.id);
2015-04-02 11:56:00 +00:00
}).concat(result.data.clips);
sortClips(updateClips);
2013-08-10 11:12:09 +00:00
});
2013-08-09 13:45:51 +00:00
},
loop: function(data) {
pandora.UI.set({videoLoop: data.loop});
},
move: function(data) {
pandora.api.orderClips({
edit: edit.id,
ids: data.ids
}, function(result) {
Ox.Request.clearCache('getEdit');
Ox.Request.clearCache('sortClips');
2013-08-09 13:45:51 +00:00
orderClips(data.ids);
});
},
muted: function(data) {
pandora.UI.set({videoMuted: data.muted});
},
open: function(data) {
pandora.UI.set(editsKey('clip'), data.ids[0]);
},
openlink: function(data) {
that.options({paused: true});
pandora.UI.set('videoPoints.' + data.item, data.annotation ? {
annotation: data.annotation.split('/')[1],
'in': data['in'],
out: data.out,
position: data.position
} : {
'in': data['in'],
out: data.out,
position: data.position
});
pandora.UI.set({
section: 'items',
item: data.item || data.annotation.split('/')[0],
itemView: ui.videoView,
});
},
2013-08-09 13:45:51 +00:00
paste: function() {
2015-10-04 09:11:44 +00:00
var clips = pandora.clipboard.paste('clip');
2014-10-06 10:35:38 +00:00
clips.length && pandora.doHistory('paste', clips, ui.edit, function(result) {
2013-08-09 13:45:51 +00:00
Ox.Request.clearCache('getEdit');
Ox.Request.clearCache('sortClips');
updateClips(edit.clips.map(function(clip) {
if (clip.index >= result.data.clips[0].index) {
clip.index += result.data.clips.length
}
return clip;
}).concat(result.data.clips));
that.options({
selected: result.data.clips.map(function(clip) {
return clip.id;
})
});
2013-08-09 13:45:51 +00:00
});
},
playing: function(data) {
var set = {};
set[editsKey('clip')] = '';
set[editsKey('position')] = data.position;
pandora.UI.set(set);
},
position: function(data) {
var set = {};
set[editsKey('clip')] = '';
set[editsKey('position')] = data.position;
pandora.UI.set(set);
},
resize: function(data) {
// sidebar resize
that.options({width: data.size});
},
resizeclips: function(data) {
pandora.UI.set({clipsSize: data.clipsSize});
},
resolution: function(data) {
pandora.UI.set({videoResolution: data.resolution});
},
scale: function(data) {
pandora.UI.set({videoScale: data.scale});
},
2013-08-11 07:43:53 +00:00
select: function(data) {
2014-02-06 10:28:35 +00:00
pandora.UI.set({editSelection: data.ids});
2013-08-11 07:43:53 +00:00
},
2013-08-09 13:45:51 +00:00
size: function(data) {
pandora.UI.set({clipSize: data.size});
},
sort: function(data) {
2014-02-06 10:28:35 +00:00
pandora.UI.set({editSort: data});
sortClips(updateClips);
2013-08-09 13:45:51 +00:00
},
split: function(data) {
var clips = [serializeClips(data.ids), serializeClips(data.split)];
pandora.doHistory('split', clips, ui.edit, function(result) {
updateClips(edit.clips.filter(function(clip) {
return !Ox.contains(data.ids, clip.id);
}).concat(result.data.clips));
});
},
subtitles: function(data) {
pandora.UI.set({videoSubtitles: data.subtitles});
},
timeline: function(data) {
updateSmallTimelineURL();
2013-08-09 13:45:51 +00:00
pandora.UI.set({videoTimeline: data.timeline});
},
toggleclips: function(data) {
pandora.UI.set({showClips: data.showClips});
},
toggletimeline: function(data) {
pandora.UI.set({showTimeline: data.showTimeline});
},
view: function(data) {
2014-02-06 10:28:35 +00:00
pandora.UI.set({editView: data.view});
2013-08-09 13:45:51 +00:00
data.view == 'grid' && enableDragAndDrop();
},
volume: function(data) {
pandora.UI.set({videoVolume: data.volume});
},
pandora_editselection: function(data) {
that.options({selected: data.value});
},
2013-08-09 13:45:51 +00:00
pandora_showclips: function(data) {
that.options({showClips: data.value});
},
pandora_showtimeline: function(data) {
that.options({showTimeline: data.value});
},
pandora_videotimeline: function(data) {
that.options({timeline: data.value});
}
});
that.updatePanel = function(callback) {
2013-08-09 14:36:30 +00:00
Ox.Request.clearCache('getEdit');
Ox.Request.clearCache('sortClips');
getEdit(function() {
updateClips();
callback && callback();
});
2013-08-09 13:45:51 +00:00
};
pandora.$ui.mainPanel.replaceElement(1, that);
updateSmallTimelineURL();
ui.edits[ui.edit].view == 'grid' && enableDragAndDrop();
}
2013-07-14 18:57:31 +00:00
function renderEdits() {
2014-11-02 19:32:47 +00:00
that.css({
'overflow-y': 'auto'
});
2014-12-26 23:16:13 +00:00
Ox.Bar({size: 24}).appendTo(that);
2014-11-02 19:32:47 +00:00
var $content = Ox.Element()
.css({padding: '16px'})
.appendTo(that),
isEditable = pandora.site.capabilities.canEditSitePages[pandora.user.level];
Ox.EditableContent({
editable: false,
value: Ox._('{0} Edits', [pandora.site.site.name]),
})
.css({
height: '32px',
fontSize: '18px',
})
.appendTo($content);
Ox.Element()
.css({height: '16px'})
.appendTo($content);
2014-11-02 19:32:47 +00:00
pandora.api.getPage({name: 'edits'}, function(result) {
Ox.EditableContent({
clickLink: pandora.clickLink,
editable: isEditable,
tooltip: isEditable ? pandora.getEditTooltip() : '',
type: 'textarea',
placeholder: isEditable ? Ox._('Doubleclick to insert text') : '',
value: result.data.text
})
.css({
width: '100%'
})
.bindEvent({
submit: function(data) {
Ox.Request.clearCache('getPage');
pandora.api.editPage({
name: 'edits',
text: data.value
});
}
})
.appendTo($content);
2013-07-14 17:22:26 +00:00
});
}
2014-02-09 08:07:33 +00:00
function renderEmbedEdit() {
that = Ox.VideoPlayer({
clickLink: pandora.clickLink,
clipRatio: pandora.site.video.previewRatio,
clips: Ox.clone(edit.clips),
2014-09-25 19:56:19 +00:00
clipTooltip: 'clips <span class="OxBright">' + Ox.SYMBOLS.shift + 'C</span>',
2014-02-09 08:07:33 +00:00
clipView: ui.edits[ui.edit].view,
controlsBottom: [
'play', 'volume', 'scale', 'timeline', 'position', 'settings'
],
controlsTooltips: {
close: Ox._('Close'),
open: Ox._('Watch on {0}', [pandora.site.site.name])
},
controlsTop: [
Ox.Fullscreen.available ? 'fullscreen' : 'space16'
].concat(
['chapterTitle', 'open']
),
chapters: edit.clips.map(function(clip) {
return {
position: clip.position,
title: pandora.getItemTitle(clip)
};
}),
duration: edit.duration,
enableFullscreen: Ox.Fullscreen.available,
enableKeyboard: true,
enableMouse: true,
enablePosition: true,
enableSubtitles: ui.videoSubtitles,
enableTimeline: true,
enableVolume: true,
height: pandora.$ui.document.height(),
paused: true,
showIconOnLoad: true,
subtitles: getSubtitles(edit.clips),
subtitlesDefaultTrack: Ox.getLanguageNameByCode(pandora.site.language),
subtitlesTrack: Ox.getLanguageNameByCode(pandora.site.language),
2014-02-09 08:07:33 +00:00
timeline: getSmallTimelineURL(),
video: getVideos(),
volume: ui.videoVolume,
width: pandora.$ui.document.width()
}).bindEvent({
fullscreen: function(data) {
Ox.Fullscreen.toggle();
setTimeout(that.resizePanel, 100);
},
open: function(data) {
that.options({paused: true});
var clip = Ox.last(edit.clips.filter(function(clip) {
return clip.position <= that.options('position');
})),
position = clip['in'] + that.options('position') - clip['position'],
url = document.location.protocol + '//'
+ document.location.hostname + '/'
+ clip.item + '/'
+ Ox.formatDuration(position) + ','
+ Ox.formatDuration(clip['in']) + ','
+ Ox.formatDuration(clip.out);
window.open(url, '_blank');
},
});
pandora.$ui.embedPanel.replaceWith(that);
updateSmallTimelineURL();
}
function orderClips(ids) {
edit.clips.forEach(function(clip) {
clip.index = ids.indexOf(clip.id);
});
edit.clips = Ox.sortBy(edit.clips, 'index');
2013-07-14 11:04:13 +00:00
updateVideos();
}
2014-01-22 18:24:21 +00:00
function getLayers(clips) {
var layers = [];
pandora.site.layers.forEach(function(layer, i) {
layers[i] = Ox.extend({}, layer, {
title: Ox._(layer.title),
item: Ox._(layer.item),
items: Ox.flatten(clips.map(function(clip) {
return clip.layers[layer.id].map(function(annotation) {
var a = Ox.clone(annotation);
2014-01-22 18:32:11 +00:00
a['in'] = Math.max(
clip['position'],
a['in'] - clip['in'] + clip['position']
);
a.out = Math.min(
clip['position'] + clip['duration'],
a.out - clip['in'] + clip['position']
);
2014-01-22 18:24:21 +00:00
return a;
});
})),
});
});
return layers;
}
2014-02-05 07:35:30 +00:00
function getSubtitles(clips) {
var subtitles = [],
subtitlesLayer = pandora.getSubtitlesLayer();
2014-02-05 07:35:30 +00:00
subtitlesLayer && clips.map(function(clip) {
if (clip.layers[subtitlesLayer]) {
clip.layers[subtitlesLayer].forEach(function(subtitle) {
subtitles.push({
id: subtitle.id,
'in': Math.max(
clip['position'],
subtitle['in'] - clip['in'] + clip['position']
),
out: Math.min(
clip['position'] + clip['duration'],
subtitle.out - clip['in'] + clip['position']
),
text: subtitle.value.replace(/\n/g, ' ').replace(/<br\/?>/g, '\n')
});
});
}
});
return subtitles;
}
2013-08-09 12:56:07 +00:00
function serializeClips(clips) {
// can be ids or clips
return clips.map(function(clip) {
if (Ox.isString(clip)) {
clip = Ox.getObjectById(edit.clips, clip);
}
return (
clip.annotation || clip.item + '/' + clip['in'] + '-' + clip.out
) + '/' + (clip.id || '');
});
}
2014-02-06 10:28:35 +00:00
function sortClips(callback) {
2014-02-10 10:53:45 +00:00
var sort = pandora.user.ui.editSort,
key = sort[0].key,
index;
2014-02-06 10:28:35 +00:00
if (key == 'position') {
key = 'in';
}
if ([
'id', 'index', 'in', 'out', 'duration',
'title', 'director', 'year', 'videoRatio'
].indexOf(key) > -1) {
2014-02-07 12:12:15 +00:00
sortBy(key);
2014-02-10 10:53:45 +00:00
index = 0;
edit.clips.forEach(function(clip) {
clip.sort = index++;
if (sort[0].operator == '-') {
clip.sort = -clip.sort;
}
});
2014-02-06 15:14:02 +00:00
updateDuration();
2014-02-06 10:28:35 +00:00
callback(edit.clips);
} else {
pandora.api.sortClips({
edit: edit.id,
sort: sort
}, function(result) {
edit.clips.forEach(function(clip) {
2014-02-07 12:12:15 +00:00
clip.sort = result.data.clips.indexOf(clip.id);
if (sort[0].operator == '-') {
clip.sort = -clip.sort;
}
2014-02-06 10:28:35 +00:00
});
2014-02-07 12:12:15 +00:00
sortBy('sort');
2014-02-06 15:14:02 +00:00
updateDuration();
2014-02-06 10:28:35 +00:00
callback(edit.clips);
});
}
2014-02-07 12:12:15 +00:00
function sortBy(key) {
2014-02-10 10:53:45 +00:00
edit.clips = Ox.sortBy(edit.clips, sort[0].operator + key);
2014-02-07 12:12:15 +00:00
}
2014-02-06 10:28:35 +00:00
}
2014-02-06 15:14:02 +00:00
function updateDuration() {
2013-07-13 23:08:01 +00:00
edit.duration = 0;
edit.clips.forEach(function(clip) {
clip.position = edit.duration;
edit.duration += clip.duration;
});
2014-02-06 15:14:02 +00:00
}
function updateClips(clips) {
clips = clips || edit.clips;
edit.clips = clips;
updateDuration();
2013-07-13 23:08:01 +00:00
that.options({
clips: Ox.clone(edit.clips),
2013-08-09 14:36:30 +00:00
duration: edit.duration,
2014-01-22 18:24:21 +00:00
layers: getLayers(edit.clips),
2013-07-13 23:08:01 +00:00
smallTimelineURL: getSmallTimelineURL(),
2014-02-05 07:35:30 +00:00
subtitles: getSubtitles(edit.clips),
2013-07-13 23:08:01 +00:00
video: getVideos()
});
2013-07-13 23:08:01 +00:00
updateSmallTimelineURL();
}
2013-07-13 23:08:01 +00:00
function updateSmallTimelineURL() {
var canvas = getSmallTimelineCanvas(),
context = canvas.getContext('2d'),
fps = getSmallTimelineFPS(),
timelineIteration = self.timelineIteration = Ox.uid();
2013-07-13 23:08:01 +00:00
Ox.serialForEach(edit.clips, function(clip) {
var callback = Ox.last(arguments);
pandora[
fps == 1 ? 'getSmallClipTimelineURL' : 'getLargeClipTimelineURL'
](clip.item, clip['in'], clip.out, ui.videoTimeline, function(url) {
2013-07-13 23:08:01 +00:00
var image = Ox.$('<img>')
.on({
load: function() {
if (timelineIteration == self.timelineIteration) {
context.drawImage(image, Math.floor(clip.position * fps), 0);
2014-02-09 08:07:33 +00:00
that.options(isEmbed ? 'timeline' : 'smallTimelineURL',
canvas.toDataURL());
callback();
} else {
callback(false);
}
2013-07-13 23:08:01 +00:00
}
})
.attr({
src: url
})[0];
});
});
}
2013-07-12 14:44:33 +00:00
2013-07-14 18:57:31 +00:00
function updateVideos() {
updateDuration();
2013-07-14 18:57:31 +00:00
that.options({
2013-08-09 14:36:30 +00:00
duration: edit.duration,
2013-07-14 18:57:31 +00:00
smallTimelineURL: getSmallTimelineURL(),
video: getVideos()
});
updateSmallTimelineURL();
}
return that;
2013-07-13 23:08:01 +00:00
};