Galeri Google Fotografi Hadiah

Ilmari Heikkinen

Situs Google Fotografi Hadiah

Baru-baru ini, kami meluncurkan bagian Galeri di situs Google Fotografi Hadiah. Galeri menampilkan daftar foto yang dapat di-scroll tanpa batas yang diambil dari Google+. Galeri ini mendapatkan daftar foto dari aplikasi AppEngine yang kami gunakan untuk memoderasi daftar foto di galeri. Kami juga merilis aplikasi galeri sebagai project open source di Google Code.

Halaman galeri

Bagian backend galeri adalah aplikasi AppEngine yang menggunakan Google+ API untuk menelusuri postingan yang berisi salah satu hashtag Google Fotografi Hadiah (mis. #megpp dan #travelgpp). Aplikasi kemudian menambahkan postingan tersebut ke daftar foto yang tidak dimoderasi. Seminggu sekali, tim konten kami memeriksa daftar foto yang tidak dimoderasi dan menandai foto yang melanggar pedoman konten kami. Setelah menekan tombol Moderasi, foto yang tidak ditandai akan ditambahkan ke daftar foto yang ditampilkan di halaman galeri.

Backend moderasi

Frontend Galeri dibuat menggunakan library Google Closure. Widget Galeri itu sendiri merupakan komponen Closure. Di bagian atas file sumber, kita memberi tahu Closure bahwa file ini menyediakan komponen bernama photographyPrize.Gallery dan memerlukan bagian library Closure yang digunakan oleh aplikasi:

goog.provide('photographyPrize.Gallery');

goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.net.Jsonp');
goog.require('goog.style');

Halaman galeri ini berisi sedikit JavaScript yang menggunakan JSONP untuk mengambil daftar foto dari aplikasi AppEngine. JSONP adalah peretasan JavaScript lintas origin sederhana yang memasukkan skrip yang terlihat seperti jsonpcallback("responseValue"). Untuk menangani hal-hal JSONP, kita menggunakan komponen goog.net.Jsonp di library Closure.

Skrip galeri memeriksa daftar foto dan menghasilkan elemen HTML untuk ditampilkan di halaman galeri. Scrolling tanpa batas berfungsi dengan mengaitkan peristiwa scroll jendela dan memuat sekumpulan foto baru saat scroll jendela dekat dengan bagian bawah halaman. Setelah memuat segmen daftar foto baru, skrip galeri membuat elemen untuk foto dan menambahkannya ke elemen galeri untuk menampilkannya.

Menampilkan daftar gambar

Metode tampilan daftar gambar adalah hal yang cukup mendasar. Kode itu akan menelusuri daftar gambar, menghasilkan elemen HTML dan tombol +1. Langkah selanjutnya adalah menambahkan segmen daftar yang dihasilkan ke elemen galeri utama galeri. Anda dapat melihat beberapa konvensi compiler Closure dalam kode di bawah ini, perhatikan definisi jenis dalam komentar JSDoc dan visibilitas @private. Metode pribadi memiliki garis bawah (_) setelah namanya.

/**
 * Displays images in imageList by putting them inside the section element.
 * Edits image urls to scale them down to imageSize x imageSize bounding
 * box.
 *
 * @param {Array.<Object>} imageList List of image objects to show. Retrieved
 *                                   by loadImages.
 * @return {Element} The generated image list container element.
 * @private
 */
photographyPrize.Gallery.prototype.displayImages_ = function(imageList) {
  
  // find the images and albums from the image list
  for (var j = 0; j < imageList.length; j++) {
    // change image urls to scale them to photographyPrize.Gallery.MAX_IMAGE_SIZE
  }

  // Go through the image list and create a gallery photo element for each image.
  // This uses the Closure library DOM helper, goog.dom.createDom:
  // element = goog.dom.createDom(tagName, className, var_childNodes);

  var category = goog.dom.createDom('div', 'category');
  for (var k = 0; k < items.length; k++) {
    var plusone = goog.dom.createDom('g:plusone');
    plusone.setAttribute('href', photoPageUrl);
    plusone.setAttribute('size', 'standard');
    plusone.setAttribute('annotation', 'none');

    var photo = goog.dom.createDom('div', {className: 'gallery-photo'}, ...)
    photo.appendChild(plusone);

    category.appendChild(photo);
  }
  this.galleryElement_.appendChild(category);
  return category;
};

Menangani peristiwa scroll

Untuk melihat kapan pengunjung men-scroll halaman ke bagian bawah dan kita harus memuat gambar baru, galeri akan dikaitkan dengan peristiwa scroll objek jendela. Untuk membahas perbedaan dalam implementasi browser, kami menggunakan beberapa fungsi utilitas praktis dari library Closure: goog.dom.getDocumentScroll() menampilkan objek {x, y} dengan posisi scroll dokumen saat ini, goog.dom.getViewportSize() menampilkan ukuran jendela, dan goog.dom.getDocumentHeight() tinggi dokumen HTML.

/**
 * Handle window scroll events by loading new images when the scroll reaches
 * the last screenful of the page.
 *
 * @param {goog.events.BrowserEvent} ev The scroll event.
 * @private
 */
photographyPrize.Gallery.prototype.handleScroll_ = function(ev) {
  var scrollY = goog.dom.getDocumentScroll().y;
  var height = goog.dom.getViewportSize().height;
  var documentHeight = goog.dom.getDocumentHeight();
  if (scrollY + height >= documentHeight - height / 2) {
    this.tryLoadingNextImages_();
  }
};

/**
 * Try loading the next batch of images objects from the server.
 * Only fires if we have already loaded the previous batch.
 *
 * @private
 */
photographyPrize.Gallery.prototype.tryLoadingNextImages_ = function() {
  // ...
};

Memuat gambar

Untuk memuat gambar dari server, kita menggunakan komponen goog.net.Jsonp. Perlu goog.Uri untuk membuat kueri. Setelah dibuat, Anda dapat mengirim kueri ke penyedia Jsonp dengan objek parameter kueri dan fungsi callback yang berhasil.

/**
 * Loads image list from the App Engine page and sets the callback function
 * for the image list load completion.
 *
 * @param {string} tag Fetch images tagged with this.
 * @param {number} limit How many images to fetch.
 * @param {number} offset Offset for the image list.
 * @param {function(Array.<Object>=)} callback Function to call
 *        with the loaded image list.
 * @private
 */
photographyPrize.Gallery.prototype.loadImages_ = function(tag, limit, offset, callback) {
  var jsonp = new goog.net.Jsonp(
      new goog.Uri(photographyPrize.Gallery.IMAGE_LIST_URL));
  jsonp.send({'tag': tag, 'limit': limit, 'offset': offset}, callback);
};

Seperti yang disebutkan di atas, skrip galeri menggunakan compiler Closure untuk mengompilasi dan meminifikasi kode. Compiler Closure juga berguna dalam menerapkan pengetikan yang benar (Anda menggunakan notasi JSDoc @type foo dalam komentar untuk menyetel jenis properti) dan juga memberi tahu saat Anda tidak memiliki komentar untuk suatu metode.

Pengujian Unit

Kami juga memerlukan pengujian unit untuk skrip galeri, jadi sebaiknya library Closure memiliki kerangka kerja pengujian unit yang terintegrasi di dalamnya. Ini mengikuti konvensi jsUnit, sehingga mudah untuk memulai.

Untuk membantu menulis pengujian unit, saya menulis skrip Ruby kecil yang mengurai file JavaScript dan menghasilkan pengujian unit yang gagal untuk setiap metode dan properti di komponen galeri. Ada skrip seperti:

Foo = function() {}
Foo.prototype.bar = function() {}
Foo.prototype.baz = "hello";

Generator pengujian menghasilkan pengujian kosong untuk setiap properti:

function testFoo() {
  fail();
  Foo();
}

function testFooPrototypeBar = function() {
  fail();
  instanceFoo.bar();
}

function testFooPrototypeBaz = function() {
  fail();
  instanceFoo.baz;
}

Pengujian yang dibuat secara otomatis ini memberi saya awal yang mudah dalam menulis pengujian untuk kode, dan semua metode serta properti dicakup secara default. Tes yang gagal memberikan efek psikologis yang bagus, yaitu saat saya harus melakukan pengujian satu per satu dan menulis pengujian yang sesuai. Dilengkapi dengan pengukur cakupan kode, membuat pengujian dan cakupan semuanya menjadi hijau adalah game yang menyenangkan.

Ringkasan

Gallery+ adalah proyek open source untuk menampilkan daftar foto Google+ yang dimoderasi dan cocok dengan #hashtag. Aplikasi ini dibuat menggunakan library Go dan Closure. Backend berjalan di App Engine. Gallery+ digunakan pada situs Google Fotografi Hadiah untuk menampilkan galeri kiriman. Dalam artikel ini, kita membahas potongan-potongan skrip frontend yang menarik. Tugas saya, Johan Euphrosine dari tim Developer Relations App Engine, sedang menulis artikel kedua yang membahas aplikasi backend. Backend ditulis dalam Go, bahasa sisi server baru Google. Jadi, jika Anda tertarik untuk melihat contoh produksi kode Go, nantikan kabar terbarunya.

Referensi