src/widgets/Controller.js
branchnew-model
changeset 1020 198c2b79f5e1
parent 1019 3ab36f402b0c
equal deleted inserted replaced
1019:3ab36f402b0c 1020:198c2b79f5e1
     1 /* Displays Play and Pause buttons, Search Button and Form, Volume Control */
     1 /* Displays Play and Pause buttons, Search Button and Form, Volume Control */
     2 
     2 
     3 IriSP.Widgets.Controller = function(player, config) {
     3 IriSP.Widgets.Controller = function(player, config) {
     4     IriSP.Widgets.Widget.call(this, player, config);
     4   IriSP.Widgets.Widget.call(this, player, config);
     5     this.lastSearchValue = "";
     5   
       
     6   this._searchLastValue = "";
     6 };
     7 };
     7 
     8 
     8 IriSP.Widgets.Controller.prototype = new IriSP.Widgets.Widget();
     9 IriSP.Widgets.Controller.prototype = new IriSP.Widgets.Widget();
     9 
    10 
    10 IriSP.Widgets.Controller.prototype.defaults = {
    11 IriSP.Widgets.Controller.prototype.defaults = {
    11     disable_annotate_btn: false,
    12     disable_annotate_btn: false,
    12     disable_search_btn: false,
    13     disable_search_btn: false
    13     disable_ctrl_f: false
    14 }
    14 };
       
    15 
    15 
    16 IriSP.Widgets.Controller.prototype.template =
    16 IriSP.Widgets.Controller.prototype.template =
    17     '<div class="Ldt-Ctrl">'
    17     '<div class="Ldt-Ctrl">'
    18     + '<div class="Ldt-Ctrl-Left">'
    18     + '<div class="Ldt-Ctrl-Left">'
    19     + '<div class="Ldt-Ctrl-button Ldt-Ctrl-Play Ldt-Ctrl-Play-PlayState Ldt-TraceMe" title="{{l10n.play_pause}}"></div>'
    19     + '<div class="Ldt-Ctrl-button Ldt-Ctrl-Play Ldt-Ctrl-Play-PlayState Ldt-TraceMe" title="{{l10n.play_pause}}"></div>'
    54         mute: "Mute",
    54         mute: "Mute",
    55         unmute: "Unmute",
    55         unmute: "Unmute",
    56         annotate: "Annotate",
    56         annotate: "Annotate",
    57         search: "Search",
    57         search: "Search",
    58         elapsed_time: "Elapsed time",
    58         elapsed_time: "Elapsed time",
    59         total_time: "Total duration",
    59         total_time: "Total time",
    60         volume: "Volume",
    60         volume: "Volume",
    61         volume_control: "Volume control"
    61         volume_control: "Volume control"
    62     },
    62     },
    63     fr: {
    63     fr: {
    64         play_pause: "Lecture/Pause",
    64         play_pause: "Lecture/Pause",
    67         pause: "Pause",
    67         pause: "Pause",
    68         mute: "Couper le son",
    68         mute: "Couper le son",
    69         unmute: "Activer le son",
    69         unmute: "Activer le son",
    70         annotate: "Annoter",
    70         annotate: "Annoter",
    71         search: "Rechercher",
    71         search: "Rechercher",
    72         elapsed_time: "Temps écoulé",
    72         elapsed_time: "Durée écoulée",
    73         total_time: "Durée totale",
    73         total_time: "Durée totale",
    74         volume: "Niveau sonore",
    74         volume: "Niveau sonore",
    75         volume_control: "Réglage du niveau sonore"
    75         volume_control: "Réglage du niveau sonore"
    76     }
    76     }
    77 };
    77 };
    85     this.$searchBlock = this.$.find(".Ldt-Ctrl-Search");
    85     this.$searchBlock = this.$.find(".Ldt-Ctrl-Search");
    86     this.$searchInput = this.$.find(".Ldt-Ctrl-SearchInput");
    86     this.$searchInput = this.$.find(".Ldt-Ctrl-SearchInput");
    87     this.$volumeBar = this.$.find(".Ldt-Ctrl-Volume-Bar");
    87     this.$volumeBar = this.$.find(".Ldt-Ctrl-Volume-Bar");
    88     
    88     
    89     // handle events
    89     // handle events
    90     this.onMediaEvent("play","playButtonUpdater");
    90     this.bindPopcorn("play","playButtonUpdater");
    91     this.onMediaEvent("pause","playButtonUpdater");
    91     this.bindPopcorn("pause","playButtonUpdater");
    92     this.onMediaEvent("volumechange","volumeUpdater");
    92     this.bindPopcorn("volumechange","volumeUpdater");
    93     this.onMediaEvent("timeupdate","timeDisplayUpdater");
    93     this.bindPopcorn("timeupdate","timeDisplayUpdater");
    94     this.onMediaEvent("loadedmetadata","volumeUpdater");
    94     this.bindPopcorn("loadedmetadata","timeDisplayUpdater");
       
    95     this.bindPopcorn("loadedmetadata","volumeUpdater");
       
    96     this.bindPopcorn("IriSP.search.matchFound","searchMatch");
       
    97     this.bindPopcorn("IriSP.search.noMatchFound","searchNoMatch");
       
    98     this.bindPopcorn("IriSP.search.triggeredSearch","triggeredSearch");
    95     
    99     
    96     // handle clicks
   100     // handle clicks
    97     this.$playButton.click(this.functionWrapper("playHandler"));
   101     this.$playButton.click(this.functionWrapper("playHandler"));
    98     
   102     
    99     this.$.find(".Ldt-Ctrl-Annotate").click(function() {
   103     this.$.find(".Ldt-Ctrl-Annotate").click(function() {
   100         _this.player.trigger("CreateAnnotation.toggle");
   104         _this.player.popcorn.trigger("IriSP.CreateAnnotation.toggle");
   101     });
   105     });
   102     this.$.find(".Ldt-Ctrl-SearchBtn").click(this.functionWrapper("searchButtonHandler"));
   106     this.$.find(".Ldt-Ctrl-SearchBtn").click(this.functionWrapper("searchButtonHandler"));
   103     
   107     
   104     this.$searchInput.keyup(this.functionWrapper("searchHandler"));
   108     this.$searchInput.keyup(this.functionWrapper("searchHandler") );
   105   
   109   
   106 	var _volctrl = this.$.find(".Ldt-Ctrl-Volume-Control");
   110 	var _volctrl = this.$.find(".Ldt-Ctrl-Volume-Control");
   107     this.$.find('.Ldt-Ctrl-Sound')
   111     this.$.find('.Ldt-Ctrl-Sound')
   108         .click(this.functionWrapper("muteHandler"))
   112         .click(this.functionWrapper("muteHandler"))
   109         .mouseover(function() {
   113         .mouseover(function() {
   115     _volctrl.mouseover(function() {
   119     _volctrl.mouseover(function() {
   116         _volctrl.show();
   120         _volctrl.show();
   117     }).mouseout(function() {
   121     }).mouseout(function() {
   118         _volctrl.hide();
   122         _volctrl.hide();
   119     });
   123     });
   120     
   124   
   121     // Handle CTRL-F
       
   122     if (!this.disable_ctrl_f) {
       
   123         var _fKey = "F".charCodeAt(0),
       
   124             _lastCtrlFTime = 0;
       
   125         IriSP.jQuery(document).keydown(function(_event) {
       
   126             if (_event.keyCode === _fKey && (_event.ctrlKey || _event.metaKey)) {
       
   127                 var _time = IriSP.jQuery.now();
       
   128                 if (_time - _lastCtrlFTime > 2000) {
       
   129                     _this.searchButtonHandler();
       
   130                 }
       
   131                 _lastCtrlFTime = _time;
       
   132                 return false;
       
   133             }
       
   134         });
       
   135     }
       
   136     
   125     
   137     // Allow Volume Cursor Dragging
   126     // Allow Volume Cursor Dragging
   138     this.$volumeBar.slider({
   127     this.$volumeBar.slider({
   139         slide: function(event, ui) {
   128         slide: function(event, ui) {
   140             _this.$volumeBar.attr("title",_this.l10n.volume+': ' + ui.value + '%');
   129             _this.$volumeBar.attr("title",_this.l10n.volume+': ' + ui.value + '%');
   141             _this.media.setVolume(ui.value / 100);
   130             _this.player.popcorn.volume(ui.value / 100);
   142         },
   131         },
   143         stop: this.functionWrapper("volumeUpdater")
   132         stop: this.functionWrapper("volumeUpdater")
   144     });
   133     });
   145 
   134 
   146     // trigger an IriSP.Player.MouseOver to the widgets that are interested (i.e : sliderWidget)
   135     // trigger an IriSP.Player.MouseOver to the widgets that are interested (i.e : sliderWidget)
   147     this.$.hover(
   136     this.$.hover(
   148         function() {
   137         function() {
   149             _this.player.trigger("Player.MouseOver");
   138             _this.player.popcorn.trigger("IriSP.Player.MouseOver");
   150         }, 
   139         }, 
   151         function() {
   140         function() {
   152             _this.player.trigger("Player.MouseOut");
   141             _this.player.popcorn.trigger("IriSP.Player.MouseOut");
   153         });
   142         });
   154     
   143     /* some players - including jwplayer - save the state of the mute button between sessions */
   155     this.timeDisplayUpdater(new IriSP.Model.Time(0));
   144 
   156     
   145     window.setTimeout(this.functionWrapper("volumeUpdater"), 1000);
   157     var annotations = this.source.getAnnotations();
       
   158     annotations.on("search", function(_text) {
       
   159         _this.$searchInput.val(_text);
       
   160         _this.showSearchBlock();
       
   161     });
       
   162     annotations.on("found", function(_text) {
       
   163         _this.$searchInput.css('background-color','#e1ffe1');
       
   164     });
       
   165     annotations.on("not-found", function(_text) {
       
   166         _this.$searchInput.css('background-color', "#d62e3a");
       
   167     });
       
   168     annotations.on("search-cleared", function() {
       
   169         _this.hideSearchBlock();
       
   170     });
       
   171    
   146    
   172 };
   147 };
   173 
   148 
   174 /* Update the elasped time div */
   149 /* Update the elasped time div */
   175 IriSP.Widgets.Controller.prototype.timeDisplayUpdater = function(_time) {
   150 IriSP.Widgets.Controller.prototype.timeDisplayUpdater = function() {
       
   151     var _curTime = this.player.popcorn.roundTime();
       
   152     if (typeof this._previousSecond !== "undefined" && _curTime === this._previousSecond) {
       
   153         return;
       
   154     }
   176   
   155   
   177     // we get it at each call because it may change.
   156     // we get it at each call because it may change.
   178     var _totalTime = this.media.duration;
   157     var _totalTime = this.source.getDuration(),
   179     this.$.find(".Ldt-Ctrl-Time-Elapsed").html(_time.toString());
   158         _elapsedTime = new IriSP.Model.Time();
       
   159         
       
   160     _elapsedTime.setSeconds(_curTime);
       
   161   
       
   162     this.$.find(".Ldt-Ctrl-Time-Elapsed").html(_elapsedTime.toString());
   180     this.$.find(".Ldt-Ctrl-Time-Total").html(_totalTime.toString());
   163     this.$.find(".Ldt-Ctrl-Time-Total").html(_totalTime.toString());
       
   164     this._previousSecond = _curTime;
   181 };
   165 };
   182 
   166 
   183 /* update the icon of the button - separate function from playHandler
   167 /* update the icon of the button - separate function from playHandler
   184    because in some cases (for instance, when the user directly clicks on
   168    because in some cases (for instance, when the user directly clicks on
   185    the jwplayer window) we have to change the icon without playing/pausing
   169    the jwplayer window) we have to change the icon without playing/pausing
   186 */
   170 */
   187 IriSP.Widgets.Controller.prototype.playButtonUpdater = function() {
   171 IriSP.Widgets.Controller.prototype.playButtonUpdater = function() {
   188     if (this.media.getPaused()) {
   172     
       
   173     var status = this.player.popcorn.media.paused;
       
   174   
       
   175     if (status) {
   189     /* the background sprite is changed by adding/removing the correct classes */
   176     /* the background sprite is changed by adding/removing the correct classes */
   190         this.$playButton
   177         this.$playButton
   191             .attr("title", this.l10n.play)
   178             .attr("title", this.l10n.play)
   192             .removeClass("Ldt-Ctrl-Play-PauseState")
   179             .removeClass("Ldt-Ctrl-Play-PauseState")
   193             .addClass("Ldt-Ctrl-Play-PlayState");
   180             .addClass("Ldt-Ctrl-Play-PlayState");
   199     }
   186     }
   200 };
   187 };
   201 
   188 
   202 
   189 
   203 IriSP.Widgets.Controller.prototype.playHandler = function() {
   190 IriSP.Widgets.Controller.prototype.playHandler = function() {
   204     if (this.media.getPaused()) {        
   191     
   205         this.media.play();
   192     var status = this.player.popcorn.media.paused;
   206     } else {
   193   
   207         this.media.pause();
   194     if (status) {        
       
   195         this.player.popcorn.play();   
       
   196     } else {
       
   197         this.player.popcorn.pause();
   208     }  
   198     }  
   209 };
   199 };
   210 
   200 
   211 IriSP.Widgets.Controller.prototype.muteHandler = function() {
   201 IriSP.Widgets.Controller.prototype.muteHandler = function() {
   212     this.media.setMuted(!this.media.getMuted());
   202     this.player.popcorn.muted(!this.player.popcorn.muted());
   213 };
   203 };
   214 
   204 
   215 IriSP.Widgets.Controller.prototype.volumeUpdater = function() {
   205 IriSP.Widgets.Controller.prototype.volumeUpdater = function() {
   216     var _muted = this.media.getMuted(),
   206     var _muted = this.player.popcorn.muted(),
   217         _vol = this.media.getVolume();
   207         _vol = this.player.popcorn.volume();
   218     if (_vol === false) {
   208     if (_vol === false) {
   219         _vol = .5;
   209         _vol = .5;
   220     }
   210     }
   221     var _soundCtl = this.$.find(".Ldt-Ctrl-Sound");
   211     var _soundCtl = this.$.find(".Ldt-Ctrl-Sound");
   222     _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
   212     _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
   223     if (_muted) {        
   213     if (_muted) {        
   224         _soundCtl.attr("title", this.l10n.unmute)
   214         _soundCtl.attr("title", this.l10n.unmute)
   225             .addClass("Ldt-Ctrl-Sound-Mute");    
   215             .addClass("Ldt-Ctrl-Sound-Mute");    
   226     } else {
   216     } else {
   227         _soundCtl.attr("title", this.l10n.mute)
   217         _soundCtl.attr("title", this.l10n.mute)
   228             .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" );
   218             .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" )
   229     }
   219     }
   230     this.$volumeBar.slider("value", _muted ? 0 : 100 * _vol);
   220     this.$volumeBar.slider("value", _muted ? 0 : 100 * _vol);
   231 };
   221 };
   232 
   222 
   233 IriSP.Widgets.Controller.prototype.showSearchBlock = function() {
   223 IriSP.Widgets.Controller.prototype.showSearchBlock = function() {
   234     this.$searchBlock.animate({ width:"160px" }, 200);
   224     this.$searchBlock.show("blind", { direction: "horizontal"}, 100);
   235     this.$searchInput.css('background-color','#fff');
   225     this.$searchInput.css('background-color','#fff');
       
   226    
   236     this.$searchInput.focus();
   227     this.$searchInput.focus();
       
   228     
       
   229     // we need this variable because some widgets can find a match in
       
   230     // their data while at the same time others don't. As we want the
       
   231     // search field to become green when there's a match, we need a 
       
   232     // variable to remember that we had one.
       
   233     this._positiveMatch = false;
       
   234 
       
   235     // tell the world the field is open
       
   236     this.player.popcorn.trigger("IriSP.search.open");
   237 };
   237 };
   238 
   238 
   239 IriSP.Widgets.Controller.prototype.hideSearchBlock = function() {
   239 IriSP.Widgets.Controller.prototype.hideSearchBlock = function() {
   240     this.$searchBlock.animate( { width: 0 }, 200);
   240     this._searchLastValue = this.$searchInput.val();
       
   241     this.$searchInput.val('');
       
   242     this.$searchBlock.hide("blind", { direction: "horizontal"}, 75);
       
   243 
       
   244     this._positiveMatch = false;
       
   245     
       
   246     this.player.popcorn.trigger("IriSP.search.closed");
   241 };
   247 };
   242 
   248 
   243 /** react to clicks on the search button */
   249 /** react to clicks on the search button */
   244 IriSP.Widgets.Controller.prototype.searchButtonHandler = function() {
   250 IriSP.Widgets.Controller.prototype.searchButtonHandler = function() {
   245     if ( !this.$searchBlock.width() ) {
   251     if ( this.$searchBlock.is(":hidden") ) {
   246         this.showSearchBlock();
   252         this.showSearchBlock();
   247         var _val = this.$searchInput.val();
   253         this.$searchInput.val(this._searchLastValue);      
   248         if (_val) {
   254         this.player.popcorn.trigger("IriSP.search", this._searchLastValue); // trigger the search to make it more natural.
   249             this.source.getAnnotations().search(_val);
       
   250         }
       
   251 	} else {
   255 	} else {
   252         this.hideSearchBlock();
   256         this.hideSearchBlock();
   253     }
   257     }
   254 };
   258 };
   255 
   259 
   256 /** this handler is called whenever the content of the search
   260 /** this handler is called whenever the content of the search
   257    field changes */
   261    field changes */
   258 IriSP.Widgets.Controller.prototype.searchHandler = function() {
   262 IriSP.Widgets.Controller.prototype.searchHandler = function() {
   259     if ( !this.$searchBlock.width() ) {
   263     this._searchLastValue = this.$searchInput.val();
   260         this.$searchBlock.css({ width:"160px" });
       
   261         this.$searchInput.css('background-color','#fff');
       
   262     }
       
   263     var _val = this.$searchInput.val();
       
   264     this._positiveMatch = false;
   264     this._positiveMatch = false;
   265     
   265   
   266     // do nothing if the search field is empty, instead of highlighting everything.
   266     // do nothing if the search field is empty, instead of highlighting everything.
   267     if (_val !== this.lastSearchValue) {
   267     if (this._searchLastValue == "") {
   268         if (_val) {
   268         this.player.popcorn.trigger("IriSP.search.cleared");
   269             this.source.getAnnotations().search(_val);
   269         this.$searchInput.css('background-color','');
   270         } else {
   270     } else {
   271             this.source.getAnnotations().trigger("clear-search");
   271         this.player.popcorn.trigger("IriSP.search", this._searchLastValue);
   272             this.$searchInput.css('background-color','');
   272     }
   273         }
   273 };
   274     }
   274 
   275     this.lastSearchValue = _val;
   275 /**
   276 };
   276   handler for the IriSP.search.found message, which is sent by some views when they
   277 
   277   highlight a match.
       
   278 */
       
   279 IriSP.Widgets.Controller.prototype.searchMatch = function() {
       
   280     this._positiveMatch = true;
       
   281     this.$searchInput.css('background-color','#e1ffe1');
       
   282 };
       
   283 
       
   284 /** the same, except that no value could be found */
       
   285 IriSP.Widgets.Controller.prototype.searchNoMatch = function() {
       
   286     if (this._positiveMatch !== true) {
       
   287         this.$searchInput.css('background-color', "#d62e3a");
       
   288     }
       
   289 };
       
   290 
       
   291 /** react to an IriSP.Player.triggeredSearch - that is, when
       
   292     a widget ask the.Player to do a search on his behalf */
       
   293 IriSP.Widgets.Controller.prototype.triggeredSearch = function(searchString) {
       
   294     this.showSearchBlock();
       
   295     this.$searchInput.attr('value', searchString);      
       
   296     this.player.popcorn.trigger("IriSP.search", searchString); // trigger the search to make it more natural.
       
   297 };
       
   298 
       
   299