<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Flickr" height="341"
               title_url="http://flickr.com/photos/me/"
	       description="The Ultimate Flickr Gadget! View recent postings from your Contacts, do photo searches or just oggle 'interesting' pictures. Simple interface, view larger photos inline and always changing. Perfect for the Flickr junkie!"
	       screenshot="http://warmbrain.com/gadgets/flickr.jpg"
	       thumbnail="http://warmbrain.com/gadgets/flickr_thumb.jpg"
	       author_email="dylan.feedback+flickr@gmail.com"
	       author="Dylan Parker"
	       author_location="Victoria BC, Canada"
  >
    <Require feature="dynamic-height" />
    <Require feature="setprefs" />
    <Require feature="tabs" />
  </ModulePrefs> 
  <UserPref name="username" display_name="Flickr Username" />
  <UserPref name="num_photo_rows" display_name="# Photo Rows" datatype="enum" default_value="4">
    <EnumValue value="2" display_value="Two" />
    <EnumValue value="3" display_value="Three" />
    <EnumValue value="4" display_value="Four" />
    <EnumValue value="5" display_value="Five" />
  </UserPref>
  <UserPref name="oldusername" default_value="" datatype="hidden" />
  <UserPref name="NSID" default_value="" datatype="hidden" />
  <UserPref name="search" default_value="blue pink" datatype="hidden" />
  <UserPref name="selectedTab" datatype="hidden"/>
  <Content type="html">
     <![CDATA[ 
<style>

#Photos {
  position:relative;
  overflow:hidden;
  text-align:center;
}

IMG.photoph,IMG.photo {
  background-color:#eaeaea;
  border:0px;
  margin:1px;
}
IMG.photoph {
  width:1px;
  height:1px;
  border-right:74px solid #eaeaea;
  border-bottom:74px solid #eaeaea;
}
IMG.photo {
  width:75px;
  height:75px;
}

#photo_zoom_container,#photo_zoom_dimmer {
  position:absolute;
  width:100%;
  height:100%;
  left:0;
  top:0;
}
#photo_zoom_container {
  z-index:1001;
  font-family:Helvetica,Verdana,sans-serif;
  font-size:80%;
  color: #FF0084;
  font-weight:bold;
}
#photo_zoom_container A {
  color:#0063DC;
}
#photo_zoom_dimmer {
  display:none;
  z-index:1000;
  background-color:white;
  filter:alpha(opacity=85);
  opacity:0.85;
}

.tablib_main_container {
  margin-bottom:10px;
  margin-top:2px;
}
.tablib_unselected, .tablib_spacerTab, .tablib_emptyTab {
  border: 0px;
  border-bottom:1px solid #89b8f2;
}
.tablib_selected, .tablib_unselected {
  font-family:Helvetica,Verdana,sans-serif;
  background-color:#fff;
  font-size:110%;
  padding:4px 0px;
  font-weight:bold;
}
.tablib_selected {
  border-color: #89b8f2;
}
.tablib_unselected {
  padding-bottom:3px;
  padding-top:5px;
  color:#85b4ee;
}
.tablib_unselected .pink {
  color:#ff85c4;
}
.tablib_selected {
  color:#0063DC;
}
.tablib_selected .pink {
  color:#FF0084;
}

#search_button {
  background-color:#0063DC;
  color:white;
  font-weight:bold;
  font-size:80%;
}
</style>

<div id="SearchTab" style="padding:6px 6px 0px 6px">
<table width="100%"><tr>
 <form style="display:inline" onsubmit="SearchTab.onsearch();return false;">
  <td width="100%"><input type="text" style="width:100%" /></td>
  <td><input id="search_button" type="submit" value="SEARCH" /></td>
 </form>
</tr></table>
</div>

<div id="Photos"><!-- Main DIV that holds photo thumbs --></div>

