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

इल्मारी हीकिनेन

Google फ़ोटोग्राफ़ी पुरस्कार वेबसाइट

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

गैलरी पेज

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

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

Gallery फ़्रंटएंड, Google Close लाइब्रेरी का इस्तेमाल करके बनाया गया है. गैलरी विजेट खुद ही एक क्लोज़र कॉम्पोनेंट है. सोर्स फ़ाइल में सबसे ऊपर, हम Close को यह बताते हैं कि यह फ़ाइल photographyPrize.Gallery नाम का एक कॉम्पोनेंट उपलब्ध कराती है और इसे ऐप्लिकेशन बंद करने वाली लाइब्रेरी के उन हिस्सों की ज़रूरत है जिनका इस्तेमाल किया जा रहा हो:

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 कॉन्टेंट को मैनेज करने के लिए, हम Close लाइब्रेरी में goog.net.Jsonp कॉम्पोनेंट का इस्तेमाल कर रहे हैं.

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

इमेज की सूची दिखाई जा रही है

इमेज सूची दिखाने का तरीका काफ़ी सामान्य है. यह इमेज सूची में जाता है, एचटीएमएल एलिमेंट और +1 बटन जनरेट करता है. अगला चरण, जनरेट किए गए सूची सेगमेंट को गैलरी के मुख्य गैलरी एलिमेंट में जोड़ना है. नीचे दिए गए कोड में, Close पर कंपाइलर के कुछ कन्वेंशन देखे जा सकते हैं. साथ ही, JSDoc टिप्पणी और @private visibility में टाइप की परिभाषाएं देखें. निजी विधियों के नाम के बाद एक अंडरस्कोर (_) होता है.

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

स्क्रोल इवेंट मैनेज करना

यह देखने के लिए कि वेबसाइट पर आने वाले व्यक्ति ने पेज को सबसे नीचे कब स्क्रोल किया है और हमें नई इमेज लोड करने की ज़रूरत है, गैलरी, विंडो ऑब्जेक्ट के स्क्रोल इवेंट से जुड़ जाती है. ब्राउज़र लागू करने के तरीके में अंतर दिखाने के लिए, हम Close लाइब्रेरी के कुछ आसान यूटिलिटी फ़ंक्शन का इस्तेमाल कर रहे हैं: 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);
};

जैसा कि ऊपर बताया गया है, कोड को कंपाइल और छोटा करने के लिए गैलरी स्क्रिप्ट, बंद करने वाले कंपाइलर का इस्तेमाल करती है. क्लोज़र कंपाइलर, सही टाइपिंग लागू करने में भी मदद करता है. प्रॉपर्टी का टाइप सेट करने के लिए, टिप्पणियों में @type foo JSDoc नोटेशन का इस्तेमाल किया जाता है. साथ ही, किसी तरीके के लिए टिप्पणी न किए जाने पर भी यह नोटेशन इस्तेमाल करता है.

यूनिट टेस्ट

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

References