[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-core/js/ -> avatar.js (source)

   1  /* global bp, BP_Uploader, _, Backbone */
   2  
   3  window.bp = window.bp || {};
   4  
   5  ( function( exports, $ ) {
   6  
   7      // Bail if not set
   8      if ( typeof BP_Uploader === 'undefined' ) {
   9          return;
  10      }
  11  
  12      bp.Models      = bp.Models || {};
  13      bp.Collections = bp.Collections || {};
  14      bp.Views       = bp.Views || {};
  15  
  16      bp.Avatar = {
  17          start: function() {
  18              var self = this;
  19  
  20              /**
  21               * Remove the bp-legacy UI
  22               *
  23               * bp.Avatar successfully loaded, we can now
  24               * safely remove the Legacy UI.
  25               */
  26              this.removeLegacyUI();
  27  
  28              // Init some vars
  29              this.views    = new Backbone.Collection();
  30              this.jcropapi = {};
  31              this.warning = null;
  32  
  33              // Set up nav
  34              this.setupNav();
  35  
  36              // Avatars are uploaded files
  37              this.avatars = bp.Uploader.filesUploaded;
  38  
  39              // The Avatar Attachment object.
  40              this.Attachment = new Backbone.Model();
  41  
  42              // Wait till the queue is reset
  43              bp.Uploader.filesQueue.on( 'reset', this.cropView, this );
  44  
  45              /**
  46               * In Administration screens we're using Thickbox
  47               * We need to make sure to reset the views if it's closed or opened
  48               */
  49              $( 'body.wp-admin' ).on( 'tb_unload', '#TB_window', function() {
  50                  self.resetViews();
  51              } );
  52  
  53              $( 'body.wp-admin' ).on( 'click', '.bp-xprofile-avatar-user-edit', function() {
  54                  self.resetViews();
  55              } );
  56          },
  57  
  58          removeLegacyUI: function() {
  59              // User
  60              if ( $( '#avatar-upload-form' ).length ) {
  61                  $( '#avatar-upload' ).remove();
  62                  $( '#avatar-upload-form p' ).remove();
  63  
  64              // Group Manage
  65              } else if ( $( '#group-settings-form' ).length ) {
  66                  $( '#group-settings-form p' ).each( function( i ) {
  67                      if ( 0 !== i ) {
  68                          $( this ).remove();
  69                      }
  70                  } );
  71  
  72                  if ( $( '#delete-group-avatar-button' ).length ) {
  73                      $( '#delete-group-avatar-button' ).remove();
  74                  }
  75  
  76              // Group Create
  77              } else if ( $( '#group-create-body' ).length ) {
  78                  $( '.main-column p #file' ).remove();
  79                  $( '.main-column p #upload' ).remove();
  80  
  81              // Admin Extended Profile
  82              } else if ( $( '#bp_xprofile_user_admin_avatar a.bp-xprofile-avatar-user-admin' ).length ) {
  83                  $( '#bp_xprofile_user_admin_avatar a.bp-xprofile-avatar-user-admin' ).remove();
  84              }
  85          },
  86  
  87          setView: function( view ) {
  88              // Clear views
  89              if ( ! _.isUndefined( this.views.models ) ) {
  90                  _.each( this.views.models, function( model ) {
  91                      model.get( 'view' ).remove();
  92                  }, this );
  93              }
  94  
  95              // Reset Views
  96              this.views.reset();
  97  
  98              // Reset Avatars (file uploaded)
  99              if ( ! _.isUndefined( this.avatars ) ) {
 100                  this.avatars.reset();
 101              }
 102  
 103              // Reset the Jcrop API
 104              if ( ! _.isEmpty( this.jcropapi ) ) {
 105                  this.jcropapi.destroy();
 106                  this.jcropapi = {};
 107              }
 108  
 109              // Load the required view
 110              switch ( view ) {
 111                  case 'upload':
 112                      this.uploaderView();
 113                      break;
 114  
 115                  case 'delete':
 116                      this.deleteView();
 117                      break;
 118              }
 119          },
 120  
 121          resetViews: function() {
 122              // Reset to the uploader view
 123              this.nav.trigger( 'bp-avatar-view:changed', 'upload' );
 124  
 125              // Reset to the uploader nav
 126              _.each( this.navItems.models, function( model ) {
 127                  if ( model.id === 'upload' ) {
 128                      model.set( { active: 1 } );
 129                  } else {
 130                      model.set( { active: 0 } );
 131                  }
 132              } );
 133          },
 134  
 135          setupNav: function() {
 136              var self = this,
 137                  initView, activeView;
 138  
 139              this.navItems = new Backbone.Collection();
 140  
 141              _.each( BP_Uploader.settings.nav, function( item, index ) {
 142                  if ( ! _.isObject( item ) ) {
 143                      return;
 144                  }
 145  
 146                  // Reset active View
 147                  activeView = 0;
 148  
 149                  if ( 0 === index ) {
 150                      initView = item.id;
 151                      activeView = 1;
 152                  }
 153  
 154                  self.navItems.add( {
 155                      id     : item.id,
 156                      name   : item.caption,
 157                      href   : '#',
 158                      active : activeView,
 159                      hide   : _.isUndefined( item.hide ) ? 0 : item.hide
 160                  } );
 161              } );
 162  
 163              this.nav = new bp.Views.Nav( { collection: this.navItems } );
 164              this.nav.inject( '.bp-avatar-nav' );
 165  
 166              // Activate the initial view (uploader)
 167              this.setView( initView );
 168  
 169              // Listen to nav changes (it's like a do_action!)
 170              this.nav.on( 'bp-avatar-view:changed', _.bind( this.setView, this ) );
 171          },
 172  
 173          uploaderView: function() {
 174              // Listen to the Queued uploads
 175              bp.Uploader.filesQueue.on( 'add', this.uploadProgress, this );
 176  
 177              // Create the BuddyPress Uploader
 178              var uploader = new bp.Views.Uploader();
 179  
 180              // Add it to views
 181              this.views.add( { id: 'upload', view: uploader } );
 182  
 183              // Display it
 184              uploader.inject( '.bp-avatar' );
 185          },
 186  
 187          uploadProgress: function() {
 188              // Create the Uploader status view
 189              var avatarStatus = new bp.Views.uploaderStatus( { collection: bp.Uploader.filesQueue } );
 190  
 191              if ( ! _.isUndefined( this.views.get( 'status' ) ) ) {
 192                  this.views.set( { id: 'status', view: avatarStatus } );
 193              } else {
 194                  this.views.add( { id: 'status', view: avatarStatus } );
 195              }
 196  
 197              // Display it
 198              avatarStatus.inject( '.bp-avatar-status' );
 199          },
 200  
 201          cropView: function() {
 202              var status;
 203  
 204              // Bail there was an error during the Upload
 205              if ( _.isEmpty( this.avatars.models ) ) {
 206                  return;
 207              }
 208  
 209              // Make sure to remove the uploads status
 210              if ( ! _.isUndefined( this.views.get( 'status' ) ) ) {
 211                  status = this.views.get( 'status' );
 212                  status.get( 'view' ).remove();
 213                  this.views.remove( { id: 'status', view: status } );
 214              }
 215  
 216              // Create the Avatars view
 217              var avatar = new bp.Views.Avatars( { collection: this.avatars } );
 218              this.views.add( { id: 'crop', view: avatar } );
 219  
 220              avatar.inject( '.bp-avatar' );
 221          },
 222  
 223          setAvatar: function( avatar ) {
 224              var self = this,
 225                  crop;
 226  
 227              // Remove the crop view
 228              if ( ! _.isUndefined( this.views.get( 'crop' ) ) ) {
 229                  // Remove the JCrop API
 230                  if ( ! _.isEmpty( this.jcropapi ) ) {
 231                      this.jcropapi.destroy();
 232                      this.jcropapi = {};
 233                  }
 234                  crop = this.views.get( 'crop' );
 235                  crop.get( 'view' ).remove();
 236                  this.views.remove( { id: 'crop', view: crop } );
 237              }
 238  
 239              // Set the avatar !
 240              bp.ajax.post( 'bp_avatar_set', {
 241                  json:          true,
 242                  original_file: avatar.get( 'url' ),
 243                  crop_w:        avatar.get( 'w' ),
 244                  crop_h:        avatar.get( 'h' ),
 245                  crop_x:        avatar.get( 'x' ),
 246                  crop_y:        avatar.get( 'y' ),
 247                  item_id:       avatar.get( 'item_id' ),
 248                  object:        avatar.get( 'object' ),
 249                  type:          _.isUndefined( avatar.get( 'type' ) ) ? 'crop' : avatar.get( 'type' ),
 250                  nonce:         avatar.get( 'nonces' ).set
 251              } ).done( function( response ) {
 252                  var avatarStatus = new bp.Views.AvatarStatus( {
 253                      value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
 254                      type : 'success'
 255                  } );
 256  
 257                  self.views.add( {
 258                      id   : 'status',
 259                      view : avatarStatus
 260                  } );
 261  
 262                  avatarStatus.inject( '.bp-avatar-status' );
 263  
 264                  // Update each avatars of the page
 265                  $( '.' + avatar.get( 'object' ) + '-' + response.item_id + '-avatar' ).each( function() {
 266                      $(this).prop( 'src', response.avatar );
 267                  } );
 268  
 269                  // Inject the Delete nav
 270                  bp.Avatar.navItems.get( 'delete' ).set( { hide: 0 } );
 271  
 272                  /**
 273                   * Set the Attachment object
 274                   *
 275                   * You can run extra actions once the avatar is set using:
 276                   * bp.Avatar.Attachment.on( 'change:url', function( data ) { your code } );
 277                   *
 278                   * In this case data.attributes will include the url to the newly
 279                   * uploaded avatar, the object and the item_id concerned.
 280                   */
 281                  self.Attachment.set( _.extend(
 282                      _.pick( avatar.attributes, ['object', 'item_id'] ),
 283                      { url: response.avatar, action: 'uploaded' }
 284                  ) );
 285  
 286              } ).fail( function( response ) {
 287                  var feedback = BP_Uploader.strings.default_error;
 288                  if ( ! _.isUndefined( response ) ) {
 289                      feedback = BP_Uploader.strings.feedback_messages[ response.feedback_code ];
 290                  }
 291  
 292                  var avatarStatus = new bp.Views.AvatarStatus( {
 293                      value : feedback,
 294                      type : 'error'
 295                  } );
 296  
 297                  self.views.add( {
 298                      id   : 'status',
 299                      view : avatarStatus
 300                  } );
 301  
 302                  avatarStatus.inject( '.bp-avatar-status' );
 303              } );
 304          },
 305  
 306          deleteView:function() {
 307              // Create the delete model
 308              var delete_model = new Backbone.Model( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
 309                  'object',
 310                  'item_id',
 311                  'nonces'
 312              ) );
 313  
 314              // Create the delete view
 315              var deleteView = new bp.Views.DeleteAvatar( { model: delete_model } );
 316  
 317              // Add it to views
 318              this.views.add( { id: 'delete', view: deleteView } );
 319  
 320              // Display it
 321              deleteView.inject( '.bp-avatar' );
 322          },
 323  
 324          deleteAvatar: function( model ) {
 325              var self = this,
 326                  deleteView;
 327  
 328              // Remove the delete view
 329              if ( ! _.isUndefined( this.views.get( 'delete' ) ) ) {
 330                  deleteView = this.views.get( 'delete' );
 331                  deleteView.get( 'view' ).remove();
 332                  this.views.remove( { id: 'delete', view: deleteView } );
 333              }
 334  
 335              // Remove the avatar !
 336              bp.ajax.post( 'bp_avatar_delete', {
 337                  json:          true,
 338                  item_id:       model.get( 'item_id' ),
 339                  object:        model.get( 'object' ),
 340                  nonce:         model.get( 'nonces' ).remove
 341              } ).done( function( response ) {
 342                  var avatarStatus = new bp.Views.AvatarStatus( {
 343                      value : BP_Uploader.strings.feedback_messages[ response.feedback_code ],
 344                      type : 'success'
 345                  } );
 346  
 347                  self.views.add( {
 348                      id   : 'status',
 349                      view : avatarStatus
 350                  } );
 351  
 352                  avatarStatus.inject( '.bp-avatar-status' );
 353  
 354                  // Update each avatars of the page
 355                  $( '.' + model.get( 'object' ) + '-' + response.item_id + '-avatar').each( function() {
 356                      $( this ).prop( 'src', response.avatar );
 357                  } );
 358  
 359                  // Remove the Delete nav
 360                  bp.Avatar.navItems.get( 'delete' ).set( { active: 0, hide: 1 } );
 361  
 362                  /**
 363                   * Reset the Attachment object
 364                   *
 365                   * You can run extra actions once the avatar is set using:
 366                   * bp.Avatar.Attachment.on( 'change:url', function( data ) { your code } );
 367                   *
 368                   * In this case data.attributes will include the url to the gravatar,
 369                   * the object and the item_id concerned.
 370                   */
 371                  self.Attachment.set( _.extend(
 372                      _.pick( model.attributes, ['object', 'item_id'] ),
 373                      { url: response.avatar, action: 'deleted' }
 374                  ) );
 375  
 376              } ).fail( function( response ) {
 377                  var feedback = BP_Uploader.strings.default_error;
 378                  if ( ! _.isUndefined( response ) ) {
 379                      feedback = BP_Uploader.strings.feedback_messages[ response.feedback_code ];
 380                  }
 381  
 382                  var avatarStatus = new bp.Views.AvatarStatus( {
 383                      value : feedback,
 384                      type : 'error'
 385                  } );
 386  
 387                  self.views.add( {
 388                      id   : 'status',
 389                      view : avatarStatus
 390                  } );
 391  
 392                  avatarStatus.inject( '.bp-avatar-status' );
 393              } );
 394          },
 395  
 396          removeWarning: function() {
 397              if ( ! _.isNull( this.warning ) ) {
 398                  this.warning.remove();
 399              }
 400          },
 401  
 402          displayWarning: function( message ) {
 403              this.removeWarning();
 404  
 405              this.warning = new bp.Views.uploaderWarning( {
 406                  value: message
 407              } );
 408  
 409              this.warning.inject( '.bp-avatar-status' );
 410          }
 411      };
 412  
 413      // Main Nav view
 414      bp.Views.Nav = bp.View.extend( {
 415          tagName:    'ul',
 416          className:  'avatar-nav-items',
 417  
 418          events: {
 419              'click .bp-avatar-nav-item' : 'toggleView'
 420          },
 421  
 422          initialize: function() {
 423              var hasAvatar = _.findWhere( this.collection.models, { id: 'delete' } );
 424  
 425              // Display a message to inform about the delete tab
 426              if ( 1 !== hasAvatar.get( 'hide' ) ) {
 427                  bp.Avatar.displayWarning( BP_Uploader.strings.has_avatar_warning );
 428              }
 429  
 430              _.each( this.collection.models, this.addNavItem, this );
 431              this.collection.on( 'change:hide', this.showHideNavItem, this );
 432          },
 433  
 434          addNavItem: function( item ) {
 435              /**
 436               * The delete nav is not added if no avatar
 437               * is set for the object
 438               */
 439              if ( 1 === item.get( 'hide' ) ) {
 440                  return;
 441              }
 442  
 443              this.views.add( new bp.Views.NavItem( { model: item } ) );
 444          },
 445  
 446          showHideNavItem: function( item ) {
 447              var isRendered = null;
 448  
 449              /**
 450               * Loop in views to show/hide the nav item
 451               * BuddyPress is only using this for the delete nav
 452               */
 453              _.each( this.views._views[''], function( view ) {
 454                  if ( 1 === view.model.get( 'hide' ) ) {
 455                      view.remove();
 456                  }
 457  
 458                  // Check to see if the nav is not already rendered
 459                  if ( item.get( 'id' ) === view.model.get( 'id' ) ) {
 460                      isRendered = true;
 461                  }
 462              } );
 463  
 464              // Add the Delete nav if not rendered
 465              if ( ! _.isBoolean( isRendered ) ) {
 466                  this.addNavItem( item );
 467              }
 468          },
 469  
 470          toggleView: function( event ) {
 471              event.preventDefault();
 472  
 473              // First make sure to remove all warnings
 474              bp.Avatar.removeWarning();
 475  
 476              var active = $( event.target ).data( 'nav' );
 477  
 478              _.each( this.collection.models, function( model ) {
 479                  if ( model.id === active ) {
 480                      model.set( { active: 1 } );
 481                      this.trigger( 'bp-avatar-view:changed', model.id );
 482                  } else {
 483                      model.set( { active: 0 } );
 484                  }
 485              }, this );
 486          }
 487      } );
 488  
 489      // Nav item view
 490      bp.Views.NavItem = bp.View.extend( {
 491          tagName:    'li',
 492          className:  'avatar-nav-item',
 493          template: bp.template( 'bp-avatar-nav' ),
 494  
 495          initialize: function() {
 496              if ( 1 === this.model.get( 'active' ) ) {
 497                  this.el.className += ' current';
 498              }
 499              this.el.id += 'bp-avatar-' + this.model.get( 'id' );
 500  
 501              this.model.on( 'change:active', this.setCurrentNav, this );
 502          },
 503  
 504          setCurrentNav: function( model ) {
 505              if ( 1 === model.get( 'active' ) ) {
 506                  this.$el.addClass( 'current' );
 507              } else {
 508                  this.$el.removeClass( 'current' );
 509              }
 510          }
 511      } );
 512  
 513      // Avatars view
 514      bp.Views.Avatars = bp.View.extend( {
 515          className: 'items',
 516  
 517          initialize: function() {
 518              _.each( this.collection.models, this.addItemView, this );
 519          },
 520  
 521          addItemView: function( item ) {
 522              // Defaults to 150
 523              var full_d = { full_h: 150, full_w: 150 };
 524  
 525              // Make sure to take in account bp_core_avatar_full_height or bp_core_avatar_full_width php filters
 526              if ( ! _.isUndefined( BP_Uploader.settings.crop.full_h ) && ! _.isUndefined( BP_Uploader.settings.crop.full_w ) ) {
 527                  full_d.full_h = BP_Uploader.settings.crop.full_h;
 528                  full_d.full_w = BP_Uploader.settings.crop.full_w;
 529              }
 530  
 531              // Set the avatar model
 532              item.set( _.extend( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
 533                  'object',
 534                  'item_id',
 535                  'nonces'
 536              ), full_d ) );
 537  
 538              // Add the view
 539              this.views.add( new bp.Views.Avatar( { model: item } ) );
 540          }
 541      } );
 542  
 543      // Avatar view
 544      bp.Views.Avatar = bp.View.extend( {
 545          className: 'item',
 546          template: bp.template( 'bp-avatar-item' ),
 547  
 548          events: {
 549              'click .avatar-crop-submit': 'cropAvatar'
 550          },
 551  
 552          initialize: function() {
 553              _.defaults( this.options, {
 554                  full_h:  BP_Uploader.settings.crop.full_h,
 555                  full_w:  BP_Uploader.settings.crop.full_w,
 556                  aspectRatio : 1
 557              } );
 558  
 559              // Display a warning if the image is smaller than minimum advised
 560              if ( false !== this.model.get( 'feedback' ) ) {
 561                  bp.Avatar.displayWarning( this.model.get( 'feedback' ) );
 562              }
 563  
 564              this.on( 'ready', this.initCropper );
 565          },
 566  
 567          initCropper: function() {
 568              var self = this,
 569                  tocrop = this.$el.find( '#avatar-to-crop img' ),
 570                  availableWidth = this.$el.width(),
 571                  selection = {}, crop_top, crop_bottom, crop_left, crop_right, nh, nw;
 572  
 573              if ( ! _.isUndefined( this.options.full_h ) && ! _.isUndefined( this.options.full_w ) ) {
 574                  this.options.aspectRatio = this.options.full_w / this.options.full_h;
 575              }
 576  
 577              selection.w = this.model.get( 'width' );
 578              selection.h = this.model.get( 'height' );
 579  
 580              /**
 581               * Make sure the crop preview is at the right of the avatar
 582               * if the available width allowes it.
 583               */
 584              if ( this.options.full_w + selection.w + 20 < availableWidth ) {
 585                  $( '#avatar-to-crop' ).addClass( 'adjust' );
 586                  this.$el.find( '.avatar-crop-management' ).addClass( 'adjust' );
 587              }
 588  
 589              if ( selection.h <= selection.w ) {
 590                  crop_top    = Math.round( selection.h / 4 );
 591                  nh = nw     = Math.round( selection.h / 2 );
 592                  crop_bottom = nh + crop_top;
 593                  crop_left   = ( selection.w - nw ) / 2;
 594                  crop_right  = nw + crop_left;
 595              } else {
 596                  crop_left   = Math.round( selection.w / 4 );
 597                  nh = nw     = Math.round( selection.w / 2 );
 598                  crop_right  = nw + crop_left;
 599                  crop_top    = ( selection.h - nh ) / 2;
 600                  crop_bottom = nh + crop_top;
 601              }
 602  
 603              // Add the cropping interface
 604              tocrop.Jcrop( {
 605                  onChange: _.bind( self.showPreview, self ),
 606                  onSelect: _.bind( self.showPreview, self ),
 607                  aspectRatio: self.options.aspectRatio,
 608                  setSelect: [ crop_left, crop_top, crop_right, crop_bottom ]
 609              }, function() {
 610                  // Get the Jcrop API
 611                  bp.Avatar.jcropapi = this;
 612              } );
 613          },
 614  
 615          cropAvatar: function( event ) {
 616              event.preventDefault();
 617  
 618              bp.Avatar.setAvatar( this.model );
 619          },
 620  
 621          showPreview: function( coords ) {
 622              if ( ! coords.w || ! coords.h ) {
 623                  return;
 624              }
 625  
 626              if ( parseInt( coords.w, 10 ) > 0 ) {
 627                  var fw = this.options.full_w;
 628                  var fh = this.options.full_h;
 629                  var rx = fw / coords.w;
 630                  var ry = fh / coords.h;
 631  
 632                  // Update the model
 633                  this.model.set( { x: coords.x, y: coords.y, w: coords.w, h: coords.h } );
 634  
 635                  $( '#avatar-crop-preview' ).css( {
 636                      maxWidth:'none',
 637                      width: Math.round( rx *  this.model.get( 'width' ) )+ 'px',
 638                      height: Math.round( ry * this.model.get( 'height' ) )+ 'px',
 639                      marginLeft: '-' + Math.round( rx * this.model.get( 'x' ) ) + 'px',
 640                      marginTop: '-' + Math.round( ry * this.model.get( 'y' ) ) + 'px'
 641                  } );
 642              }
 643          }
 644      } );
 645  
 646      // BuddyPress Avatar Feedback view
 647      bp.Views.AvatarStatus = bp.View.extend( {
 648          tagName: 'p',
 649          className: 'updated',
 650          id: 'bp-avatar-feedback',
 651  
 652          initialize: function() {
 653              this.el.className += ' ' + this.options.type;
 654              this.value = this.options.value;
 655          },
 656  
 657          render: function() {
 658              this.$el.html( this.value );
 659              return this;
 660          }
 661      } );
 662  
 663      // BuddyPress Avatar Delete view
 664      bp.Views.DeleteAvatar = bp.View.extend( {
 665          tagName: 'div',
 666          id: 'bp-delete-avatar-container',
 667          template: bp.template( 'bp-avatar-delete' ),
 668  
 669          events: {
 670              'click #bp-delete-avatar': 'deleteAvatar'
 671          },
 672  
 673          deleteAvatar: function( event ) {
 674              event.preventDefault();
 675  
 676              bp.Avatar.deleteAvatar( this.model );
 677          }
 678      } );
 679  
 680      bp.Avatar.start();
 681  
 682  })( bp, jQuery );


Generated: Mon Nov 18 01:01:37 2019 Cross-referenced by PHPXref 0.7.1