Show:

File: platform/plugins/Streams/web/js/tools/photoSelector.js

(function (Q, $) {

/**
 * Streams Tools
 * @module Streams-tools
 */

/**
 * Interface for selecting facebook photos from user albums
 * @class Streams photoSelector
 * @constructor
 * @param {Object} [options] this object contains function parameters
 *   @param {Q.Event} [options.onSelect] Triggered when the user selects a photo.
 *   @param {Q.Event} [options.beforePhotos] Triggered when photos are about to be rendered.
 *   @param {Q.Event} [options.onPhotos] Triggered when photos have been rendered.
 *   @param {Q.Event} [options.onPhotosLoaded] Triggered when the photos have all been loaded.
 *   @param {String} [options.uid='me'] Optional. The uid of the user on the platform whose photos are shown. Facebook only allows 'me' or a page id as a value.
 *   @param {String} [$options.fetchBy='album'] The tool supports different algoriths for fetching photos. Can be either by 'album' or 'tags'. Maybe more will be added later.
 *   @param {String} [$options.preprocessAlbums] Optional function to process the albums array before presenting it in the select. Receives a reference to the albums array as the first parameter, and a callback to call when it's done as the second.
 *   @param {String} [$options.preprocessPhotos] Optional function to process the photos array before presenting it in the select. Receives a reference to the albums array as the first parameter, and a callback to call when it's done as the second.
 *   @param {Q.Event} [options.onLoad] Q.Event, callback or callback string name which is called when bunch of photos has been loaded.
 *   @param {Q.Event} [options.onError] Q.Event, callback or callback string which will be called for each image that is unable to load. Image DOM element will be passed as first argument.
 *   @param {String} [options.platform='facebook]  Has to be "facebook" for now. 
 *   @param {String} [options.prompt=false]
 *   Specifies type of prompt if user is not logged in or didn't give required permission for the tool.
 *  Can be either 'button', 'dialog' or null|false.
 *  In first case just shows simple button which opens facebook login popup.
 *  In second case shows Users.facebookDialog prompting user to login.
 *  In third case will not show any prompt and will just hide the tool.
 *   @param {String} [options.promptTitle]  Used only when 'prompt' equals 'dialog' - will be title of the dialog.
 *   @param {String} [options.promptText]  Used either for button caption when 'prompt' equals 'button' or dialog text when 'prompt' equals 'dialog'.
 *   @param {Boolean} [options.oneLine]  If true, all the images are shown in a large horizontally scrolling line.
 *   @default false
 *   @param {Boolean} [options.cache]  If true, photos will be cached using localStorage (if available).
 *   @default false
 */
Q.Tool.define("Streams/photoSelector", function _Streams_photoSelector_constructor (o) {

	var tool = this;
	var state = tool.state;
	var $te = $(tool.element);

	if (!state.onSelect) {
		console.warn("Streams/photoSelector: please provide the onSelect option");
	}
	if (state.platform !== 'facebook') {
		console.warn("Only facebook is supported as a platform for now");
	}
	
	var fields = 'id,album,created_time,from,icon,images,link,name,name_tags,updated_time,width,event,place,picture';

	var fetchBy = {
		album: Q.getter(function(albumId, callback) {
			FB.api('/'+albumId+'/photos?limit=100&fields='+fields,
			function (response) {
				_returnedPhotos(response, callback);
			});
		}),
		tags: Q.getter(function(callback) {
			FB.api('/'+state.uid+'/photos?limit=100&type=tagged&fields='+fields,
			function (response) {
				_returnedPhotos(response, callback);
			});
		})
	};
	
	function _returnedPhotos(response, callback) {
		if (!response || response.error) {
			return;
		}
		var photos = response.data;
		if (state.preprocessPhotos) {
			state.preprocessPhotos.call(tool, photos, function () {
				callback.call(tool, photos);
			});
		} else {
			callback.call(tool, photos);
		}
	}

	function fetchAlbums(callback) {
		FB.api('/'+state.uid+'/albums?fields=id,name,type,created_time', function (response) {
			if (!response || response.error) {
				return;
			}
			var albums = response.data;
			if (state.preprocessAlbums) {
				state.preprocessAlbums.call(tool, albums, function () {
					callback.call(tool, albums);
				});
			} else {
				callback.call(tool, albums);
			}
		});
	}

	function showAlbums(albums) {
		tool.$albums = $('<select class="Streams_photoSelector_albums" />')
		.on('change keydown input', Q.debounce(function() {
			fetchPhotos();
		}, 50));
		$te.empty().append(tool.$albums);
		Q.each(albums, function () {
			tool.$albums.append(
				$('<option />', {value: this.id})
				.text(this.name)
				.data('album', this)
			);
		});
	}

	function fetchPhotos() {
		switch (state.fetchBy) {
		case 'album':
			fetchBy.album(tool.$albums.val(), showPhotos);
			break;
		case 'tags':
			fetchBy.tags(showPhotos);
			break;
		default:
			break;
		}
	}

	function showPhotos(photos) {
		
		var album = tool.$albums
			? tool.$albums.find(':selected').data('album')
			: null;
		Q.handle(state.beforePhotos, this, [album]);
		if (state.fetchBy == 'tags') {
			$te.empty();
		}
		tool.$photosContainer = $te.find('.Streams_photoSelector_container');
		if (tool.$photosContainer.length) {
			tool.$photosContainer.empty();
		} else {
			tool.$photosContainer = $('<div class="Streams_photoSelector_container" />')
				.appendTo($te);
		}
		
		var p = new Q.Pipe();
		var w = [];
		if (Q.isEmpty(photos)) {
			tool.$photosContainer.append('<div class="Streams_photoSelector_noPhotos">No photos found for these criteria.</div>');
		} else {
			Q.each(photos, function () {
				var photo = this;
				var $img = $('<img />')
				.on('load', p.fill(src))
				.attr({src: photo.picture})
				.data('photo', this)
				.appendTo(tool.$photosContainer)
				.on(Q.Pointer.fastclick, function () {
					Q.handle(state.onSelect, tool, [this, photo, photo.images]);
				});
				w.push(src);
			});
			if (state.oneLine) {
				tool.$photosContainer.addClass('Streams_photoSelector_oneLine');
			}
		}
		
		Q.handle(state.onPhotos, tool, [album]);
		p.add(w, function () {
			Q.handle(state.onPhotosLoaded, tool, [album]);
		}).run();
		
	}
	
	function _fetch() {
		switch (state.fetchBy) {
		case 'album':
			fetchAlbums(function (albums) {
				showAlbums(albums);
				fetchPhotos();
			});
			break;
		case 'tags':
			fetchPhotos();
			break;
		default:
			break;
		}
	}

	function onSuccessfulLogin($te) {
		FB.api('/me/permissions', function (response) {
			var authorized = false;
			Q.each(response.data, function (i, data) {
				if (data.permission == 'user_photos' && data.status == 'granted') {
					authorized = true;
				}
			});
			if (authorized) {
				_fetch();
			} else {
				Q.plugins.Users.facebookDialog({
					'title': 'Permissions request',
					'content': 'The application requires access to your photos.',
					'buttons': [
						{
							'label': 'Grant permissions',
							'handler': function(dialog) {
								Q.plugins.Users.login({
									using: 'facebook',
									scope: 'user_photos',
									onCancel: function() {
										dialog.close();
										$te.empty();
									},
									onSuccess: function() {
										dialog.close();
										_fetch();
									}
								});
							},
							'default': true
						},
						{
							'label': 'Cancel',
							'handler': function(dialog) {
								$te.empty();
								dialog.close();
							}
						}
					],
					'position': null,
					'shadow': true
				});
			}
		});
	}

	switch (state.platform) {
	case 'facebook':
		$te.find('*').remove();
		$te.removeClass('Streams_photoSelector_by_album Streams_photoSelector_by_tags')
			.addClass('Streams_photoSelector_by_' + state.fetchBy);
		var src = Q.url('/{{Q}}/img/throbbers/loading.gif');
		$te.append('<div class="Streams_tools_throbber"><img src="'+src+'" alt="" /></div>');
		Q.plugins.Users.login({
			tryQuietly: true,
			using: 'facebook',
			scope: 'user_photos',
			onSuccess: function() {
				onSuccessfulLogin($te);
			},
			onCancel: function() {	
				if (state.prompt == 'dialog') {
					// we may show the dialog asking user to login
					Q.plugins.Users.facebookDialog({
						'title': state.promptTitle,
						'content': state.promptText,
						'buttons': [
							{
								'label': 'Login',
								'handler': function(dialog) {
									Q.plugins.Users.login( {
										using: 'facebook',
										onCancel: function() {
											dialog.close();
											$te.hide();
										},
										onSuccess: function() {
											dialog.close();
											onSuccessfulLogin($te);
										}
									});
								},
								'default': true
							},
							{
								'label': 'Cancel',
								'handler': function(dialog) {
									$te.hide();
									dialog.close();
								}
							}
						],
						'position': null,
						'shadow': true
					});
				} else if (state.prompt == 'button') {
					// or a button, clicking on it will cause facebook
					// login popup to appear
					var button = $('<button class="Q_main_button">' + state.promptText + '</button>');
					$te.empty().append(button);
					button.click(function()
					{
						Q.plugins.Users.login(
						{
							using: 'facebook',
							onSuccess: function()
							{
								onSuccessfulLogin($te);
							}
						});
					});
				} else {
					// or just hide photo selector, and wait for a login
					$te.hide();
					Q.plugins.Users.onLogin.set(function() {
						Q.plugins.Users.onLogin.remove('photo-selector-login');
						$te.show();
						onSuccessfulLogin($te);
					}, 'Streams/photoSelector');
				}
			}
		});
		break;
	}
},

{
	onSelect: new Q.Event(),
	beforePhotos: new Q.Event(),
	onPhotos: new Q.Event(),
	onPhotosLoaded: new Q.Event(),
	preprocessAlbums: function (albums, callback) {
		Q.each(albums, function (i) {
			if (this.type === 'profile') {
				albums.splice(i, 1);
				albums.unshift(this);
			}
		});
		callback();
	},
	uid: 'me',
	fetchBy: 'album',
	onLoad: new Q.Event(function() {}),
	onError: new Q.Event(function() {}),
	platform: 'facebook',
	prompt: false,
	promptTitle: 'Login required',
	promptText: 'Please log into Facebook to to view photos.',
	oneLine: false,
	cache: false
}

);

})(Q, jQuery);