แกลเลอรีของ Google Photography Prize

เว็บไซต์ Google Photography Prize

เมื่อไม่นานมานี้เราได้เปิดตัวส่วนแกลเลอรีในเว็บไซต์ Google Photography Prize แกลเลอรีจะแสดงรายการรูปภาพที่เลื่อนดูได้แบบไม่สิ้นสุดซึ่งดึงมาจาก Google+ โดยระบบจะดึงข้อมูลรายการรูปภาพจากแอป AppEngine ที่เราใช้เพื่อดูแลจัดการรายการรูปภาพในแกลเลอรี นอกจากนี้ เรายังได้เปิดตัวแอปแกลเลอรีเป็นโปรเจ็กต์โอเพนซอร์สใน Google Code ด้วย

หน้าแกลเลอรี

แบ็กเอนด์ของแกลเลอรีเป็นแอป AppEngine ที่ใช้ Google+ API เพื่อค้นหาโพสต์ที่มีแฮชแท็ก Google Photography Prize อย่างน้อย 1 รายการ (เช่น #megpp และ #travelgpp) จากนั้นแอปจะเพิ่มโพสต์เหล่านั้นลงในรายการรูปภาพที่ไม่มีการควบคุมดูแล ทีมเนื้อหาจะตรวจสอบรายการรูปภาพที่ไม่มีการควบคุมดูแลทุกสัปดาห์และแจ้งว่าไม่เหมาะสมกับรูปภาพที่ละเมิดหลักเกณฑ์ด้านเนื้อหา หลังจากกดปุ่ม "กลั่นกรอง" ระบบจะเพิ่มรูปภาพที่ยกเลิกการแจ้งว่าไม่เหมาะสมลงในรายการรูปภาพที่แสดงในหน้าแกลเลอรี

แบ็กเอนด์การดูแล

ฟรอนต์เอนด์ของแกลเลอรีสร้างขึ้นโดยใช้ไลบรารี Closure ของ Google วิดเจ็ตแกลเลอรีเองก็เป็นคอมโพเนนต์การปิด ที่ด้านบนของไฟล์ต้นฉบับ เราบอก Closure ว่าไฟล์นี้มีคอมโพเนนต์ชื่อ photographyPrize.Gallery และต้องการใช้ชิ้นส่วนของไลบรารี Closure ที่แอปใช้

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');

หน้าแกลเลอรีมี JavaScript เล็กน้อยที่ใช้ JSONP เพื่อดึงข้อมูลรายการรูปภาพจากแอป AppEngine ซึ่ง JSONP คือการแฮ็ก JavaScript ข้ามแหล่งที่มาแบบง่ายที่แทรกสคริปต์ที่มีลักษณะเหมือน jsonpcallback("responseValue") เราใช้คอมโพเนนต์ goog.net.Jsonp ในไลบรารี Closure เพื่อจัดการกับ JSONP

สคริปต์แกลเลอรีจะเรียกดูรายการรูปภาพและสร้างองค์ประกอบ HTML เพื่อแสดงรูปภาพในหน้าแกลเลอรี การเลื่อนได้ไม่รู้จบจะทํางานโดยเชื่อมต่อกับเหตุการณ์การเลื่อนหน้าต่างและโหลดรูปภาพชุดใหม่เมื่อการเลื่อนหน้าต่างใกล้ถึงด้านล่างของหน้า หลังจากโหลดกลุ่มรายการรูปภาพใหม่แล้ว สคริปต์แกลเลอรีจะสร้างองค์ประกอบสําหรับรูปภาพและเพิ่มลงในองค์ประกอบแกลเลอรีเพื่อแสดง

การแสดงรายการรูปภาพ

วิธีการแสดงรายการรูปภาพเป็นวิธีการพื้นฐาน โดยจะเรียกใช้รายการรูปภาพ สร้างองค์ประกอบ HTML และปุ่ม +1 ขั้นตอนถัดไปคือการเพิ่มกลุ่มรายการที่สร้างขึ้นไปยังองค์ประกอบแกลเลอรีหลักของแกลเลอรี คุณจะเห็นรูปแบบบางอย่างของคอมไพเลอร์ Closure ในโค้ดด้านล่าง โปรดสังเกตคำจำกัดความของประเภทในความคิดเห็น JSDoc และระดับการเข้าถึง @private เมธอดส่วนตัวจะมีขีดล่าง (_) ต่อจากชื่อ

/**
 * 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;
};

การจัดการเหตุการณ์การเลื่อน

หากต้องการดูว่าเมื่อใดที่ผู้เข้าชมเลื่อนหน้าเว็บไปจนสุดและเราจําเป็นต้องโหลดรูปภาพใหม่ แกลเลอรีจะเชื่อมต่อกับเหตุการณ์การเลื่อนของออบเจ็กต์หน้าต่าง เราใช้ฟังก์ชันยูทิลิตีที่มีประโยชน์จากไลบรารี Closure เพื่อปกปิดความแตกต่างในการใช้งานเบราว์เซอร์ ดังนี้ goog.dom.getDocumentScroll() จะแสดงออบเจ็กต์ {x, y} ที่มีตำแหน่งการเลื่อนเอกสารปัจจุบัน goog.dom.getViewportSize() จะแสดงขนาดหน้าต่าง และ goog.dom.getDocumentHeight() จะแสดงความสูงของเอกสาร 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() {
  // ...
};

กำลังโหลดรูปภาพ

เราใช้คอมโพเนนต์ goog.net.Jsonp เพื่อโหลดรูปภาพจากเซิร์ฟเวอร์ ใช้เวลา goog.Uri ในการแสดงผล เมื่อสร้างแล้ว คุณจะส่งการค้นหาไปยังผู้ให้บริการ Jsonp ได้ด้วยออบเจ็กต์พารามิเตอร์การค้นหาและฟังก์ชัน Callback ที่ดำเนินการสำเร็จ

/**
 * 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);
};

ดังที่กล่าวไว้ข้างต้น สคริปต์แกลเลอรีใช้คอมไพเลอร์ Closure เพื่อคอมไพล์และย่อโค้ด นอกจากนี้ คอมไพเลอร์ Closure ยังมีประโยชน์ในการบังคับใช้การพิมพ์ที่ถูกต้อง (คุณใช้@type fooเครื่องหมาย JSDoc ในความคิดเห็นเพื่อตั้งค่าประเภทของพร็อพเพอร์ตี้) และบอกคุณด้วยเมื่อไม่มีความคิดเห็นสำหรับเมธอด

การทดสอบ 1 หน่วย

เราต้องใช้การทดสอบ 1 หน่วยสำหรับสคริปต์แกลเลอรีด้วย ดังนั้นการที่ไลบรารี Closure มีเฟรมเวิร์กการทดสอบ 1 หน่วยในตัวจึงสะดวกมาก ซึ่งเป็นไปตามรูปแบบของ jsUnit จึงเริ่มต้นใช้งานได้ง่ายๆ

เพื่อช่วยในการเขียนการทดสอบหน่วย เราได้เขียนสคริปต์ Ruby เล็กๆ ที่แยกวิเคราะห์ไฟล์ JavaScript และสร้างการทดสอบหน่วยที่ไม่ผ่านสําหรับเมธอดและพร็อพเพอร์ตี้แต่ละรายการในคอมโพเนนต์แกลเลอรี เมื่อมีสคริปต์ เช่น

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

เครื่องมือสร้างการทดสอบจะสร้างการทดสอบว่างสําหรับพร็อพเพอร์ตี้แต่ละรายการ ดังนี้

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

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

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

การทดสอบที่สร้างขึ้นโดยอัตโนมัติเหล่านี้ช่วยให้ฉันเริ่มต้นเขียนการทดสอบโค้ดได้ง่าย และครอบคลุมเมธอดและพร็อพเพอร์ตี้ทั้งหมดโดยค่าเริ่มต้น การทดสอบที่ไม่ผ่านสร้างผลทางจิตวิทยาที่ดีซึ่งทำให้ฉันต้องทําการทดสอบทีละรายการและเขียนการทดสอบที่เหมาะสม เมื่อใช้ร่วมกับเครื่องมือวัดการครอบคลุมโค้ด คุณจะสนุกกับการทำให้การทดสอบและการครอบคลุมเป็นสีเขียวทั้งหมด

สรุป

Gallery+ เป็นโปรเจ็กต์โอเพนซอร์สสำหรับแสดงรายการรูปภาพ Google+ ที่มีการควบคุมซึ่งตรงกับแฮชแท็ก # ซึ่งสร้างขึ้นโดยใช้ Go และไลบรารี Closure แบ็กเอนด์ทำงานบน App Engine ระบบจะใช้ Gallery+ ในเว็บไซต์ Google Photography Prize เพื่อแสดงแกลเลอรีที่ส่งเข้ามา บทความนี้อธิบายข้อมูลที่น่าสนใจเกี่ยวกับสคริปต์ฝั่งหน้าเว็บ เพื่อนร่วมงานของฉัน Johan Euphrosine จากทีมความสัมพันธ์นักพัฒนาแอปของ App Engine กำลังเขียนบทความที่ 2 เกี่ยวกับแอปแบ็กเอนด์ ซึ่งเขียนด้วยภาษา Go ซึ่งเป็นภาษาฝั่งเซิร์ฟเวอร์ใหม่ของ Google ดังนั้นหากคุณสนใจดูตัวอย่างโค้ด Go สำหรับเวอร์ชันที่ใช้งานจริง โปรดติดตามเรา

ข้อมูลอ้างอิง