/**
* IPS Social Suite 4
* (c) 2013 Invision Power Services - http://www.invisionpower.com
*
* ips.browse.list.js - Gallery browse list controller
*
* Author: Rikki Tissier
*/
;( function($, _, undefined){
"use strict";
ips.controller.register('gallery.front.browse.list', {
initialize: function () {
this.on( 'change', '[data-role="moderation"]', this.selectImage );
this.on( 'tableRowsUpdated', this.rowsUpdated );
},
/**
* Refreshes the patchwork when table rows are updated
*
* @returns {void}
*/
rowsUpdated: function () {
var patchwork = ips.ui.photoLayout.getObj( this.scope );
if( patchwork ){
patchwork.refresh();
}
},
/**
* Toggles classes when the moderation checkbox is checked
*
* @param {event} e Event object
* @returns {void}
*/
selectImage: function (e) {
// e.stopPropagation();
// Can't do that or the moderator floating menu never shows up
var row = $( e.currentTarget ).closest('.cGalleryImageItem');
row.toggleClass( 'cGalleryImageItem_selected', $( e.currentTarget ).is(':checked') );
//return false;
}
});
}(jQuery, _));/**
* IPS Social Suite 4
* (c) 2013 Invision Power Services - http://www.invisionpower.com
*
* ips.submit.existingAlbums.js - Allows user to select an existing gallery album
*
* Author: Rikki Tissier
*/
;( function($, _, undefined){
"use strict";
ips.controller.register('gallery.front.submit.existingAlbums', {
initialize: function () {
this.on( 'click', '[data-action="chooseAlbum"]', this.chooseAlbum );
},
chooseAlbum: function (e) {
e.preventDefault();
// Submit the request via ajax and see what we get back
var link = $( e.currentTarget );
var url = link.attr('href');
ips.getAjax()( url )
.done( function (response) {
link.trigger( 'closeDialog' );
$( document ).trigger( 'albumSelected', response );
});
}
});
}(jQuery, _)); 1){
e.preventDefault();
ips.ui.alert.show( {
type: 'confirm',
icon: 'warn',
message: ips.getString('images_without_data', { count: ips.pluralize( ips.getString( 'count_image' ), unsavedCount ) }),
subText: ips.getString('images_without_data_desc'),
callbacks: {
ok: function () {
window.location = $( e.currentTarget ).attr('href');
}
}
});
}
},
deleteImage: function (e) {
e.preventDefault();
var link = $( e.currentTarget );
var self = this;
ips.getAjax()( link.attr('href') )
.done( function (response) {
var carousel = self.scope.find('.cDownloadsCarousel');
var numberOfItems = 0;
// Update the excess count if necessary
if( self.scope.find('.cGallerySubmit_excess').length ){
numberOfItems = carousel.find('.ipsCarousel_item:not( .cGallerySubmit_excess )').length;
}
// Remove the one we deleted
carousel.find('.cGallerySubmit_current').remove();
self._replaceForm( response.nextForm );
self._selectItem( response.nextType, response.nextID );
self._setFormLoading( false );
if( numberOfItems ){
carousel.find('.ipsCarousel_item').slice( 0, numberOfItems ).removeClass('cGallerySubmit_excess');
}
});
},
/**
* Called when an album has been created in a dialog
*
* @param {event} e Event object
* @param {object} data Event data
* @returns {void}
*/
createAlbum: function (e, data) {
this._changeAlbumInfo( 'new_album', data.response.maxImages || null, data.response.name );
},
/**
* Called when an existing album has been chosen by the user
*
* @param {event} e Event object
* @param {object} data Event data
* @returns {void}
*/
albumSelected: function (e, data) {
this._changeAlbumInfo( 'existing_album', data.maxImages || null, data.name );
},
/**
* Updates the album row after the user adds/chooses an album
*
* @param {string} lang Language string to use
* @param {number} maxImages The maximum number of images that will be saved
* @param {string} albumName Event object
* @returns {void}
*/
_changeAlbumInfo: function (lang, maxImages, albumName) {
var self = this;
var images = this.scope.find('.cDownloadsCarousel > li');
var count = images.length;
var words = ips.getString( lang, {
count: ips.pluralize( ips.getString( 'count_images' ), ( maxImages > 0 && count > maxImages ) ? maxImages : count ),
album: albumName
});
this.scope.find('[data-role="albumChosen"]').show().find('[data-role="text"]').html( words ).show();
this.scope.find('[data-role="albumChoice"]').hide();
setTimeout( function () {
ips.utils.anim.go( 'pulseOnce', self.scope.find('[data-role="albumChosen"]') );
}, 500)
// Do we need to mark any images as 'excess'?
if( maxImages > 0 && count > maxImages ){
var excess = images.slice( maxImages );
excess.addClass('cGallerySubmit_excess');
}
},
/**
* User clicks the 'change' link to edit the album being saved to
*
* @param {event} e Event object
* @returns {void}
*/
changeAlbum: function (e) {
e.preventDefault();
this.scope.find('[data-role="albumChosen"]').hide();
this.scope.find('[data-role="albumChoice"]').show();
this.scope.find('.cDownloadsCarousel .cGallerySubmit_excess').removeClass('cGallerySubmit_excess');
ips.getAjax()( ips.getSetting('baseURL') + 'index.php?app=gallery&module=gallery&controller=submit&do=clearAlbum' );
},
/**
* User has clicked a specific image to edit
*
* @param {event} e Event object
* @returns {void}
*/
switchImage: function (e) {
e.preventDefault();
var self = this;
var link = $( e.currentTarget );
var item = link.closest('.ipsCarousel_item');
var type = item.attr('data-type');
var id = item.attr('data-index');
var doSwitchImage = function () {
self._setFormLoading( true );
ips.getAjax()( link.attr('href') )
.done( function (response) {
self._replaceForm( response );
self._selectItem( type, id );
self._setFormLoading( false );
});
};
// Warn user about unsaved data
if( this._changedForm ){
ips.ui.alert.show( {
type: 'confirm',
icon: 'warn',
message: ips.getString('unsaved_image_data'),
subText: ips.getString('unsaved_image_data_desc'),
callbacks: {
ok: function () {
doSwitchImage();
}
}
});
} else {
doSwitchImage();
}
},
/**
* Event handler for submitting a form
*
* @param {event} e Event object
* @returns {void}
*/
submitImageForm: function (e) {
e.preventDefault();
var self = this;
var form = $( e.currentTarget );
form.find('button[type="submit"]').prop( 'disabled', true ).text( ips.getString('saving') );
this._setFormLoading( true );
ips.getAjax()( form.action, {
data: form.serialize(),
dataType: 'json',
method: form.attr('method')
})
.done( function (response) {
console.log('-------');
console.log(response);
console.log('-------');
if( response.done ){
window.location = self.scope.find('[data-action="finish"]').attr('href');
} else {
self._replaceForm( response.nextForm );
self._selectItem( response.nextType, response.nextID, true );
}
})
.fail( function (jqXHR, textStatus, errorThrown) {
self._replaceForm( jqXHR.responseText );
})
.always( function () {
self._setFormLoading( false );
});
},
/**
* Replaces the current edit form with the new content
*
* @param {string} contents New form contents
* @returns {void}
*/
_replaceForm: function (contents) {
// This method doesn't check this flag, it just resets it
this._changedForm = false;
// Replace the form with the new form
var editArea = this.scope.find('[data-role="editForm"]');
editArea.css({
height: editArea.height() + 'px'
});
editArea.html( contents );
ips.utils.anim.go( 'fadeIn fast', editArea );
$( document ).trigger('contentChange', [ editArea ]);
$('html, body').animate( { scrollTop: this.scope.find('#elImageInfo').offset().top } );
},
/**
* Sets the loading status of the form
*
* @param {boolean} state Show as loading?
* @returns {void}
*/
_setFormLoading: function (state) {
var form = this.scope.find('[data-role="editForm"]');
if( state ){
form.animate( { opacity: 0.6 }, 'fast' );
var dims = ips.utils.position.getElemDims( form );
var position = ips.utils.position.getElemPosition( form );
this.scope.append( $('').addClass('ipsLoading').attr('data-role', 'loading').css({
left: position.viewportOffset.left + 'px',
top: position.viewportOffset.top + 'px',
width: dims.width + 'px',
height: dims.height + 'px',
position: 'absolute',
zIndex: ips.ui.zIndex()
}) );
} else {
form.animate( { opacity: 1 }, 'fast' );
this.scope.find('[data-role="loading"]').remove();
}
},
/**
* Highlights an item in the image slider
*
* @param {string} type The type of item being highlighted (images or movies)
* @param {string} id Index of the item being highlighted
* @param {boolean} markCurrentAsDone Show the current item as 'done' before switching?
* @returns {void}
*/
_selectItem: function (type, id, markCurrentAsDone) {
var allImages = this.scope.find('[data-role="uploadedImages"]');
var currentItem = allImages.find('.cGallerySubmit_current');
// Make the new item active in the slider
currentItem
.removeClass('cGallerySubmit_current')
.toggleClass( 'cGallerySubmit_done', ( markCurrentAsDone === true || currentItem.hasClass('cGallerySubmit_done') ) ? true : false );
allImages
.find('[data-type="' + type + '"][data-index="' + id + '"]')
.addClass('cGallerySubmit_current');
}
});
}(jQuery, _));]]>= 0; i-- ){
var id = $( collectionItems[ i ] ).attr('data-imageID');
if( !_.isUndefined( id ) ){
newPrevID = id;
} else {
break;
}
}
if( newPrevID ){
this._loadAndReplace( newPrevID, 'prev', newPrevItems.length, newPrevItems )
.done( function (response) {
self._updateNavButton( 'prev', response.moreResults );
});
}
}
// Add any images we need to load at the END of the slider
if( collectionItems.length < ( index + 4 ) ){
newNextItems = this._addPlaceholders( 'end', ( index + 4 ) - collectionItems.length );
collectionItems = collection.find('li');
// Work out the index
for( var i = index; i < collectionItems.length; i++ ){
var id = $( collectionItems[ i ] ).attr('data-imageID');
if( !_.isUndefined( id ) ){
newNextID = id;
} else {
break;
}
}
if( newNextID ){
this._loadAndReplace( newNextID, 'next', 10, newNextItems )
.done( function (response) {
self._updateNavButton( 'next', response.moreResults );
});
}
}
// Get the position of the middle image
var newLeftPos = item.position().left;
var leftPos = parseInt( collection.css('left') ) * -1;
var leftIdx = ( ( leftPos ) / 50 );
var middleLI = collectionItems.slice( leftIdx + 3, leftIdx + 4 );
var middleLeftPos = middleLI.position().left;
var difference = newLeftPos - middleLeftPos;
collection.animate({
left: "-=" + difference + 'px'
});
collectionItems.removeClass('cGalleryAlbumSlider_active');
item.addClass('cGalleryAlbumSlider_active');
},
/**
* Paginates to the previous screen of thumbs in the slider
*
* @param {event} e Event object
* @returns {void}
*/
prevCollection: function (e) {
e.preventDefault();
var self = this;
var collection = this.scope.find('[data-role="collection"]');
var collectionItems = collection.find('li');
var toLoad = 0;
var newItems = [];
// Get the first image shown
var leftPos = parseInt( collection.css('left') ) * -1;
var leftIdx = ( ( leftPos ) / 50 );
var leftMostLI = collectionItems.slice( leftIdx, leftIdx + 1 );
var leftMostID = leftMostLI.attr('data-imageID');
// Make sure we have enough elements before this one
if( leftIdx < 6 ){
toLoad = 6 - leftIdx;
}
var newLeftPos = leftPos * -1;
if( toLoad > 0 ){
// Add spacers for the incoming images
newItems = this._addPlaceholders( 'start', toLoad );
// We need to change the left of the slider to compensate for the new items
newLeftPos = ( leftPos + ( newItems.length * 50 ) ) * -1;
collection.css({
left: newLeftPos + 'px'
});
}
collection.animate({
left: ( newLeftPos + 300 ) + 'px'
});
// Now load any images we need
if( toLoad > 0 ){
this._loadAndReplace( leftMostID, 'prev', 6, newItems )
.done( function (response) {
self._updateNavButton( 'prev', response.moreResults );
self._updateNavButton( 'next', true );
});
} else {
self._updateNavButton( 'next', true );
}
},
/**
* Paginates to the next screen of images in the slider
*
* @param {event} e Event object
* @returns {void}
*/
nextCollection: function (e) {
e.preventDefault();
var self = this;
var collection = this.scope.find('[data-role="collection"]');
var collectionItems = collection.find('li');
var toLoad = 0;
var newItems = [];
// Figure out the right-most image
var leftPos = parseInt( collection.css('left') ) * 1;
var leftIdx = ( ( 350 - leftPos ) / 50 ) - 1;
// Get the left position
var rightMostLI = collectionItems.slice( leftIdx, leftIdx + 1 );
var rightMostID = rightMostLI.attr('data-imageID');
var rightPos = rightMostLI.position();
// Make sure we have enough elements after this one
if( collectionItems.length < ( leftIdx + 6 ) ){
toLoad = ( collectionItems.length + 5 ) - leftIdx;
}
// If we are loading any new images, create a holder and append it to our list
if( toLoad > 0 ){
// Add spacers for the incoming images
newItems = this._addPlaceholders( 'end', toLoad );
}
collection.animate({
left: ( rightPos.left * -1 ) + 'px'
});
// Now load any images we need
if( toLoad > 0 ){
this._loadAndReplace( rightMostID, 'next', 6, newItems )
.done( function (response) {
self._updateNavButton( 'next', response.moreResults );
self._updateNavButton( 'prev', true );
});
} else {
self._updateNavButton( 'prev', true );
}
},
/**
* Adds placeholder images to the slider in the location specified
*
* @param {string} location 'start' or 'end'
* @param {number} count Number to add
* @returns {array} Array of placeholder elements created
*/
_addPlaceholders: function (location, count) {
var collection = this.scope.find('[data-role="collection"]');
var newItems = [];
for( var i = 0; i < count; i++ ){
var item = $('').addClass('cGalleryTrimmedImage ipsAreaBackground ipsLoading_tiny');
newItems.push( item );
if( location == 'end' ){
collection.append( item );
} else {
collection.prepend( item );
}
}
return newItems;
},
/**
* Loads more slider images
*
* @param {string} direction Which direction we're updating
* @param {boolean} moreResults Are there more results to see?
* @returns {void}
*/
_updateNavButton: function (direction, moreResults) {
var elem = this.scope.find('[data-role="collectionNav"] [data-action="' + direction + '"]');
if( moreResults ){
elem.removeAttr('data-disabled');
} else {
elem.attr( 'data-disabled', true );
}
},
/**
* Loads more slider images
*
* @param {number} fromID The ID to fetch from
* @param {string} direction next or prev
* @param {number} count Number of items to get
* @param {array} itemsArray Array of placeholders that will be replaced with the loaded images
* @returns {void}
*/
_loadAndReplace: function (fromID, direction, count, itemsArray) {
var deferred = $.Deferred();
this.scope.find('[data-role="collectionNav"] [data-action]').attr( 'data-disabled', true );
ips.getAjax()( ips.getSetting('baseURL') + 'index.php?app=gallery&module=gallery&controller=browse&do=getImages&image=' + fromID + '&direction=' + direction + '&count=' + ( count + 1 ) )
.done( function (response) {
// Now replace each of the placeholders
for( var i = 0; i < itemsArray.length; i++ ){
if( response[ i ] ){
itemsArray[ i ].replaceWith( response[i] );
}
}
deferred.resolve({
moreResults: !( response.length <= count )
});
});
return deferred.promise();
},
/**
* Navigates the page to the next image
*
* @param {event} e Event object
* @returns {void}
*/
nextImage: function (e) {
e.preventDefault();
var url = $( e.currentTarget ).attr('href');
var id = $( e.currentTarget ).attr('data-imageID');
var title = $( e.currentTarget ).attr('title');
History.pushState( {
controller: 'gallery.front.view.image',
imageID: id
}, title, url );
},
/**
* Navigates the page to the previous image
*
* @param {event} e Event object
* @returns {void}
*/
prevImage: function (e) {
e.preventDefault();
var url = $( e.currentTarget ).attr('href');
var id = $( e.currentTarget ).attr('data-imageID');
var title = $( e.currentTarget ).attr('title');
History.pushState( {
controller: 'gallery.front.view.image',
imageID: id
}, title, url );
},
/**
* Sets the current image as the user's profile picture
*
* @param {event} e Event object
* @returns {void}
*/
setAsProfile: function (e) {
e.preventDefault();
var url = $( e.currentTarget ).attr('href');
ips.ui.alert.show( {
type: 'confirm',
icon: 'question',
message: ips.getString('set_as_photo_confirm'),
callbacks: {
ok: function () {
ips.getAjax()( url, {
showLoading: true
} )
.done( function (response) {
ips.ui.flashMsg.show( response.message );
})
.fail( function () {
window.location = url;
});
}
}
});
},
/**
* Sets the image as a cover photo
*
* @param {event} e Event object
* @returns {void}
*/
setAsCover: function (e) {
e.preventDefault();
var url = $( e.currentTarget ).attr('href');
ips.getAjax()( url, {
showLoading: true
} )
.done( function (response) {
ips.ui.flashMsg.show( response.message );
})
.fail( function () {
window.location = url;
});
},
/**
* Event handler for window resizing
*
* @returns {void}
*/
windowResize: function () {
this._setUpSizing();
},
/**
* Loads the specified URL to fetch a new image
*
* @param {string} url URL of new image to fetch
* @returns {void}
*/
_loadURL: function (url) {
var self = this;
this._setImageLoading( true );
// Cancel any request running
if( this._ajaxObj && _.isFunction( this._ajaxObj.abort ) ){
this._ajaxObj.abort();
}
this._ajaxObj = ips.getAjax();
this._ajaxObj( url, {
dataType: 'json'
} )
.done( function (response) {
self._updateImage( response );
})
.fail( function( jqXHR, textStatus, errorThrown ) {
if( Debug.isEnabled() ){
Debug.error( errorThrown );
} else {
window.location = url;
}
});
},
/**
* Handles a response from the server with new image info
*
* @param {object} response Server response json
* @returns {void}
*/
_updateImage: function (response) {
this.cleanContents();
this.scope.find('[data-role="imageInfo"]').html( response.info );
this.scope.find('[data-role="imageFrame"]').replaceWith( response.image );
if( response.slider )
{
this.scope.find('#elGalleryNav > ul').html( response.slider );
}
if( response.image.match( /,,,
*/
_getPosition: function () {
var position = [];
var parent = this.scope.closest('.cGalleryViewImage');
var notePos = this.scope.position();
// Left
position[0] = ( notePos['left'] / parent.width() ) * 100;
// Top
position[1] = ( notePos['top'] / parent.height() ) * 100;
// Width
position[2] = ( this.scope.width() / parent.width() ) * 100;
// Height
position[3] = ( this.scope.height() / parent.height() ) * 100;
return position.join(',');
},
/**
* Takes note out of editing state
*
* @returns {void}
*/
_stopEditing: function () {
this._editing = false;
this._draggingNotEditing = false;
this.scope
.removeClass('cGalleryNote_editing')
.find('.cGalleryNote_note')
.html( this._note )
.end()
.find('.cGalleryNote_delete')
.remove();
},
/**
* Adds the note text to the note
*
* @returns {void}
*/
_buildNote: function () {
this.scope.find('.cGalleryNote_note').html( this._note );
},
/**
* When the note is editable, loads jQuery UI and sets up resizable/draggable
*
* @returns {void}
*/
_setUpEditable: function () {
if( !this._editable ){
return;
}
var self = this;
ips.loader.get( ['core/interface/jquery/jquery-ui.js'] ).then( function () {
self.scope.resizable({
handles: 'se',
stop: self._updatePosition.bind( self )
});
self.scope.draggable({
containment: self.scope.closest('.cGalleryViewImage'),
start: self._startDragging.bind( self ),
stop: self._updatePosition.bind( self )
});
// A workaround for an issue in resizable, where the container will jump because it uses percentage
// sizing, but resizable uses absolute sizing.
self.scope.find('.ui-resizable-handle').on('mouseover', function () {
self.scope.closest('.cGalleryViewImage').css( {
height: self.scope.closest('.cGalleryViewImage').height() + 'px'
});
});
});
},
/**
* Event handler for start event on Draggable. If we aren't already editing, set a flag so that
* when we stop dragging, the click doens't incorrectly put note into editing mode
*
* @returns {void}
*/
_startDragging: function () {
if( !this._editing ){
this._draggingNotEditing = true;
}
},
/**
* Saves the current position of the note. Called when resizable or draggable stop
*
* @returns {void}
*/
_updatePosition: function () {
var self = this;
// If this is a new note, we don't want to update the position remotely yet.
// We'll only do that once the note text is saved for the first time.
if( this.scope.attr('data-noteID') == 'new' ){
return;
}
this._saveNote( false, true )
.done( function () {
// If we were editing before updating pos/dims, we don't want to run the stop method
// otherwise changes to the note text will be lost.
if( !self._editing ){
self._stopEditing();
}
});
},
/**
* Positions the note based on the attributes on the scope element
*
* @returns {void}
*/
_initialPosition: function () {
var left = this.scope.attr('data-posLeft');
var top = this.scope.attr('data-posTop');
var width = this.scope.attr('data-dimWidth');
var height = this.scope.attr('data-dimHeight');
// Position the note
this.scope.css({
left: left + '%',
top: top + '%',
width: width + '%',
height: height + '%'
});
}
});
}(jQuery, _));]]>\
{{/showThumb}}\
{{^showThumb}}\
\
{{/showThumb}}\
\
{{#showThumb}}{{/showThumb}}\