diff --git a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js index fe787cf04f..a8e0140885 100644 --- a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js +++ b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js @@ -348,9 +348,6 @@ // Automatically handle clicks and form submissions through Ajax, when same-domain ajaxEnabled: true, - // Automatically load and show pages based on location.hash - hashListeningEnabled: true, - // disable to prevent jquery from bothering with links linkBindingEnabled: true, @@ -374,8 +371,6 @@ // where it is provided on the window object phonegapNavigationEnabled: false, - pushStateEnabled: true, - // allows users to opt in to ignoring content by marking a parent element as // data-ignored ignoreContentEnabled: false, @@ -441,17 +436,6 @@ nsNormalize: function( prop ) { return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) ); - }, - - // Find the closest javascript page element to gather settings data jsperf test - // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit - // possibly naive, but it shows that the parsing overhead for *just* the page selector vs - // the page and dialog selector is negligable. This could probably be speed up by - // doing a similar parent node traversal to the one found in the inherited theme code above - closestPageData: function( $target ) { - return $target - .closest( ":jqmData(role='page'), :jqmData(role='dialog')" ) - .data( "mobile-page" ); } }); @@ -855,16 +839,6 @@ $.mobile.browser.oldIE = (function() { })(); $.extend( $.support, { - // Note, Chrome for iOS has an extremely quirky implementation of popstate. - // We've chosen to take the shortest path to a bug fix here for issue #5426 - // See the following link for information about the regex chosen - // https://developers.google.com/chrome/mobile/docs/user-agent#chrome_for_ios_user-agent - pushState: "pushState" in history && - "replaceState" in history && - // When running inside a FF iframe, calling replaceState causes an error - !( window.navigator.userAgent.indexOf( "Firefox" ) >= 0 && window.top !== window ) && - ( window.navigator.userAgent.search(/CriOS/) === -1 ), - mediaquery: $.mobile.media( "only all" ), cssPseudoElement: !!propExists( "content" ), touchOverflow: !!propExists( "overflowScrolling" ), @@ -908,18 +882,6 @@ $.mobile.ajaxBlacklist = originalEventName: undefined, - // If pushstate support is present and push state support is defined to - // be true on the mobile namespace. - isPushStateEnabled: function() { - return $.support.pushState && - this.isHashChangeEnabled(); - }, - - // !! assumes mobile namespace is present - isHashChangeEnabled: function() { - return $.mobile.hashListeningEnabled === true; - }, - // TODO a lot of duplication between popstate and hashchange popstate: function( event ) { var newEvent = new $.Event( "navigate" ), @@ -973,13 +935,8 @@ $.mobile.ajaxBlacklist = self.bound = true; - if ( self.isPushStateEnabled() ) { - self.originalEventName = "popstate"; - $win.bind( "popstate.navigate", self.popstate ); - } else if ( self.isHashChangeEnabled() ) { - self.originalEventName = "hashchange"; - $win.bind( "hashchange.navigate", self.hashchange ); - } + self.originalEventName = "popstate"; + $win.bind("popstate.navigate", self.popstate); } }; })( jQuery ); @@ -2360,8 +2317,7 @@ $.widget( "mobile.page", { this.ignoreInitialHashChange = true; $.mobile.window.bind({ - "popstate.history": $.proxy( this.popstate, this ), - "hashchange.history": $.proxy( this.hashchange, this ) + "popstate.history": $.proxy( this.popstate, this ) }); }; @@ -2420,8 +2376,7 @@ $.widget( "mobile.page", { // TODO reconsider name go: function( url, data, noEvents ) { - var state, href, hash, popstateEvent, - isPopStateEvent = $.event.special.navigate.isPushStateEnabled(); + var state, href, hash, popstateEvent; // Get the url as it would look squashed on to the current resolution url href = path.squash( url ); @@ -2464,21 +2419,19 @@ $.widget( "mobile.page", { title: document.title }, data); - if ( isPopStateEvent ) { - popstateEvent = new $.Event( "popstate" ); - popstateEvent.originalEvent = { - type: "popstate", - state: null - }; + popstateEvent = new $.Event("popstate"); + popstateEvent.originalEvent = { + type: "popstate", + state: null + }; - this.squash( url, state ); + this.squash(url, state); - // Trigger a new faux popstate event to replace the one that we - // caught that was triggered by the hash setting above. - if ( !noEvents ) { - this.ignorePopState = true; - $.mobile.window.trigger( popstateEvent ); - } + // Trigger a new faux popstate event to replace the one that we + // caught that was triggered by the hash setting above. + if (!noEvents) { + this.ignorePopState = true; + $.mobile.window.trigger(popstateEvent); } // record the history entry so that the information can be included @@ -2497,12 +2450,6 @@ $.widget( "mobile.page", { popstate: function( event ) { var hash, state; - // Partly to support our test suite which manually alters the support - // value to test hashchange. Partly to prevent all around weirdness - if ( !$.event.special.navigate.isPushStateEnabled() ) { - return; - } - // If this is the popstate triggered by the actual alteration of the hash // prevent it completely. History is tracked manually if ( this.preventHashAssignPopState ) { @@ -2572,62 +2519,6 @@ $.widget( "mobile.page", { event.historyState.direction = direction; } }); - }, - - // NOTE must bind before `navigate` special event hashchange binding otherwise the - // navigation data won't be attached to the hashchange event in time for those - // bindings to attach it to the `navigate` special event - // TODO add a check here that `hashchange.navigate` is bound already otherwise it's - // broken (exception?) - hashchange: function( event ) { - var history, hash; - - // If hashchange listening is explicitly disabled or pushstate is supported - // avoid making use of the hashchange handler. - if (!$.event.special.navigate.isHashChangeEnabled() || - $.event.special.navigate.isPushStateEnabled() ) { - return; - } - - // On occasion explicitly want to prevent the next hash from propogating because we only - // with to alter the url to represent the new state do so here - if ( this.preventNextHashChange ) { - this.preventNextHashChange = false; - event.stopImmediatePropagation(); - return; - } - - history = this.history; - hash = path.parseLocation().hash; - - // If this is a hashchange caused by the back or forward button - // make sure to set the state of our history stack properly - this.history.direct({ - url: hash, - - // When the url is either forward or backward in history include the entry - // as data on the event object for merging as data in the navigate event - present: function( historyEntry, direction ) { - // make sure to create a new object to pass down as the navigate event data - event.hashchangeState = $.extend({}, historyEntry); - event.hashchangeState.direction = direction; - }, - - // When we don't find a hash in our history clearly we're aiming to go there - // record the entry as new for future traversal - // - // NOTE it's not entirely clear that this is the right thing to do given that we - // can't know the users intention. It might be better to explicitly _not_ - // support location.hash assignment in preference to $.navigate calls - // TODO first arg to add should be the href, but it causes issues in identifying - // embeded pages - missing: function() { - history.add( hash, { - hash: hash, - title: document.title - }); - } - }); } }); })( jQuery ); @@ -2792,24 +2683,7 @@ $.widget( "mobile.page", { go: function( steps ) { - //if hashlistening is enabled use native history method - if ( $.mobile.hashListeningEnabled ) { - window.history.go( steps ); - } else { - - //we are not listening to the hash so handle history internally - var activeIndex = $.mobile.navigate.history.activeIndex, - index = activeIndex + parseInt( steps, 10 ), - url = $.mobile.navigate.history.stack[ index ].url, - direction = ( steps >= 1 )? "forward" : "back"; - - //update the history object - $.mobile.navigate.history.activeIndex = index; - $.mobile.navigate.history.previousIndex = activeIndex; - - //change to the new page - this.change( url, { direction: direction, changeHash: false, fromHashChange: true } ); - } + window.history.go(steps); }, // TODO rename _handleDestination @@ -3542,7 +3416,7 @@ $.widget( "mobile.page", { role: settings.role }; - if ( settings.changeHash !== false && $.mobile.hashListeningEnabled ) { + if ( settings.changeHash !== false ) { $.mobile.navigate( this.window[ 0 ].encodeURI( url ), params, true); } else if ( toPage[ 0 ] !== $.mobile.firstPage[ 0 ] ) { $.mobile.navigate.history.add( url, params ); @@ -3874,17 +3748,14 @@ $.widget( "mobile.page", { // the hash is not valid (contains more than one # or does not start with #) // or there is no page with that hash, change to the first page in the DOM // Remember, however, that the hash can also be a path! - if ( ! ( $.mobile.hashListeningEnabled && - $.mobile.path.isHashValid( location.hash ) && + if ( ! ( $.mobile.path.isHashValid( location.hash ) && ( $( hashPage ).is( "[data-role='page']" ) || $.mobile.path.isPath( hash ) || hash === $.mobile.dialogHashKey ) ) ) { // make sure to set initial popstate state if it exists // so that navigation back to the initial page works properly - if ( $.event.special.navigate.isPushStateEnabled() ) { - $.mobile.navigate.navigator.squash( path.parseLocation().href ); - } + $.mobile.navigate.navigator.squash(path.parseLocation().href); $.mobile.changePage( $.mobile.firstPage, { reverse: true, @@ -3892,16 +3763,10 @@ $.widget( "mobile.page", { fromHashChange: true }); } else { - // trigger hashchange or navigate to squash and record the correct - // history entry for an initial hash path - if ( !$.event.special.navigate.isPushStateEnabled() ) { - $window.trigger( "hashchange", [true] ); - } else { - // TODO figure out how to simplify this interaction with the initial history entry - // at the bottom js/navigate/navigate.js - $.mobile.navigate.history.stack = []; - $.mobile.navigate( $.mobile.path.isPath( location.hash ) ? location.hash : location.href ); - } + // TODO figure out how to simplify this interaction with the initial history entry + // at the bottom js/navigate/navigate.js + $.mobile.navigate.history.stack = []; + $.mobile.navigate($.mobile.path.isPath(location.hash) ? location.hash : location.href); } } }); diff --git a/dashboard-ui/voice/textprocessor-en-us.js b/dashboard-ui/voice/textprocessor-en-us.js new file mode 100644 index 0000000000..9ae3e88cea --- /dev/null +++ b/dashboard-ui/voice/textprocessor-en-us.js @@ -0,0 +1,244 @@ +define([], function () { + + function parseTextInternal(text) { + + var result = { + action: '', + itemName: '', + itemType: '', + category: '', + filters: [], + removeWords: [], + sortby: '', + sortorder: 'Ascending', + limit: null, + userId: Dashboard.getCurrentUserId() + }; + + var textLower = text.toLowerCase(); + var words = text.toLowerCase().split(' '); + + var displayWords = [ + 'show', + 'pull up', + 'display', + 'go to', + 'view' + ]; + + if (displayWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + if (words.indexOf('guide') != -1) { + result.action = 'show'; + result.category = 'tvguide'; + } + + if (words.indexOf('recordings') != -1) { + result.action = 'show'; + result.category = 'recordings'; + } + + result.removeWords = displayWords; + return result; + } + + var searchWords = [ + 'search', + 'search for', + 'find', + 'query' + ]; + + if (searchWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + // Search + result.action = 'search'; + + result.removeWords = searchWords; + return result; + } + + var playWords = [ + 'play', + 'watch' + ]; + + if (playWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + // Play + result.action = 'play'; + + result.removeWords = playWords; + return result; + } + + var controlWords = [ + 'use', + 'control' + ]; + + if (controlWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + // Play + result.action = 'control'; + + result.removeWords = controlWords; + return result; + } + + var enableWords = [ + 'enable', + 'turn on' + ]; + + if (enableWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + // Play + result.action = 'enable'; + + result.removeWords = enableWords; + return result; + } + + var disableWords = [ + 'disable', + 'turn off' + ]; + + if (disableWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + // Play + result.action = 'disable'; + + result.removeWords = disableWords; + return result; + } + + var toggleWords = [ + 'toggle' + ]; + + if (toggleWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { + + // Play + result.action = 'toggle'; + + result.removeWords = toggleWords; + return result; + } + + if (words.indexOf('shuffle') != -1) { + + // Play + result.action = 'shuffle'; + + result.removeWords.push('shuffle'); + return result; + } + + if (words.indexOf('record') != -1) { + + // Record + result.action = 'record'; + + result.removeWords.push('record'); + return result; + } + + if (words.indexOf('guide') != -1) { + result.action = 'show'; + result.category = 'tvguide'; + return result; + } + + return result; + } + + function parseContext(text, result) { + + text = text.toLowerCase(); + + var i, length; + + for (i = 0, length = result.removeWords.length; i < length; i++) { + + text = text.replace(result.removeWords[i], ''); + } + + text = text.trim(); + + var removeAtStart = [ + 'my' + ]; + + for (i = 0, length = removeAtStart.length; i < length; i++) { + + if (text.indexOf(removeAtStart[i]) == 0) { + text = text.substring(removeAtStart[i].length); + } + } + + result.what = text; + + text = text.trim(); + var words = text.toLowerCase().split(' '); + + if (words.indexOf('favorite') != -1) { + result.filters.push('favorite'); + } + + if (text.indexOf('latest movies') != -1 || text.indexOf('latest films') != -1) { + + result.sortby = 'datecreated'; + result.sortorder = 'Descending'; + result.filters.push('unplayed'); + result.itemType = 'Movie'; + + return; + } + + if (text.indexOf('latest episodes') != -1) { + + result.sortby = 'datecreated'; + result.sortorder = 'Descending'; + result.filters.push('unplayed'); + result.itemType = 'Episode'; + + return; + } + + if (text.indexOf('next up') != -1) { + + result.category = 'nextup'; + + return; + } + + if (text.indexOf('movies') != -1 || text.indexOf('films') != -1) { + + result.itemType = 'Movie'; + + return; + } + + if (text.indexOf('shows') != -1 || text.indexOf('series') != -1) { + + result.itemType = 'Series'; + + return; + } + + if (text.indexOf('songs') != -1) { + + result.itemType = 'Audio'; + + return; + } + } + + return function (text) { + var result = parseTextInternal(text); + parseContext(text, result); + return result; + } +}); \ No newline at end of file diff --git a/dashboard-ui/voice/voice.js b/dashboard-ui/voice/voice.js index 942325854a..1fc840042a 100644 --- a/dashboard-ui/voice/voice.js +++ b/dashboard-ui/voice/voice.js @@ -51,293 +51,47 @@ var deferred = DeferredBuilder.Deferred(); - processTextInternal(text, deferred); + require(['voice/textprocessor-en-us.js'], function (parseText) { + + var result = parseText(text); + + switch (result.action) { + + case 'show': + showCommand(result); + break; + case 'play': + playCommand(result); + break; + case 'shuffle': + playCommand(result, true); + break; + case 'search': + playCommand(result); + break; + case 'control': + controlCommand(result); + break; + case 'enable': + enableCommand(result); + break; + case 'disable': + disableCommand(result); + break; + case 'toggle': + toggleCommand(result); + break; + default: + deferred.reject(); + return; + } + + deferred.resolve(); + }); return deferred.promise(); } - function parseContext(text, result) { - - text = text.toLowerCase(); - - var i, length; - - for (i = 0, length = result.removeWords.length; i < length; i++) { - - text = text.replace(result.removeWords[i], ''); - } - - text = text.trim(); - - var removeAtStart = [ - 'my' - ]; - - for (i = 0, length = removeAtStart.length; i < length; i++) { - - if (text.indexOf(removeAtStart[i]) == 0) { - text = text.substring(removeAtStart[i].length); - } - } - - result.what = text; - - text = text.trim(); - var words = text.toLowerCase().split(' '); - - if (words.indexOf('favorite') != -1) { - result.filters.push('favorite'); - } - - if (text.indexOf('latest movies') != -1 || text.indexOf('latest films') != -1) { - - result.sortby = 'datecreated'; - result.sortorder = 'Descending'; - result.filters.push('unplayed'); - result.itemType = 'Movie'; - - return; - } - - if (text.indexOf('latest episodes') != -1) { - - result.sortby = 'datecreated'; - result.sortorder = 'Descending'; - result.filters.push('unplayed'); - result.itemType = 'Episode'; - - return; - } - - if (text.indexOf('next up') != -1) { - - result.category = 'nextup'; - - return; - } - - if (text.indexOf('movies') != -1 || text.indexOf('films') != -1) { - - result.itemType = 'Movie'; - - return; - } - - if (text.indexOf('shows') != -1 || text.indexOf('series') != -1) { - - result.itemType = 'Series'; - - return; - } - - if (text.indexOf('songs') != -1) { - - result.itemType = 'Audio'; - - return; - } - } - - function parseText(text) { - - var result = { - action: '', - itemName: '', - itemType: '', - category: '', - filters: [], - removeWords: [], - sortby: '', - sortorder: 'Ascending', - limit: null, - userId: Dashboard.getCurrentUserId() - }; - - var textLower = text.toLowerCase(); - var words = text.toLowerCase().split(' '); - - var displayWords = [ - 'show', - 'pull up', - 'display', - 'go to', - 'view' - ]; - - if (displayWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - if (words.indexOf('guide') != -1) { - result.action = 'show'; - result.category = 'tvguide'; - } - - if (words.indexOf('recordings') != -1) { - result.action = 'show'; - result.category = 'recordings'; - } - - result.removeWords = displayWords; - return result; - } - - var searchWords = [ - 'search', - 'search for', - 'find', - 'query' - ]; - - if (searchWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - // Search - result.action = 'search'; - - result.removeWords = searchWords; - return result; - } - - var playWords = [ - 'play', - 'watch' - ]; - - if (playWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - // Play - result.action = 'play'; - - result.removeWords = playWords; - return result; - } - - var controlWords = [ - 'use', - 'control' - ]; - - if (controlWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - // Play - result.action = 'control'; - - result.removeWords = controlWords; - return result; - } - - var enableWords = [ - 'enable', - 'turn on' - ]; - - if (enableWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - // Play - result.action = 'enable'; - - result.removeWords = enableWords; - return result; - } - - var disableWords = [ - 'disable', - 'turn off' - ]; - - if (disableWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - // Play - result.action = 'disable'; - - result.removeWords = disableWords; - return result; - } - - var toggleWords = [ - 'toggle' - ]; - - if (toggleWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { - - // Play - result.action = 'toggle'; - - result.removeWords = toggleWords; - return result; - } - - if (words.indexOf('shuffle') != -1) { - - // Play - result.action = 'shuffle'; - - result.removeWords.push('shuffle'); - return result; - } - - if (words.indexOf('record') != -1) { - - // Record - result.action = 'record'; - - result.removeWords.push('record'); - return result; - } - - if (words.indexOf('guide') != -1) { - result.action = 'show'; - result.category = 'tvguide'; - return result; - } - - return result; - } - - function processTextInternal(text, deferred) { - - var result = parseText(text); - - switch (result.action) { - - case 'show': - parseContext(text, result); - showCommand(result); - break; - case 'play': - parseContext(text, result); - playCommand(result); - break; - case 'shuffle': - parseContext(text, result); - playCommand(result, true); - break; - case 'search': - parseContext(text, result); - playCommand(result); - break; - case 'control': - parseContext(text, result); - controlCommand(result); - break; - case 'enable': - parseContext(text, result); - enableCommand(result); - break; - case 'disable': - parseContext(text, result); - disableCommand(result); - break; - case 'toggle': - parseContext(text, result); - toggleCommand(result); - break; - default: - deferred.reject(); - return; - } - - deferred.resolve(); - } - function showCommand(result) { if (result.category == 'tvguide') {