گالری جایزه عکاسی گوگل

Ilmari Heikkinen

وب سایت جایزه عکاسی گوگل

اخیراً بخش گالری را در سایت جایزه عکاسی گوگل راه اندازی کردیم. گالری یک لیست پیمایشی بی نهایت از عکس های واکشی شده از +Google را نشان می دهد. فهرست عکس‌ها را از یک برنامه AppEngine دریافت می‌کند که ما برای تعدیل فهرست عکس‌های موجود در گالری از آن استفاده می‌کنیم. ما همچنین برنامه گالری را به عنوان یک پروژه منبع باز در Google Code منتشر کردیم.

صفحه گالری

پس‌زمینه گالری یک برنامه AppEngine است که از Google+ API برای جستجوی پست‌هایی با یکی از هشتگ‌های جایزه عکاسی Google روی آن (مانند #megpp و #travelgpp) استفاده می‌کند. سپس این برنامه آن پست ها را به لیست عکس های کنترل نشده خود اضافه می کند. هفته‌ای یک‌بار، تیم محتوای ما فهرستی از عکس‌های کنترل‌نشده را بررسی می‌کند و عکس‌هایی را که دستورالعمل‌های محتوای ما را نقض می‌کنند، علامت‌گذاری می‌کند. پس از زدن دکمه Moderate، عکس های بدون پرچم به لیست عکس های نمایش داده شده در صفحه گالری اضافه می شوند.

باطن اعتدال

نمای گالری با استفاده از کتابخانه Google Close ساخته شده است. ویجت گالری خود جزء بسته شدن است. در بالای فایل منبع، به 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');

صفحه گالری دارای کمی جاوا اسکریپت است که از JSONP برای بازیابی لیست عکس ها از برنامه AppEngine استفاده می کند. JSONP یک هک جاوا اسکریپت با منبع متقابل ساده است که اسکریپتی شبیه به jsonpcallback("responseValue") تزریق می کند. برای رسیدگی به موارد JSONP، ما از مؤلفه goog.net.Jsonp در کتابخانه Closure استفاده می‌کنیم.

اسکریپت گالری لیست عکس ها را مرور می کند و عناصر 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 است. پس از ایجاد، می توانید یک پرس و جو را با یک شی پارامتر query و یک تابع callback موفقیت به ارائه دهنده Jsonp ارسال کنید.

/**
 * 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 در نظرات خود برای تنظیم نوع خاصیت استفاده می‌کنید) و همچنین به شما می‌گوید که چه زمانی برای یک متد نظری ندارید.

تست های واحد

ما همچنین به تست های واحد برای اسکریپت گالری نیاز داشتیم، بنابراین مفید است که کتابخانه Closure یک چارچوب تست واحد در آن تعبیه شده باشد. از قراردادهای jsUnit پیروی می کند، بنابراین شروع کار با آن آسان است.

برای کمک به من در نوشتن تست‌های واحد، یک اسکریپت Ruby کوچک نوشتم که فایل جاوا اسکریپت را تجزیه می‌کند و برای هر متد و ویژگی در مؤلفه گالری، یک آزمون واحد ناموفق ایجاد می‌کند. با توجه به اسکریپتی مانند:

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 برای نمایش گالری ارسالی استفاده می شود. در این مقاله به قسمت های شاداب اسکریپت فرانت اند پرداختیم. همکار من Johan Euphrosine از تیم App Engine Developer Relations در حال نوشتن دومین مقاله در مورد برنامه Backend است. پشتیبان به زبان Go، زبان سمت سرور جدید گوگل نوشته شده است. پس اگر علاقه مند به دیدن نمونه تولید کد Go هستید، با ما همراه باشید!

مراجع