Google फ़ोटोग्राफ़ी पुरस्कार गैलरी

Ilmari Heikkinen

Google Photography Prize की वेबसाइट

हमने हाल ही में, Google Photography Prize की साइट पर गैलरी सेक्शन लॉन्च किया है. गैलरी में, Google+ से फ़ेच की गई फ़ोटो की एक ऐसी सूची दिखती है जिसे स्क्रोल करके कभी खत्म नहीं किया जा सकता. यह सूची, AppEngine ऐप्लिकेशन से मिलती है. हम इसका इस्तेमाल, गैलरी में फ़ोटो की सूची को मॉडरेट करने के लिए करते हैं. हमने Gallery ऐप्लिकेशन को Google Code पर, ओपन सोर्स प्रोजेक्ट के तौर पर भी रिलीज़ किया है.

गैलरी पेज

गैलरी का बैकएंड, AppEngine ऐप्लिकेशन है. यह Google+ API का इस्तेमाल करके, Google Photography Prize के किसी हैशटैग (जैसे, #megpp और #travelgpp) वाली पोस्ट खोजता है. इसके बाद, ऐप्लिकेशन उन पोस्ट को बिना मॉडरेट की गई फ़ोटो की सूची में जोड़ देता है. हमारी कॉन्टेंट टीम, हफ़्ते में एक बार उन फ़ोटो की सूची देखती है जिन्हें मॉडरेट नहीं किया गया है. साथ ही, उन फ़ोटो को फ़्लैग करती है जो हमारे कॉन्टेंट से जुड़े दिशा-निर्देशों का उल्लंघन करती हैं. 'मॉडरेट करें' बटन दबाने के बाद, फ़्लैग नहीं की गई फ़ोटो, गैलरी पेज पर दिखाई गई फ़ोटो की सूची में जोड़ दी जाती हैं.

मॉडरेशन बैकएंड

गैलरी का फ़्रंटएंड, Google Closure लाइब्रेरी का इस्तेमाल करके बनाया गया है. गैलरी विजेट, क्लोज़र कॉम्पोनेंट होता है. सोर्स फ़ाइल में सबसे ऊपर, हम 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 है, जो AppEngine ऐप्लिकेशन से फ़ोटो की सूची वापस पाने के लिए, JSONP का इस्तेमाल करता है. JSONP, क्रॉस-ऑरिजिन JavaScript हैक है, जो jsonpcallback("responseValue") जैसी स्क्रिप्ट इंजेक्ट करता है. JSONP से जुड़ी चीज़ों को हैंडल करने के लिए, हम Closure लाइब्रेरी में goog.net.Jsonp कॉम्पोनेंट का इस्तेमाल कर रहे हैं.

गैलरी स्क्रिप्ट, फ़ोटो की सूची में जाकर उनके लिए एचटीएमएल एलिमेंट जनरेट करती है, ताकि उन्हें गैलरी पेज पर दिखाया जा सके. इनफ़ाइनाइट स्क्रोलिंग, विंडो स्क्रोल इवेंट से जुड़कर काम करती है. साथ ही, जब विंडो स्क्रोल पेज के सबसे नीचे तक पहुंच जाती है, तब फ़ोटो का एक नया बैच लोड करती है. फ़ोटो की नई सूची का सेगमेंट लोड करने के बाद, गैलरी स्क्रिप्ट फ़ोटो के लिए एलिमेंट बनाती है और उन्हें दिखाने के लिए गैलरी एलिमेंट में जोड़ती है.

इमेज की सूची दिखाना

इमेज की सूची दिखाने का तरीका बहुत आसान है. यह इमेज की सूची में जाकर, एचटीएमएल एलिमेंट और +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() एचटीएमएल दस्तावेज़ की ऊंचाई दिखाता है.

/**
 * 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 प्रोवाइडर को क्वेरी भेजी जा सकती है.

/**
 * 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 स्क्रिप्ट लिखी है. यह स्क्रिप्ट, 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 पर चलता है. सबमिट की गई गैलरी दिखाने के लिए, Google Photography Prize की वेबसाइट पर Gallery+ का इस्तेमाल किया जाता है. इस लेख में, हमने फ़्रंटएंड स्क्रिप्ट के बारे में जानकारी दी है. App Engine की डेवलपर रिलेशनशिप टीम के मेरे सहयोगी, जोहान यूफ़्रोसिन, बैकएंड ऐप्लिकेशन के बारे में दूसरा लेख लिख रहे हैं. बैकएंड को Go में लिखा गया है, जो Google की नई सर्वर-साइड भाषा है. इसलिए, अगर आपको Go कोड का प्रोडक्शन उदाहरण देखना है, तो हमारे साथ बने रहें!

रेफ़रंस