<script>
// Our Flickr API Object that only impls the methods we need
var Flickr = (function(){
  var api_url = "http://api.flickr.com/services/rest/?";
  var api_key = "8aa99c6da16162fa00d7a5992f28d426";
  var base_url = api_url + "api_key=" + api_key + "&format=json&nojsoncallback=1";
  var SEVEN_DAYS = 604800;
  var ONE_HOUR = 3600;
  var FIFTEEN_MINUTES = 900;

  // Common method to query Flickr REST interface
  function fetch(method, params, callback, cache_secs) {
    var url = base_url + "&method=" + method;
    for (var i in params) {
      url += "&" + _esc(i) + "=" + _esc(params[i]);
    }
    _IG_FetchContent(url, callback, cache_secs);
  }

  return {
    MAX_PHOTOS : 100,

    people : {
      findByUsername : function(username, callback) {
        fetch("flickr.people.findByUsername",
	      { "username" : username }, callback, SEVEN_DAYS);
      }
    },

    photos : {
      getContactsPublicPhotos : function(user_id, count, callback) {
        fetch("flickr.photos.getContactsPublicPhotos", { 
	        "user_id" : user_id, 
	        "count" : Math.min(count, Flickr.MAX_PHOTOS) 
	      }, callback, ONE_HOUR);
      },

      search : function(text, per_page, page, sort, callback) {
        fetch("flickr.photos.search", {
	        "text" : text,
		"per_page" : Math.min(per_page, Flickr.MAX_PHOTOS),
		"page" : page,
		"sort" : sort,
		"extras" : "owner_name"
	      }, callback, ONE_HOUR);
      }
    },

    interestingness : {
      getList : function(per_page, page, callback) {
        fetch("flickr.interestingness.getList", { 
	        "per_page" : Math.min(per_page, Flickr.MAX_PHOTOS),
	        "page" : page,
		"extras" : "owner_name"
	      }, callback, FIFTEEN_MINUTES);
      }
    }
  };
})();

// Handles rendering of the 'Contacts' tab
var ContactsTab = (function(){
  var photos = null;

  return {
    onclick : function(div_id) {
      if (photos) {
        Gadget.set_photos(photos);
	return;
      }
      Gadget.set_photos([]);  // So nothing visible while we fetch
      Gadget.maybe_update_nsid(function() {
        Flickr.photos.getContactsPublicPhotos(Gadget.prefs().getString("NSID"),
	                                      Gadget.num_photos_visible() * 4,
	                                      function(data) {
          var obj = eval("(" + data + ")");
          if (!obj || obj.stat == "fail") {
            return Gadget.error("Unable to retrieve photos.<br>" + obj.message);
          }
         
          photos = obj.photos.photo;
          if (photos.length == 0) {
            return Gadget.error("You don't seem to have any contacts.");
          }

	  Gadget.set_photos(photos);
        });
      });
    }
  };
})();

var SearchTab = (function() {
  var div = null;
  var search_field = null;
  var photos = null;
  var search = null;
  var inited = false;

  function init(div_id) {
    div = _gel(div_id);
    search_field = div.getElementsByTagName("INPUT")[0];
    search_field.value = Gadget.prefs().getString("search");
    inited = true;
  }

  function render() {
    if (search_field.value == "") return;
    if (photos) {
      Gadget.set_photos(photos);
      return;
    }
    Gadget.set_photos([]);
    Flickr.photos.search(search_field.value,
                         Gadget.num_photos_visible() * 4, 1,
			 "interestingness-desc",
                         function(data) {
      var obj = eval("(" + data + ")");
      if (!obj || obj.stat == "fail") {
        return Gadget.error("Unable to retrieve photos.<br>" + obj.message);
      }

      photos = obj.photos.photo;
      if (photos.length == 0) {
        return Gadget.error("No photos found for: <b>" + _hesc(search_field.value) + "</b>");    
      }

      for (var i = photos.length-1; i >= 0; i--) {  // Mix them up a bit
	var rnd = Math.floor(Math.random() * (photos.length-1));
        var tmp = photos[i];
	photos[i] = photos[rnd];
	photos[rnd] = tmp;
      }
      Gadget.set_photos(photos);
    });  
  }

  return {
    onsearch : function() {
      if (search_field.value != Gadget.prefs().getString("search")) {
        Gadget.prefs().set("search", search_field.value);  // Save the pref
        photos = null;
      }
      render();
    },

    onclick : function(div_id) {
      if (!inited) init(div_id);
      render();
    }
  };
})();

var InterestingTab = (function() {
  var photos = null;

  return {
    onclick : function(div_id) {
      if (photos) {
        Gadget.set_photos(photos);
	return;
      }
      Gadget.set_photos([]);  // So nothing visible while we fetch
      Flickr.interestingness.getList(Gadget.num_photos_visible() * 4, 1,
                                     function(data) {
        var obj = eval("(" + data + ")");
        if (!obj || obj.stat == "fail") {
          return Gadget.error("Unable to retrieve photos.<br>" + obj.message);
        }

        photos = obj.photos.photo;
	for (var i = photos.length-1; i >= 0; i--) {  // Mix them up a bit
	  var rnd = Math.floor(Math.random() * (photos.length-1));
          var tmp = photos[i];
	  photos[i] = photos[rnd];
	  photos[rnd] = tmp;
	}
        Gadget.set_photos(photos);
      });
    }
  };
})();


