src/widgets/Controller.js
branchnew-model
changeset 1019 3ab36f402b0c
parent 919 972099304059
child 1020 198c2b79f5e1
equal deleted inserted replaced
946:919e362b9db1 1019:3ab36f402b0c
     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   
     5     this.lastSearchValue = "";
     6   this._searchLastValue = "";
       
     7 };
     6 };
     8 
     7 
     9 IriSP.Widgets.Controller.prototype = new IriSP.Widgets.Widget();
     8 IriSP.Widgets.Controller.prototype = new IriSP.Widgets.Widget();
    10 
     9 
    11 IriSP.Widgets.Controller.prototype.defaults = {
    10 IriSP.Widgets.Controller.prototype.defaults = {
    12     disable_annotate_btn: false,
    11     disable_annotate_btn: false,
    13     disable_search_btn: false
    12     disable_search_btn: false,
    14 }
    13     disable_ctrl_f: false
       
    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 time",
    59         total_time: "Total duration",
    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: "Durée écoulée",
    72         elapsed_time: "Temps écoulé",
    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.bindPopcorn("play","playButtonUpdater");
    90     this.onMediaEvent("play","playButtonUpdater");
    91     this.bindPopcorn("pause","playButtonUpdater");
    91     this.onMediaEvent("pause","playButtonUpdater");
    92     this.bindPopcorn("volumechange","volumeUpdater");
    92     this.onMediaEvent("volumechange","volumeUpdater");
    93     this.bindPopcorn("timeupdate","timeDisplayUpdater");
    93     this.onMediaEvent("timeupdate","timeDisplayUpdater");
    94     this.bindPopcorn("loadedmetadata","timeDisplayUpdater");
    94     this.onMediaEvent("loadedmetadata","volumeUpdater");
    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");
       
    99     
    95     
   100     // handle clicks
    96     // handle clicks
   101     this.$playButton.click(this.functionWrapper("playHandler"));
    97     this.$playButton.click(this.functionWrapper("playHandler"));
   102     
    98     
   103     this.$.find(".Ldt-Ctrl-Annotate").click(function() {
    99     this.$.find(".Ldt-Ctrl-Annotate").click(function() {
   104         _this.player.popcorn.trigger("IriSP.CreateAnnotation.toggle");
   100         _this.player.trigger("CreateAnnotation.toggle");
   105     });
   101     });
   106     this.$.find(".Ldt-Ctrl-SearchBtn").click(this.functionWrapper("searchButtonHandler"));
   102     this.$.find(".Ldt-Ctrl-SearchBtn").click(this.functionWrapper("searchButtonHandler"));
   107     
   103     
   108     this.$searchInput.keyup(this.functionWrapper("searchHandler") );
   104     this.$searchInput.keyup(this.functionWrapper("searchHandler"));
   109   
   105   
   110 	var _volctrl = this.$.find(".Ldt-Ctrl-Volume-Control");
   106 	var _volctrl = this.$.find(".Ldt-Ctrl-Volume-Control");
   111     this.$.find('.Ldt-Ctrl-Sound')
   107     this.$.find('.Ldt-Ctrl-Sound')
   112         .click(this.functionWrapper("muteHandler"))
   108         .click(this.functionWrapper("muteHandler"))
   113         .mouseover(function() {
   109         .mouseover(function() {
   119     _volctrl.mouseover(function() {
   115     _volctrl.mouseover(function() {
   120         _volctrl.show();
   116         _volctrl.show();
   121     }).mouseout(function() {
   117     }).mouseout(function() {
   122         _volctrl.hide();
   118         _volctrl.hide();
   123     });
   119     });
   124   
   120     
       
   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     }
   125     
   136     
   126     // Allow Volume Cursor Dragging
   137     // Allow Volume Cursor Dragging
   127     this.$volumeBar.slider({
   138     this.$volumeBar.slider({
   128         slide: function(event, ui) {
   139         slide: function(event, ui) {
   129             _this.$volumeBar.attr("title",_this.l10n.volume+': ' + ui.value + '%');
   140             _this.$volumeBar.attr("title",_this.l10n.volume+': ' + ui.value + '%');
   130             _this.player.popcorn.volume(ui.value / 100);
   141             _this.media.setVolume(ui.value / 100);
   131         },
   142         },
   132         stop: this.functionWrapper("volumeUpdater")
   143         stop: this.functionWrapper("volumeUpdater")
   133     });
   144     });
   134 
   145 
   135     // trigger an IriSP.Player.MouseOver to the widgets that are interested (i.e : sliderWidget)
   146     // trigger an IriSP.Player.MouseOver to the widgets that are interested (i.e : sliderWidget)
   136     this.$.hover(
   147     this.$.hover(
   137         function() {
   148         function() {
   138             _this.player.popcorn.trigger("IriSP.Player.MouseOver");
   149             _this.player.trigger("Player.MouseOver");
   139         }, 
   150         }, 
   140         function() {
   151         function() {
   141             _this.player.popcorn.trigger("IriSP.Player.MouseOut");
   152             _this.player.trigger("Player.MouseOut");
   142         });
   153         });
   143     /* some players - including jwplayer - save the state of the mute button between sessions */
   154     
   144 
   155     this.timeDisplayUpdater(new IriSP.Model.Time(0));
   145     window.setTimeout(this.functionWrapper("volumeUpdater"), 1000);
   156     
       
   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     });
   146    
   171    
   147 };
   172 };
   148 
   173 
   149 /* Update the elasped time div */
   174 /* Update the elasped time div */
   150 IriSP.Widgets.Controller.prototype.timeDisplayUpdater = function() {
   175 IriSP.Widgets.Controller.prototype.timeDisplayUpdater = function(_time) {
   151     var _curTime = this.player.popcorn.roundTime();
       
   152     if (typeof this._previousSecond !== "undefined" && _curTime === this._previousSecond) {
       
   153         return;
       
   154     }
       
   155   
   176   
   156     // we get it at each call because it may change.
   177     // we get it at each call because it may change.
   157     var _totalTime = this.source.getDuration(),
   178     var _totalTime = this.media.duration;
   158         _elapsedTime = new IriSP.Model.Time();
   179     this.$.find(".Ldt-Ctrl-Time-Elapsed").html(_time.toString());
   159         
       
   160     _elapsedTime.setSeconds(_curTime);
       
   161   
       
   162     this.$.find(".Ldt-Ctrl-Time-Elapsed").html(_elapsedTime.toString());
       
   163     this.$.find(".Ldt-Ctrl-Time-Total").html(_totalTime.toString());
   180     this.$.find(".Ldt-Ctrl-Time-Total").html(_totalTime.toString());
   164     this._previousSecond = _curTime;
       
   165 };
   181 };
   166 
   182 
   167 /* update the icon of the button - separate function from playHandler
   183 /* update the icon of the button - separate function from playHandler
   168    because in some cases (for instance, when the user directly clicks on
   184    because in some cases (for instance, when the user directly clicks on
   169    the jwplayer window) we have to change the icon without playing/pausing
   185    the jwplayer window) we have to change the icon without playing/pausing
   170 */
   186 */
   171 IriSP.Widgets.Controller.prototype.playButtonUpdater = function() {
   187 IriSP.Widgets.Controller.prototype.playButtonUpdater = function() {
   172     
   188     if (this.media.getPaused()) {
   173     var status = this.player.popcorn.media.paused;
       
   174   
       
   175     if (status) {
       
   176     /* the background sprite is changed by adding/removing the correct classes */
   189     /* the background sprite is changed by adding/removing the correct classes */
   177         this.$playButton
   190         this.$playButton
   178             .attr("title", this.l10n.play)
   191             .attr("title", this.l10n.play)
   179             .removeClass("Ldt-Ctrl-Play-PauseState")
   192             .removeClass("Ldt-Ctrl-Play-PauseState")
   180             .addClass("Ldt-Ctrl-Play-PlayState");
   193             .addClass("Ldt-Ctrl-Play-PlayState");
   186     }
   199     }
   187 };
   200 };
   188 
   201 
   189 
   202 
   190 IriSP.Widgets.Controller.prototype.playHandler = function() {
   203 IriSP.Widgets.Controller.prototype.playHandler = function() {
   191     
   204     if (this.media.getPaused()) {        
   192     var status = this.player.popcorn.media.paused;
   205         this.media.play();
   193   
       
   194     if (status) {        
       
   195         this.player.popcorn.play();   
       
   196     } else {
   206     } else {
   197         this.player.popcorn.pause();
   207         this.media.pause();
   198     }  
   208     }  
   199 };
   209 };
   200 
   210 
   201 IriSP.Widgets.Controller.prototype.muteHandler = function() {
   211 IriSP.Widgets.Controller.prototype.muteHandler = function() {
   202     this.player.popcorn.muted(!this.player.popcorn.muted());
   212     this.media.setMuted(!this.media.getMuted());
   203 };
   213 };
   204 
   214 
   205 IriSP.Widgets.Controller.prototype.volumeUpdater = function() {
   215 IriSP.Widgets.Controller.prototype.volumeUpdater = function() {
   206     var _muted = this.player.popcorn.muted(),
   216     var _muted = this.media.getMuted(),
   207         _vol = this.player.popcorn.volume();
   217         _vol = this.media.getVolume();
   208     if (_vol === false) {
   218     if (_vol === false) {
   209         _vol = .5;
   219         _vol = .5;
   210     }
   220     }
   211     var _soundCtl = this.$.find(".Ldt-Ctrl-Sound");
   221     var _soundCtl = this.$.find(".Ldt-Ctrl-Sound");
   212     _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
   222     _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
   213     if (_muted) {        
   223     if (_muted) {        
   214         _soundCtl.attr("title", this.l10n.unmute)
   224         _soundCtl.attr("title", this.l10n.unmute)
   215             .addClass("Ldt-Ctrl-Sound-Mute");    
   225             .addClass("Ldt-Ctrl-Sound-Mute");    
   216     } else {
   226     } else {
   217         _soundCtl.attr("title", this.l10n.mute)
   227         _soundCtl.attr("title", this.l10n.mute)
   218             .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" )
   228             .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" );
   219     }
   229     }
   220     this.$volumeBar.slider("value", _muted ? 0 : 100 * _vol);
   230     this.$volumeBar.slider("value", _muted ? 0 : 100 * _vol);
   221 };
   231 };
   222 
   232 
   223 IriSP.Widgets.Controller.prototype.showSearchBlock = function() {
   233 IriSP.Widgets.Controller.prototype.showSearchBlock = function() {
   224     this.$searchBlock.show("blind", { direction: "horizontal"}, 100);
   234     this.$searchBlock.animate({ width:"160px" }, 200);
   225     this.$searchInput.css('background-color','#fff');
   235     this.$searchInput.css('background-color','#fff');
   226    
       
   227     this.$searchInput.focus();
   236     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._searchLastValue = this.$searchInput.val();
   240     this.$searchBlock.animate( { width: 0 }, 200);
   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");
       
   247 };
   241 };
   248 
   242 
   249 /** react to clicks on the search button */
   243 /** react to clicks on the search button */
   250 IriSP.Widgets.Controller.prototype.searchButtonHandler = function() {
   244 IriSP.Widgets.Controller.prototype.searchButtonHandler = function() {
   251     if ( this.$searchBlock.is(":hidden") ) {
   245     if ( !this.$searchBlock.width() ) {
   252         this.showSearchBlock();
   246         this.showSearchBlock();
   253         this.$searchInput.val(this._searchLastValue);      
   247         var _val = this.$searchInput.val();
   254         this.player.popcorn.trigger("IriSP.search", this._searchLastValue); // trigger the search to make it more natural.
   248         if (_val) {
       
   249             this.source.getAnnotations().search(_val);
       
   250         }
   255 	} else {
   251 	} else {
   256         this.hideSearchBlock();
   252         this.hideSearchBlock();
   257     }
   253     }
   258 };
   254 };
   259 
   255 
   260 /** this handler is called whenever the content of the search
   256 /** this handler is called whenever the content of the search
   261    field changes */
   257    field changes */
   262 IriSP.Widgets.Controller.prototype.searchHandler = function() {
   258 IriSP.Widgets.Controller.prototype.searchHandler = function() {
   263     this._searchLastValue = this.$searchInput.val();
   259     if ( !this.$searchBlock.width() ) {
       
   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 (this._searchLastValue == "") {
   267     if (_val !== this.lastSearchValue) {
   268         this.player.popcorn.trigger("IriSP.search.cleared");
   268         if (_val) {
   269         this.$searchInput.css('background-color','');
   269             this.source.getAnnotations().search(_val);
   270     } else {
   270         } else {
   271         this.player.popcorn.trigger("IriSP.search", this._searchLastValue);
   271             this.source.getAnnotations().trigger("clear-search");
   272     }
   272             this.$searchInput.css('background-color','');
   273 };
   273         }
   274 
   274     }
   275 /**
   275     this.lastSearchValue = _val;
   276   handler for the IriSP.search.found message, which is sent by some views when they
   276 };
   277   highlight a match.
   277 
   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