├── README.md
├── assets
└── bigbuckbunny.webm
├── css
└── style.css
├── index.html
├── js
└── index.js
└── vendor
├── ansiparse.js
├── ffmpeg-worker-webm.js
├── log.js
├── rangeslider.css
└── rangeslider.js
/README.md:
--------------------------------------------------------------------------------
1 | # browser-video-editor
2 | Edit videos in the browser with ffmpeg.js
3 |
4 | WIP: Experimental start with subclip editor
5 |
--------------------------------------------------------------------------------
/assets/bigbuckbunny.webm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skomski/browser-video-editor/f88d1ce7d17b8711fd3096659e43f1b7477ecfa5/assets/bigbuckbunny.webm
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | .log {
2 | position: relative;
3 | margin-top: 35px
4 | }
5 |
6 | .log .log-tail {
7 | z-index: 80;
8 | position: absolute;
9 | display: block;
10 | top: 0;
11 | right: 0;
12 | margin: 10px 10px 0 0
13 | }
14 |
15 | .log .log-tail .tail-label {
16 | display: none;
17 | cursor: pointer
18 | }
19 |
20 | .log .log-tail:hover .tail-label {
21 | display: inline-block
22 | }
23 |
24 | .log .log-tail:hover .tail-status {
25 | display: none
26 | }
27 |
28 | .log .log-tail.scrolling {
29 | position: fixed;
30 | right: 32px
31 | }
32 |
33 | .log .log-tail.bottom {
34 | bottom: 45px;
35 | top: inherit
36 | }
37 |
38 | .log .log-tail .tail-status {
39 | position: relative;
40 | display: inline-block;
41 | height: 20px;
42 | width: 20px;
43 | vertical-align: middle;
44 | background-color: #696867;
45 | border-radius: 50%
46 | }
47 |
48 | .log .log-tail .tail-status:after {
49 | content: "";
50 | display: block;
51 | height: 10px;
52 | width: 10px;
53 | background: #F2F2F2;
54 | border-radius: 50%
55 | }
56 |
57 | .log .log-tail.active .tail-status {
58 | background-color: #6b0
59 | }
60 |
61 | .log .to-top {
62 | z-index: 80;
63 | position: absolute;
64 | display: block;
65 | bottom: 2px;
66 | right: 2px;
67 | margin-right: 2px;
68 | padding-right: 16px;
69 | text-align: right;
70 | color: #999;
71 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNSIgd2lkdGg9IjEwIj4KPHBhdGggZmlsbD0iI2MyYzJjMiIgZD0iTTEwLDUsNSwwLDAsNXoiLz4KPC9zdmc+Cgo=) right 6px no-repeat
72 | }
73 |
74 | .flash .log .close,.hooks-error .log .close,.log .flash .close,.log .hooks-error .close,.log .icon,.log .popup .close,.popup .log .close {
75 | width: 1.3em;
76 | height: 1.1em;
77 | margin-right: 6px;
78 | vertical-align: middle
79 | }
80 |
81 | .log .icon--down {
82 | width: .7em;
83 | height: .9em;
84 | margin-right: 4px
85 | }
86 |
87 | .log-header {
88 | height: 44px;
89 | margin: 0;
90 | padding: .7em .8em .6em;
91 | text-align: right;
92 | background-color: #444
93 | }
94 |
95 | .log-header a {
96 | margin-left: .4em
97 | }
98 |
99 | .log-body {
100 | position: relative
101 | }
102 |
103 | .log-body pre {
104 | clear: left;
105 | min-height: 42px;
106 | padding: 15px 0;
107 | color: #F1F1F1;
108 | font-family: monospace;
109 | font-size: 12px;
110 | line-height: 19px;
111 | white-space: pre-wrap;
112 | word-wrap: break-word;
113 | background-color: #2a2a2a;
114 | counter-reset: line-numbering;
115 | margin: 0
116 | }
117 |
118 | .log-body .cut {
119 | padding: 20px 15px 0 55px
120 | }
121 |
122 | .log-body p {
123 | position: relative;
124 | padding: 0 15px 0 55px;
125 | margin: 0;
126 | min-height: 16px
127 | }
128 |
129 | .log-body p:hover {
130 | background-color: #444!important
131 | }
132 |
133 | .log-body p.highlight {
134 | background-color: #666
135 | }
136 |
137 | .log-body p.highlight a {
138 | color: #fff
139 | }
140 |
141 | .log-body p a {
142 | display: inline-block;
143 | text-align: right;
144 | min-width: 40px;
145 | margin-left: -33px;
146 | cursor: pointer;
147 | text-decoration: none
148 | }
149 |
150 | .log-body p a::before {
151 | content: counter(line-numbering);
152 | counter-increment: line-numbering;
153 | padding-right: 1em
154 | }
155 |
156 | .log-body .fold {
157 | position: relative;
158 | height: 16px;
159 | overflow: hidden;
160 | cursor: pointer
161 | }
162 |
163 | .log-body .fold.open {
164 | height: auto
165 | }
166 |
167 | .log-body .fold p:first-of-type {
168 | padding-right: 190px;
169 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAiIHdpZHRoPSIxMCI+CjxwYXRoIGQ9Im0wLjUsMS41LDQuNSw3LDQuNS03eiIgc3Ryb2tlPSIjNTU1IiBzdHJva2Utd2lkdGg9IjAuNSIgZmlsbD0iIzY2NiIvPgo8L3N2Zz4KCg==) 8px 3px no-repeat #333
170 | }
171 |
172 | .log-body .fold p:first-of-type.highlight {
173 | background-color: #777
174 | }
175 |
176 | .log-body .fold:not(.open) p:first-of-type {
177 | visibility: visible;
178 | height: auto;
179 | min-height: 16px;
180 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAiIHdpZHRoPSIxMCI+CjxwYXRoIGQ9Ik0yLDksOSw1LDIsMXoiIHN0cm9rZT0iIzU1NSIgc3Ryb2tlLXdpZHRoPSIwLjUiIGZpbGw9IiM2NjYiLz4KPC9zdmc+Cgo=)
181 | }
182 |
183 | .log-body .fold .fold-name {
184 | position: absolute;
185 | z-index: 1;
186 | display: block;
187 | top: 2px;
188 | right: 85px;
189 | padding: 0 7px 2px;
190 | line-height: 10px;
191 | font-size: 10px;
192 | background-color: #666;
193 | border-radius: 6px;
194 | color: #bbb
195 | }
196 |
197 | .log-body .fold-end,.log-body .fold-start:not(.fold) {
198 | display: none
199 | }
200 |
201 | .log-body .duration {
202 | position: absolute;
203 | display: block;
204 | top: 2px;
205 | right: 12px;
206 | padding: 0 7px 2px;
207 | line-height: 10px;
208 | font-size: 10px;
209 | background-color: #666;
210 | border-radius: 6px;
211 | color: #bbb
212 | }
213 |
214 | .log-body .loading {
215 | padding: 25px 0 0 10px
216 | }
217 |
218 | .log-notice {
219 | background-color: #A6ADAD;
220 | color: #fff;
221 | min-height: 70px;
222 | line-height: 35px;
223 | text-align: center
224 | }
225 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ffmpeg.js browser example
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
Input
22 |
23 |
65 |
66 |
67 |
68 |
69 |
70 |
Output
71 |
72 |
75 |
76 |
77 |
78 | Terminal
79 |
80 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | var inputFile;
5 | var filesElement;
6 | var Logger;
7 |
8 | function LogOutput(text) { Logger.set(Logger.num++, text); }
9 |
10 | var lastCommand;
11 |
12 | function LogNewCommand(name, text) {
13 | LogEndCommand(0);
14 | lastCommand = `${name}-${Logger.num}`;
15 | LogOutput(`fold:start:${lastCommand}\x1B\[K\n`);
16 | LogOutput(text);
17 | }
18 |
19 | function LogEndCommand(exitCode) {
20 | if (lastCommand) {
21 | LogOutput(`exit code: ${exitCode}\nfold:end:${lastCommand}\x1B\[K\n`);
22 | }
23 | lastCommand = undefined;
24 | }
25 |
26 | function IsSupported() {
27 | return document.querySelector && window.URL && window.Worker;
28 | }
29 |
30 | function parseArguments(text) {
31 | text = text.replace(/\s+/g, ' ');
32 | var args = [];
33 | // Allow double quotes to not split args.
34 | text.split('"').forEach(function(t, i) {
35 | t = t.trim();
36 | if ((i % 2) === 1) {
37 | args.push(t);
38 | } else {
39 | args = args.concat(t.split(" "));
40 | }
41 | });
42 | return args;
43 | }
44 |
45 | function RunMp4Conversion() {
46 | if (!inputFile)
47 | return alert('You need to select a source file!');
48 |
49 | var startTime = document.getElementById("start").value;
50 | var duration = document.getElementById("duration").value;
51 | var scale = document.getElementById("scale").value;
52 | var fps = document.getElementById("fps").value;
53 | var filters = `fps=${fps},scale=${scale}:-1`;
54 |
55 | var inputName = inputFile.name;
56 |
57 | var reader = new FileReader();
58 |
59 | reader.addEventListener("loadend", function() {
60 | var createMp4Command =
61 | `-hide_banner -ss ${startTime} -t ${duration} -i "${inputName}"
62 | -vf "${filters}" -pix_fmt yuv420p -strict -2 -y output.mp4`;
63 |
64 | RunCommand(
65 | {
66 | text : createMp4Command,
67 | data : [ {name : inputName, data : new Uint8Array(reader.result)} ],
68 | prettyName : 'convert-to-mp4'
69 | },
70 | function(err, buffers) {
71 | if (err)
72 | throw err;
73 |
74 | buffers.forEach(function(file) {
75 | filesElement.appendChild(
76 | getDownloadLink(file.data, file.name, 'video/mp4'));
77 | });
78 | LogOutput(`\nConverted ${inputName} to mp4!`);
79 | });
80 | });
81 | reader.readAsArrayBuffer(inputFile);
82 | }
83 |
84 | function RunGifConversion() {
85 | if (!inputFile)
86 | return alert('You need to select a source file!');
87 |
88 | var startTime = document.getElementById("start").value;
89 | var duration = document.getElementById("duration").value;
90 | var scale = document.getElementById("scale").value;
91 | var fps = document.getElementById("fps").value;
92 | var palette = 'palette.jpg';
93 | var filters = `fps=${fps},scale=${scale}:-1:flags=lanczos`;
94 |
95 | var inputName = inputFile.name;
96 |
97 | var reader = new FileReader();
98 |
99 | reader.addEventListener("loadend", function() {
100 | var createPaletteCommand =
101 | `-hide_banner -ss ${startTime} -t ${duration} -i "${inputName}"
102 | -vf "${filters}, palettegen" -y ${palette}`;
103 |
104 | var createGifCommand =
105 | `-hide_banner -ss ${startTime} -t ${duration} -i "${inputName}"
106 | -i ${palette} -lavfi "${filters} [x]; [x][1:v] paletteuse"
107 | -y output.gif`;
108 |
109 | RunCommand(
110 | {
111 | text : createPaletteCommand,
112 | data : [ {name : inputName, data : new Uint8Array(reader.result)} ],
113 | prettyName : 'gif-palettegen'
114 | },
115 | function(err, buffers) {
116 | if (err)
117 | throw err;
118 | if (buffers.length === 0)
119 | LogOutput('\nFailed to generate palette for ' + inputName);
120 |
121 | RunCommand(
122 | {
123 | text : createGifCommand,
124 | prettyName : 'convert-to-gif',
125 | data : [
126 | {name : inputName, data : new Uint8Array(reader.result)},
127 | {name : buffers[0].name, data : buffers[0].data}
128 | ]
129 | },
130 | function(err, buffers) {
131 | if (err)
132 | throw err;
133 | buffers.forEach(function(file) {
134 | filesElement.appendChild(
135 | getDownloadLink(file.data, file.name));
136 | });
137 | LogOutput(`\nConverted ${inputName} to gif!`);
138 | });
139 | });
140 | });
141 | reader.readAsArrayBuffer(inputFile);
142 | }
143 |
144 | function GetRandomInt(min, max) {
145 | return Math.floor(Math.random() * (max - min)) + min;
146 | }
147 |
148 | function getDownloadLink(fileData, fileName, fileType) {
149 | if (fileData instanceof Blob) {
150 | var blob = fileData;
151 | var src = window.URL.createObjectURL(fileData);
152 | } else {
153 | var blob = new Blob([ fileData ]);
154 | var src = window.URL.createObjectURL(blob);
155 | }
156 |
157 | var container = document.createElement('div');
158 | container.className = 'row';
159 |
160 | var col1 = document.createElement('div');
161 | col1.className = 'col-md-8';
162 |
163 | var col2 = document.createElement('div');
164 | col2.className = 'col-md-4';
165 |
166 | var header = document.createElement('h3');
167 | header.textContent = fileName;
168 |
169 | var downloadLink = document.createElement('a');
170 | downloadLink.download = fileName;
171 | downloadLink.href = src;
172 | downloadLink.className = 'btn btn-default btn-block';
173 | downloadLink.textContent = 'Download';
174 |
175 | var imgurLink = document.createElement('a');
176 | imgurLink.className = 'btn btn-default btn-block';
177 | imgurLink.textContent = 'Export to imgur.com';
178 | imgurLink.onclick =
179 | function(e) {
180 | var formData = new FormData();
181 | formData.append('image', blob);
182 | $.ajax({
183 | url : 'https://api.imgur.com/3/image',
184 | type : 'POST',
185 | data : formData,
186 | cache : false,
187 | contentType : false,
188 | processData : false,
189 | headers : {Authorization : 'Client-ID 9b029b06fdc00ba'},
190 | success : function(data) {
191 | imgurLink.href = "https://i.imgur.com/" + data.data.id;
192 | imgurLink.className += ' btn-success';
193 | imgurLink.target = '_blank';
194 | imgurLink.textContent = 'Uploaded to ' + imgurLink.href;
195 | },
196 | error : function(xhr, textStatus, error) {
197 | imgurLink.className += ' btn-warning';
198 | imgurLink.textContent = `${error}: ${xhr.responseJSON.data.error}`;
199 | },
200 | xhr : function() {
201 | var xhr = new window.XMLHttpRequest();
202 | xhr.upload.addEventListener("progress", function(evt) {
203 | if (evt.lengthComputable) {
204 | var percentComplete = evt.loaded / evt.total;
205 | imgurLink.textContent =
206 | 'Uploading: ' + percentComplete * 100 + '%';
207 | } else {
208 | imgurLink.textContent = 'Uploading...';
209 | }
210 | }, false);
211 | return xhr;
212 | }
213 | });
214 | }
215 |
216 | var streamableLink = document.createElement('a');
217 | streamableLink.className = 'btn btn-default btn-block';
218 | streamableLink.textContent = 'Export to streamable.com';
219 | streamableLink.onclick =
220 | function(e) {
221 | var formData = new FormData();
222 | formData.append('File', blob);
223 | $.ajax({
224 | url : 'https://api.streamable.com/upload',
225 | type : 'POST',
226 | data : formData,
227 | cache : false,
228 | contentType : false,
229 | processData : false,
230 | success : function(data) {
231 | streamableLink.href = "https://streamable.com/" + data.shortcode;
232 | streamableLink.className += ' btn-success';
233 | streamableLink.target = '_blank';
234 | streamableLink.textContent = 'Uploaded to ' + streamableLink.href;
235 | },
236 | error : function(xhr, textStatus, error) {
237 | streamableLink.className += ' btn-warning';
238 | streamableLink.textContent =
239 | `${error}: ${xhr.responseJSON.data.error}`;
240 | },
241 | xhr : function() {
242 | var xhr = new window.XMLHttpRequest();
243 | xhr.upload.addEventListener("progress", function(evt) {
244 | if (evt.lengthComputable) {
245 | var percentComplete = evt.loaded / evt.total;
246 | streamableLink.textContent =
247 | 'Uploading: ' + percentComplete * 100 + '%';
248 | } else {
249 | streamableLink.textContent = 'Uploading...';
250 | }
251 | }, false);
252 | return xhr;
253 | }
254 | });
255 | };
256 |
257 | col2.appendChild(header);
258 | col2.appendChild(downloadLink);
259 | col2.appendChild(streamableLink);
260 | col2.appendChild(imgurLink);
261 |
262 | if (fileName.match(/\.jpeg|\.gif|\.jpg|\.png/)) {
263 | var img = document.createElement('img');
264 | img.src = src;
265 | img.className = 'img-thumbnail';
266 | col1.appendChild(img);
267 | } else {
268 | var video = document.createElement('video');
269 | video.controls = true;
270 | video.width = 640;
271 | video.height = 480;
272 | video.id = 'preview-video-' + (Math.random() * 1000).toFixed(0);
273 | video.className = 'video-js vjs-default-skin';
274 |
275 | var source = document.createElement('source');
276 | source.src = src;
277 | source.type = fileType || fileData.type;
278 | video.appendChild(source);
279 |
280 | col1.appendChild(video);
281 | }
282 |
283 | container.appendChild(col1);
284 | container.appendChild(col2);
285 | return container;
286 | }
287 |
288 | function RunCommand(options, cb) {
289 | var worker = new Worker("../vendor/ffmpeg-worker-webm.js");
290 | var lastError;
291 |
292 | worker.onmessage = function(event) {
293 | var message = event.data;
294 | switch (message.type) {
295 | case 'ready':
296 | break;
297 | case 'stdout':
298 | LogOutput(message.data + "\n");
299 | break;
300 | case 'stderr':
301 | LogOutput(message.data + "\n");
302 | break;
303 | case 'start':
304 | break;
305 | case 'done':
306 | var buffers = message.data.MEMFS;
307 | if (cb)
308 | cb(lastError, buffers);
309 | worker.terminate();
310 | break;
311 | case 'exit':
312 | LogEndCommand(message.data);
313 | if (message.data > 0)
314 | lastError = new Error(`exit code: ${message.data}`);
315 | break;
316 | case 'run':
317 | break;
318 | default:
319 | throw new Error('Unhandled switch case', message.type);
320 | }
321 | };
322 |
323 | var args = parseArguments(options.text);
324 | LogNewCommand(options.prettyName || args[0],
325 | '$ ffmpeg ' + args.join(' ') + '\n');
326 | worker.postMessage({type : "run", arguments : args, MEMFS : options.data});
327 | }
328 |
329 | function handleFileSelect(evt) {
330 | document.getElementById('inputpreview').innerHTML = '';
331 |
332 | var files = evt.target.files; // FileList object
333 | if (files.length === 0)
334 | return;
335 | inputFile = files[0];
336 |
337 | var newElement = getDownloadLink(inputFile, inputFile.name);
338 | document.getElementById('inputpreview').appendChild(newElement);
339 | if (inputFile.name.match(/\.jpeg|\.gif|\.jpg|\.png/)) return;
340 | var videoPlayer = videojs(newElement.firstChild.firstChild.id);
341 | videoPlayer.rangeslider();
342 | videoPlayer.ready(function() {
343 | videoPlayer.volume(0);
344 | videoPlayer.play();
345 | videoPlayer.on('sliderchange', function() {
346 | var values = videoPlayer.getValueSlider();
347 | document.getElementById('start').value = values.start;
348 | document.getElementById('duration').value = values.end - values.start;
349 | });
350 | videoPlayer.on('loadedRangeSlider', function() {
351 | videoPlayer.pause();
352 | videoPlayer.setValueSlider(0, 5);
353 | });
354 | });
355 | }
356 |
357 | document.addEventListener("DOMContentLoaded", function() {
358 | if (!IsSupported()) {
359 | alert(`This website is not supported by your browser!
360 | Update to new Chrome or Firefox.`);
361 | return;
362 | }
363 |
364 | document.getElementById('files')
365 | .addEventListener('change', handleFileSelect, false);
366 |
367 | filesElement = document.querySelector("#outputfiles");
368 |
369 | document.getElementById('uploadForm')
370 | .onsubmit = function(e) { return false; };
371 |
372 | $('#convertgif')
373 | .on('click', function(e) {
374 | RunGifConversion();
375 | return true;
376 | });
377 |
378 | $('#convertmp4')
379 | .on('click', function(e) {
380 | RunMp4Conversion();
381 | return true;
382 | });
383 |
384 | Logger = Log.create();
385 | Logger.num = 0;
386 | LogOutput('Loading JavaScript files (it may take a minute)\n');
387 |
388 | window.onerror = function(msg, url, line, col, error) {
389 | LogOutput(`\n${msg} line: ${line} col: ${col} url: ${url}\n`);
390 | };
391 |
392 | $('#log')
393 | .on('click', '.fold',
394 | function() { return $(this).toggleClass('open'); });
395 |
396 | RunCommand({text : '-version -hide_banner'}, function(err) {
397 | if (err)
398 | throw err;
399 | RunCommand({text : '-formats -hide_banner'}, function(err) {
400 | if (err)
401 | throw err;
402 | LogOutput('Sample commands executing fine!');
403 | });
404 | });
405 | });
406 | })();
407 |
--------------------------------------------------------------------------------
/vendor/ansiparse.js:
--------------------------------------------------------------------------------
1 | ansiparse = function (str) {
2 | //
3 | // I'm terrible at writing parsers.
4 | //
5 | var matchingControl = null,
6 | matchingData = null,
7 | matchingText = '',
8 | ansiState = [],
9 | result = [],
10 | state = {},
11 | eraseChar;
12 |
13 | //
14 | // General workflow for this thing is:
15 | // \033\[33mText
16 | // | | |
17 | // | | matchingText
18 | // | matchingData
19 | // matchingControl
20 | //
21 | // In further steps we hope it's all going to be fine. It usually is.
22 | //
23 |
24 | //
25 | // Erases a char from the output
26 | //
27 | eraseChar = function () {
28 | var index, text;
29 | if (matchingText.length) {
30 | matchingText = matchingText.substr(0, matchingText.length - 1);
31 | }
32 | else if (result.length) {
33 | index = result.length - 1;
34 | text = result[index].text;
35 | if (text.length === 1) {
36 | //
37 | // A result bit was fully deleted, pop it out to simplify the final output
38 | //
39 | result.pop();
40 | }
41 | else {
42 | result[index].text = text.substr(0, text.length - 1);
43 | }
44 | }
45 | };
46 |
47 | for (var i = 0; i < str.length; i++) {
48 | if (matchingControl != null) {
49 | if (matchingControl == '\033' && str[i] == '\[') {
50 | //
51 | // We've matched full control code. Lets start matching formating data.
52 | //
53 |
54 | //
55 | // "emit" matched text with correct state
56 | //
57 | if (matchingText) {
58 | state.text = matchingText;
59 | result.push(state);
60 | state = {};
61 | matchingText = "";
62 | }
63 |
64 | matchingControl = null;
65 | matchingData = '';
66 | }
67 | else {
68 | //
69 | // We failed to match anything - most likely a bad control code. We
70 | // go back to matching regular strings.
71 | //
72 | matchingText += matchingControl + str[i];
73 | matchingControl = null;
74 | }
75 | continue;
76 | }
77 | else if (matchingData != null) {
78 | if (str[i] == ';') {
79 | //
80 | // `;` separates many formatting codes, for example: `\033[33;43m`
81 | // means that both `33` and `43` should be applied.
82 | //
83 | // TODO: this can be simplified by modifying state here.
84 | //
85 | ansiState.push(matchingData);
86 | matchingData = '';
87 | }
88 | else if (str[i] == 'm') {
89 | //
90 | // `m` finished whole formatting code. We can proceed to matching
91 | // formatted text.
92 | //
93 | ansiState.push(matchingData);
94 | matchingData = null;
95 | matchingText = '';
96 |
97 | //
98 | // Convert matched formatting data into user-friendly state object.
99 | //
100 | // TODO: DRY.
101 | //
102 | ansiState.forEach(function (ansiCode) {
103 | if (ansiparse.foregroundColors[ansiCode]) {
104 | state.foreground = ansiparse.foregroundColors[ansiCode];
105 | }
106 | else if (ansiparse.backgroundColors[ansiCode]) {
107 | state.background = ansiparse.backgroundColors[ansiCode];
108 | }
109 | else if (ansiCode == 39) {
110 | delete state.foreground;
111 | }
112 | else if (ansiCode == 49) {
113 | delete state.background;
114 | }
115 | else if (ansiparse.styles[ansiCode]) {
116 | state[ansiparse.styles[ansiCode]] = true;
117 | }
118 | else if (ansiCode == 22) {
119 | state.bold = false;
120 | }
121 | else if (ansiCode == 23) {
122 | state.italic = false;
123 | }
124 | else if (ansiCode == 24) {
125 | state.underline = false;
126 | }
127 | });
128 | ansiState = [];
129 | }
130 | else {
131 | matchingData += str[i];
132 | }
133 | continue;
134 | }
135 |
136 | if (str[i] == '\033') {
137 | matchingControl = str[i];
138 | }
139 | else if (str[i] == '\u0008') {
140 | eraseChar();
141 | }
142 | else {
143 | matchingText += str[i];
144 | }
145 | }
146 |
147 | if (matchingText) {
148 | state.text = matchingText + (matchingControl ? matchingControl : '');
149 | result.push(state);
150 | }
151 | return result;
152 | }
153 |
154 | ansiparse.foregroundColors = {
155 | '30': 'black',
156 | '31': 'red',
157 | '32': 'green',
158 | '33': 'yellow',
159 | '34': 'blue',
160 | '35': 'magenta',
161 | '36': 'cyan',
162 | '37': 'white',
163 | '90': 'grey'
164 | };
165 |
166 | ansiparse.backgroundColors = {
167 | '40': 'black',
168 | '41': 'red',
169 | '42': 'green',
170 | '43': 'yellow',
171 | '44': 'blue',
172 | '45': 'magenta',
173 | '46': 'cyan',
174 | '47': 'white'
175 | };
176 |
177 | ansiparse.styles = {
178 | '1': 'bold',
179 | '3': 'italic',
180 | '4': 'underline'
181 | };
182 |
183 | if (typeof module == "object" && typeof window == "undefined") {
184 | module.exports = ansiparse;
185 | }
186 |
187 |
--------------------------------------------------------------------------------
/vendor/log.js:
--------------------------------------------------------------------------------
1 | var Log = function() {
2 | this.autoCloseFold = true;
3 | this.listeners = [];
4 | this.renderer = new Log.Renderer;
5 | this.children = new Log.Nodes(this);
6 | this.parts = {};
7 | this.folds = new Log.Folds(this);
8 | this.times = new Log.Times(this);
9 | return this;
10 | };
11 |
12 | Log.extend = function(one, other) {
13 | var name;
14 | for (name in other) {
15 | one[name] = other[name];
16 | }
17 | return one;
18 | };
19 |
20 | Log.extend(Log, {
21 | DEBUG: false,
22 | SLICE: 500,
23 | TIMEOUT: 25,
24 | FOLD: /fold:(start|end):([\w_\-\.]+)/,
25 | TIME: /time:(start|end):([\w_\-\.]+):?([\w_\-\.\=\,]*)/,
26 | create: function(options) {
27 | var listener, log, _i, _len, _ref;
28 | options || (options = {});
29 | log = new Log();
30 | if (options.limit) {
31 | log.listeners.push(log.limit = new Log.Limit(options.limit));
32 | }
33 | _ref = options.listeners || [];
34 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
35 | listener = _ref[_i];
36 | log.listeners.push(listener);
37 | }
38 | return log;
39 | }
40 | });
41 |
42 | var newLineAtTheEndRegexp, newLineRegexp, rRegexp, removeCarriageReturns;
43 |
44 | Log.Node = function(id, num) {
45 | this.id = id;
46 | this.num = num;
47 | this.key = Log.Node.key(this.id);
48 | this.children = new Log.Nodes(this);
49 | return this;
50 | };
51 |
52 | Log.extend(Log.Node, {
53 | key: function(id) {
54 | if (id) {
55 | return id.split('-').map(function(i) {
56 | return '000000'.concat(i).slice(-6);
57 | }).join('');
58 | }
59 | }
60 | });
61 |
62 | Log.extend(Log.Node.prototype, {
63 | addChild: function(node) {
64 | return this.children.add(node);
65 | },
66 | remove: function() {
67 | this.log.remove(this.element);
68 | return this.parent.children.remove(this);
69 | }
70 | });
71 |
72 | Object.defineProperty(Log.Node.prototype, 'log', {
73 | get: function() {
74 | var _ref;
75 | return this._log || (this._log = ((_ref = this.parent) != null ? _ref.log : void 0) || this.parent);
76 | }
77 | });
78 |
79 | Object.defineProperty(Log.Node.prototype, 'firstChild', {
80 | get: function() {
81 | return this.children.first;
82 | }
83 | });
84 |
85 | Object.defineProperty(Log.Node.prototype, 'lastChild', {
86 | get: function() {
87 | return this.children.last;
88 | }
89 | });
90 |
91 | Log.Nodes = function(parent) {
92 | if (parent) {
93 | this.parent = parent;
94 | }
95 | this.items = [];
96 | this.index = {};
97 | return this;
98 | };
99 |
100 | Log.extend(Log.Nodes.prototype, {
101 | add: function(item) {
102 | var ix, next, prev, _ref, _ref1;
103 | ix = this.position(item) || 0;
104 | this.items.splice(ix, 0, item);
105 | if (this.parent) {
106 | item.parent = this.parent;
107 | }
108 | prev = function(item) {
109 | while (item && !item.children.last) {
110 | item = item.prev;
111 | }
112 | return item != null ? item.children.last : void 0;
113 | };
114 | next = function(item) {
115 | while (item && !item.children.first) {
116 | item = item.next;
117 | }
118 | return item != null ? item.children.first : void 0;
119 | };
120 | if (item.prev = this.items[ix - 1] || prev((_ref = this.parent) != null ? _ref.prev : void 0)) {
121 | item.prev.next = item;
122 | }
123 | if (item.next = this.items[ix + 1] || next((_ref1 = this.parent) != null ? _ref1.next : void 0)) {
124 | item.next.prev = item;
125 | }
126 | return item;
127 | },
128 | remove: function(item) {
129 | this.items.splice(this.items.indexOf(item), 1);
130 | if (item.next) {
131 | item.next.prev = item.prev;
132 | }
133 | if (item.prev) {
134 | item.prev.next = item.next;
135 | }
136 | if (this.items.length === 0) {
137 | return this.parent.remove();
138 | }
139 | },
140 | position: function(item) {
141 | var ix, _i, _ref;
142 | for (ix = _i = _ref = this.items.length - 1; _i >= 0; ix = _i += -1) {
143 | if (this.items[ix].key < item.key) {
144 | return ix + 1;
145 | }
146 | }
147 | },
148 | indexOf: function() {
149 | return this.items.indexOf.apply(this.items, arguments);
150 | },
151 | slice: function() {
152 | return this.items.slice.apply(this.items, arguments);
153 | },
154 | each: function(func) {
155 | return this.items.slice().forEach(func);
156 | },
157 | map: function(func) {
158 | return this.items.map(func);
159 | }
160 | });
161 |
162 | Object.defineProperty(Log.Nodes.prototype, 'first', {
163 | get: function() {
164 | return this.items[0];
165 | }
166 | });
167 |
168 | Object.defineProperty(Log.Nodes.prototype, 'last', {
169 | get: function() {
170 | return this.items[this.length - 1];
171 | }
172 | });
173 |
174 | Object.defineProperty(Log.Nodes.prototype, 'length', {
175 | get: function() {
176 | return this.items.length;
177 | }
178 | });
179 |
180 | Log.Part = function(id, num, string) {
181 | Log.Node.apply(this, arguments);
182 | this.string = string || '';
183 | this.string = this.string.replace(/\033\[1000D/gm, '\r');
184 | this.string = this.string.replace(/\r+\n/gm, '\n');
185 | this.strings = this.string.split(/^/gm) || [];
186 | this.slices = ((function() {
187 | var _results;
188 | _results = [];
189 | while (this.strings.length > 0) {
190 | _results.push(this.strings.splice(0, Log.SLICE));
191 | }
192 | return _results;
193 | }).call(this));
194 | return this;
195 | };
196 |
197 | Log.extend(Log.Part, {
198 | create: function(log, num, string) {
199 | var part;
200 | part = new Log.Part(num.toString(), num, string);
201 | log.addChild(part);
202 | return part.process(0, -1);
203 | }
204 | });
205 |
206 | Log.Part.prototype = Log.extend(new Log.Node, {
207 | remove: function() {},
208 | process: function(slice, num) {
209 | var node, span, spans, string, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3, _ref4,
210 | _this = this;
211 | _ref = this.slices[slice] || [];
212 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
213 | string = _ref[_i];
214 | if ((_ref1 = this.log.limit) != null ? _ref1.limited : void 0) {
215 | return;
216 | }
217 | spans = [];
218 | _ref2 = Log.Deansi.apply(string);
219 | for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
220 | node = _ref2[_j];
221 | span = Log.Span.create(this, "" + this.id + "-" + (num += 1), num, node.text, node["class"]);
222 | span.render();
223 | spans.push(span);
224 | }
225 | if ((_ref3 = spans[0]) != null ? (_ref4 = _ref3.line) != null ? _ref4.cr : void 0 : void 0) {
226 | spans[0].line.clear();
227 | }
228 | }
229 | if (!(slice >= this.slices.length - 1)) {
230 | return setTimeout((function() {
231 | return _this.process(slice + 1, num);
232 | }), Log.TIMEOUT);
233 | }
234 | }
235 | });
236 |
237 | newLineAtTheEndRegexp = new RegExp("\n$");
238 |
239 | newLineRegexp = new RegExp("\n");
240 |
241 | rRegexp = new RegExp("\r");
242 |
243 | removeCarriageReturns = function(string) {
244 | var index;
245 | index = string.lastIndexOf("\r");
246 | if (index === -1) {
247 | return string;
248 | }
249 | return string.substr(index + 1);
250 | };
251 |
252 | Log.Span = function(id, num, text, classes) {
253 | var fold, time, _ref;
254 | Log.Node.apply(this, arguments);
255 | if (fold = text.match(Log.FOLD)) {
256 | this.fold = true;
257 | this.event = fold[1];
258 | this.text = this.name = fold[2];
259 | } else if (time = text.match(Log.TIME)) {
260 | this.time = true;
261 | this.event = time[1];
262 | this.name = time[2];
263 | this.stats = time[3];
264 | } else {
265 | this.text = text;
266 | this.text = removeCarriageReturns(this.text);
267 | this.text = this.text.replace(newLineAtTheEndRegexp, '');
268 | this.nl = !!((_ref = text[text.length - 1]) != null ? _ref.match(newLineRegexp) : void 0);
269 | this.cr = !!text.match(rRegexp);
270 | this["class"] = this.cr && ['clears'] || classes;
271 | }
272 | return this;
273 | };
274 |
275 | Log.extend(Log.Span, {
276 | create: function(parent, id, num, text, classes) {
277 | var span;
278 | span = new Log.Span(id, num, text, classes);
279 | parent.addChild(span);
280 | return span;
281 | },
282 | render: function(parent, id, num, text, classes) {
283 | var span;
284 | span = this.create(parent, id, num, text, classes);
285 | return span.render();
286 | }
287 | });
288 |
289 | Log.Span.prototype = Log.extend(new Log.Node, {
290 | render: function() {
291 | var tail;
292 | if (this.time && this.event === 'end' && this.prev) {
293 | if (Log.DEBUG) {
294 | console.log("S.0 insert " + this.id + " after prev " + this.prev.id);
295 | }
296 | this.nl = this.prev.nl;
297 | this.log.insert(this.data, {
298 | after: this.prev.element
299 | });
300 | this.line = this.prev.line;
301 | } else if (!this.fold && this.prev && !this.prev.fold && !this.prev.nl) {
302 | if (Log.DEBUG) {
303 | console.log("S.1 insert " + this.id + " after prev " + this.prev.id);
304 | }
305 | this.log.insert(this.data, {
306 | after: this.prev.element
307 | });
308 | this.line = this.prev.line;
309 | } else if (!this.fold && this.next && !this.next.fold && !this.next.time) {
310 | if (Log.DEBUG) {
311 | console.log("S.2 insert " + this.id + " before next " + this.next.id);
312 | }
313 | this.log.insert(this.data, {
314 | before: this.next.element
315 | });
316 | this.line = this.next.line;
317 | } else {
318 | this.line = Log.Line.create(this.log, [this]);
319 | this.line.render();
320 | }
321 | if (this.nl && (tail = this.tail).length > 0) {
322 | this.split(tail);
323 | }
324 | if (this.time) {
325 | return this.log.times.add(this);
326 | }
327 | },
328 | remove: function() {
329 | Log.Node.prototype.remove.apply(this);
330 | if (this.line) {
331 | return this.line.remove(this);
332 | }
333 | },
334 | split: function(spans) {
335 | var line, span, _i, _len;
336 | if (Log.DEBUG) {
337 | console.log("S.4 split [" + (spans.map(function(span) {
338 | return span.id;
339 | }).join(', ')) + "]");
340 | }
341 | for (_i = 0, _len = spans.length; _i < _len; _i++) {
342 | span = spans[_i];
343 | this.log.remove(span.element);
344 | }
345 | line = Log.Line.create(this.log, spans);
346 | line.render();
347 | if (line.cr) {
348 | return line.clear();
349 | }
350 | },
351 | clear: function() {
352 | if (this.prev && this.isSibling(this.prev) && this.isSequence(this.prev)) {
353 | this.prev.clear();
354 | return this.prev.remove();
355 | }
356 | },
357 | isSequence: function(other) {
358 | return this.parent.num - other.parent.num === this.log.children.indexOf(this.parent) - this.log.children.indexOf(other.parent);
359 | },
360 | isSibling: function(other) {
361 | var _ref, _ref1;
362 | return ((_ref = this.element) != null ? _ref.parentNode : void 0) === ((_ref1 = other.element) != null ? _ref1.parentNode : void 0);
363 | },
364 | siblings: function(type) {
365 | var siblings, span;
366 | siblings = [];
367 | while ((span = (span || this)[type]) && this.isSibling(span)) {
368 | siblings.push(span);
369 | }
370 | return siblings;
371 | }
372 | });
373 |
374 | Object.defineProperty(Log.Span.prototype, 'data', {
375 | get: function() {
376 | return {
377 | id: this.id,
378 | type: 'span',
379 | text: this.text,
380 | "class": this["class"],
381 | time: this.time
382 | };
383 | }
384 | });
385 |
386 | Object.defineProperty(Log.Span.prototype, 'line', {
387 | get: function() {
388 | return this._line;
389 | },
390 | set: function(line) {
391 | if (this.line) {
392 | this.line.remove(this);
393 | }
394 | this._line = line;
395 | if (this.line) {
396 | return this.line.add(this);
397 | }
398 | }
399 | });
400 |
401 | Object.defineProperty(Log.Span.prototype, 'element', {
402 | get: function() {
403 | return document.getElementById(this.id);
404 | }
405 | });
406 |
407 | Object.defineProperty(Log.Span.prototype, 'head', {
408 | get: function() {
409 | return this.siblings('prev').reverse();
410 | }
411 | });
412 |
413 | Object.defineProperty(Log.Span.prototype, 'tail', {
414 | get: function() {
415 | return this.siblings('next');
416 | }
417 | });
418 |
419 | Log.Line = function(log) {
420 | this.log = log;
421 | this.spans = [];
422 | return this;
423 | };
424 |
425 | Log.extend(Log.Line, {
426 | create: function(log, spans) {
427 | var line, span, _i, _len;
428 | if ((span = spans[0]) && span.fold) {
429 | line = new Log.Fold(log, span.event, span.name);
430 | } else {
431 | line = new Log.Line(log);
432 | }
433 | for (_i = 0, _len = spans.length; _i < _len; _i++) {
434 | span = spans[_i];
435 | span.line = line;
436 | }
437 | return line;
438 | }
439 | });
440 |
441 | Log.extend(Log.Line.prototype, {
442 | add: function(span) {
443 | var ix;
444 | if (span.cr) {
445 | this.cr = true;
446 | }
447 | if (this.spans.indexOf(span) > -1) {
448 |
449 | } else if ((ix = this.spans.indexOf(span.prev)) > -1) {
450 | return this.spans.splice(ix + 1, 0, span);
451 | } else if ((ix = this.spans.indexOf(span.next)) > -1) {
452 | return this.spans.splice(ix, 0, span);
453 | } else {
454 | return this.spans.push(span);
455 | }
456 | },
457 | remove: function(span) {
458 | var ix;
459 | if ((ix = this.spans.indexOf(span)) > -1) {
460 | return this.spans.splice(ix, 1);
461 | }
462 | },
463 | render: function() {
464 | var fold;
465 | if ((fold = this.prev) && fold.event === 'start' && fold.active) {
466 | if (this.next && !this.next.fold) {
467 | if (Log.DEBUG) {
468 | console.log("L.0 insert " + this.id + " before next " + this.next.id);
469 | }
470 | return this.element = this.log.insert(this.data, {
471 | before: this.next.element
472 | });
473 | } else {
474 | if (Log.DEBUG) {
475 | console.log("L.0 insert " + this.id + " into fold " + fold.id);
476 | }
477 | fold = this.log.folds.folds[fold.name].fold;
478 | return this.element = this.log.insert(this.data, {
479 | into: fold
480 | });
481 | }
482 | } else if (this.prev) {
483 | if (Log.DEBUG) {
484 | console.log("L.1 insert " + this.spans[0].id + " after prev " + this.prev.id);
485 | }
486 | return this.element = this.log.insert(this.data, {
487 | after: this.prev.element
488 | });
489 | } else if (this.next) {
490 | if (Log.DEBUG) {
491 | console.log("L.2 insert " + this.spans[0].id + " before next " + this.next.id);
492 | }
493 | return this.element = this.log.insert(this.data, {
494 | before: this.next.element
495 | });
496 | } else {
497 | if (Log.DEBUG) {
498 | console.log("L.3 insert " + this.spans[0].id + " into #log");
499 | }
500 | return this.element = this.log.insert(this.data);
501 | }
502 | },
503 | clear: function() {
504 | var cr, _i, _len, _ref, _results;
505 | _ref = this.crs;
506 | _results = [];
507 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
508 | cr = _ref[_i];
509 | _results.push(cr.clear());
510 | }
511 | return _results;
512 | }
513 | });
514 |
515 | Object.defineProperty(Log.Line.prototype, 'id', {
516 | get: function() {
517 | var _ref;
518 | return (_ref = this.spans[0]) != null ? _ref.id : void 0;
519 | }
520 | });
521 |
522 | Object.defineProperty(Log.Line.prototype, 'data', {
523 | get: function() {
524 | return {
525 | type: 'paragraph',
526 | nodes: this.nodes
527 | };
528 | }
529 | });
530 |
531 | Object.defineProperty(Log.Line.prototype, 'nodes', {
532 | get: function() {
533 | return this.spans.map(function(span) {
534 | return span.data;
535 | });
536 | }
537 | });
538 |
539 | Object.defineProperty(Log.Line.prototype, 'prev', {
540 | get: function() {
541 | var _ref;
542 | return (_ref = this.spans[0].prev) != null ? _ref.line : void 0;
543 | }
544 | });
545 |
546 | Object.defineProperty(Log.Line.prototype, 'next', {
547 | get: function() {
548 | var _ref;
549 | return (_ref = this.spans[this.spans.length - 1].next) != null ? _ref.line : void 0;
550 | }
551 | });
552 |
553 | Object.defineProperty(Log.Line.prototype, 'crs', {
554 | get: function() {
555 | return this.spans.filter(function(span) {
556 | return span.cr;
557 | });
558 | }
559 | });
560 |
561 | Log.Fold = function(log, event, name) {
562 | Log.Line.apply(this, arguments);
563 | this.fold = true;
564 | this.event = event;
565 | this.name = name;
566 | return this;
567 | };
568 |
569 | Log.Fold.prototype = Log.extend(new Log.Line, {
570 | render: function() {
571 | var element, _ref;
572 | if (this.prev && this.prev.element) {
573 | if (Log.DEBUG) {
574 | console.log("F.1 insert " + this.id + " after prev " + this.prev.id);
575 | }
576 | element = this.prev.element;
577 | this.element = this.log.insert(this.data, {
578 | after: element
579 | });
580 | } else if (this.next) {
581 | if (Log.DEBUG) {
582 | console.log("F.2 insert " + this.id + " before next " + this.next.id);
583 | }
584 | element = this.next.element || this.next.element.parentNode;
585 | this.element = this.log.insert(this.data, {
586 | before: element
587 | });
588 | } else {
589 | if (Log.DEBUG) {
590 | console.log("F.3 insert " + this.id);
591 | }
592 | this.element = this.log.insert(this.data);
593 | }
594 | if (this.span.next && ((_ref = this.span.prev) != null ? _ref.isSibling(this.span.next) : void 0)) {
595 | this.span.prev.split([this.span.next].concat(this.span.next.tail));
596 | }
597 | return this.active = this.log.folds.add(this.data);
598 | }
599 | });
600 |
601 | Object.defineProperty(Log.Fold.prototype, 'id', {
602 | get: function() {
603 | return "fold-" + this.event + "-" + this.name;
604 | }
605 | });
606 |
607 | Object.defineProperty(Log.Fold.prototype, 'span', {
608 | get: function() {
609 | return this.spans[0];
610 | }
611 | });
612 |
613 | Object.defineProperty(Log.Fold.prototype, 'data', {
614 | get: function() {
615 | return {
616 | type: 'fold',
617 | id: this.id,
618 | event: this.event,
619 | name: this.name
620 | };
621 | }
622 | });
623 |
624 | Log.prototype = Log.extend(new Log.Node, {
625 | set: function(num, string) {
626 | if (this.parts[num]) {
627 | return console.log("part " + num + " exists");
628 | } else {
629 | this.parts[num] = true;
630 | return Log.Part.create(this, num, string);
631 | }
632 | },
633 | insert: function(data, pos) {
634 | this.trigger('insert', data, pos);
635 | return this.renderer.insert(data, pos);
636 | },
637 | remove: function(node) {
638 | this.trigger('remove', node);
639 | return this.renderer.remove(node);
640 | },
641 | hide: function(node) {
642 | this.trigger('hide', node);
643 | return this.renderer.hide(node);
644 | },
645 | trigger: function() {
646 | var args, ix, listener, _i, _len, _ref, _results;
647 | args = [this].concat(Array.prototype.slice.apply(arguments));
648 | _ref = this.listeners;
649 | _results = [];
650 | for (ix = _i = 0, _len = _ref.length; _i < _len; ix = ++_i) {
651 | listener = _ref[ix];
652 | _results.push(listener.notify.apply(listener, args));
653 | }
654 | return _results;
655 | }
656 | });
657 |
658 | Log.Listener = function() {};
659 |
660 | Log.extend(Log.Listener.prototype, {
661 | notify: function(log, event) {
662 | if (this[event]) {
663 | return this[event].apply(this, [log].concat(Array.prototype.slice.call(arguments, 2)));
664 | }
665 | }
666 | });
667 |
668 | Log.Folds = function(log) {
669 | this.log = log;
670 | this.folds = {};
671 | return this;
672 | };
673 |
674 | Log.extend(Log.Folds.prototype, {
675 | add: function(data) {
676 | var fold, _base, _name;
677 | fold = (_base = this.folds)[_name = data.name] || (_base[_name] = new Log.Folds.Fold);
678 | fold.receive(data, {
679 | autoCloseFold: this.log.autoCloseFold
680 | });
681 | return fold.active;
682 | }
683 | });
684 |
685 | Log.Folds.Fold = function() {
686 | return this;
687 | };
688 |
689 | Log.extend(Log.Folds.Fold.prototype, {
690 | receive: function(data, options) {
691 | this[data.event] = data.id;
692 | if (this.start && this.end && !this.active) {
693 | return this.activate(options);
694 | }
695 | },
696 | activate: function(options) {
697 | var fragment, nextSibling, node, parentNode, toRemove, _i, _len, _ref;
698 | options || (options = {});
699 | if (Log.DEBUG) {
700 | console.log("F.n - activate " + this.start);
701 | }
702 | toRemove = this.fold.parentNode;
703 | parentNode = toRemove.parentNode;
704 | nextSibling = toRemove.nextSibling;
705 | parentNode.removeChild(toRemove);
706 | fragment = document.createDocumentFragment();
707 | _ref = this.nodes;
708 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
709 | node = _ref[_i];
710 | fragment.appendChild(node);
711 | }
712 | this.fold.appendChild(fragment);
713 | parentNode.insertBefore(toRemove, nextSibling);
714 | this.fold.setAttribute('class', this.classes(options['autoCloseFold']));
715 | return this.active = true;
716 | },
717 | classes: function(autoCloseFold) {
718 | var classes;
719 | classes = this.fold.getAttribute('class').split(' ');
720 | classes.push('fold');
721 | if (!autoCloseFold) {
722 | classes.push('open');
723 | }
724 | if (this.fold.childNodes.length > 2) {
725 | classes.push('active');
726 | }
727 | return classes.join(' ');
728 | }
729 | });
730 |
731 | Object.defineProperty(Log.Folds.Fold.prototype, 'fold', {
732 | get: function() {
733 | return this._fold || (this._fold = document.getElementById(this.start));
734 | }
735 | });
736 |
737 | Object.defineProperty(Log.Folds.Fold.prototype, 'nodes', {
738 | get: function() {
739 | var node, nodes;
740 | node = this.fold;
741 | nodes = [];
742 | while ((node = node.nextSibling) && node.id !== this.end) {
743 | nodes.push(node);
744 | }
745 | return nodes;
746 | }
747 | });
748 |
749 | Log.Times = function(log) {
750 | this.log = log;
751 | this.times = {};
752 | return this;
753 | };
754 |
755 | Log.extend(Log.Times.prototype, {
756 | add: function(node) {
757 | var time, _base, _name;
758 | time = (_base = this.times)[_name = node.name] || (_base[_name] = new Log.Times.Time);
759 | return time.receive(node);
760 | },
761 | duration: function(name) {
762 | if (this.times[name]) {
763 | return this.times[name].duration;
764 | }
765 | }
766 | });
767 |
768 | Log.Times.Time = function() {
769 | return this;
770 | };
771 |
772 | Log.extend(Log.Times.Time.prototype, {
773 | receive: function(node) {
774 | this[node.event] = node;
775 | if (Log.DEBUG) {
776 | console.log("T.0 - " + node.event + " " + node.name);
777 | }
778 | if (this.start && this.end) {
779 | return this.finish();
780 | }
781 | },
782 | finish: function() {
783 | var element;
784 | if (Log.DEBUG) {
785 | console.log("T.1 - finish " + this.start.name);
786 | }
787 | element = document.getElementById(this.start.id);
788 | if (element) {
789 | return this.update(element);
790 | }
791 | },
792 | update: function(element) {
793 | element.setAttribute('class', 'duration');
794 | element.setAttribute('title', "This command finished after " + this.duration + " seconds.");
795 | return element.lastChild.nodeValue = "" + this.duration + "s";
796 | }
797 | });
798 |
799 | Object.defineProperty(Log.Times.Time.prototype, 'duration', {
800 | get: function() {
801 | var duration;
802 | duration = this.stats.duration / 1000 / 1000 / 1000;
803 | return duration.toFixed(2);
804 | }
805 | });
806 |
807 | Object.defineProperty(Log.Times.Time.prototype, 'stats', {
808 | get: function() {
809 | var stat, stats, _i, _len, _ref;
810 | if (!(this.end && this.end.stats)) {
811 | return {};
812 | }
813 | stats = {};
814 | _ref = this.end.stats.split(',');
815 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
816 | stat = _ref[_i];
817 | stat = stat.split('=');
818 | stats[stat[0]] = stat[1];
819 | }
820 | return stats;
821 | }
822 | });
823 |
824 | Log.Deansi = {
825 | CLEAR_ANSI: /(?:\033)(?:\[0?c|\[[0356]n|\[7[lh]|\[\?25[lh]|\(B|H|\[(?:\d+(;\d+){,2})?G|\[(?:[12])?[JK]|[DM]|\[0K)/gm,
826 | apply: function(string) {
827 | var nodes,
828 | _this = this;
829 | if (!string) {
830 | return [];
831 | }
832 | string = string.replace(this.CLEAR_ANSI, '');
833 | nodes = ansiparse(string).map(function(part) {
834 | return _this.node(part);
835 | });
836 | return nodes;
837 | },
838 | node: function(part) {
839 | var classes, node;
840 | node = {
841 | type: 'span',
842 | text: part.text
843 | };
844 | if (classes = this.classes(part)) {
845 | node["class"] = classes.join(' ');
846 | }
847 | return node;
848 | },
849 | classes: function(part) {
850 | var result;
851 | result = [];
852 | result = result.concat(this.colors(part));
853 | if (result.length > 0) {
854 | return result;
855 | }
856 | },
857 | colors: function(part) {
858 | var colors;
859 | colors = [];
860 | if (part.foreground) {
861 | colors.push(part.foreground);
862 | }
863 | if (part.background) {
864 | colors.push("bg-" + part.background);
865 | }
866 | if (part.bold) {
867 | colors.push('bold');
868 | }
869 | if (part.italic) {
870 | colors.push('italic');
871 | }
872 | if (part.underline) {
873 | colors.push('underline');
874 | }
875 | return colors;
876 | },
877 | hidden: function(part) {
878 | if (part.text.match(/\r/)) {
879 | part.text = part.text.replace(/^.*\r/gm, '');
880 | return true;
881 | }
882 | }
883 | };
884 |
885 | Log.Limit = function(max_lines) {
886 | this.max_lines = max_lines || 1000;
887 | return this;
888 | };
889 |
890 | Log.Limit.prototype = Log.extend(new Log.Listener, {
891 | count: 0,
892 | insert: function(log, node, pos) {
893 | if (node.type === 'paragraph' && !node.hidden) {
894 | return this.count += 1;
895 | }
896 | }
897 | });
898 |
899 | Object.defineProperty(Log.Limit.prototype, 'limited', {
900 | get: function() {
901 | return this.count >= this.max_lines;
902 | }
903 | });
904 |
905 | Log.Renderer = function() {
906 | this.frag = document.createDocumentFragment();
907 | this.para = this.createParagraph();
908 | this.span = this.createSpan();
909 | this.text = document.createTextNode('');
910 | this.fold = this.createFold();
911 | return this;
912 | };
913 |
914 | Log.extend(Log.Renderer.prototype, {
915 | insert: function(data, pos) {
916 | var after, before, into, node;
917 | node = this.render(data);
918 | if (into = pos != null ? pos.into : void 0) {
919 | if (typeof into === 'String') {
920 | into = document.getElementById(pos != null ? pos.into : void 0);
921 | }
922 | if (pos != null ? pos.prepend : void 0) {
923 | this.prependTo(node, into);
924 | } else {
925 | this.appendTo(node, into);
926 | }
927 | } else if (after = pos != null ? pos.after : void 0) {
928 | if (typeof after === 'String') {
929 | after = document.getElementById(pos);
930 | }
931 | this.insertAfter(node, after);
932 | } else if (before = pos != null ? pos.before : void 0) {
933 | if (typeof before === 'String') {
934 | before = document.getElementById(pos != null ? pos.before : void 0);
935 | }
936 | this.insertBefore(node, before);
937 | } else {
938 | this.insertBefore(node);
939 | }
940 | return node;
941 | },
942 | hide: function(node) {
943 | node.setAttribute('class', this.addClass(node.getAttribute('class'), 'hidden'));
944 | return node;
945 | },
946 | remove: function(node) {
947 | if (node) {
948 | node.parentNode.removeChild(node);
949 | }
950 | return node;
951 | },
952 | render: function(data) {
953 | var frag, node, type, _i, _len;
954 | if (data instanceof Array) {
955 | frag = this.frag.cloneNode(true);
956 | for (_i = 0, _len = data.length; _i < _len; _i++) {
957 | node = data[_i];
958 | node = this.render(node);
959 | if (node) {
960 | frag.appendChild(node);
961 | }
962 | }
963 | return frag;
964 | } else {
965 | data.type || (data.type = 'paragraph');
966 | type = data.type[0].toUpperCase() + data.type.slice(1);
967 | return this["render" + type](data);
968 | }
969 | },
970 | renderParagraph: function(data) {
971 | var node, para, type, _i, _len, _ref;
972 | para = this.para.cloneNode(true);
973 | if (data.id) {
974 | para.setAttribute('id', data.id);
975 | }
976 | if (data.hidden) {
977 | para.setAttribute('style', 'display: none;');
978 | }
979 | _ref = data.nodes || [];
980 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
981 | node = _ref[_i];
982 | type = node.type[0].toUpperCase() + node.type.slice(1);
983 | node = this["render" + type](node);
984 | para.appendChild(node);
985 | }
986 | return para;
987 | },
988 | renderFold: function(data) {
989 | var fold;
990 | fold = this.fold.cloneNode(true);
991 | fold.setAttribute('id', data.id || ("fold-" + data.event + "-" + data.name));
992 | fold.setAttribute('class', "fold-" + data.event);
993 | if (data.event === 'start') {
994 | fold.lastChild.lastChild.nodeValue = data.name;
995 | } else {
996 | fold.removeChild(fold.lastChild);
997 | }
998 | return fold;
999 | },
1000 | renderSpan: function(data) {
1001 | var span;
1002 | span = this.span.cloneNode(true);
1003 | if (data.id) {
1004 | span.setAttribute('id', data.id);
1005 | }
1006 | if (data["class"]) {
1007 | span.setAttribute('class', data["class"]);
1008 | }
1009 | span.lastChild.nodeValue = data.text || '';
1010 | return span;
1011 | },
1012 | renderText: function(data) {
1013 | var text;
1014 | text = this.text.cloneNode(true);
1015 | text.nodeValue = data.text;
1016 | return text;
1017 | },
1018 | createParagraph: function() {
1019 | var para;
1020 | para = document.createElement('p');
1021 | para.appendChild(document.createElement('a'));
1022 | return para;
1023 | },
1024 | createFold: function() {
1025 | var fold;
1026 | fold = document.createElement('div');
1027 | fold.appendChild(this.createSpan());
1028 | fold.lastChild.setAttribute('class', 'fold-name');
1029 | return fold;
1030 | },
1031 | createSpan: function() {
1032 | var span;
1033 | span = document.createElement('span');
1034 | span.appendChild(document.createTextNode(' '));
1035 | return span;
1036 | },
1037 | insertBefore: function(node, other) {
1038 | var log;
1039 | if (other) {
1040 | return other.parentNode.insertBefore(node, other);
1041 | } else {
1042 | log = document.getElementById('log');
1043 | return log.insertBefore(node, log.firstChild);
1044 | }
1045 | },
1046 | insertAfter: function(node, other) {
1047 | if (other.nextSibling) {
1048 | return this.insertBefore(node, other.nextSibling);
1049 | } else {
1050 | return this.appendTo(node, other.parentNode);
1051 | }
1052 | },
1053 | prependTo: function(node, other) {
1054 | if (other.firstChild) {
1055 | return other.insertBefore(node, other.firstChild);
1056 | } else {
1057 | return appendTo(node, other);
1058 | }
1059 | },
1060 | appendTo: function(node, other) {
1061 | return other.appendChild(node);
1062 | },
1063 | addClass: function(classes, string) {
1064 | if (classes != null ? classes.indexOf(string) : void 0) {
1065 | return;
1066 | }
1067 | if (classes) {
1068 | return "" + classes + " " + string;
1069 | } else {
1070 | return string;
1071 | }
1072 | }
1073 | });
1074 |
1075 | window.Log = Log;
1076 |
--------------------------------------------------------------------------------
/vendor/rangeslider.css:
--------------------------------------------------------------------------------
1 | /*Range Slider Bar Time*/
2 | .vjs-default-skin .vjs-timebar-RS{
3 | color: red;
4 | top: -1em;
5 | height: 100%;
6 | position: relative;
7 | background: rgba(100,100,100,.5);/*Quitar*/
8 | }
9 |
10 |
11 |
12 | /*Selection Range Slider Bar Selected*/
13 | .vjs-default-skin .vjs-rangeslider-holder{height: 100%;}
14 |
15 | .vjs-default-skin .vjs-selectionbar-RS{
16 | height: 100%;
17 | float: left;
18 | width: 100%;
19 | left: 0em;
20 | right: 0em;
21 | position:absolute;
22 | background-color: #FFE800;
23 | background: #FFE800;
24 | background: -moz-linear-gradient(top, #FFE800, #A69700);
25 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#FFE800), to(#A69700));
26 | background: -webkit-linear-gradient(top, #FFE800, #A69700);
27 | background: -o-linear-gradient(top, #FFE800, #A69700);
28 | background: -ms-linear-gradient(top, #FFE800, #A69700);
29 | background: linear-gradient(top, #FFE800, #A69700);
30 | opacity: 0.8;
31 | }
32 |
33 | .vjs-default-skin div.vjs-rangeslider-holder.locked > div.vjs-selectionbar-RS {
34 | background-color: #FF6565;
35 | background: #FF6565;
36 | background: -moz-linear-gradient(top, #FF6565, #300000);
37 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#FF6565), to(#300000));
38 | background: -webkit-linear-gradient(top, #FF6565, #300000);
39 | background: -o-linear-gradient(top, #FF6565, #300000);
40 | background: -ms-linear-gradient(top, #FF6565, #300000);
41 | background: linear-gradient(top, #FF6565, #300000);
42 | }
43 |
44 |
45 | /*Arrow and Handle*/
46 | .vjs-default-skin div.vjs-rangeslider-handle {
47 | position: absolute;
48 | margin-top: 0;
49 | cursor: pointer !important;
50 | background-color: transparent;
51 | }
52 |
53 | .vjs-default-skin .vjs-selectionbar-left-RS{height: 100%;left: 0;z-index:10}
54 | .vjs-default-skin .vjs-selectionbar-right-RS{height: 100%;left: 100%;z-index:20}
55 |
56 | .vjs-default-skin div.vjs-selectionbar-left-RS,
57 | .vjs-default-skin div.vjs-selectionbar-right-RS {
58 | top: 0em;
59 | position: absolute;
60 | width:0em;
61 | }
62 |
63 | .vjs-default-skin div.vjs-selectionbar-arrow-RS {
64 | width: 0;
65 | height: 0;
66 | border-left: 1em solid transparent;
67 | border-right: 1em solid transparent;
68 | border-top: 1em solid #FFF273;
69 | margin-left: -1em;
70 | opacity: 0.8;
71 |
72 | position: absolute;
73 | top: -1em;
74 | }
75 | .vjs-default-skin div.vjs-rangeslider-handle.active > div.vjs-selectionbar-arrow-RS {
76 | border-top-color: #5F5FB3;
77 | }
78 |
79 | .vjs-default-skin div.vjs-rangeslider-holder.locked .vjs-rangeslider-handle > div.vjs-selectionbar-arrow-RS {
80 | border-top-color: #FF6565;
81 | }
82 |
83 | .vjs-default-skin div.vjs-selectionbar-line-RS {
84 | width: 1px;
85 | height: 1em;
86 | background-color: #FFF273;
87 |
88 | position:absolute;
89 | top: 0em;
90 | }
91 | .vjs-default-skin div.vjs-rangeslider-handle.active > div.vjs-selectionbar-line-RS {
92 | background-color: #5F5FB3;
93 | }
94 |
95 | .vjs-default-skin div.vjs-rangeslider-holder.locked .vjs-rangeslider-handle > div.vjs-selectionbar-line-RS {
96 | background-color: #FF6565;
97 | }
98 |
99 |
100 | /* Time Panel */
101 | .vjs-default-skin .vjs-timepanel-RS{
102 | width: 100%;
103 | height: 1em;
104 | font-weight: bold;
105 | font-size: 15px;
106 | top: -2em;
107 | position: absolute;
108 | visibility:visible;
109 | opacity:1;
110 | transition-delay:0s;
111 | }
112 | .vjs-default-skin .vjs-timepanel-RS.disable{
113 | visibility:hidden;
114 | opacity:0;
115 | -webkit-transition: visibility 1s linear 1s,opacity 1s linear;
116 | -moz-transition: visibility 1s linear 1s,opacity 1s linear;
117 | -o-transition: visibility 1s linear 1s,opacity 1s linear;
118 | transition:visibility 1s linear 1s,opacity 1s linear;
119 | }
120 |
121 | .vjs-default-skin .vjs-timepanel-left-RS,
122 | .vjs-default-skin .vjs-timepanel-right-RS{
123 | font-weight: normal;
124 | font-size: 1em;
125 | color: #666666;
126 | border: 1px solid #666666;
127 | background-color: white;
128 | border-radius: 5px;
129 | position: absolute;
130 | height:116%;
131 | padding-right: 0.3em;
132 | padding-left: 0.3em;
133 | }
134 | .vjs-default-skin .vjs-timepanel-left-RS{
135 | left:0.5%
136 | }
137 | .vjs-default-skin .vjs-timepanel-right-RS{
138 | left:92%
139 | }
140 |
141 |
142 |
143 |
144 | /* Control Time Panel */
145 | .vjs-default-skin .vjs-controltimepanel-RS{
146 | width: 18em;
147 | font-size: 1em;
148 | line-height: 3em;
149 | }
150 |
151 | .vjs-default-skin .vjs-controltimepanel-RS input{
152 | width: 1.5em;
153 | background: rgba(102, 168, 204, 0.16);
154 | border: 1px solid transparent;
155 | color: black;
156 | font-size: 1em;
157 | margin-left: 2px;
158 | text-align: center;
159 | color: white;
160 | }
161 |
162 | .vjs-default-skin .vjs-controltimepanel-left-RS{
163 | width: 50%;
164 | float: left;
165 | }
166 | .vjs-default-skin .vjs-controltimepanel-right-RS{
167 | float:right;
168 | width: 48%;
169 | }
170 | .vjs-default-skin .vjs-controltimepanel-RS input{
171 | margin: 0;
172 | padding: 0;
173 | display: table-cell;
174 | }
175 |
176 |
177 | /* ---------------- Video-js plugin ---------------- */
178 |
179 | .vjs-default-skin *, .vjs-default-skin *:before, .vjs-default-skin *:after {
180 | -moz-box-sizing: border-box;
181 | -webkit-box-sizing: border-box;
182 | box-sizing: border-box;
183 | margin: 0;
184 | padding: 0;
185 | }
186 |
187 |
--------------------------------------------------------------------------------
/vendor/rangeslider.js:
--------------------------------------------------------------------------------
1 | /*
2 | RangeSlider v1.1 (https://github.com/danielcebrian/rangeslider-videojs)
3 | Copyright (C) 2014 Daniel Cebrián Robles
4 | License GNU:
5 | https://github.com/danielcebrian/rangeslider-videojs/blob/master/License-GNU.rst
6 | License Apache:
7 | https://github.com/danielcebrian/rangeslider-videojs/blob/master/License-Apache.rst
8 | */
9 |
10 | (function() {
11 | 'use strict';
12 |
13 | function blockTextSelection() {
14 | document.body.focus();
15 | document.onselectstart = function() { return false; };
16 | }
17 | function hasElClass(element, classToCheck) {
18 | return ((' ' + element.className + ' ')
19 | .indexOf(' ' + classToCheck + ' ') !== -1);
20 | }
21 | function addElClass(element, classToAdd) {
22 | if (!hasElClass(element, classToAdd)) {
23 | element.className = element.className === ''
24 | ? classToAdd
25 | : element.className + ' ' + classToAdd;
26 | }
27 | }
28 | function removeElClass(element, classToRemove) {
29 | if (!hasElClass(element, classToRemove)) {
30 | return;
31 | }
32 |
33 | var classNames = element.className.split(' ');
34 |
35 | // no arr.indexOf in ie8, and we don't want to add a big shim
36 | for (var i = classNames.length - 1; i >= 0; i--) {
37 | if (classNames[i] === classToRemove) {
38 | classNames.splice(i, 1);
39 | }
40 | }
41 |
42 | element.className = classNames.join(' ');
43 | }
44 | function findElPosition(el) {
45 | var box;
46 |
47 | if (el.getBoundingClientRect && el.parentNode) {
48 | box = el.getBoundingClientRect();
49 | }
50 |
51 | if (!box) {
52 | return {left : 0, top : 0};
53 | }
54 |
55 | const docEl = document.documentElement;
56 | const body = document.body;
57 |
58 | const clientLeft = docEl.clientLeft || body.clientLeft || 0;
59 | const scrollLeft = window.pageXOffset || body.scrollLeft;
60 | const left = box.left + scrollLeft - clientLeft;
61 |
62 | const clientTop = docEl.clientTop || body.clientTop || 0;
63 | const scrollTop = window.pageYOffset || body.scrollTop;
64 | const top = box.top + scrollTop - clientTop;
65 |
66 | // Android sometimes returns slightly off decimal values, so need to round
67 | return {left : Math.round(left), top : Math.round(top)};
68 | }
69 |
70 | //-- Load RangeSlider plugin in videojs
71 | function RangeSlider_(options) {
72 | var player = this;
73 |
74 | player.rangeslider = new RangeSlider(player, options);
75 |
76 | // When the DOM and the video media is loaded
77 | function initialVideoFinished(event) {
78 | var plugin = player.rangeslider;
79 | // All components will be initialize after they have been loaded by
80 | // videojs
81 | for (var index in plugin.components) {
82 | plugin.components[index].init_();
83 | }
84 |
85 | if (plugin.options.hidden)
86 | plugin.hide(); // Hide the Range Slider
87 |
88 | if (plugin.options.locked)
89 | plugin.lock(); // Lock the Range Slider
90 |
91 | if (plugin.options.panel == false)
92 | plugin.hidePanel(); // Hide the second Panel
93 |
94 | if (plugin.options.controlTime == false)
95 | plugin.hidecontrolTime(); // Hide the control time panel
96 |
97 | plugin._reset();
98 | player.trigger(
99 | 'loadedRangeSlider'); // Let know if the Range Slider DOM is ready
100 | }
101 | if (player.techName == 'Youtube') {
102 | // Detect youtube problems
103 | player.one('error', function(e) {
104 | switch (player.error) {
105 | case 2:
106 | alert(
107 | "The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.");
108 | case 5:
109 | alert(
110 | "The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.");
111 | case 100:
112 | alert(
113 | "The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.");
114 | break;
115 | case 101:
116 | alert(
117 | "The owner of the requested video does not allow it to be played in embedded players.");
118 | break;
119 | case 150:
120 | alert(
121 | "The owner of the requested video does not allow it to be played in embedded players.");
122 | break;
123 | default:
124 | alert("Unknown Error");
125 | break;
126 | }
127 | });
128 | player.on('firstplay', initialVideoFinished);
129 | } else {
130 | player.one('playing', initialVideoFinished);
131 | }
132 |
133 | console.log("Loaded Plugin RangeSlider");
134 | }
135 | videojs.plugin('rangeslider', RangeSlider_);
136 |
137 | //-- Plugin
138 | function RangeSlider(player, options) {
139 | var player = player || this;
140 |
141 | this.player = player;
142 |
143 | this.components = {}; // holds any custom components we add to the player
144 |
145 | options = options || {}; // plugin options
146 |
147 | if (!options.hasOwnProperty('locked'))
148 | options.locked = false; // lock slider handles
149 |
150 | if (!options.hasOwnProperty('hidden'))
151 | options.hidden = false; // hide slider handles
152 |
153 | if (!options.hasOwnProperty('panel'))
154 | options.panel = true; // Show Second Panel
155 |
156 | if (!options.hasOwnProperty('controlTime'))
157 | options.controlTime =
158 | true; // Show Control Time to set the arrows in the edition
159 |
160 | this.options = options;
161 |
162 | this.init();
163 | }
164 |
165 | //-- Methods
166 | RangeSlider.prototype = {
167 | /*Constructor*/
168 | init : function() {
169 | var player = this.player || {};
170 |
171 | this.updatePrecision = 3;
172 |
173 | // position in second of the arrows
174 | this.start = 0;
175 | this.end = 0;
176 |
177 | // components of the plugin
178 | var controlBar = player.controlBar;
179 | var seekBar = controlBar.progressControl.seekBar;
180 | this.components.RSTimeBar = seekBar.RSTimeBar;
181 | this.components.ControlTimePanel = controlBar.ControlTimePanel;
182 |
183 | // Save local component
184 | this.rstb = this.components.RSTimeBar;
185 | this.box = this.components.SeekRSBar = this.rstb.SeekRSBar;
186 | this.bar = this.components.SelectionBar = this.box.SelectionBar;
187 | this.left = this.components.SelectionBarLeft = this.box.SelectionBarLeft;
188 | this.right = this.components.SelectionBarRight =
189 | this.box.SelectionBarRight;
190 | this.tp = this.components.TimePanel = this.box.TimePanel;
191 | this.tpl = this.components.TimePanelLeft = this.tp.TimePanelLeft;
192 | this.tpr = this.components.TimePanelRight = this.tp.TimePanelRight;
193 | this.ctp = this.components.ControlTimePanel;
194 | this.ctpl = this.components.ControlTimePanelLeft =
195 | this.ctp.ControlTimePanelLeft;
196 | this.ctpr = this.components.ControlTimePanelRight =
197 | this.ctp.ControlTimePanelRight;
198 |
199 | },
200 | lock : function() {
201 | this.options.locked = true;
202 | this.ctp.enable(false);
203 | if (typeof this.box != 'undefined')
204 | addElClass(this.box.el_, 'locked');
205 | },
206 | unlock : function() {
207 | this.options.locked = false;
208 | this.ctp.enable();
209 | if (typeof this.box != 'undefined')
210 | removeElClass(this.box.el_, 'locked');
211 | },
212 | show : function() {
213 | this.options.hidden = false;
214 | if (typeof this.rstb != 'undefined') {
215 | this.rstb.show();
216 | if (this.options.controlTime)
217 | this.showcontrolTime();
218 | }
219 | },
220 | hide : function() {
221 | this.options.hidden = true;
222 | if (typeof this.rstb != 'undefined') {
223 | this.rstb.hide();
224 | this.ctp.hide();
225 | }
226 | },
227 | showPanel : function() {
228 | this.options.panel = true;
229 | if (typeof this.tp != 'undefined')
230 | removeElClass(this.tp.el_, 'disable');
231 | },
232 | hidePanel : function() {
233 | this.options.panel = false;
234 | if (typeof this.tp != 'undefined')
235 | addElClass(this.tp.el_, 'disable');
236 | },
237 | showcontrolTime : function() {
238 | this.options.controlTime = true;
239 | if (typeof this.ctp != 'undefined')
240 | this.ctp.show();
241 | },
242 | hidecontrolTime : function() {
243 | this.options.controlTime = false;
244 | if (typeof this.ctp != 'undefined')
245 | this.ctp.hide();
246 | },
247 | setValue : function(index, seconds, writeControlTime) {
248 | // index = 0 for the left Arrow and 1 for the right Arrow. Value in
249 | // seconds
250 | var writeControlTime =
251 | typeof writeControlTime != 'undefined' ? writeControlTime : true;
252 |
253 | var percent = this._percent(seconds);
254 | var isValidIndex = (index === 0 || index === 1);
255 | var isChangeable = !this.locked;
256 | if (isChangeable && isValidIndex)
257 | this.box.setPosition(index, percent, writeControlTime);
258 | },
259 | setValues : function(start, end, writeControlTime) {
260 | // index = 0 for the left Arrow and 1 for the right Arrow. Value in
261 | // seconds
262 | var writeControlTime =
263 | typeof writeControlTime != 'undefined' ? writeControlTime : true;
264 |
265 | this._reset();
266 |
267 | this._setValuesLocked(start, end, writeControlTime);
268 | },
269 | getValues : function() { // get values in seconds
270 | var values = {}, start, end;
271 | start = this.start || this._getArrowValue(0);
272 | end = this.end || this._getArrowValue(1);
273 | return {start : start, end : end};
274 | },
275 | playBetween : function(start, end, showRS) {
276 | showRS = typeof showRS == 'undefined' ? true : showRS;
277 | this.player.currentTime(start);
278 | this.player.play();
279 | if (showRS) {
280 | this.show();
281 | this._reset();
282 | } else {
283 | this.hide();
284 | }
285 | this._setValuesLocked(start, end);
286 |
287 | this.bar.activatePlay(start, end);
288 | },
289 | loop : function(start, end, show) {
290 | var player = this.player;
291 |
292 | if (player) {
293 | player.on("pause",
294 | videojs.bind(this, function() { this.looping = false; }));
295 |
296 | show = typeof show === 'undefined' ? true : show;
297 |
298 | if (show) {
299 | this.show();
300 | this._reset();
301 | } else {
302 | this.hide();
303 | }
304 | this._setValuesLocked(start, end);
305 |
306 | this.timeStart = start;
307 | this.timeEnd = end;
308 | this.looping = true;
309 |
310 | this.player.currentTime(start);
311 | this.player.play();
312 |
313 | this.player.on("timeupdate", videojs.bind(this, this.bar.process_loop));
314 | }
315 | },
316 | _getArrowValue : function(index) {
317 | var index = index || 0;
318 | var duration = this.player.duration();
319 |
320 | duration = typeof duration == 'undefined' ? 0 : duration;
321 |
322 | var percentage =
323 | this[index === 0 ? "left" : "right"].el_.style.left.replace("%", "");
324 | if (percentage == "")
325 | percentage = index === 0 ? 0 : 100;
326 |
327 | return (this._seconds(percentage / 100))
328 | .toFixed(this.updatePrecision - 1);
329 | },
330 | _percent : function(seconds) {
331 | var duration = this.player.duration();
332 | if (isNaN(duration)) {
333 | return 0;
334 | }
335 | return Math.min(1, Math.max(0, seconds / duration));
336 | },
337 | _seconds : function(percent) {
338 | var duration = this.player.duration();
339 | if (isNaN(duration)) {
340 | return 0;
341 | }
342 | return Math.min(duration, Math.max(0, percent * duration));
343 | },
344 | _reset : function() {
345 | var duration = this.player.duration();
346 | this.tpl.el_.style.left = '0%';
347 | this.tpr.el_.style.left = '100%';
348 | this._setValuesLocked(0, duration);
349 | },
350 | _setValuesLocked : function(start, end, writeControlTime) {
351 | var triggerSliderChange = typeof writeControlTime != 'undefined';
352 | var writeControlTime =
353 | typeof writeControlTime != 'undefined' ? writeControlTime : true;
354 | if (this.options.locked) {
355 | this.unlock(); // It is unlocked to change the bar position. In the end
356 | // it will return the value.
357 | this.setValue(0, start, writeControlTime);
358 | this.setValue(1, end, writeControlTime);
359 | this.lock();
360 | } else {
361 | this.setValue(0, start, writeControlTime);
362 | this.setValue(1, end, writeControlTime);
363 | }
364 |
365 | // Trigger slider change
366 | if (triggerSliderChange) {
367 | this._triggerSliderChange();
368 | }
369 | },
370 | _checkControlTime : function(index, TextInput, timeOld) {
371 | var h = TextInput[0], m = TextInput[1], s = TextInput[2],
372 | newHour = h.value, newMin = m.value, newSec = s.value, obj, objNew,
373 | objOld;
374 | index = index || 0;
375 |
376 | if (newHour != timeOld[0]) {
377 | obj = h;
378 | objNew = newHour;
379 | objOld = timeOld[0];
380 | } else if (newMin != timeOld[1]) {
381 | obj = m;
382 | objNew = newMin;
383 | objOld = timeOld[1];
384 | } else if (newSec != timeOld[2]) {
385 | obj = s;
386 | objNew = newSec;
387 | objOld = timeOld[2];
388 | } else {
389 | return false;
390 | }
391 |
392 | var duration = this.player.duration() || 0, durationSel;
393 |
394 | var intRegex = /^\d+$/; // check if the objNew is an integer
395 | if (!intRegex.test(objNew) || objNew > 60) {
396 | objNew = objNew == "" ? "" : objOld;
397 | }
398 |
399 | newHour = newHour == "" ? 0 : newHour;
400 | newMin = newMin == "" ? 0 : newMin;
401 | newSec = newSec == "" ? 0 : newSec;
402 |
403 | durationSel = videojs.TextTrack.prototype.parseCueTime(
404 | newHour + ":" + newMin + ":" + newSec);
405 | if (durationSel > duration) {
406 | obj.value = objOld;
407 | obj.style.border = "1px solid red";
408 | } else {
409 | obj.value = objNew;
410 | h.style.border = m.style.border = s.style.border =
411 | "1px solid transparent";
412 | this.setValue(index, durationSel, false);
413 |
414 | // Trigger slider change
415 | this._triggerSliderChange();
416 | }
417 | if (index === 1) {
418 | var oldTimeLeft = this.ctpl.el_.children,
419 | durationSelLeft = videojs.TextTrack.prototype.parseCueTime(
420 | oldTimeLeft[0].value + ":" + oldTimeLeft[1].value + ":" +
421 | oldTimeLeft[2].value);
422 | if (durationSel < durationSelLeft) {
423 | obj.style.border = "1px solid red";
424 | }
425 | } else {
426 |
427 | var oldTimeRight = this.ctpr.el_.children,
428 | durationSelRight = videojs.TextTrack.prototype.parseCueTime(
429 | oldTimeRight[0].value + ":" + oldTimeRight[1].value + ":" +
430 | oldTimeRight[2].value);
431 | if (durationSel > durationSelRight) {
432 | obj.style.border = "1px solid red";
433 | }
434 | }
435 | },
436 | _triggerSliderChange : function() { this.player.trigger("sliderchange"); }
437 | };
438 |
439 | //----------------Public Functions----------------//
440 |
441 | //-- Public Functions added to video-js
442 | var videojsPlayer = videojs.getComponent('Player');
443 |
444 | // Lock the Slider bar and it will not be possible to change the arrow
445 | // positions
446 | videojsPlayer.prototype.lockSlider = function() {
447 | return this.rangeslider.lock();
448 | };
449 |
450 | // Unlock the Slider bar and it will be possible to change the arrow positions
451 | videojsPlayer.prototype.unlockSlider = function() {
452 | return this.rangeslider.unlock();
453 | };
454 |
455 | // Show the Slider Bar Component
456 | videojsPlayer.prototype.showSlider = function() {
457 | return this.rangeslider.show();
458 | };
459 |
460 | // Hide the Slider Bar Component
461 | videojsPlayer.prototype.hideSlider = function() {
462 | return this.rangeslider.hide();
463 | };
464 |
465 | // Show the Panel with the seconds of the selection
466 | videojsPlayer.prototype.showSliderPanel = function() {
467 | return this.rangeslider.showPanel();
468 | };
469 |
470 | // Hide the Panel with the seconds of the selection
471 | videojsPlayer.prototype.hideSliderPanel = function() {
472 | return this.rangeslider.hidePanel();
473 | };
474 |
475 | // Show the control Time to edit the position of the arrows
476 | videojsPlayer.prototype.showControlTime = function() {
477 | return this.rangeslider.showcontrolTime();
478 | };
479 |
480 | // Hide the control Time to edit the position of the arrows
481 | videojsPlayer.prototype.hideControlTime = function() {
482 | return this.rangeslider.hidecontrolTime();
483 | };
484 |
485 | // Set a Value in second for both arrows
486 | videojsPlayer.prototype.setValueSlider = function(start, end) {
487 | return this.rangeslider.setValues(start, end);
488 | };
489 |
490 | // The video will be played in a selected section
491 | videojsPlayer.prototype.playBetween = function(start, end) {
492 | return this.rangeslider.playBetween(start, end);
493 | };
494 |
495 | // The video will loop between to values
496 | videojsPlayer.prototype.loopBetween = function(start, end) {
497 | return this.rangeslider.loop(start, end);
498 | };
499 |
500 | // Set a Value in second for the arrows
501 | videojsPlayer.prototype.getValueSlider = function() {
502 | return this.rangeslider.getValues();
503 | };
504 |
505 | //----------------Create new Components----------------//
506 |
507 | //--Charge the new Component into videojs
508 | videojs.getComponent('SeekBar')
509 | .prototype.options_.children.push('RSTimeBar'); // Range Slider Time Bar
510 | videojs.getComponent('ControlBar')
511 | .prototype.options_.children.push(
512 | 'ControlTimePanel'); // Panel with the time of the range slider
513 |
514 | var Component = videojs.getComponent('Component');
515 | //-- Design the new components
516 |
517 | /**
518 | * Range Slider Time Bar
519 | * @param {videojs.Player|Object} player
520 | * @param {Object=} options
521 | * @constructor
522 | */
523 | videojs.RSTimeBar = videojs.extend(Component, {
524 | /** @constructor */
525 | constructor : function(player, options) {
526 | Component.call(this, player, options);
527 | }
528 | });
529 |
530 | videojs.RSTimeBar.prototype.init_ = function() {
531 | this.rs = this.player_.rangeslider;
532 | };
533 |
534 | videojs.RSTimeBar.prototype.options_ = {children : {'SeekRSBar' : {}}};
535 |
536 | videojs.RSTimeBar.prototype.createEl = function() {
537 | return Component.prototype.createEl.call(
538 | this, 'div', {className : 'vjs-timebar-RS', innerHTML : ''});
539 | };
540 |
541 | /**
542 | * Seek Range Slider Bar and holder for the selection bars
543 | * @param {videojs.Player|Object} player
544 | * @param {Object=} options
545 | * @constructor
546 | */
547 | videojs.SeekRSBar = videojs.extend(videojs.getComponent('Component'), {
548 | /** @constructor */
549 | constructor : function(player, options) {
550 | Component.call(this, player, options);
551 | this.on('mousedown', this.onMouseDown);
552 | }
553 | });
554 |
555 | videojs.SeekRSBar.prototype.init_ = function() {
556 | this.rs = this.player_.rangeslider;
557 | };
558 |
559 | videojs.SeekRSBar.prototype.options_ = {
560 | children : {
561 | 'SelectionBar' : {},
562 | 'SelectionBarLeft' : {},
563 | 'SelectionBarRight' : {},
564 | 'TimePanel' : {},
565 | }
566 | };
567 |
568 | videojs.SeekRSBar.prototype.createEl = function() {
569 | return Component.prototype.createEl.call(
570 | this, 'div', {className : 'vjs-rangeslider-holder'});
571 | };
572 |
573 | videojs.SeekRSBar.prototype.onMouseDown = function(event) {
574 | event.preventDefault();
575 | blockTextSelection();
576 |
577 | if (!this.rs.options.locked) {
578 | videojs.on(document, "mousemove", videojs.bind(this, this.onMouseMove));
579 | videojs.on(document, "mouseup", videojs.bind(this, this.onMouseUp));
580 | }
581 | };
582 |
583 | videojs.SeekRSBar.prototype.onMouseUp = function(event) {
584 | videojs.off(document, "mousemove", this.onMouseMove, false);
585 | videojs.off(document, "mouseup", this.onMouseUp, false);
586 | };
587 |
588 | videojs.SeekRSBar.prototype.onMouseMove = function(event) {
589 | var left = this.calculateDistance(event);
590 |
591 | if (this.rs.left.pressed)
592 | this.setPosition(0, left);
593 | else if (this.rs.right.pressed)
594 | this.setPosition(1, left);
595 |
596 | this.player_.currentTime(this.rs._seconds(left));
597 |
598 | // Trigger slider change
599 | if (this.rs.left.pressed || this.rs.right.pressed) {
600 | this.rs._triggerSliderChange();
601 | }
602 | };
603 |
604 | videojs.SeekRSBar.prototype.setPosition = function(index, left,
605 | writeControlTime) {
606 | var writeControlTime =
607 | typeof writeControlTime != 'undefined' ? writeControlTime : true;
608 | // index = 0 for left side, index = 1 for right side
609 | var index = index || 0;
610 |
611 | // Position shouldn't change when handle is locked
612 | if (this.rs.options.locked)
613 | return false;
614 |
615 | // Check for invalid position
616 | if (isNaN(left))
617 | return false;
618 |
619 | // Check index between 0 and 1
620 | if (!(index === 0 || index === 1))
621 | return false;
622 |
623 | // Alias
624 | var ObjLeft = this.rs.left.el_, ObjRight = this.rs.right.el_,
625 | Obj = this.rs[index === 0 ? 'left' : 'right'].el_,
626 | tpr = this.rs.tpr.el_, tpl = this.rs.tpl.el_, bar = this.rs.bar,
627 | ctp = this.rs[index === 0 ? 'ctpl' : 'ctpr'].el_;
628 |
629 | // Check if left arrow is passing the right arrow
630 | if ((index === 0 ? bar.updateLeft(left) : bar.updateRight(left))) {
631 | Obj.style.left = (left * 100) + '%';
632 | index === 0 ? bar.updateLeft(left) : bar.updateRight(left);
633 |
634 | this.rs[index === 0 ? 'start' : 'end'] = this.rs._seconds(left);
635 |
636 | // Fix the problem when you press the button and the two arrow are
637 | // underhand
638 | // left.zIndex = 10 and right.zIndex=20. This is always less in this case:
639 | if (index === 0) {
640 | if ((left) >= 0.9)
641 | ObjLeft.style.zIndex = 25;
642 | else
643 | ObjLeft.style.zIndex = 10;
644 | }
645 |
646 | //-- Panel
647 | var TimeText = videojs.formatTime(this.rs._seconds(left)),
648 | tplTextLegth = tpl.children[0].innerHTML.length;
649 | var MaxP, MinP, MaxDisP;
650 | if (tplTextLegth <= 4) // 0:00
651 | MaxDisP = this.player_.isFullScreen ? 3.25 : 6.5;
652 | else if (tplTextLegth <= 5) // 00:00
653 | MaxDisP = this.player_.isFullScreen ? 4 : 8;
654 | else // 0:00:00
655 | MaxDisP = this.player_.isFullScreen ? 5 : 10;
656 | if (TimeText.length <= 4) { // 0:00
657 | MaxP = this.player_.isFullScreen ? 97 : 93;
658 | MinP = this.player_.isFullScreen ? 0.1 : 0.5;
659 | } else if (TimeText.length <= 5) { // 00:00
660 | MaxP = this.player_.isFullScreen ? 96 : 92;
661 | MinP = this.player_.isFullScreen ? 0.1 : 0.5;
662 | } else { // 0:00:00
663 | MaxP = this.player_.isFullScreen ? 95 : 91;
664 | MinP = this.player_.isFullScreen ? 0.1 : 0.5;
665 | }
666 |
667 | if (index === 0) {
668 | tpl.style.left =
669 | Math.max(MinP, Math.min(MaxP, (left * 100 - MaxDisP / 2))) + '%';
670 |
671 | if ((tpr.style.left.replace("%", "") -
672 | tpl.style.left.replace("%", "")) <= MaxDisP)
673 | tpl.style.left =
674 | Math.max(MinP, Math.min(MaxP, tpr.style.left.replace("%", "") -
675 | MaxDisP)) +
676 | '%';
677 | tpl.children[0].innerHTML = TimeText;
678 | } else {
679 | tpr.style.left =
680 | Math.max(MinP, Math.min(MaxP, (left * 100 - MaxDisP / 2))) + '%';
681 |
682 | if (((tpr.style.left.replace("%", "") || 100) -
683 | tpl.style.left.replace("%", "")) <= MaxDisP)
684 | tpr.style.left =
685 | Math.max(MinP, Math.min(MaxP, tpl.style.left.replace("%", "") -
686 | 0 + MaxDisP)) +
687 | '%';
688 | tpr.children[0].innerHTML = TimeText;
689 | }
690 | //-- Control Time
691 | if (writeControlTime) {
692 | var time = TimeText.split(":"), h, m, s;
693 | if (time.length == 2) {
694 | h = 0;
695 | m = time[0];
696 | s = time[1];
697 | } else {
698 | h = time[0];
699 | m = time[1];
700 | s = time[2];
701 | }
702 | ctp.children[0].value = h;
703 | ctp.children[1].value = m;
704 | ctp.children[2].value = s;
705 | }
706 | }
707 | return true;
708 | };
709 |
710 | videojs.SeekRSBar.prototype.calculateDistance = function(event) {
711 | var rstbX = this.getRSTBX();
712 | var rstbW = this.getRSTBWidth();
713 | var handleW = this.getWidth();
714 |
715 | // Adjusted X and Width, so handle doesn't go outside the bar
716 | rstbX = rstbX + (handleW / 2);
717 | rstbW = rstbW - handleW;
718 |
719 | // Percent that the click is through the adjusted area
720 | return Math.max(0, Math.min(1, (event.pageX - rstbX) / rstbW));
721 | };
722 |
723 | videojs.SeekRSBar.prototype.getRSTBWidth = function() {
724 | return this.el_.offsetWidth;
725 | };
726 | videojs.SeekRSBar.prototype.getRSTBX = function() {
727 | return findElPosition(this.el_).left;
728 | };
729 | videojs.SeekRSBar.prototype.getWidth = function() {
730 | return this.rs.left.el_.offsetWidth; // does not matter left or right
731 | };
732 |
733 | /**
734 | * This is the bar with the selection of the RangeSlider
735 | * @param {videojs.Player|Object} player
736 | * @param {Object=} options
737 | * @constructor
738 | */
739 | videojs.SelectionBar = videojs.extend(videojs.getComponent('Component'), {
740 | /** @constructor */
741 | constructor : function(player, options) {
742 | Component.call(this, player, options);
743 | this.on('mouseup', this.onMouseUp);
744 | this.fired = false;
745 | }
746 | });
747 |
748 | videojs.SelectionBar.prototype.init_ = function() {
749 | this.rs = this.player_.rangeslider;
750 | };
751 |
752 | videojs.SelectionBar.prototype.createEl = function() {
753 | return Component.prototype.createEl.call(
754 | this, 'div', {className : 'vjs-selectionbar-RS'});
755 | };
756 |
757 | videojs.SelectionBar.prototype.onMouseUp = function() {
758 | var start = this.rs.left.el_.style.left.replace("%", ""),
759 | end = this.rs.right.el_.style.left.replace("%", ""),
760 | duration = this.player_.duration(), precision = this.rs.updatePrecision,
761 | segStart = (start * duration / 100).toFixed(precision),
762 | segEnd = (end * duration / 100).toFixed(precision);
763 | this.player_.currentTime(segStart);
764 | this.player_.play();
765 | this.rs.bar.activatePlay(segStart, segEnd);
766 | };
767 |
768 | videojs.SelectionBar.prototype.updateLeft = function(left) {
769 | var rightVal =
770 | this.rs.right.el_.style.left != '' ? this.rs.right.el_.style.left : 100;
771 | var right = parseFloat(rightVal) / 100;
772 |
773 | var width =
774 | (right - left)
775 | .toFixed(this.rs.updatePrecision); // round necessary for not get
776 | // 0.6e-7 for example that
777 | // it's not able for the html
778 | // css width
779 |
780 | //(right+0.00001) is to fix the precision of the css in html
781 | if (left <= (right + 0.00001)) {
782 | this.rs.bar.el_.style.left = (left * 100) + '%';
783 | this.rs.bar.el_.style.width = (width * 100) + '%';
784 | return true;
785 | }
786 | return false;
787 | };
788 |
789 | videojs.SelectionBar.prototype.updateRight = function(right) {
790 | var leftVal =
791 | this.rs.left.el_.style.left != '' ? this.rs.left.el_.style.left : 0;
792 | var left = parseFloat(leftVal) / 100;
793 |
794 | var width =
795 | (right - left)
796 | .toFixed(this.rs.updatePrecision); // round necessary for not get
797 | // 0.6e-7 for example that
798 | // it's not able for the html
799 | // css width
800 |
801 | //(right+0.00001) is to fix the precision of the css in html
802 | if ((right + 0.00001) >= left) {
803 | this.rs.bar.el_.style.width = (width * 100) + '%';
804 | this.rs.bar.el_.style.left = ((right - width) * 100) + '%';
805 | return true;
806 | }
807 | return false;
808 | };
809 |
810 | videojs.SelectionBar.prototype.activatePlay = function(start, end) {
811 | this.timeStart = start;
812 | this.timeEnd = end;
813 |
814 | this.suspendPlay();
815 |
816 | this.player_.on("timeupdate", videojs.bind(this, this._processPlay));
817 | };
818 |
819 | videojs.SelectionBar.prototype.suspendPlay = function() {
820 | this.fired = false;
821 | this.player_.off("timeupdate", videojs.bind(this, this._processPlay));
822 | };
823 |
824 | videojs.SelectionBar.prototype._processPlay = function() {
825 | // Check if current time is between start and end
826 | if (this.player_.currentTime() >= this.timeStart &&
827 | (this.timeEnd < 0 || this.player_.currentTime() < this.timeEnd)) {
828 | if (this.fired) { // Do nothing if start has already been called
829 | return;
830 | }
831 | this.fired = true; // Set fired flag to true
832 | } else {
833 | if (!this.fired) { // Do nothing if end has already been called
834 | return;
835 | }
836 | this.fired = false; // Set fired flat to false
837 | this.player_.pause(); // Call end function
838 | this.player_.currentTime(this.timeEnd);
839 | this.suspendPlay();
840 | }
841 | };
842 |
843 | videojs.SelectionBar.prototype.process_loop = function() {
844 | var player = this.player;
845 |
846 | if (player && this.looping) {
847 | var current_time = player.currentTime();
848 |
849 | if (current_time < this.timeStart ||
850 | this.timeEnd > 0 && this.timeEnd < current_time) {
851 | player.currentTime(this.timeStart);
852 | }
853 | }
854 | };
855 |
856 | /**
857 | * This is the left arrow to select the RangeSlider
858 | * @param {videojs.Player|Object} player
859 | * @param {Object=} options
860 | * @constructor
861 | */
862 | videojs.SelectionBarLeft = videojs.extend(videojs.getComponent('Component'), {
863 | /** @constructor */
864 | constructor : function(player, options) {
865 | Component.call(this, player, options);
866 | this.on('mousedown', this.onMouseDown);
867 | this.pressed = false;
868 | }
869 | });
870 |
871 | videojs.SelectionBarLeft.prototype.init_ = function() {
872 | this.rs = this.player_.rangeslider;
873 | };
874 |
875 | videojs.SelectionBarLeft.prototype.createEl = function() {
876 | return Component.prototype.createEl.call(this, 'div', {
877 | className : 'vjs-rangeslider-handle vjs-selectionbar-left-RS',
878 | innerHTML :
879 | ''
880 | });
881 | };
882 |
883 | videojs.SelectionBarLeft.prototype.onMouseDown = function(event) {
884 | event.preventDefault();
885 | blockTextSelection();
886 | if (!this.rs.options.locked) {
887 | this.pressed = true;
888 | videojs.on(document, "mouseup", videojs.bind(this, this.onMouseUp));
889 | addElClass(this.el_, 'active');
890 | }
891 | };
892 |
893 | videojs.SelectionBarLeft.prototype.onMouseUp = function(event) {
894 | videojs.off(document, "mouseup", this.onMouseUp, false);
895 | removeElClass(this.el_, 'active');
896 | if (!this.rs.options.locked) {
897 | this.pressed = false;
898 | }
899 | };
900 |
901 | /**
902 | * This is the right arrow to select the RangeSlider
903 | * @param {videojs.Player|Object} player
904 | * @param {Object=} options
905 | * @constructor
906 | */
907 | videojs.SelectionBarRight =
908 | videojs.extend(videojs.getComponent('Component'), {
909 | /** @constructor */
910 | constructor : function(player, options) {
911 | Component.call(this, player, options);
912 | this.on('mousedown', this.onMouseDown);
913 | this.pressed = false;
914 | }
915 | });
916 |
917 | videojs.SelectionBarRight.prototype.init_ = function() {
918 | this.rs = this.player_.rangeslider;
919 | };
920 |
921 | videojs.SelectionBarRight.prototype.createEl = function() {
922 | return Component.prototype.createEl.call(this, 'div', {
923 | className : 'vjs-rangeslider-handle vjs-selectionbar-right-RS',
924 | innerHTML :
925 | ''
926 | });
927 | };
928 |
929 | videojs.SelectionBarRight.prototype.onMouseDown = function(event) {
930 | event.preventDefault();
931 | blockTextSelection();
932 | if (!this.rs.options.locked) {
933 | this.pressed = true;
934 | videojs.on(document, "mouseup", videojs.bind(this, this.onMouseUp));
935 | addElClass(this.el_, 'active');
936 | }
937 | };
938 |
939 | videojs.SelectionBarRight.prototype.onMouseUp = function(event) {
940 | videojs.off(document, "mouseup", this.onMouseUp, false);
941 | removeElClass(this.el_, 'active');
942 | if (!this.rs.options.locked) {
943 | this.pressed = false;
944 | }
945 | };
946 |
947 | /**
948 | * This is the time panel
949 | * @param {videojs.Player|Object} player
950 | * @param {Object=} options
951 | * @constructor
952 | */
953 | videojs.TimePanel = videojs.extend(videojs.getComponent('Component'), {
954 | /** @constructor */
955 | constructor : function(player, options) { Component.call(this, player, options); }
956 | });
957 |
958 | videojs.TimePanel.prototype.init_ = function() {
959 | this.rs = this.player_.rangeslider;
960 | };
961 |
962 | videojs.TimePanel.prototype.options_ = {
963 | children : {
964 | 'TimePanelLeft' : {},
965 | 'TimePanelRight' : {},
966 | }
967 | };
968 |
969 | videojs.TimePanel.prototype.createEl = function() {
970 | return Component.prototype.createEl.call(this, 'div',
971 | {className : 'vjs-timepanel-RS'});
972 | };
973 |
974 | /**
975 | * This is the left time panel
976 | * @param {videojs.Player|Object} player
977 | * @param {Object=} options
978 | * @constructor
979 | */
980 | videojs.TimePanelLeft = videojs.extend(videojs.getComponent('Component'), {
981 | /** @constructor */
982 | constructor : function(player, options) { Component.call(this, player, options); }
983 | });
984 |
985 | videojs.TimePanelLeft.prototype.init_ = function() {
986 | this.rs = this.player_.rangeslider;
987 | };
988 |
989 | videojs.TimePanelLeft.prototype.createEl = function() {
990 | return Component.prototype.createEl.call(this, 'div', {
991 | className : 'vjs-timepanel-left-RS',
992 | innerHTML : '00:00'
993 | });
994 | };
995 |
996 | /**
997 | * This is the right time panel
998 | * @param {videojs.Player|Object} player
999 | * @param {Object=} options
1000 | * @constructor
1001 | */
1002 | videojs.TimePanelRight = videojs.extend(videojs.getComponent('Component'), {
1003 | /** @constructor */
1004 | constructor : function(player, options) { Component.call(this, player, options); }
1005 | });
1006 |
1007 | videojs.TimePanelRight.prototype.init_ = function() {
1008 | this.rs = this.player_.rangeslider;
1009 | };
1010 |
1011 | videojs.TimePanelRight.prototype.createEl = function() {
1012 | return Component.prototype.createEl.call(this, 'div', {
1013 | className : 'vjs-timepanel-right-RS',
1014 | innerHTML : '00:00'
1015 | });
1016 | };
1017 |
1018 | /**
1019 | * This is the control time panel
1020 | * @param {videojs.Player|Object} player
1021 | * @param {Object=} options
1022 | * @constructor
1023 | */
1024 | videojs.ControlTimePanel = videojs.extend(videojs.getComponent('Component'), {
1025 | /** @constructor */
1026 | constructor : function(player, options) { Component.call(this, player, options); }
1027 | });
1028 |
1029 | videojs.ControlTimePanel.prototype.init_ = function() {
1030 | this.rs = this.player_.rangeslider;
1031 | };
1032 |
1033 | videojs.ControlTimePanel.prototype.options_ = {
1034 | children : {
1035 | 'ControlTimePanelLeft' : {},
1036 | 'ControlTimePanelRight' : {},
1037 | }
1038 | };
1039 |
1040 | videojs.ControlTimePanel.prototype.createEl = function() {
1041 | return Component.prototype.createEl.call(this, 'div', {
1042 | className : 'vjs-controltimepanel-RS vjs-control',
1043 | });
1044 | };
1045 |
1046 | videojs.ControlTimePanel.prototype.enable = function(enable) {
1047 | var enable = typeof enable != 'undefined' ? enable : true;
1048 | this.rs.ctpl.el_.children[0].disabled = enable ? "" : "disabled";
1049 | this.rs.ctpl.el_.children[1].disabled = enable ? "" : "disabled";
1050 | this.rs.ctpl.el_.children[2].disabled = enable ? "" : "disabled";
1051 | this.rs.ctpr.el_.children[0].disabled = enable ? "" : "disabled";
1052 | this.rs.ctpr.el_.children[1].disabled = enable ? "" : "disabled";
1053 | this.rs.ctpr.el_.children[2].disabled = enable ? "" : "disabled";
1054 | };
1055 |
1056 | /**
1057 | * This is the control left time panel
1058 | * @param {videojs.Player|Object} player
1059 | * @param {Object=} options
1060 | * @constructor
1061 | */
1062 | videojs.ControlTimePanelLeft =
1063 | videojs.extend(videojs.getComponent('Component'), {
1064 | /** @constructor */
1065 | constructor : function(player, options) {
1066 | Component.call(this, player, options);
1067 | this.on('keyup', this.onKeyUp);
1068 | this.on('keydown', this.onKeyDown);
1069 | }
1070 | });
1071 |
1072 | videojs.ControlTimePanelLeft.prototype.init_ = function() {
1073 | this.rs = this.player_.rangeslider;
1074 | this.timeOld = {};
1075 | };
1076 |
1077 | videojs.ControlTimePanelLeft.prototype.createEl = function() {
1078 | return Component.prototype.createEl.call(this, 'div', {
1079 | className : 'vjs-controltimepanel-left-RS',
1080 | innerHTML :
1081 | 'Start: ::'
1082 | });
1083 | };
1084 |
1085 | videojs.ControlTimePanelLeft.prototype.onKeyDown = function(event) {
1086 | this.timeOld[0] = this.el_.children[0].value;
1087 | this.timeOld[1] = this.el_.children[1].value;
1088 | this.timeOld[2] = this.el_.children[2].value;
1089 | };
1090 |
1091 | videojs.ControlTimePanelLeft.prototype.onKeyUp = function(event) {
1092 | this.rs._checkControlTime(0, this.el_.children, this.timeOld);
1093 | };
1094 |
1095 | /**
1096 | * This is the control right time panel
1097 | * @param {videojs.Player|Object} player
1098 | * @param {Object=} options
1099 | * @constructor
1100 | */
1101 | videojs.ControlTimePanelRight =
1102 | videojs.extend(videojs.getComponent('Component'), {
1103 | /** @constructor */
1104 | constructor : function(player, options) {
1105 | Component.call(this, player, options);
1106 | this.on('keyup', this.onKeyUp);
1107 | this.on('keydown', this.onKeyDown);
1108 | }
1109 | });
1110 |
1111 | videojs.ControlTimePanelRight.prototype.init_ = function() {
1112 | this.rs = this.player_.rangeslider;
1113 | this.timeOld = {};
1114 | };
1115 |
1116 | videojs.ControlTimePanelRight.prototype.createEl = function() {
1117 | return Component.prototype.createEl.call(this, 'div', {
1118 | className : 'vjs-controltimepanel-right-RS',
1119 | innerHTML :
1120 | 'End: ::'
1121 | });
1122 | };
1123 |
1124 | videojs.ControlTimePanelRight.prototype.onKeyDown = function(event) {
1125 | this.timeOld[0] = this.el_.children[0].value;
1126 | this.timeOld[1] = this.el_.children[1].value;
1127 | this.timeOld[2] = this.el_.children[2].value;
1128 | };
1129 |
1130 | videojs.ControlTimePanelRight.prototype.onKeyUp = function(event) {
1131 | this.rs._checkControlTime(1, this.el_.children, this.timeOld);
1132 | };
1133 | })();
1134 |
--------------------------------------------------------------------------------