|
177 | 177 | this.metaDiv = $(media).data('meta-div'); |
178 | 178 | } |
179 | 179 |
|
| 180 | + if ($(media).data('search') !== undefined && $(media).data('search') !== "") { |
| 181 | + // conducting a search currently requires an external div in which to write the results |
| 182 | + if ($(media).data('search-div') !== undefined && $(media).data('search-div') !== "") { |
| 183 | + this.searchString = $(media).data('search'); |
| 184 | + this.searchDiv = $(media).data('search-div'); |
| 185 | + } |
| 186 | + } |
| 187 | + |
180 | 188 | this.ableIndex = AblePlayer.nextIndex; |
181 | 189 | AblePlayer.nextIndex += 1; |
182 | 190 |
|
|
624 | 632 | thisObj.updateDescription(); |
625 | 633 | thisObj.updateCaption(); |
626 | 634 | thisObj.updateTranscript(); |
| 635 | + thisObj.showSearchResults(); |
627 | 636 | thisObj.initializing = false; |
628 | 637 | thisObj.refreshControls(); |
629 | 638 |
|
|
5405 | 5414 | return main; |
5406 | 5415 | }; |
5407 | 5416 | })(); |
| 5417 | +(function () { |
| 5418 | + AblePlayer.prototype.showSearchResults = function() { |
| 5419 | + |
| 5420 | + // search VTT file for all instances of searchTerms |
| 5421 | + // Currently just supports search terms separated with one or more spaces |
| 5422 | + |
| 5423 | + // TODO: Add support for more robust search syntax: |
| 5424 | + // Search terms wrapped in quotation marks ("") must occur exactly as they appear in the quotes |
| 5425 | + // Search terms with an attached minus sign (e.g., -term) are to be excluded from results |
| 5426 | + // Boolean AND/OR operators |
| 5427 | + // ALSO: Add localization support |
| 5428 | + |
| 5429 | + var thisObj = this; |
| 5430 | + |
| 5431 | + if (this.searchDiv && this.searchString) { |
| 5432 | + if ($('#' + this.SearchDiv)) { |
| 5433 | + var resultsArray = this.searchFor(this.searchString); |
| 5434 | + if (resultsArray.length > 0) { |
| 5435 | + var resultsSummary = $('<p>',{ |
| 5436 | + 'class': 'able-search-results-summary' |
| 5437 | + }); |
| 5438 | + var resultsSummaryText = 'Found <strong>' + resultsArray.length + '</strong> matching items. '; |
| 5439 | + resultsSummaryText += 'Click the time associated with any item '; |
| 5440 | + resultsSummaryText += 'to play the video from that point.'; |
| 5441 | + resultsSummary.html(resultsSummaryText); |
| 5442 | + var resultsList = $('<ul>'); |
| 5443 | + for (var i in resultsArray) { |
| 5444 | + var resultsItem = $('<li>',{ |
| 5445 | + }); |
| 5446 | + var itemStartTime = this.secondsToTime(resultsArray[i]['start']); |
| 5447 | + var itemStartSpan = $('<span>',{ |
| 5448 | + 'class': 'able-search-results-time', |
| 5449 | + 'data-start': resultsArray[i]['start'], |
| 5450 | + 'title': itemStartTime['title'], |
| 5451 | + 'tabindex': '0' |
| 5452 | + }); |
| 5453 | + itemStartSpan.text(itemStartTime['value']); |
| 5454 | + // add a listener for clisk on itemStart |
| 5455 | + itemStartSpan.click(function(event) { |
| 5456 | + var spanStart = parseFloat($(this).attr('data-start')); |
| 5457 | + // Add a tiny amount so that we're inside the span. |
| 5458 | + spanStart += .01; |
| 5459 | + thisObj.seeking = true; |
| 5460 | + thisObj.seekTo(spanStart); |
| 5461 | + }); |
| 5462 | + |
| 5463 | + var itemText = $('<span>',{ |
| 5464 | + 'class': 'able-search-result-text' |
| 5465 | + }) |
| 5466 | + itemText.html('...' + resultsArray[i]['caption'] + '...'); |
| 5467 | + resultsItem.append(itemStartSpan, itemText); |
| 5468 | + resultsList.append(resultsItem); |
| 5469 | + } |
| 5470 | + $('#' + this.searchDiv).append(resultsSummary, resultsList); |
| 5471 | + } |
| 5472 | + else { |
| 5473 | + var noResults = $('<p>').text('No results found.'); |
| 5474 | + $('#' + this.searchDiv).append(noResults); |
| 5475 | + } |
| 5476 | + } |
| 5477 | + } |
| 5478 | + }; |
| 5479 | + |
| 5480 | + AblePlayer.prototype.searchFor = function(searchString) { |
| 5481 | + |
| 5482 | + // return chronological array of caption cues that match searchTerms |
| 5483 | + |
| 5484 | + var captionLang, captions, results, caption, c, i, j; |
| 5485 | + |
| 5486 | + // split searchTerms into an array |
| 5487 | + var searchTerms = searchString.split(' '); |
| 5488 | + |
| 5489 | + if (this.captions.length > 0) { |
| 5490 | + captionLang = this.captions[0].language; // in case it's needed later |
| 5491 | + captions = this.captions[0].cues; |
| 5492 | + if (captions.length > 0) { |
| 5493 | + var results = []; |
| 5494 | + c = 0; |
| 5495 | + for (i in captions) { |
| 5496 | + if (captions[i].components.children[0]['type'] === 'string') { |
| 5497 | + caption = captions[i].components.children[0]['value']; |
| 5498 | + for (j in searchTerms) { |
| 5499 | + if (caption.indexOf(searchTerms[j]) !== -1) { |
| 5500 | + results[c] = []; |
| 5501 | + results[c]['start'] = captions[i].start; |
| 5502 | + results[c]['caption'] = this.highlightSearchTerm(searchTerms,j,caption); |
| 5503 | + c++; |
| 5504 | + break; |
| 5505 | + } |
| 5506 | + } |
| 5507 | + } |
| 5508 | + } |
| 5509 | + } |
| 5510 | + } |
| 5511 | + |
| 5512 | + return results; |
| 5513 | + }; |
| 5514 | + |
| 5515 | + AblePlayer.prototype.highlightSearchTerm = function(searchTerms, index, resultString) { |
| 5516 | + |
| 5517 | + // highlight ALL found searchTerms in the current resultString |
| 5518 | + // index is the first index in the searchTerm array where a match has already been found |
| 5519 | + // Need to step through the remaining terms to see if they're present as well |
| 5520 | + |
| 5521 | + var i, searchTerm, termIndex, termLength, str1, str2, str3; |
| 5522 | + |
| 5523 | + for (i=index; i<searchTerms.length; i++) { |
| 5524 | + |
| 5525 | + searchTerm = searchTerms[i]; |
| 5526 | + termIndex = resultString.indexOf(searchTerm); |
| 5527 | + if (termIndex !== -1) { |
| 5528 | + termLength = searchTerm.length; |
| 5529 | + if (termLength > 0) { |
| 5530 | + str1 = resultString.substring(0, termIndex); |
| 5531 | + str2 = '<span class="able-search-term">' + searchTerm + '</span>'; |
| 5532 | + str3 = resultString.substring(termIndex+termLength); |
| 5533 | + resultString = str1 + str2 + str3; |
| 5534 | + } |
| 5535 | + else { |
| 5536 | + str1 = '<span class="able-search-term">' + searchTerm + '</span>'; |
| 5537 | + str2 = resultString.substring(termIndex+termLength); |
| 5538 | + resultString = str1 + str2; |
| 5539 | + } |
| 5540 | + } |
| 5541 | + } |
| 5542 | + return resultString; |
| 5543 | + }; |
| 5544 | + |
| 5545 | + AblePlayer.prototype.secondsToTime = function(totalSeconds) { |
| 5546 | + |
| 5547 | + // return an array of totalSeconds converted into two formats |
| 5548 | + // time['value'] = HH:MM:SS with hours dropped if there are none |
| 5549 | + // time['title'] = a speakable rendering, so speech rec users can easily speak the link |
| 5550 | + |
| 5551 | + // first, round down to nearest second |
| 5552 | + var totalSeconds = Math.floor(totalSeconds); |
| 5553 | + |
| 5554 | + var hours = parseInt( totalSeconds / 3600 ) % 24; |
| 5555 | + var minutes = parseInt( totalSeconds / 60 ) % 60; |
| 5556 | + var seconds = totalSeconds % 60; |
| 5557 | + var value = ''; |
| 5558 | + var title = ''; |
| 5559 | + if (hours > 0) { |
| 5560 | + value += hours + ':'; |
| 5561 | + title + hours + ' hours '; |
| 5562 | + } |
| 5563 | + if (minutes < 10) { |
| 5564 | + value += '0' + minutes + ':'; |
| 5565 | + if (minutes > 0) { |
| 5566 | + title += minutes + ' minutes '; |
| 5567 | + } |
| 5568 | + } |
| 5569 | + else { |
| 5570 | + value += minutes + ':'; |
| 5571 | + title += minutes + ' minutes '; |
| 5572 | + } |
| 5573 | + if (seconds < 10) { |
| 5574 | + value += '0' + seconds; |
| 5575 | + if (seconds > 0) { |
| 5576 | + title += seconds + ' seconds '; |
| 5577 | + } |
| 5578 | + } |
| 5579 | + else { |
| 5580 | + value += seconds; |
| 5581 | + title += seconds + ' seconds '; |
| 5582 | + } |
| 5583 | + var time = []; |
| 5584 | + time['value'] = value; |
| 5585 | + time['title'] = title; |
| 5586 | + return time; |
| 5587 | + }; |
| 5588 | +})(); |
5408 | 5589 | (function () { |
5409 | 5590 | // Media events |
5410 | 5591 | AblePlayer.prototype.onMediaUpdateTime = function () { |
5411 | 5592 | if (!this.startedPlaying) { |
5412 | 5593 | if (this.startTime) { |
5413 | 5594 | if (this.startTime === this.media.currentTime) { |
5414 | 5595 | // media has already scrubbed to start time |
5415 | | - if (this.autoplay) { |
| 5596 | + if (this.autoplay || this.seeking) { |
5416 | 5597 | this.playMedia(); |
5417 | | - } |
| 5598 | + this.seeking = false; |
| 5599 | + } |
5418 | 5600 | } |
5419 | 5601 | else { |
5420 | 5602 | // continue seeking ahead until currentTime == startTime |
|
0 commit comments