/* * Flickr Album * http://ifiona2012.blogspot.com/ * * Dual licensed under the MIT or GPL Version 2 licenses. * Email: [email protected] * */ function FlickrUtil() { "use strict"; } (function() { "use strict"; // Flickr rest service address FlickrUtil.prototype.DOMAIN = "http://api.flickr.com/services/rest/"; /* * Cross-domain ajax request. * * @param url AJAX request URL. * @param successCallback AJAX success callback function. */ var ajax = function( url, successCallback ) { $.ajax({ url : url, type : "GET", dataType : "jsonp", // JSONP cross-domain request crossDomain : true, async : true, cache : true, jsonp : "jsoncallback", // Callback function is called 'jsoncallback'. success : successCallback }); }; /* * Append query string to the GET URL. * * @param params An object of parameters to serialize * @param url URL appended to. */ var appendQuery = function( params, url ) { var // Serialize params object into a URL query string p = $.param(params), s; url = url || FlickrUtil.prototype.DOMAIN; s = url.indexOf('?') < 0 ? "?" : "&"; return url + s + p; }; /* * Get a list of public photos for the given user. * * @param userId The NSID of the user who's photos to return. * @param apiKey Flickr API application key. See http://www.flickr.com/services/api/misc.api_keys.html for more details. * @param perPage Number of photos to return per page. * @param page The page of results to return. * @param successCallback AJAX success callback function. */ FlickrUtil.prototype.getPublicPhotos = function( userId, apiKey, perPage, page, successCallback ) { var method = "flickr.people.getPublicPhotos", params, url; params = { // flickr API: 'flickr.people.getPublicPhotos'. method: method, // The NSID of the user who's photos to return. user_id: userId, // Flickr API application key. api_key : apiKey, // Return JSON formatted data. format : "json" }; if (perPage) { // Set parameter per_page if perPage is specified. params.per_page = perPage; } if (page) { // Set parameter page if page is specified. params.page = page; } // Append query parameters to the flickr domain. url = appendQuery(params); // JSONP GET request. ajax(url, successCallback); }; /* * Returns the available sizes for a photo. * * @param photoId The id of the photo to fetch size information for. * @param apiKey Flickr API application key. See http://www.flickr.com/services/api/misc.api_keys.html for more details. * @param successCallback AJAX success callback function. */ FlickrUtil.prototype.getSizes = function( photoId, apiKey, successCallback ) { var method = "flickr.photos.getSizes", params, url; params = { // flickr API: flickr.photos.getSizes. method: method, // The id of the photo to fetch size information for. photo_id: photoId, // Flickr API application key. api_key : apiKey, // Return JSON formatted data. format : "json" }; // Append query parameters to the flickr domain url = appendQuery(params); // JSONP GET request. ajax(url, successCallback); }; }()); function Photo( obj ) { "use strict"; // An array of object // { // "id": "XXX", // "owner": "XXX", // "secret": "XXX", // "server": "XXX", // "farm": XXX, // "title": "XXX", // "ispublic": XXX, // "isfriend": 0, // "isfamily": 0 // } this.data = obj; // An array of object // { // "label": "Square/Large Square/Thumbnail/Small/Small 320/Medium/Medium 640/Medium 800/Large/Large 1600/Large 2048", // "width": XXX, // "height": XXX, // "source": "http:XXX", // "url": "http:XXX", // "media": "photo", // } this.sizes = null; } (function() { "use strict"; /* * Get the ID of the photo. */ Photo.prototype.getId = function() { return this.data.id; }; /* * Get the title of the photo. */ Photo.prototype.getTitle = function() { return this.data.title; }; /* * Query the available sizes for the photo and set it to the photo. * * @param apiKey Flickr API application key. See http://www.flickr.com/services/api/misc.api_keys.html for more details. * @param callback After setting sizes for the photo, invoke callback function. * @param context The given this value of invoking callback function. */ Photo.prototype.querySizes = function( apiKey, callback, context ) { var id = this.getId(), self = this; FlickrUtil.prototype.getSizes(id, apiKey, function(data) { if (data && data.sizes) { // Set available sizes for the photo. self.setSizes(data.sizes.size); if (typeof callback === "function") { // Invoke callback function with this value set to context. callback.call(context, self); } } }); }; /* * Set available sizes for the photo. * * @param sizes An array. of available sizes. */ Photo.prototype.setSizes = function( sizes ) { this.sizes = sizes; }; /* * Get size object of specified label. * * @param label 'Square/Large Square/Thumbnail/Small/Small 320/Medium/Medium 640/Medium 800/Large/Large 1600/Large 2048' */ Photo.prototype.getSize = function( label ) { var size, i = 0; if (this.sizes) { // Loop through sizes array. for (; i < this.sizes.length; i++) { size = this.sizes[i]; if (size.label === label) { // Find size object of specified label. return size; } } } return null; }; /* * Return the URL source of 'Large Square' image. */ Photo.prototype.getThumbnail = function() { var size = this.getSize('Large Square'); if (size) { // Return the URL source. return size.source; } return ""; }; /* * Return the URL source of 'Medium 800' image. */ Photo.prototype.getLarge = function() { var size = this.getSize('Medium 800'); if (size) { // Return the URL source. return size.source; } return ""; }; }()); function Album( userId, apiKey, perPage ) { "use strict"; // The NSID of the given user. this.userId = userId; // Flickr API application key. this.apiKey = apiKey; // Number of photos to return per page. this.perPage = perPage || 50; // The loaded photos. this.photos = []; // Current page of photos. this.page = 0; // Total pages of photos. this.pages = 0; // Total number of photos. this.total = 0; // Map of {id: index} to boost search efficiency. this.indexes = {}; } (function() { "use strict"; /* * Get public photos of the given user. * * @param context The given this value of invoking callback function. * @param callback After loading photos, invoke callback function. */ Album.prototype.loadPhotos = function( context, callback ) { var self = this; FlickrUtil.prototype.getPublicPhotos(this.userId, this.apiKey, this.perPage, this.page + 1, function( data ) { var photoArray = null, start = self.photos.length, id, i = 0; if (data.photos) { // Set total number of photos. self.total = data.photos.total; // Set total pages of photos. self.pages = data.photos.pages; // Set the current page number. self.page++; photoArray = data.photos.photo; if (photoArray) { // Loop through photos. for (; i < photoArray.length; i++) { // Create Photo and append it to the album. self.addPhoto(photoArray[i]); } } if (typeof callback === "function") { // Invoke callback function with this value set to context. callback.call(context, self.photos, start); } } }); }; /* * Add photo to the album. * * @param data An object of {"id": "XXX", "owner": "XXX", "secret": "XXX", "server": "XXX", "farm": XXX, "title": "XXX", "ispublic": XXX, "isfriend": 0, "isfamily": 0} */ Album.prototype.addPhoto = function( data ) { var // Create photo object. photo = new Photo(data), index = this.photos.length, // Get the id of the photo. id = photo.getId(); // Generate map of {id: index}. this.generateIndexes(id, index); // Append photo to the album. this.photos[index] = photo; return photo; }; /* * Generate Indexes to boost search efficiency. * * @param id The id of the photo. * @param index The index of the photo. */ Album.prototype.generateIndexes = function( id, index ) { this.indexes[id] = index; }; /* * Search photo by index. * * @param index The index of the photo. */ Album.prototype.getPhoto = function( index ) { if (index >= 0 && index < this.photos.length) { return this.photos[index]; } // Not found. return null; }; /* * Search the index of the photo. * * @param photo The photo object to search for. */ Album.prototype.getIndex = function( photo ) { var id, i = 0; if (photo) { // The id of the photo. id = photo.getId(); if (this.indexes.hasOwnProperty(id)) { // Search via indexes map. return this.indexes[id]; } else { // Loop through photos. for (; i < this.photos.length; i++) { if (id === this.photos[i].getId()) { return i; } } } } // Not found. return -1; }; /* * Get the current size of the album. */ Album.prototype.size = function() { return this.photos.length; }; /* * Check whether the album is fully loaded. */ Album.prototype.isFull = function() { return this.page >= this.pages; }; }()); function Carousel( userId, apiKey, slideWindow, carousel ) { "use strict"; // The NSID of the given user. this.userId = userId; // The flickr application key. this.apiKey = apiKey; // Create the photo album. this.album = new Album(userId, apiKey, slideWindow), // The carousel ul element. this.carousel = carousel; // The number of thumbnails per slide. this.perSlide = 3; // The current slide number. this.currentSlide = 1; // The index of selected photo. this.selectedIndex = -1; // The size of thumbnail images. this.size = 3; } (function() { "use strict"; /* * Load photos and create thumbnail images. */ Carousel.prototype.load = function() { this.album.loadPhotos(this, this.insert); }; /* * Check whether two slides ahead are already loaded. */ Carousel.prototype.twoSlidesAheadExist = function() { var // The number of thumbnails of 2 slides. twoSlides = this.perSlide * 2, // The size of album. photos = album.size(); // Whether the next 2 pages are already loaded. return (photos - this.size) < twoSlides; }; /* * Pre-load and create thumbnail images out of performance concern. */ Carousel.prototype.prefetch = function() { if (// Whether the album is fully loaded. !this.album.isFull() && // Whether two slides ahead are already loaded. this.twoSlidesAheadExist()) { // Pre-load photos. this.load(); } }; /* * Create and insert thumbnail images into DOM. * * @param photos The array of photos. * @param start The start index to deal with. */ Carousel.prototype.insert = function( photos, start ) { var photo, i = start, carousel = this.carousel, id, title, selector; // Loop through photos starting at start index. for (; i < photos.length; i++) { photo = photos[i]; // The id of the photo. id = photo.getId(); // The title of the photo. title = photo.getTitle(); if (i < this.perSlide) { // The thumbnail images in the first slide are already created. // Simply set the properties of the image element. selector = carousel.find('li:nth-child(' + (i + 1) +')') // Set id of li to the id of the photo. selector.find('a').prop('id', id); // Set alt of img to the title of the photo. selector.find('img').prop('alt', title); } else { // Create thumbnail images and insert into DOM. $('<li class="ui-thumbnail ui-photo-gap"> \ <a id="' + id + '" href="#" class="ui-photo-border"> \ <img alt="' + title + '"> \ </a> \ </li>').appendTo(carousel); // Increment the size of thumbnails. this.size++; } // Retrieve sizes for the given photo. photo.querySizes(this.apiKey, this.update, this); } }; /* * Update thumbnail image source. */ Carousel.prototype.update = function( photo ) { var id = photo.getId(), url = photo.getThumbnail(), index; $('#' + id).find('img').prop('src', url); if (this.selectedIndex < 0) { index = this.album.getIndex(photo); if (index === 0) { // Display first photo. this.goNextPhoto(); } } }; /* * Navigate to next slide. */ Carousel.prototype.goNextSlide = function() { this.currentSlide++; // Trigger slide event. $('body').trigger("slide", [-1, this.prevSlideEnabled(), this.nextSlideEnabled()]); // Prefetch photo data if needed. this.prefetch(); }; /* * Navigate to previous slide. */ Carousel.prototype.goPrevSlide = function() { this.currentSlide--; // Trigger slide event. $('body').trigger("slide", [+1, this.prevSlideEnabled(), this.nextSlideEnabled()]); }; /* * Check whether there is next slide. */ Carousel.prototype.nextSlideEnabled = function() { var slides = this.currentSlide * this.perSlide; return slides < this.album.total; }; /* * Check whether there is previous slide. */ Carousel.prototype.prevSlideEnabled = function() { return this.currentSlide > 1; }; /* * Select photo of given index. * * @param index The index of the photo. */ Carousel.prototype.selectIndex = function( index ) { var photo = this.album.getPhoto(index), slides = this.currentSlide * this.perSlide, url, id, title; // Display photo. if (photo) { // Get the URL of the large photo. url = photo.getLarge(); // Get the id of the photo. id = photo.getId(); // Get title of the photo. title = photo.getTitle(); if (url && url != "") { // Update selected index. this.selectedIndex = index; // Trigger switch photo event. $('body').trigger("switch", [id, url, title, this.prevPhotoEnabled(), this.nextPhotoEnabled()]); if (index >= slides && this.nextSlideEnabled()) { // Navigate to next slide this.goNextSlide(); } else if (index < (slides - this.perSlide) && this.prevSlideEnabled()) { // Navigate to previous slide this.goPrevSlide(); } } } }; /* * Select specified photo. * * @param id The id of the photo. */ Carousel.prototype.selectPhoto = function( id ) { var index = this.album.indexes[id], photo = this.album.getPhoto(index), url, id, title; // Display photo. if (photo) { // Get the URL of the large photo. url = photo.getLarge(); // Get the id of the photo. id = photo.getId(); // Get title of the photo. title = photo.getTitle(); if (url && url != "") { // Update selected index. this.selectedIndex = index; // Trigger switch photo event. $('body').trigger("switch", [id, url, title, this.prevPhotoEnabled(), this.nextPhotoEnabled()]); } } }; /* * Navigate to next photo. */ Carousel.prototype.goNextPhoto = function() { this.selectIndex(this.selectedIndex + 1); }; /* * Navigate to previous photo. */ Carousel.prototype.goPrevPhoto = function() { this.selectIndex(this.selectedIndex - 1); }; /* * Check whether there is next photo. */ Carousel.prototype.nextPhotoEnabled = function() { return this.selectedIndex < this.album.total - 1; }; /* * Check whether there is previous photo. */ Carousel.prototype.prevPhotoEnabled = function() { return this.selectedIndex > 0; }; }()); $(function() { "use strict"; var userId = "82394319@N04", apiKey = "d39392ef30a5c208d20d47e86583d3f4", slideWindow = 15, offsetLeft = 0, carousel = new Carousel(userId, apiKey, slideWindow, $('#carousel')), slideNextBtn = $('#slideNextBtn'), slidePrevBtn = $('#slidePrevBtn'), photoNextBtn = $('#photoNextBtn'), photoPrevBtn = $('#photoPrevBtn'), selectedThumbnail = null; $('body').on('slide', function( event, direction, prevSlideEnabled, nextSlideEnabled ) { //get the width of the li items var item_width = ($('#carousel li').outerWidth() + 70) * 3; //calculae the new left indent of the unordered list var left_indent = offsetLeft + direction * item_width; //make the sliding effect using jquery's anumate function ' $('#carousel:not(:animated)').animate({'left' : left_indent}, 500, function(){ //and get the left indent to the value left_indent $('#carousel').css({'left' : left_indent}); offsetLeft = left_indent; if (// Allow slide right. nextSlideEnabled && // Slide right button is disabled. slideNextBtn.hasClass('ui-disabled')) { // Enable slide right button. slideNextBtn.removeClass('ui-disabled'); } else if (// Disallow slide right. !nextSlideEnabled && // Slide right button is enabled. !slideNextBtn.hasClass('ui-disabled')) { // Disable slide right button. slideNextBtn.addClass('ui-disabled'); } if (// Allow slide left. prevSlideEnabled && // Slide left button is disabled. slidePrevBtn.hasClass('ui-disabled')) { // Enable slide left button. slidePrevBtn.removeClass('ui-disabled'); } else if (// Disallow slide left. !prevSlideEnabled && // Slide left button is enabled. !slidePrevBtn.hasClass('ui-disabled')) { // Disable slide left button. slidePrevBtn.addClass('ui-disabled'); } }); }); $('body').on('switch', function( event, id, url, title, prevPhotoEnabled, nextPhotoEnabled ) { var thumbnail; // Display photo. $('#photo').prop('src', url); // Display title. $('#photoTitle').text(title); // Highlight thumbnail. thumbnail = $('#' + id); thumbnail.addClass('ui-selected'); if (selectedThumbnail) { // Remove highlight of previous selected thumbnail. selectedThumbnail.removeClass('ui-selected'); } selectedThumbnail = thumbnail; if (// Allow switch right. nextPhotoEnabled && // Switch right button is disabled. photoNextBtn.hasClass('ui-disabled')) { // Enable switch right button. photoNextBtn.removeClass('ui-disabled'); } else if (// Disallow switch right. !nextPhotoEnabled && // Switch right button is enabled. !photoNextBtn.hasClass('ui-disabled')) { // Disable switch right button. photoNextBtn.addClass('ui-disabled'); } if (// Allow switch left. prevPhotoEnabled && // Switch left button is disabled. photoPrevBtn.hasClass('ui-disabled')) { // Enable switch left button. photoPrevBtn.removeClass('ui-disabled'); } else if (// Disallow switch left. !prevPhotoEnabled && // Switch left button is enabled. !photoPrevBtn.hasClass('ui-disabled')) { // Disable switch left button. photoPrevBtn.addClass('ui-disabled'); } }); //when user clicks the image for sliding right slideNextBtn.on('click', function() { carousel.goNextSlide(); return false; }); //when user clicks the image for sliding right slidePrevBtn.on('click', function() { carousel.goPrevSlide(); return false; }); photoPrevBtn.on('click', function() { carousel.goPrevPhoto(); return false; }); photoNextBtn.on('click', function() { carousel.goNextPhoto(); return false; }); $('#carousel').on('click', function(event) { var elem, id; // Prevent default behavior. event.preventDefault(); elem = event.srcElement; if (elem instanceof HTMLImageElement) { elem = elem.parentElement; } if (elem instanceof HTMLAnchorElement) { id = elem.id; carousel.selectPhoto(id); } return false; }); carousel.load(); });
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Flickr Album</title> <link rel="stylesheet" href="css/style.css"> <!--[if IE]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body> <header> <div class="ui-header-bar"></div> <div class="ui-logo"></div> </header> <section class="ui-body"> <div class="ui-photo-gallery ui-photo-border"> <img id="photo" title="Tree" alt="Tree" src="https://lh4.googleusercontent.com/-Hm1SRQlDoOg/UFBY4O4fDJI/AAAAAAAAAvg/hepttL9t4vA/s800/photo-holder.png"> <p id="photoTitle" class="ui-photo-title">Tree</p> <a id="photoPrevBtn" href="#" title="Prev" class="ui-photo-prev-btn"> <span class="ui-photo-prev-inner"></span> </a> <a id="photoNextBtn" href="#" title="Next" class="ui-photo-next-btn"> <span class="ui-photo-next-inner"></span> </a> </div> <div class="ui-slide-strip"> <a id="slidePrevBtn" href="#" title="Prev" class="ui-slide-prev-btn ui-disabled"> <span class="ui-slide-prev-inner"></span> </a> <div class="ui-carousel-container"> <ul id="carousel" class="ui-carousel"> <li class="ui-thumbnail"> <a href="#" class="ui-photo-border"> <img alt="thumb1" src="https://lh3.googleusercontent.com/-2hMHqHSBEJo/UFBY5AZOBaI/AAAAAAAAAvo/g9KvHectYg8/s150/thumbnail-holder.png"> </a> </li> <li class="ui-thumbnail ui-photo-gap"> <a href="#" class="ui-photo-border"> <img alt="thumb2" src="https://lh3.googleusercontent.com/-2hMHqHSBEJo/UFBY5AZOBaI/AAAAAAAAAvo/g9KvHectYg8/s150/thumbnail-holder.png"> </a> </li> <li class="ui-thumbnail ui-photo-gap"> <a href="#" class="ui-photo-border"> <img alt="thumb3" src="https://lh3.googleusercontent.com/-2hMHqHSBEJo/UFBY5AZOBaI/AAAAAAAAAvo/g9KvHectYg8/s150/thumbnail-holder.png"> </a> </li> </ul> </div> <a id="slideNextBtn" href="#" title="Next" class="ui-slide-next-btn"> <span class="ui-slide-next-inner"></span> </a> </div> </section> </body> </html>
html, body { height: 100%; } body { font-family: Helvetica, Arial, "Trebuchet MS", Tahoma, sans-serif; font-size: 100%; color: #333; margin: 0; background-color: #dddacf; background: url(../resources/icons/bg-texture.png) repeat; } p { margin: 0; } a, a span, ul, li { display: inline-block; } .ui-header-bar { height: 40px; background: url(https://lh5.googleusercontent.com/-BCEyRJqtWI8/UFBY0dh28_I/AAAAAAAAAvI/ExKqRYG3h2A/s230/header-bar.png) repeat-x; } .ui-logo { position: relative; top: -30px; width: 800px; height: 185px; margin: 0 auto; background: url(https://lh3.googleusercontent.com/-wqwH7m3grlk/UFBY1oQw-TI/AAAAAAAAAvQ/PT0sr6dR5ZM/s800/logo-bar.png) no-repeat; z-index: 10; } .ui-body { margin-top: -70px; padding-bottom: 50px; } .ui-photo-border { border-radius: 6px; -webkit-border-radius: 6px; -moz-border-radius: 6px; box-shadow: 0 5px 5px #ccc; -webkit-box-shadow: 0 5px 5px #ccc; -moz-box-shadow: 0 5px 5px #ccc; } .ui-photo-gallery { position: relative; margin: 0 auto; width: 800px; background: #fff; border: 10px solid #fff; } .ui-photo-prev-btn { position: absolute; top: 244px; left: -30px; } .ui-photo-prev-inner, .ui-photo-next-inner, .ui-slide-prev-inner, .ui-slide-next-inner { background-image: url(https://lh4.googleusercontent.com/-GUVGpNqiOXQ/UFBY29sEH7I/AAAAAAAAAvY/8e2xXHL3S1M/s130/nav-btn.png); } .ui-photo-prev-inner, .ui-photo-next-inner { width: 60px; height: 65px; } .ui-photo-prev-inner { background-position: 0 0; } .ui-photo-next-btn { position: absolute; top: 244px; right: -35px; } .ui-photo-next-inner { background-position: -65px 0; } .ui-slide-strip { margin: 40px auto 0 auto; width: 800px; text-align: center; } .ui-slide-prev-btn, .ui-slide-next-btn { position: relative; top: -50px; } .ui-slide-prev-inner, .ui-slide-next-inner { width: 30px; height: 35px; } .ui-slide-prev-inner { background-position: 0 -65px; } .ui-slide-next-inner { background-position: -30px -65px; } .ui-carousel-container { display: inline-block; width: 660px; height: 180px; overflow: hidden; padding: 10px; margin: 0 20px; } .ui-carousel { width: 660px; height: 180px; white-space: nowrap; padding: 0; margin: 0; position: relative; left: 0; } .ui-photo-gap { margin-left: 70px; } .ui-thumbnail > a { padding: 10px; background: #fff; -o-transform: rotate(-6deg); -webkit-transform: rotate(-6deg); -moz-transform: rotate(-6deg); -moz-transition: -moz-transform .15s linear; -o-transition: -o-transform .15s linear; -webkit-transition: -webkit-transform .15s linear; position: relative; } .ui-thumbnail:nth-child(even) > a { -o-transform: rotate(4deg); -webkit-transform: rotate(4deg); -moz-transform: rotate(4deg); top: 5px; } .ui-thumbnail:nth-child(3n) > a { -o-transform: rotate(-3deg); -webkit-transform: rotate(-3deg); -moz-transform: rotate(-3deg); top: -5px; } .ui-thumbnail:nth-child(5n) > a { -o-transform: rotate(2deg); -webkit-transform: rotate(2deg); -moz-transform: rotate(2deg); top: -2px; } .ui-thumbnail a:hover, .ui-selected { background: rgba(255, 255, 204, .5) !important; -o-transform: rotate(0deg); -webkit-transform: rotate(0deg); -moz-transform: rotate(0deg); -moz-box-shadow: 2px 2px 5px #a3a3a3; -webkit-box-shadow: 2px 2px 5px #a3a3a3; box-shadow: 2px 2px 5px #a3a3a3; } .ui-photo-title { text-align: right; font-style: italic; } .ui-disabled { opacity: .5; } .ui-disabled, .ui-disabled a { cursor: default !important; pointer-events: none; }