function my_add_event(el, etype, func) {
  if (el.addEventListener) {
    el.addEventListener(etype, func, false);
  } else if (el.attachEvent) {
    el.attachEvent("on" + etype, func);
  } else {
    el["on" + etype] = func;
  }
}

// Methods general and accessible to all tabs
// Also handles the rendering of the photo grid
var Gadget = (function() {
  var tabs = new _IG_Tabs(__MODULE_ID__, "Interesting");
  var prefs = new _IG_Prefs(__MODULE_ID__);
  var photos_div = _gel("Photos");
  var photos = [];
  var placeholder_html = null;
  var interval_id = null;
  var use_igoogle_cache = false;
  var loading_image_src = _IG_GetCachedUrl("http://warmbrain.com/gadgets/loading.gif");

  //_IG_AddDOMEventHandler(window, "resize", function() {
  my_add_event(window, "resize", function() {
    render_photos();
  });

  function maybe_update_nsid_callback(data, next_callback) {
    var obj = eval("(" + data + ")");
    if (obj.stat == "fail") {
      var msg = prefs.getString("username") == "" ?
        "Please specify your Flickr username in the gadget settings." :
	"Problem retrieving user: <b>" + obj.message + "</b>";
      return Gadget.error(msg);
    }

    // Save the return values so we only do the lookup once
    prefs.set("oldusername", "__UP_username__");
    prefs.set("NSID", obj.user.nsid);

    next_callback();
  }
   
  function image_host(id) {
    try {
      if (parseInt(id) % 2 == 0) {
        return "img0";
      }
    } catch(e) {}
    return "img1";
  }

  function render_photos() {
    // Figure out how many to render...
    var num_photos = Math.min(Gadget.num_photos_visible(), photos.length);
    var phs = Gadget.photos_div().getElementsByTagName("A");
    for (var i = 0; i < num_photos; i++) {
      var a = phs[i];
      var img = phs[i].firstChild;
      var src = "http://farm" + photos[i].farm + ".static.flickr.com" +
                "/" + photos[i].server +
                "/" + photos[i].id +
                "_" + photos[i].secret + "_s.jpg";
      if (use_igoogle_cache) {
        // iGoogle cache -- allow to cache for 7 days
        src = _IG_GetCachedUrl(src, 604800);
        src = "http://" + image_host(photos[i].id) + ".gmodules.com" + src;
      }
      if (img.src != src) {
        img.src = src;
        img.className = "photo";
        a.href = "http://flickr.com/photos/" + _esc(photos[i].owner) + "/" + photos[i].id + "/";
	a.setAttribute("onclick", "Gadget.show_zoom(" + i + ");return false;");
	a.onclick = photo_click_func(i);
        a.title = (photos[i].username || photos[i].ownername || "Unknown") + " :: " + (photos[i].title || "Untitled");
      }
    }

    _IG_AdjustIFrameHeight();
  }

  function photo_click_func(index) {
    return function() {
      Gadget.show_zoom(index);
      return false;
    }
  }

  function reset_placeholders() {
    if (!placeholder_html) {
      var s = [];
      for (var i = 0; i < Flickr.MAX_PHOTOS; i++) {
        s.push("<a target='_top'><img class=photoph></a>");
      }
      // Now the hidden HTML that will contain a 'zoomed' image
      s.push("<div id=photo_zoom_dimmer>&nbsp;</div>");
      s.push("<table cellspacing=0 cellpadding=0 border=0 id=photo_zoom_container style='display:none'>")
      s.push("<tr><td align=center valign=middle onclick='Gadget.hide_zoom()'>")
      s.push("</td></tr></table>");
      placeholder_html = s.join("");
    }
    photos_div.innerHTML = placeholder_html;
  }

  function set_zoomed_image(src, width, height, caption, style) {
    var html = [ "<img id=photo_zoom_image src='" + src + "'" ];
    if (width) {
      html.push(" width=" + width);
    }
    if (height) {
      html.push(" height=" + height);
    }
    if (style) {
      html.push(" style='" + style + "'");
    }
    html.push(">");

    if (caption) {
      html.push("<br>" + caption);
    }

    _gel("photo_zoom_container").getElementsByTagName("TD")[0].innerHTML = html.join("");
  }

  return {
    init : function() {
      tabs.addTab("Contacts", {
        tooltip : "Most recent photos posted by " + prefs.getString("username") + "'s contacts",
        callback : ContactsTab.onclick
      });
      tabs.addTab("Interesting", {
        tooltip : "Latest \'Interesting\' photos as defined by Flickr",
        callback : InterestingTab.onclick
      });
      tabs.addTab("Search", {
        tooltip : "\'Interesting\' photos matching your search keywords",
        callback : SearchTab.onclick,
	contentContainer : _gel("SearchTab")
      });

      // Update the titles to make the last char pink
      tabs.getTabs()[0].getNameContainer().innerHTML = "Contact<span class=pink>s</span>";
      tabs.getTabs()[1].getNameContainer().innerHTML = "Interestin<span class=pink>g</span>";
      tabs.getTabs()[2].getNameContainer().innerHTML = "Searc<span class=pink>h</span>";

      _gel("Photos").style.height = (77 * prefs.getInt("num_photo_rows")) + "px";

      // If/when there are photos visible, we swap them around every few seconds
      interval_id = setInterval(function() {
        if (photos.length == 0) return;
        var num_photos = Math.min(Gadget.num_photos_visible(), photos.length);
	if (num_photos == photos.length) return;
        var ix = Math.floor(Math.random() * (num_photos));
        var iy = Math.floor(Math.random() * (photos.length-num_photos-1)) + num_photos;
	var tmp = photos[ix];
	photos[ix] = photos[iy];
	photos[iy] = tmp;
	render_photos();
      }, 3000);
    },

    num_photos_visible : function() {
      return parseInt(Gadget.photos_div().offsetWidth/77.0) * prefs.getInt("num_photo_rows");
    },

    maybe_update_nsid : function(callback) {
      if (prefs.getString("oldusername") != "__UP_username__" ||
          prefs.getString("NSID") == "") {
        // Username has changed, so we need to lookup the new NSID
        Flickr.people.findByUsername("__UP_username__",
                                     _IG_Callback(maybe_update_nsid_callback, callback));
      } else {
        callback();
      }
    },

    error : function(msg) {
      Gadget.set_photos([]);
      photos_div.innerHTML = "<br><center><i>" + msg + "</i></center><br>";
      _IG_AdjustIFrameHeight();
    },

    prefs : function() {
      return prefs;
    },

    photos_div : function() {
      return photos_div;
    },

    set_photos : function(data) {
      photos = data;
      reset_placeholders();
      render_photos();
    },

    show_zoom : function(i) {
      var SCALE = 0.8;
      var FLICKR = "http://flickr.com";
      var photo = photos[i];
      if (!photo) return;
      _gel("photo_zoom_container").style.display = "";
      _gel("photo_zoom_dimmer").style.display = "block";
      
      set_zoomed_image(loading_image_src, 16, 16, null, null);
      var img = new Image();
      img.onload = function() {
        var w = null;
	var h = null;
	var divw = Gadget.photos_div().offsetWidth;
	var divh = Gadget.photos_div().offsetHeight;
	if (((divw*SCALE)/img.width)*img.height < divh-35) {
	  w = parseInt(divw * SCALE);
	} else {
	  h = parseInt(divh * SCALE);
	}
	var FLICKR_ROOT_URL = "http://flickr.com/photos/" + _esc(photo.owner) + "/";
	var caption = [
          "<a href='" + FLICKR_ROOT_URL + photo.id + "/' target=_top>",
          _hesc(photo.title || "Untitled"),
	  "</a> <nobr>&nbsp;&bull;&nbsp; ",
	  "<a href='" + FLICKR_ROOT_URL + "' target=_top>",
          _hesc(photo.username || photo.ownername || "Unknown"),
	  "</a></nobr>"
	];
	set_zoomed_image(img.src, w, h, caption.join(""), "border:5px solid white;margin-bottom:2px");
      }
      img.src = "http://farm" + photos[i].farm + ".static.flickr.com" +
                "/" + photos[i].server +
                "/" + photos[i].id +
                "_" + photos[i].secret + ".jpg";
    },

    hide_zoom : function() {
      _gel("photo_zoom_container").style.display = "none";
      _gel("photo_zoom_dimmer").style.display = "none";
    }
  };
})();

Gadget.init();

</script>
<style>
// Define tab style
.tablib_selected, .tablib_unselected, .tablib_spacertab, .tablib_main_container {
  border-style:dotted;
  border:1px solid red;
}
</style>
     ]]>

  </Content> 
</Module>

