[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-templates/bp-nouveau/js/ -> buddypress-messages.js (source)

   1  /* global wp, bp, BP_Nouveau, _, Backbone, tinymce, tinyMCE */
   2  /* jshint devel: true */
   3  /* @version 3.1.0 */
   4  window.wp = window.wp || {};
   5  window.bp = window.bp || {};
   6  
   7  ( function( exports, $ ) {
   8  
   9      // Bail if not set
  10      if ( typeof BP_Nouveau === 'undefined' ) {
  11          return;
  12      }
  13  
  14      _.extend( bp, _.pick( wp, 'Backbone', 'ajax', 'template' ) );
  15  
  16      bp.Models      = bp.Models || {};
  17      bp.Collections = bp.Collections || {};
  18      bp.Views       = bp.Views || {};
  19  
  20      bp.Nouveau = bp.Nouveau || {};
  21  
  22      /**
  23       * [Nouveau description]
  24       * @type {Object}
  25       */
  26      bp.Nouveau.Messages = {
  27          /**
  28           * [start description]
  29           * @return {[type]} [description]
  30           */
  31          start: function() {
  32              this.views    = new Backbone.Collection();
  33              this.threads  = new bp.Collections.Threads();
  34              this.messages = new bp.Collections.Messages();
  35              this.router   = new bp.Nouveau.Messages.Router();
  36              this.box      = 'inbox';
  37  
  38              this.setupNav();
  39  
  40              Backbone.history.start( {
  41                  pushState: true,
  42                  root: BP_Nouveau.messages.rootUrl
  43              } );
  44          },
  45  
  46          setupNav: function() {
  47              var self = this;
  48  
  49              // First adapt the compose nav
  50              $( '#compose-personal-li' ).addClass( 'last' );
  51  
  52              // Then listen to nav click and load the appropriate view
  53              $( '#subnav a' ).on( 'click', function( event ) {
  54                  event.preventDefault();
  55  
  56                  var view_id = $( event.target ).prop( 'id' );
  57  
  58                  // Remove the editor to be sure it will be added dynamically later
  59                  self.removeTinyMCE();
  60  
  61                  // The compose view is specific (toggle behavior)
  62                  if ( 'compose' === view_id ) {
  63                      // If it exists, it means the user wants to remove it
  64                      if ( ! _.isUndefined( self.views.get( 'compose' ) ) ) {
  65                          var form = self.views.get( 'compose' );
  66                          form.get( 'view' ).remove();
  67                          self.views.remove( { id: 'compose', view: form } );
  68  
  69                          // Back to inbox
  70                          if ( 'single' === self.box ) {
  71                              self.box = 'inbox';
  72                          }
  73  
  74                          // Navigate back to current box
  75                          self.router.navigate( self.box + '/', { trigger: true } );
  76  
  77                      // Otherwise load it
  78                      } else {
  79                          self.router.navigate( 'compose/', { trigger: true } );
  80                      }
  81  
  82                  // Other views are classic.
  83                  } else {
  84  
  85                      if ( self.box !== view_id || ! _.isUndefined( self.views.get( 'compose' ) ) ) {
  86                          self.clearViews();
  87  
  88                          self.router.navigate( view_id + '/', { trigger: true } );
  89                      }
  90                  }
  91              } );
  92          },
  93  
  94          removeTinyMCE: function() {
  95              if ( typeof tinymce !== 'undefined' ) {
  96                  var editor = tinymce.get( 'message_content' );
  97  
  98                  if ( editor !== null ) {
  99                      tinymce.EditorManager.execCommand( 'mceRemoveEditor', true, 'message_content' );
 100                  }
 101              }
 102          },
 103  
 104          tinyMCEinit: function() {
 105              if ( typeof window.tinyMCE === 'undefined' || window.tinyMCE.activeEditor === null || typeof window.tinyMCE.activeEditor === 'undefined' ) {
 106                  return;
 107              } else {
 108                  // Mentions isn't available, so bail.
 109                  if ( _.isEmpty( exports.mentions ) ) {
 110                      return;
 111                  }
 112  
 113                  $( window.tinyMCE.activeEditor.contentDocument.activeElement )
 114                      .atwho( 'setIframe', $( '#message_content_ifr' )[0] )
 115                      .bp_mentions( {
 116                          data: [],
 117                          suffix: ' '
 118                      } );
 119              }
 120          },
 121  
 122          removeFeedback: function() {
 123              var feedback;
 124  
 125              if ( ! _.isUndefined( this.views.get( 'feedback' ) ) ) {
 126                  feedback = this.views.get( 'feedback' );
 127                  feedback.get( 'view' ).remove();
 128                  this.views.remove( { id: 'feedback', view: feedback } );
 129              }
 130          },
 131  
 132          displayFeedback: function( message, type ) {
 133              var feedback;
 134  
 135              // Make sure to remove the feedbacks
 136              this.removeFeedback();
 137  
 138              if ( ! message ) {
 139                  return;
 140              }
 141  
 142              feedback = new bp.Views.Feedback( {
 143                  value: message,
 144                  type:  type || 'info'
 145              } );
 146  
 147              this.views.add( { id: 'feedback', view: feedback } );
 148  
 149              feedback.inject( '.bp-messages-feedback' );
 150          },
 151  
 152          clearViews: function() {
 153              // Clear views
 154              if ( ! _.isUndefined( this.views.models ) ) {
 155                  _.each( this.views.models, function( model ) {
 156                      model.get( 'view' ).remove();
 157                  }, this );
 158  
 159                  this.views.reset();
 160              }
 161          },
 162  
 163          composeView: function() {
 164              // Remove all existing views.
 165              this.clearViews();
 166  
 167              // Create the loop view
 168              var form = new bp.Views.messageForm( {
 169                  model: new bp.Models.Message()
 170              } );
 171  
 172              this.views.add( { id: 'compose', view: form } );
 173  
 174              form.inject( '.bp-messages-content' );
 175          },
 176  
 177          threadsView: function() {
 178              // Activate the appropriate nav
 179              $( '#subnav ul li' ).each( function( l, li ) {
 180                  $( li ).removeClass( 'current selected' );
 181              } );
 182              $( '#subnav a#' + this.box ).closest( 'li' ).addClass( 'current selected' );
 183  
 184              // Create the loop view
 185              var threads_list = new bp.Views.userThreads( { collection: this.threads, box: this.box } );
 186  
 187              this.views.add( { id: 'threads', view: threads_list } );
 188  
 189              threads_list.inject( '.bp-messages-content' );
 190  
 191              // Attach filters
 192              this.displayFilters( this.threads );
 193          },
 194  
 195          displayFilters: function( collection ) {
 196              var filters_view;
 197  
 198              // Create the model
 199              this.filters = new Backbone.Model( {
 200                  'page'         : 1,
 201                  'total_page'   : 0,
 202                  'search_terms' : '',
 203                  'box'          : this.box
 204              } );
 205  
 206              // Use it in the filters viex
 207              filters_view = new bp.Views.messageFilters( { model: this.filters, threads: collection } );
 208  
 209              this.views.add( { id: 'filters', view: filters_view } );
 210  
 211              filters_view.inject( '.bp-messages-filters' );
 212          },
 213  
 214          singleView: function( thread ) {
 215              // Remove all existing views.
 216              this.clearViews();
 217  
 218              this.box = 'single';
 219  
 220              // Create the single thread view
 221              var single_thread = new bp.Views.userMessages( { collection: this.messages, thread: thread } );
 222  
 223              this.views.add( { id: 'single', view: single_thread } );
 224  
 225              single_thread.inject( '.bp-messages-content' );
 226          }
 227      };
 228  
 229      bp.Models.Message = Backbone.Model.extend( {
 230          defaults: {
 231              send_to         : [],
 232              subject         : '',
 233              message_content : '',
 234              meta            : {}
 235          },
 236  
 237          sendMessage: function() {
 238              if ( true === this.get( 'sending' ) ) {
 239                  return;
 240              }
 241  
 242              this.set( 'sending', true, { silent: true } );
 243  
 244              var sent = bp.ajax.post( 'messages_send_message', _.extend(
 245                  {
 246                      nonce: BP_Nouveau.messages.nonces.send
 247                  },
 248                  this.attributes
 249              ) );
 250  
 251              this.set( 'sending', false, { silent: true } );
 252  
 253              return sent;
 254          }
 255      } );
 256  
 257      bp.Models.Thread = Backbone.Model.extend( {
 258          defaults: {
 259              id            : 0,
 260              message_id    : 0,
 261              subject       : '',
 262              excerpt       : '',
 263              content       : '',
 264              unread        : true,
 265              sender_name   : '',
 266              sender_link   : '',
 267              sender_avatar : '',
 268              count         : 0,
 269              date          : 0,
 270              display_date  : '',
 271              recipients    : []
 272          },
 273  
 274          updateReadState: function( options ) {
 275              options = options || {};
 276              options.data = _.extend(
 277                  _.pick( this.attributes, ['id', 'message_id'] ),
 278                  {
 279                      action : 'messages_thread_read',
 280                      nonce  : BP_Nouveau.nonces.messages
 281                  }
 282              );
 283  
 284              return bp.ajax.send( options );
 285          }
 286      } );
 287  
 288      bp.Models.messageThread = Backbone.Model.extend( {
 289          defaults: {
 290              id            : 0,
 291              content       : '',
 292              sender_id     : 0,
 293              sender_name   : '',
 294              sender_link   : '',
 295              sender_avatar : '',
 296              date          : 0,
 297              display_date  : ''
 298          }
 299      } );
 300  
 301      bp.Collections.Threads = Backbone.Collection.extend( {
 302          model: bp.Models.Thread,
 303  
 304          initialize : function() {
 305              this.options = { page: 1, total_page: 0 };
 306          },
 307  
 308          sync: function( method, model, options ) {
 309              options         = options || {};
 310              options.context = this;
 311              options.data    = options.data || {};
 312  
 313              // Add generic nonce
 314              options.data.nonce = BP_Nouveau.nonces.messages;
 315  
 316              if ( 'read' === method ) {
 317                  options.data = _.extend( options.data, {
 318                      action: 'messages_get_user_message_threads'
 319                  } );
 320  
 321                  return bp.ajax.send( options );
 322              }
 323          },
 324  
 325          parse: function( resp ) {
 326  
 327              if ( ! _.isArray( resp.threads ) ) {
 328                  resp.threads = [resp.threads];
 329              }
 330  
 331              _.each( resp.threads, function( value, index ) {
 332                  if ( _.isNull( value ) ) {
 333                      return;
 334                  }
 335  
 336                  resp.threads[index].id            = value.id;
 337                  resp.threads[index].message_id    = value.message_id;
 338                  resp.threads[index].subject       = value.subject;
 339                  resp.threads[index].excerpt       = value.excerpt;
 340                  resp.threads[index].content       = value.content;
 341                  resp.threads[index].unread        = value.unread;
 342                  resp.threads[index].sender_name   = value.sender_name;
 343                  resp.threads[index].sender_link   = value.sender_link;
 344                  resp.threads[index].sender_avatar = value.sender_avatar;
 345                  resp.threads[index].count         = value.count;
 346                  resp.threads[index].date          = new Date( value.date );
 347                  resp.threads[index].display_date  = value.display_date;
 348                  resp.threads[index].recipients    = value.recipients;
 349                  resp.threads[index].star_link     = value.star_link;
 350                  resp.threads[index].is_starred    = value.is_starred;
 351              } );
 352  
 353              if ( ! _.isUndefined( resp.meta ) ) {
 354                  this.options.page       = resp.meta.page;
 355                  this.options.total_page = resp.meta.total_page;
 356              }
 357  
 358              if ( bp.Nouveau.Messages.box ) {
 359                  this.options.box = bp.Nouveau.Messages.box;
 360              }
 361  
 362              if ( ! _.isUndefined( resp.extraContent ) ) {
 363                  _.extend( this.options, _.pick( resp.extraContent, [
 364                      'beforeLoop',
 365                      'afterLoop'
 366                  ] ) );
 367              }
 368  
 369              return resp.threads;
 370          },
 371  
 372          doAction: function( action, ids, options ) {
 373              options         = options || {};
 374              options.context = this;
 375              options.data    = options.data || {};
 376  
 377              options.data = _.extend( options.data, {
 378                  action: 'messages_' + action,
 379                  nonce : BP_Nouveau.nonces.messages,
 380                  id    : ids
 381              } );
 382  
 383              return bp.ajax.send( options );
 384          }
 385      } );
 386  
 387      bp.Collections.Messages = Backbone.Collection.extend( {
 388          model: bp.Models.messageThread,
 389          options: {},
 390  
 391          sync: function( method, model, options ) {
 392              options         = options || {};
 393              options.context = this;
 394              options.data    = options.data || {};
 395  
 396              // Add generic nonce
 397              options.data.nonce = BP_Nouveau.nonces.messages;
 398  
 399              if ( 'read' === method ) {
 400                  options.data = _.extend( options.data, {
 401                      action: 'messages_get_thread_messages'
 402                  } );
 403  
 404                  return bp.ajax.send( options );
 405              }
 406  
 407              if ( 'create' === method ) {
 408                  options.data = _.extend( options.data, {
 409                      action : 'messages_send_reply',
 410                      nonce  : BP_Nouveau.messages.nonces.send
 411                  }, model || {} );
 412  
 413                  return bp.ajax.send( options );
 414              }
 415          },
 416  
 417          parse: function( resp ) {
 418  
 419              if ( ! _.isArray( resp.messages ) ) {
 420                  resp.messages = [resp.messages];
 421              }
 422  
 423              _.each( resp.messages, function( value, index ) {
 424                  if ( _.isNull( value ) ) {
 425                      return;
 426                  }
 427  
 428                  resp.messages[index].id            = value.id;
 429                  resp.messages[index].content       = value.content;
 430                  resp.messages[index].sender_id     = value.sender_id;
 431                  resp.messages[index].sender_name   = value.sender_name;
 432                  resp.messages[index].sender_link   = value.sender_link;
 433                  resp.messages[index].sender_avatar = value.sender_avatar;
 434                  resp.messages[index].date          = new Date( value.date );
 435                  resp.messages[index].display_date  = value.display_date;
 436                  resp.messages[index].star_link     = value.star_link;
 437                  resp.messages[index].is_starred    = value.is_starred;
 438              } );
 439  
 440              if ( ! _.isUndefined( resp.thread ) ) {
 441                  this.options.thread_id      = resp.thread.id;
 442                  this.options.thread_subject = resp.thread.subject;
 443                  this.options.recipients     = resp.thread.recipients;
 444              }
 445  
 446              return resp.messages;
 447          }
 448      } );
 449  
 450      // Extend wp.Backbone.View with .prepare() and .inject()
 451      bp.Nouveau.Messages.View = bp.Backbone.View.extend( {
 452          inject: function( selector ) {
 453              this.render();
 454              $(selector).html( this.el );
 455              this.views.ready();
 456          },
 457  
 458          prepare: function() {
 459              if ( ! _.isUndefined( this.model ) && _.isFunction( this.model.toJSON ) ) {
 460                  return this.model.toJSON();
 461              } else {
 462                  return {};
 463              }
 464          }
 465      } );
 466  
 467      // Feedback view
 468      bp.Views.Feedback = bp.Nouveau.Messages.View.extend( {
 469          tagName: 'div',
 470          className: 'bp-messages bp-user-messages-feedback',
 471          template  : bp.template( 'bp-messages-feedback' ),
 472  
 473          initialize: function() {
 474              this.model = new Backbone.Model( {
 475                  type: this.options.type || 'info',
 476                  message: this.options.value
 477              } );
 478          }
 479      } );
 480  
 481      // Hook view
 482      bp.Views.Hook = bp.Nouveau.Messages.View.extend( {
 483          tagName: 'div',
 484          template  : bp.template( 'bp-messages-hook' ),
 485  
 486          initialize: function() {
 487              this.model = new Backbone.Model( {
 488                  extraContent: this.options.extraContent
 489              } );
 490  
 491              this.el.className = 'bp-messages-hook';
 492  
 493              if ( this.options.className ) {
 494                  this.el.className += ' ' + this.options.className;
 495              }
 496          }
 497      } );
 498  
 499      bp.Views.messageEditor = bp.Nouveau.Messages.View.extend( {
 500          template  : bp.template( 'bp-messages-editor' ),
 501  
 502          initialize: function() {
 503              this.on( 'ready', this.activateTinyMce, this );
 504          },
 505  
 506          activateTinyMce: function() {
 507              if ( typeof tinymce !== 'undefined' ) {
 508                  tinymce.EditorManager.execCommand( 'mceAddEditor', true, 'message_content' );
 509              }
 510          }
 511      } );
 512  
 513      bp.Views.messageForm = bp.Nouveau.Messages.View.extend( {
 514          tagName   : 'form',
 515          id        : 'send_message_form',
 516          className : 'standard-form',
 517          template  : bp.template( 'bp-messages-form' ),
 518  
 519          events: {
 520              'click #bp-messages-send'  : 'sendMessage',
 521              'click #bp-messages-reset' : 'resetForm'
 522          },
 523  
 524          initialize: function() {
 525              // Clone the model to set the resetted one
 526              this.resetModel = this.model.clone();
 527  
 528              // Add the editor view
 529              this.views.add( '#bp-message-content', new bp.Views.messageEditor() );
 530  
 531              this.model.on( 'change', this.resetFields, this );
 532  
 533              // Activate bp_mentions
 534              this.on( 'ready', this.addMentions, this );
 535          },
 536  
 537          addMentions: function() {
 538              var sendToInput = $( this.el ).find( '#send-to-input' ),
 539                  mention = bp.Nouveau.getLinkParams( null, 'r' ) || null;
 540  
 541              // Add autocomplete to send_to field
 542              sendToInput.bp_mentions( {
 543                  data: [],
 544                  suffix: ' '
 545              } );
 546  
 547              // Check for mention
 548              if ( ! _.isNull( mention ) ) {
 549                  sendToInput.val( '@' + _.escape( mention ) + ' ' );
 550                  sendToInput.focus();
 551              }
 552          },
 553  
 554          resetFields: function( model ) {
 555              // Clean inputs
 556              _.each( model.previousAttributes(), function( value, input ) {
 557                  if ( 'message_content' === input ) {
 558                      // tinyMce
 559                      if ( undefined !== tinyMCE.activeEditor && null !== tinyMCE.activeEditor ) {
 560                          tinyMCE.activeEditor.setContent( '' );
 561                      }
 562  
 563                  // All except meta or empty value
 564                  } else if ( 'meta' !== input && false !== value ) {
 565                      $( 'input[name="' + input + '"]' ).val( '' );
 566                  }
 567              } );
 568  
 569              // Listen to this to eventually reset your custom inputs.
 570              $( this.el ).trigger( 'message:reset', _.pick( model.previousAttributes(), 'meta' ) );
 571          },
 572  
 573          sendMessage: function( event ) {
 574              var meta = {}, errors = [], self = this;
 575              event.preventDefault();
 576  
 577              bp.Nouveau.Messages.removeFeedback();
 578  
 579              // Set the content and meta
 580              _.each( this.$el.serializeArray(), function( pair ) {
 581                  pair.name = pair.name.replace( '[]', '' );
 582  
 583                  // Group extra fields in meta
 584                  if ( -1 === _.indexOf( ['send_to', 'subject', 'message_content'], pair.name ) ) {
 585                      if ( _.isUndefined( meta[ pair.name ] ) ) {
 586                          meta[ pair.name ] = pair.value;
 587                      } else {
 588                          if ( ! _.isArray( meta[ pair.name ] ) ) {
 589                              meta[ pair.name ] = [ meta[ pair.name ] ];
 590                          }
 591  
 592                          meta[ pair.name ].push( pair.value );
 593                      }
 594  
 595                  // Prepare the core model
 596                  } else {
 597                      // Send to
 598                      if ( 'send_to' === pair.name ) {
 599                          var usernames = pair.value.match( /(^|[^@\w\-])@([a-zA-Z0-9_\-]{1,50})\b/g );
 600  
 601                          if ( ! usernames ) {
 602                              errors.push( 'send_to' );
 603                          } else {
 604                              usernames = usernames.map( function( username ) {
 605                                  username = $.trim( username );
 606                                  return username;
 607                              } );
 608  
 609                              if ( ! usernames || ! $.isArray( usernames ) ) {
 610                                  errors.push( 'send_to' );
 611                              }
 612  
 613                              this.model.set( 'send_to', usernames, { silent: true } );
 614                          }
 615  
 616                      // Subject and content
 617                      } else {
 618                          // Message content
 619                          if ( 'message_content' === pair.name && undefined !== tinyMCE.activeEditor ) {
 620                              pair.value = tinyMCE.activeEditor.getContent();
 621                          }
 622  
 623                          if ( ! pair.value ) {
 624                              errors.push( pair.name );
 625                          } else {
 626                              this.model.set( pair.name, pair.value, { silent: true } );
 627                          }
 628                      }
 629                  }
 630  
 631              }, this );
 632  
 633              if ( errors.length ) {
 634                  var feedback = '';
 635                  _.each( errors, function( e ) {
 636                      feedback += BP_Nouveau.messages.errors[ e ] + '<br/>';
 637                  } );
 638  
 639                  bp.Nouveau.Messages.displayFeedback( feedback, 'error' );
 640                  return;
 641              }
 642  
 643              // Set meta
 644              this.model.set( 'meta', meta, { silent: true } );
 645  
 646              // Send the message.
 647              this.model.sendMessage().done( function( response ) {
 648                  // Reset the model
 649                  self.model.set( self.resetModel );
 650  
 651                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
 652  
 653                  // Remove tinyMCE
 654                  bp.Nouveau.Messages.removeTinyMCE();
 655  
 656                  // Remove the form view
 657                  var form = bp.Nouveau.Messages.views.get( 'compose' );
 658                  form.get( 'view' ).remove();
 659                  bp.Nouveau.Messages.views.remove( { id: 'compose', view: form } );
 660  
 661                  bp.Nouveau.Messages.router.navigate( 'sentbox/', { trigger: true } );
 662              } ).fail( function( response ) {
 663                  if ( response.feedback ) {
 664                      bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
 665                  }
 666              } );
 667          },
 668  
 669          resetForm: function( event ) {
 670              event.preventDefault();
 671  
 672              this.model.set( this.resetModel );
 673          }
 674      } );
 675  
 676      bp.Views.userThreads = bp.Nouveau.Messages.View.extend( {
 677          tagName   : 'div',
 678  
 679          events: {
 680              'click .subject' : 'changePreview'
 681          },
 682  
 683          initialize: function() {
 684              var Views = [
 685                  new bp.Nouveau.Messages.View( { tagName: 'ul', id: 'message-threads', className: 'message-lists' } ),
 686                  new bp.Views.previewThread( { collection: this.collection } )
 687              ];
 688  
 689              _.each( Views, function( view ) {
 690                  this.views.add( view );
 691              }, this );
 692  
 693              // Load threads for the active view
 694              this.requestThreads();
 695  
 696              this.collection.on( 'reset', this.cleanContent, this );
 697              this.collection.on( 'add', this.addThread, this );
 698          },
 699  
 700          requestThreads: function() {
 701              this.collection.reset();
 702  
 703              bp.Nouveau.Messages.displayFeedback( BP_Nouveau.messages.loading, 'loading' );
 704  
 705              this.collection.fetch( {
 706                  data    : _.pick( this.options, 'box' ),
 707                  success : _.bind( this.threadsFetched, this ),
 708                  error   : this.threadsFetchError
 709              } );
 710          },
 711  
 712          threadsFetched: function() {
 713              bp.Nouveau.Messages.removeFeedback();
 714  
 715              // Display the bp_after_member_messages_loop hook.
 716              if ( this.collection.options.afterLoop ) {
 717                  this.views.add( new bp.Views.Hook( { extraContent: this.collection.options.afterLoop, className: 'after-messages-loop' } ), { at: 1 } );
 718              }
 719  
 720              // Display the bp_before_member_messages_loop hook.
 721              if ( this.collection.options.beforeLoop ) {
 722                  this.views.add( new bp.Views.Hook( { extraContent: this.collection.options.beforeLoop, className: 'before-messages-loop' } ), { at: 0 } );
 723              }
 724  
 725              // Inform the user about how to use the UI.
 726              bp.Nouveau.Messages.displayFeedback( BP_Nouveau.messages.howto, 'info' );
 727          },
 728  
 729          threadsFetchError: function( collection, response ) {
 730              bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
 731          },
 732  
 733          cleanContent: function() {
 734              _.each( this.views._views['#message-threads'], function( view ) {
 735                  view.remove();
 736              } );
 737          },
 738  
 739          addThread: function( thread ) {
 740              var selected = this.collection.findWhere( { active: true } );
 741  
 742              if ( _.isUndefined( selected ) ) {
 743                  thread.set( 'active', true );
 744              }
 745  
 746              this.views.add( '#message-threads', new bp.Views.userThread( { model: thread } ) );
 747          },
 748  
 749          setActiveThread: function( active ) {
 750              if ( ! active ) {
 751                  return;
 752              }
 753  
 754              _.each( this.collection.models, function( thread ) {
 755                  if ( thread.id === active ) {
 756                      thread.set( 'active', true );
 757                  } else {
 758                      thread.unset( 'active' );
 759                  }
 760              }, this );
 761          },
 762  
 763          changePreview: function( event ) {
 764              var target = $( event.currentTarget );
 765  
 766              event.preventDefault();
 767              bp.Nouveau.Messages.removeFeedback();
 768  
 769              // If the click is done on an active conversation, open it.
 770              if ( target.closest( '.thread-item' ).hasClass( 'selected' ) ) {
 771                  bp.Nouveau.Messages.router.navigate(
 772                      'view/' + target.closest( '.thread-content' ).data( 'thread-id' ) + '/',
 773                      { trigger: true }
 774                  );
 775  
 776              // Otherwise activate the conversation and display its preview.
 777              } else {
 778                  this.setActiveThread( target.closest( '.thread-content' ).data( 'thread-id' ) );
 779  
 780                  $( '.message-action-view' ).focus();
 781              }
 782          }
 783      } );
 784  
 785      bp.Views.userThread = bp.Nouveau.Messages.View.extend( {
 786          tagName   : 'li',
 787          template  : bp.template( 'bp-messages-thread' ),
 788          className : 'thread-item',
 789  
 790          events: {
 791              'click .message-check' : 'singleSelect'
 792          },
 793  
 794          initialize: function() {
 795              if ( this.model.get( 'active' ) ) {
 796                  this.el.className += ' selected';
 797              }
 798  
 799              if ( this.model.get( 'unread' ) ) {
 800                  this.el.className += ' unread';
 801              }
 802  
 803              if ( 'sentbox' === bp.Nouveau.Messages.box ) {
 804                  var recipientsCount = this.model.get( 'recipients' ).length, toOthers = '';
 805  
 806                  if ( 2 === recipientsCount ) {
 807                      toOthers = BP_Nouveau.messages.toOthers.one;
 808                  } else if ( 2 < recipientsCount ) {
 809                      toOthers = BP_Nouveau.messages.toOthers.more.replace( '%d', Number( recipientsCount - 1 ) );
 810                  }
 811  
 812                  this.model.set( {
 813                      recipientsCount: recipientsCount,
 814                      toOthers: toOthers
 815                  }, { silent: true } );
 816              } else if ( this.model.get( 'recipientsCount' )  ) {
 817                  this.model.unset( 'recipientsCount', { silent: true } );
 818              }
 819  
 820              this.model.on( 'change:active', this.toggleClass, this );
 821              this.model.on( 'change:unread', this.updateReadState, this );
 822              this.model.on( 'change:checked', this.bulkSelect, this );
 823              this.model.on( 'remove', this.cleanView, this );
 824          },
 825  
 826          toggleClass: function( model ) {
 827              if ( true === model.get( 'active' ) ) {
 828                  $( this.el ).addClass( 'selected' );
 829              } else {
 830                  $( this.el ).removeClass( 'selected' );
 831              }
 832          },
 833  
 834          updateReadState: function( model, state ) {
 835              if ( false === state ) {
 836                  $( this.el ).removeClass( 'unread' );
 837              } else {
 838                  $( this.el ).addClass( 'unread' );
 839              }
 840          },
 841  
 842          bulkSelect: function( model ) {
 843              if ( $( '#bp-message-thread-' + model.get( 'id' ) ).length ) {
 844                  $( '#bp-message-thread-' + model.get( 'id' ) ).prop( 'checked',model.get( 'checked' ) );
 845              }
 846          },
 847  
 848          singleSelect: function( event ) {
 849              var isChecked = $( event.currentTarget ).prop( 'checked' );
 850  
 851              // To avoid infinite loops
 852              this.model.set( 'checked', isChecked, { silent: true } );
 853  
 854              var hasChecked = false;
 855  
 856              _.each( this.model.collection.models, function( model ) {
 857                  if ( true === model.get( 'checked' ) ) {
 858                      hasChecked = true;
 859                  }
 860              } );
 861  
 862              if ( hasChecked ) {
 863                  $( '#user-messages-bulk-actions' ).closest( '.bulk-actions-wrap' ).removeClass( 'bp-hide' );
 864  
 865                  // Inform the user about how to use the bulk actions.
 866                  bp.Nouveau.Messages.displayFeedback( BP_Nouveau.messages.howtoBulk, 'info' );
 867              } else {
 868                  $( '#user-messages-bulk-actions' ).closest( '.bulk-actions-wrap' ).addClass( 'bp-hide' );
 869  
 870                  bp.Nouveau.Messages.removeFeedback();
 871              }
 872          },
 873  
 874          cleanView: function() {
 875              this.views.view.remove();
 876          }
 877      } );
 878  
 879      bp.Views.previewThread = bp.Nouveau.Messages.View.extend( {
 880          tagName: 'div',
 881          id: 'thread-preview',
 882          template  : bp.template( 'bp-messages-preview' ),
 883  
 884          events: {
 885              'click .actions button' : 'doAction',
 886              'click .actions a'      : 'doAction'
 887          },
 888  
 889          initialize: function() {
 890              this.collection.on( 'change:active', this.setPreview, this );
 891              this.collection.on( 'change:is_starred', this.updatePreview, this );
 892              this.collection.on( 'reset', this.emptyPreview, this );
 893              this.collection.on( 'remove', this.emptyPreview, this );
 894          },
 895  
 896          render: function() {
 897              // Only render if we have some content to render
 898              if ( _.isUndefined( this.model ) || true !== this.model.get( 'active' ) ) {
 899                  return;
 900              }
 901  
 902              bp.Nouveau.Messages.View.prototype.render.apply( this, arguments );
 903          },
 904  
 905          setPreview: function( model ) {
 906              var self = this;
 907  
 908              this.model = model;
 909  
 910              if ( true === model.get( 'unread' ) ) {
 911                  this.model.updateReadState().done( function() {
 912                      self.model.set( 'unread', false );
 913                  } );
 914              }
 915  
 916              this.render();
 917          },
 918  
 919          updatePreview: function( model ) {
 920              if ( true === model.get( 'active' ) ) {
 921                  this.render();
 922              }
 923          },
 924  
 925          emptyPreview: function() {
 926              $( this.el ).html( '' );
 927          },
 928  
 929          doAction: function( event ) {
 930              var action = $( event.currentTarget ).data( 'bp-action' ), self = this, options = {}, mid,
 931                  feedback = BP_Nouveau.messages.doingAction;
 932  
 933              if ( ! action ) {
 934                  return event;
 935              }
 936  
 937              event.preventDefault();
 938  
 939              var model = this.collection.findWhere( { active: true } );
 940  
 941              if ( ! model.get( 'id' ) ) {
 942                  return;
 943              }
 944  
 945              mid = model.get( 'id' );
 946  
 947              // Open the full conversation
 948              if ( 'view' === action ) {
 949                  bp.Nouveau.Messages.router.navigate(
 950                      'view/' + mid + '/',
 951                      { trigger: true }
 952                  );
 953  
 954                  return;
 955  
 956              // Star/Unstar actions needs to use a specific id and nonce.
 957              } else if ( 'star' === action || 'unstar' === action ) {
 958                  options.data = {
 959                      'star_nonce' : model.get( 'star_nonce' )
 960                  };
 961  
 962                  mid = model.get( 'starred_id' );
 963              }
 964  
 965              if ( ! _.isUndefined( feedback[ action ] ) ) {
 966                  bp.Nouveau.Messages.displayFeedback( feedback[ action ], 'loading' );
 967              }
 968  
 969              this.collection.doAction( action, mid, options ).done( function( response ) {
 970                  // Remove previous feedback.
 971                  bp.Nouveau.Messages.removeFeedback();
 972  
 973                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
 974  
 975                  if ( 'delete' === action || ( 'starred' === self.collection.options.box && 'unstar' === action ) ) {
 976                      // Remove from the list of messages
 977                      self.collection.remove( model.get( 'id' ) );
 978  
 979                      // And Requery
 980                      self.collection.fetch( {
 981                          data : _.pick( self.collection.options, ['box', 'search_terms', 'page'] )
 982                      } );
 983                  } else if ( 'unstar' === action || 'star' === action ) {
 984                      // Update the model attributes--updates the star icon.
 985                      _.each( response.messages, function( updated ) {
 986                          model.set( updated );
 987                      } );
 988                      model.set( _.first( response.messages ) );
 989                  } else if ( response.messages ) {
 990                      model.set( _.first( response.messages ) );
 991                  }
 992              } ).fail( function( response ) {
 993                  // Remove previous feedback.
 994                  bp.Nouveau.Messages.removeFeedback();
 995  
 996                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
 997              } );
 998          }
 999      } );
1000  
1001      bp.Views.Pagination = bp.Nouveau.Messages.View.extend( {
1002          tagName   : 'li',
1003          className : 'last filter',
1004          template  :  bp.template( 'bp-messages-paginate' )
1005      } );
1006  
1007      bp.Views.BulkActions = bp.Nouveau.Messages.View.extend( {
1008          tagName   : 'div',
1009          template  :  bp.template( 'bp-bulk-actions' ),
1010  
1011          events : {
1012              'click #user_messages_select_all' : 'bulkSelect',
1013              'click .bulk-apply'               : 'doBulkAction'
1014          },
1015  
1016          bulkSelect: function( event ) {
1017              var isChecked = $( event.currentTarget ).prop( 'checked' );
1018  
1019              if ( isChecked ) {
1020                  $( this.el ).find( '.bulk-actions-wrap' ).removeClass( 'bp-hide' ).addClass( 'bp-show' );
1021  
1022                  // Inform the user about how to use the bulk actions.
1023                  bp.Nouveau.Messages.displayFeedback( BP_Nouveau.messages.howtoBulk, 'info' );
1024              } else {
1025                  $( this.el ).find( '.bulk-actions-wrap' ).addClass( 'bp-hide' );
1026  
1027                  bp.Nouveau.Messages.removeFeedback();
1028              }
1029  
1030              _.each( this.collection.models, function( model ) {
1031                  model.set( 'checked', isChecked );
1032              } );
1033          },
1034  
1035          doBulkAction: function( event ) {
1036              var self = this, options = {}, ids, attr = 'id',
1037                  feedback = BP_Nouveau.messages.doingAction;
1038  
1039              event.preventDefault();
1040  
1041              var action = $( '#user-messages-bulk-actions' ).val();
1042  
1043              if ( ! action ) {
1044                  return;
1045              }
1046  
1047              var threads    = this.collection.where( { checked: true } );
1048              var thread_ids = _.map( threads, function( model ) {
1049                  return model.get( 'id' );
1050              } );
1051  
1052              // Default to thread ids
1053              ids = thread_ids;
1054  
1055              // We need to get the starred ids
1056              if ( 'star' === action || 'unstar' === action ) {
1057                  ids = _.map( threads, function( model ) {
1058                      return model.get( 'starred_id' );
1059                  } );
1060  
1061                  if ( 1 === ids.length ) {
1062                      options.data = {
1063                          'star_nonce' : threads[0].get( 'star_nonce' )
1064                      };
1065                  }
1066  
1067                  // Map with first message starred in the thread
1068                  attr = 'starred_id';
1069              }
1070  
1071              // Message id to Thread id
1072              var m_tid = _.object( _.map( threads, function (model) {
1073                  return [model.get( attr ), model.get( 'id' )];
1074              } ) );
1075  
1076              if ( ! _.isUndefined( feedback[ action ] ) ) {
1077                  bp.Nouveau.Messages.displayFeedback( feedback[ action ], 'loading' );
1078              }
1079  
1080              this.collection.doAction( action, ids, options ).done( function( response ) {
1081                  // Remove previous feedback.
1082                  bp.Nouveau.Messages.removeFeedback();
1083  
1084                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1085  
1086                  if ( 'delete' === action || ( 'starred' === self.collection.options.box && 'unstar' === action ) ) {
1087                      // Remove from the list of messages
1088                      self.collection.remove( thread_ids );
1089  
1090                      // And Requery
1091                      self.collection.fetch( {
1092                          data : _.pick( self.collection.options, ['box', 'search_terms', 'page'] )
1093                      } );
1094                  } else if ( response.messages ) {
1095                      // Update each model attributes
1096                      _.each( response.messages, function( updated, id ) {
1097                          var model = self.collection.get( m_tid[id] );
1098                          model.set( updated );
1099                      } );
1100                  }
1101              } ).fail( function( response ) {
1102                  // Remove previous feedback.
1103                  bp.Nouveau.Messages.removeFeedback();
1104  
1105                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1106              } );
1107          }
1108      } );
1109  
1110      bp.Views.messageFilters = bp.Nouveau.Messages.View.extend( {
1111          tagName: 'ul',
1112          template:  bp.template( 'bp-messages-filters' ),
1113  
1114          events : {
1115              'search #user_messages_search'      : 'resetSearchTerms',
1116              'submit #user_messages_search_form' : 'setSearchTerms',
1117              'click #bp-messages-next-page'      : 'nextPage',
1118              'click #bp-messages-prev-page'      : 'prevPage'
1119          },
1120  
1121          initialize: function() {
1122              this.model.on( 'change', this.filterThreads, this );
1123              this.options.threads.on( 'sync', this.addPaginatation, this );
1124          },
1125  
1126          addPaginatation: function( collection ) {
1127              _.each( this.views._views, function( view ) {
1128                  if ( ! _.isUndefined( view ) ) {
1129                      _.first( view ).remove();
1130                  }
1131              } );
1132  
1133              this.views.add( new bp.Views.Pagination( { model: new Backbone.Model( collection.options ) } ) );
1134  
1135              this.views.add( '.user-messages-bulk-actions', new bp.Views.BulkActions( {
1136                  model: new Backbone.Model( BP_Nouveau.messages.bulk_actions ),
1137                  collection : collection
1138              } ) );
1139          },
1140  
1141          filterThreads: function() {
1142              bp.Nouveau.Messages.displayFeedback( BP_Nouveau.messages.loading, 'loading' );
1143  
1144              this.options.threads.reset();
1145              _.extend( this.options.threads.options, _.pick( this.model.attributes, ['box', 'search_terms'] ) );
1146  
1147              this.options.threads.fetch( {
1148                  data    : _.pick( this.model.attributes, ['box', 'search_terms', 'page'] ),
1149                  success : this.threadsFiltered,
1150                  error   : this.threadsFilterError
1151              } );
1152          },
1153  
1154          threadsFiltered: function() {
1155              bp.Nouveau.Messages.removeFeedback();
1156          },
1157  
1158          threadsFilterError: function( collection, response ) {
1159              bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1160          },
1161  
1162          resetSearchTerms: function( event ) {
1163              event.preventDefault();
1164  
1165              if ( ! $( event.target ).val() ) {
1166                  $( event.target ).closest( 'form' ).submit();
1167              } else {
1168                  $( event.target ).closest( 'form' ).find( '[type=submit]' ).addClass('bp-show').removeClass('bp-hide');
1169              }
1170          },
1171  
1172          setSearchTerms: function( event ) {
1173              event.preventDefault();
1174  
1175              this.model.set( {
1176                  'search_terms': $( event.target ).find( 'input[type=search]' ).val() || '',
1177                  page: 1
1178              } );
1179          },
1180  
1181          nextPage: function( event ) {
1182              event.preventDefault();
1183  
1184              this.model.set( 'page', this.model.get( 'page' ) + 1 );
1185          },
1186  
1187          prevPage: function( event ) {
1188              event.preventDefault();
1189  
1190              this.model.set( 'page', this.model.get( 'page' ) - 1 );
1191          }
1192      } );
1193  
1194      bp.Views.userMessagesHeader = bp.Nouveau.Messages.View.extend( {
1195          tagName  : 'div',
1196          template : bp.template( 'bp-messages-single-header' ),
1197  
1198          events: {
1199              'click .actions a' : 'doAction',
1200              'click .actions button' : 'doAction'
1201          },
1202  
1203          doAction: function( event ) {
1204              var action = $( event.currentTarget ).data( 'bp-action' ), self = this, options = {},
1205                  feedback = BP_Nouveau.messages.doingAction;
1206  
1207              if ( ! action ) {
1208                  return event;
1209              }
1210  
1211              event.preventDefault();
1212  
1213              if ( ! this.model.get( 'id' ) ) {
1214                  return;
1215              }
1216  
1217              if ( 'star' === action || 'unstar' === action ) {
1218                  var opposite = {
1219                      'star'  : 'unstar',
1220                      'unstar' : 'star'
1221                  };
1222  
1223                  options.data = {
1224                      'star_nonce' : this.model.get( 'star_nonce' )
1225                  };
1226  
1227                  $( event.currentTarget ).addClass( 'bp-hide' );
1228                  $( event.currentTarget ).parent().find( '[data-bp-action="' + opposite[ action ] + '"]' ).removeClass( 'bp-hide' );
1229  
1230              }
1231  
1232              if ( ! _.isUndefined( feedback[ action ] ) ) {
1233                  bp.Nouveau.Messages.displayFeedback( feedback[ action ], 'loading' );
1234              }
1235  
1236              bp.Nouveau.Messages.threads.doAction( action, this.model.get( 'id' ), options ).done( function( response ) {
1237                  // Remove all views
1238                  if ( 'delete' === action ) {
1239                      bp.Nouveau.Messages.clearViews();
1240                  } else if ( response.messages ) {
1241                      self.model.set( _.first( response.messages ) );
1242                  }
1243  
1244                  // Remove previous feedback.
1245                  bp.Nouveau.Messages.removeFeedback();
1246  
1247                  // Display the feedback
1248                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1249              } ).fail( function( response ) {
1250                  // Remove previous feedback.
1251                  bp.Nouveau.Messages.removeFeedback();
1252  
1253                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1254              } );
1255          }
1256      } );
1257  
1258      bp.Views.userMessagesEntry = bp.Views.userMessagesHeader.extend( {
1259          tagName  : 'li',
1260          template : bp.template( 'bp-messages-single-list' ),
1261  
1262          events: {
1263              'click [data-bp-action]' : 'doAction'
1264          },
1265  
1266          initialize: function() {
1267              this.model.on( 'change:is_starred', this.updateMessage, this );
1268          },
1269  
1270          updateMessage: function( model ) {
1271              if ( this.model.get( 'id' ) !== model.get( 'id' ) ) {
1272                  return;
1273              }
1274  
1275              this.render();
1276          }
1277      } );
1278  
1279      bp.Views.userMessages = bp.Nouveau.Messages.View.extend( {
1280          tagName  : 'div',
1281          template : bp.template( 'bp-messages-single' ),
1282  
1283          initialize: function() {
1284              // Load Messages
1285              this.requestMessages();
1286  
1287              // Init a reply
1288              this.reply = new bp.Models.messageThread();
1289  
1290              this.collection.on( 'add', this.addMessage, this );
1291  
1292              // Add the editor view
1293              this.views.add( '#bp-message-content', new bp.Views.messageEditor() );
1294          },
1295  
1296          events: {
1297              'click #send_reply_button' : 'sendReply'
1298          },
1299  
1300          requestMessages: function() {
1301              var data = {};
1302  
1303              this.collection.reset();
1304  
1305              bp.Nouveau.Messages.displayFeedback( BP_Nouveau.messages.loading, 'loading' );
1306  
1307              if ( _.isUndefined( this.options.thread.attributes ) ) {
1308                  data.id = this.options.thread.id;
1309  
1310              } else {
1311                  data.id        = this.options.thread.get( 'id' );
1312                  data.js_thread = ! _.isEmpty( this.options.thread.get( 'subject' ) );
1313              }
1314  
1315              this.collection.fetch( {
1316                  data: data,
1317                  success : _.bind( this.messagesFetched, this ),
1318                  error   : this.messagesFetchError
1319              } );
1320          },
1321  
1322          messagesFetched: function( collection, response ) {
1323              if ( ! _.isUndefined( response.thread ) ) {
1324                  this.options.thread = new Backbone.Model( response.thread );
1325              }
1326  
1327              bp.Nouveau.Messages.removeFeedback();
1328  
1329              this.views.add( '#bp-message-thread-header', new bp.Views.userMessagesHeader( { model: this.options.thread } ) );
1330          },
1331  
1332          messagesFetchError: function( collection, response ) {
1333              if ( response.feedback && response.type ) {
1334                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1335              }
1336          },
1337  
1338          addMessage: function( message ) {
1339              this.views.add( '#bp-message-thread-list', new bp.Views.userMessagesEntry( { model: message } ) );
1340          },
1341  
1342          addEditor: function() {
1343              // Load the Editor
1344              this.views.add( '#bp-message-content', new bp.Views.messageEditor() );
1345          },
1346  
1347          sendReply: function( event ) {
1348              event.preventDefault();
1349  
1350              if ( true === this.reply.get( 'sending' ) ) {
1351                  return;
1352              }
1353  
1354              this.reply.set ( {
1355                  thread_id : this.options.thread.get( 'id' ),
1356                  content   : tinyMCE.activeEditor.getContent(),
1357                  sending   : true
1358              } );
1359  
1360              this.collection.sync( 'create', _.pick( this.reply.attributes, ['thread_id', 'content' ] ), {
1361                  success : _.bind( this.replySent, this ),
1362                  error   : _.bind( this.replyError, this )
1363              } );
1364          },
1365  
1366          replySent: function( response ) {
1367              var reply = this.collection.parse( response );
1368  
1369              // Reset the form
1370              tinyMCE.activeEditor.setContent( '' );
1371              this.reply.set( 'sending', false );
1372  
1373              this.collection.add( _.first( reply ) );
1374          },
1375  
1376          replyError: function( response ) {
1377              if ( response.feedback && response.type ) {
1378                  bp.Nouveau.Messages.displayFeedback( response.feedback, response.type );
1379              }
1380          }
1381      } );
1382  
1383      bp.Nouveau.Messages.Router = Backbone.Router.extend( {
1384          routes: {
1385              'compose/' : 'composeMessage',
1386              'view/:id/': 'viewMessage',
1387              'sentbox/' : 'sentboxView',
1388              'starred/' : 'starredView',
1389              'inbox/'   : 'inboxView',
1390              ''        : 'inboxView'
1391          },
1392  
1393          composeMessage: function() {
1394              bp.Nouveau.Messages.composeView();
1395          },
1396  
1397          viewMessage: function( thread_id ) {
1398              if ( ! thread_id ) {
1399                  return;
1400              }
1401  
1402              // Try to get the corresponding thread
1403              var thread = bp.Nouveau.Messages.threads.get( thread_id );
1404  
1405              if ( undefined === thread ) {
1406                  thread    = {};
1407                  thread.id = thread_id;
1408              }
1409  
1410              bp.Nouveau.Messages.singleView( thread );
1411          },
1412  
1413          sentboxView: function() {
1414              bp.Nouveau.Messages.box = 'sentbox';
1415              bp.Nouveau.Messages.threadsView();
1416          },
1417  
1418          starredView: function() {
1419              bp.Nouveau.Messages.box = 'starred';
1420              bp.Nouveau.Messages.threadsView();
1421          },
1422  
1423          inboxView: function() {
1424              bp.Nouveau.Messages.box = 'inbox';
1425              bp.Nouveau.Messages.threadsView();
1426          }
1427      } );
1428  
1429      // Launch BP Nouveau Groups
1430      bp.Nouveau.Messages.start();
1431  
1432  } )( bp, jQuery );


Generated: Fri Nov 22 01:01:38 2019 Cross-referenced by PHPXref 0.7.1