Source of groupr

/* appjet:version 0.1 */ import('dlog') import('storage') import('lib-tpl') import('lib-groupr') import('lib-flickr-minimal') import('lib-sessions') // canonical domain is husk.org, not appjet (under new name) /* if (request.headers['Host'] != "groupr.husk.org") {     print("Requested site from appjet, redirecting");     response.redirect('http://groupr.husk.org/'); } */ //import('storage') //storage.domain = {appjet:"groupr.husk.org", husk:"husk.org"};    // live //storage.domain = {appjet:"groupr.jgate.de", husk:"husk.org"};    // live //storage.domain = {appjet:"localhost:8090", husk:"local.husk.org"}; // dev // should these live somewhere else? (eventually user object) var photosperrow  = 9; var groupsperpage = 9; // Flickr-style date offsets // At the start to make sure it's picked up (voodoo?) function offset () {     var now = new Date();     var offset; var seconds = Math.floor((now.getTime() - this.getTime())/1000);     if (seconds < 60) {         offset = "a moment ago";     } else if (seconds < 60*100) {         offset = Math.round(seconds/60) + " minutes ago";     } else if (seconds < 60*60*30) {         offset = Math.round(seconds/(60*60))+" hours ago";     } else if (seconds < 60*60*24*10) {         offset = Math.round(seconds/(60*60*24))+" days ago"     } else if (seconds < 60*60*24*7*7) {         offset = Math.round(seconds/(60*60*24*7))+" weeks ago"     } else  {         offset = Math.round(seconds/(60*60*24*31))+" months ago"     }     if (offset.match("^1 ")) {       offset = offset.replace("s ago"" ago");     }     return offset; } Date.prototype.offset = offset; // dispatching take 2 dirs  = ['groups/',  'list/',  'recent/',  'login/',  'faq/',  'todo/',          'changelog/',          'login',    'signin''frob',     'signout''css/groupr.css',          'js/groupr.js',          /^\/(groups)\/page(\d+)/,          /^\/(json)\/([\w.]+)/,          /^\/(group)\/([0-9A-Z@]+)\/page(\d+)/i,          /^\/(group)\/([0-9A-Z@]+)/i,]; var dispatched = false; DISPATCH: for (index in dirs) {     path = dirs[index];     var function_name = null;     if (path instanceof RegExp) {       if (match = request.path.match(path)) {         function_name = match[1];         function_arg  = match[2];         extra_arg     = (match[3]) ? match[3] : null;         dlog.info("regex dispatch with name "+function_name+" and args "+function_arg+", "+extra_arg);       }     } else {       if (path.match(/\/$/)) {         redir = path.replace(/\/$/, '');         if (request.path == "/"+redir) {           response.redirect("/"+redir+"/");           dispatched = true;         }       }       if (request.path == "/"+path) {         function_name = "page_"+(path.indexOf("/") > 0 ? path.substr(0, path.indexOf("/")) : path);         function_arg  = null;         extra_arg     = null;       }     }     if (function_name) {       dlog.info("about to function_call '"+function_name+"'");       function_call = appjet._internal.global[function_name];       function_call(function_arg, extra_arg);       dispatched = true;     }     if (dispatched) { break DISPATCH }; } if (!dispatched && request.path == "/") { page_groups(); dispatched = true; } if (!dispatched) {   page_404(); } function page_404() {   printp("404 Not Found"); } // zimki.render? function render(name, data) {   templates = templates();   template = templates[name];   data['session'] = session;   page.setMode("plain");   print(raw(template.process(data)))   bytecodes = appjet._native.bytecodeCount();   dlog.info(request.path+" has bytecode count: "+bytecodes+" ("+(bytecodes/1500000)+"%)") } // hack function groups(page) { page_groups(page) }; function group(group, page) { page_group(group, page) }; // main pages function page_groups(pageno) {     var token = session.token;     if (!token || token == null) {         // TODO stash redirect         // TODO standard code block         return response.redirect('/signin');     }     var data = {};     data['domain']  = storage.domain.appjet;     data['husk']    = storage.domain.husk;     data.groups      = groupr_groups(token);     data.groupphotos = groupr_group_photos(token, data.groups);     data.page        = pageno ? pageno : 1;     dlog.info("page_groups for page "+pageno);     // zimki.log.debug("Using page '"+data['page']+"'");     data['ppr']           = 9;     data['groupsperpage'] = groupsperpage;     return render("groups.html", data); } function page_group(group_id, pageno) {     var token = session.token;     if (!pageno) { pageno = 1 }     dlog.info("page_group for group "+group_id+" and page "+pageno);     if (!group_id.match('@')) {         // TODO catch bogus names         dlog.info("need to convert group name "+group_id+" to id");         var group_info = flickr.make_call('flickr.urls.lookupGroup', {url: 'http://flickr.com/groups/'+group_id }, true);         group_id = group_info.group.id;     }     var data = {};     data['domain']  = storage.domain.appjet;     data['husk']    = storage.domain.husk;     if (token) {         dlog.info("finding group info from cached metadata");         groups = groupr_groups(token);         data.groupinfo = {};         for (index in groups.groups.group) {           if (groups.groups.group[index].id == group_id) {             data.groupinfo = groups.groups.group[index];             break;           }         }     }     // TODO catch unauthed groups     if (!data.groupinfo) {         data.groupinfo   = {};     }     photos = groupr_get_interesting_photos(token, group_id, pageno);     data.groupphotos = {};     data.groupphotos[group_id] = photos;     data.group_id = group_id;     data['ppr']           = 9;     data['groupsperpage'] = groupsperpage;     data['page']          = pageno;     return render("group.html", data); } function page_list() {     var token = session.token;     if (!token) {         // TODO stash redirect         return response.redirect('/signin');     }     var data = {};     data['domain']  = storage.domain.appjet;     data['husk']    = storage.domain.husk;     data.groups      = groupr_groups(token);     data.groupphotos = groupr_group_photos(token, data.groups);     // data.groupinfo   = groupr_group_details(token, data.groups);     return render('list.html', data); } function page_recent() {     var token = session.token;     if (!token) {         // TODO stash redirect         return response.redirect('/signin');     }     var data = {};     data['domain']  = storage.domain.appjet;     data['husk']    = storage.domain.husk;     data.groups      = groupr_groups(token);     data.groupphotos = groupr_group_photos(token, data.groups);     data.groupadded  = groupr_all_photos(token, data.groups, data.groupphotos);     // data.groupinfo   = groupr_group_details(token, data.groups);     return render('recent.html', data); } function page_signin() {   data = flickr.request_auth_url("read");   data['faq'] = groupr_faq('all');   // data['zimki']:{alias:'groupr'}};   return render("signin.html", data) } function page_signout() {     // just delete everything. Seems to work.     session.token = "";     session.user  = "";     session.nick  = "";     session.name  = "";     return response.redirect('/signin'); } function page_faq() {     var data = {};     // var session = zimki.session;     if (session.token) {         data['faq'] = groupr_faq();         return render('faq.html', data);     } else {         return zimki.redirect(zimki.root+'/signin');     } } // I wanted to keep todo items somewhere function page_todo() {     return render('todo.html', {}); } function page_changelog() {     return render('changelog.html', {} ); } // store token in the session (for now TODO users) function page_frob(args) {     var data = {};     var frob = request.params.frob;     if (!frob) {         // TODO store URL in session         return response.redirect('/signin');     }     var api_args = { 'frob' : frob, };     returned = flickr.make_call('flickr.auth.getToken', api_args, true);     dlog.info("getToken call got response "+returned);     // look ma, error handling     if (returned.stat != "ok") {         data['error'] = returned.err;         return render('frob.html', data);     } else {       // stash token and user details (I hope...)       session.token = returned.auth.token._content;       session.user  = returned.auth.user.nsid;       session.nick  = returned.auth.user.username;       session.name  = returned.auth.user.fullname;       // s.save();       response.redirect('/groups/');     } } function faq() {     var data = {};     // var session = zimki.session;     if (session.token) {         data['faq'] = groupr_faq();         return render('faq.html', data);     } else {         return zimki.redirect(zimki.root+'/signin');     } } function page_css() {   page.setMode("plain");   response.setHeader("Content-Type""text/css")   return render("groupr.css", {}); } function json(call) {   if (call == "groupr.group.getPhotos") {     page.setMode("plain");     print(raw(groupr_get_photos(session.token, request.params.group_id, true, request.params.page, request.params.count)))     return;   }   if (call == "groupr.groups.getPhotosNext") {     groups   = groupr_groups(session.token)     returned = groupr_group_nextphotos(session.token, groups, request.params.bycount)     page.setMode("plain");     print(raw(returned));     return;   }   page.setMode("plain");   print(raw("{stat:fail, message:'Method "+call+" not implemented'}")); } function templates() {   return {     "changelog.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <title>groupr - change log</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" /> </head> <body>     <div id="nav">       <div class="wrap">       <h1><span class="group">group</span><span class="r">r</span> -  change log</h1>       {if session.token}           <ul>               <li><a href="/">groups</a></li>               <li><a href="/list/">list</a></li>               <li><a href="/recent/">recent</a></li>               <li><a href="/faq/">faq</a></li>               <li><a href="/todo/">to do</a></li>               <li><b>changes</b></li>               <li class="last"><a href="/signout">sign out</a></li>           </ul>           <ul class="right">               <li class="last">signed in as ${session.nick}</li>           </ul>       {else}           <ul class="right">               <li class="last"><b>not <a href="/signin/">signed in</a></b></li>           </ul>       {/if}       </div>     </div>     <div id="changelog" class="list">         <h2>releases</h2>         <pre>         + 2009-02-03 groupr14           - add group page with sort-by-interestingness and paging         + 2008-11-05 groupr13           - rewrite dispatch, to add 404 handler and dir redirects           - move domain information to storage from code           - suppress paging when only one page of groups         + 2008-11-04 groupr12           - release completed 'recent' page           - navigation refresh         + 2008-10-28 groupr11           - prototype recent page           - list page also auto-updates, works           - centre content in page; cosmetic tweaks         + 2008-10-25 groupr10           - revive groupr on appjet, now zimki's dead           - show next group cache status           - properly enable list page           - slight style tweaks in the header         + 2007-03-xx groupr09           - group list page with sorting client-side table         + 2007-03-02 groupr08           - split changelog/todo           - internal renaming to html for templates           - css and js in seperate (templated) files           - css validity           - add todo/changelog to navigation           - fix bug with using HTML IDs not Flickr IDs in URLs           - dot animation for loading progress           - fix for long group name details bug (partial)         + 2007-02-25 groupr07           - properly removed togglePhoto functionality           - xhtml validity           - fix bugs with multiple groups paging at the same time           - fix jquery onready callback invocation (for Safari)         + 2007-02-23 groupr06           - fix faq number of groups           - removed togglePhoto functionality         + 2007-02-22 groupr05           - scrolling within groups           - changelog           - remove broken faq sign in link           - add todo/changelog to faq           - fix bug with page handling           - useful title attribute on page links           - css fixes           - cut groups per page to nine         + 2007-02-05 groupr04           - todo published           - mouseover group information           - nicer paging URLs and numbers         + 2007-02-02 groupr03           - faq available when logged in           - change to sign in / sign out           - better group navigation           - better main navigation           - code split up internally         + 2007-01-31 groupr02           - use cached photos were possible           - minimal css         + 2007-01-31 groupr01           - first release         </pre>     </div>     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",           "faq.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <title>groupr - frequently asked questions</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" /> </head> <body>     <div id="nav">       <div class="wrap">           <h1><span class="group">group</span><span class="r">r</span> - frequently asked questions</h1>       {if session.token}           <ul>               <li><a href="/">groups</a></li>               <li><a href="/list/">list</a></li>               <li><a href="/recent/">recent</a></li>               <li><b>faq</b></li>               <li><a href="/todo/">to do</a></li>               <li><a href="/changelog/">changes</a></li>               <li class="last"><a href="/signout">sign out</a></li>           </ul>           <ul class="right">               <li class="last">signed in as ${session.nick}</li>           </ul>       {else}           <ul class="right">               <li class="last"><b>not <a href="/signin/">signed in</a></b></li>           </ul>       {/if}       </div>     </div>     <div id="faq">       <h2>groupr faqs</h2>       ${faq}     </div>     <div id="credit">         <div class="wrap">               <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",                 "frob.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <title>groupr - error</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" /> </head> <body>     <div id="nav">         <div class="wrap">             <h1><span class="group">group</span><span class="r">r</span> - authorisation error</h1>             <ul class="right">                 <li class="last"><b>not signed in</b></li>             </ul>         </div>     </div>     <div id="faq">         <p>Something's gone a bit wrong, because you shouldn't see this.</p>         <p>Once I figure out how to send an email, there'll be a form here to report the issue. Until then, er, sorry.</p>         {if error != ''}<p>For what it's worth, the Flickr error details are: <span style="color:red">${error}</span>.</p>{/if}     </div>     <div id="credit">         <div class="wrap">               <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",                 "groups.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <head>   <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />   <title>groupr - photos in your flickr groups{if page > 1} (page ${page}){/if}</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" />   <base href="http://${husk}/" />   <script language="JavaScript" type="text/JavaScript" src="/js/jquery.js"></script>   <script language="JavaScript" type="text/JavaScript" src="/js/groupr.js"></script>   <script language="JavaScript" type="text/JavaScript">     // <![CDATA[     var grouppage   = {};     var ajaxcount   = 0;     var ppr = ${ppr};     {var grouplist = groups.groups.group}     jQuery(document).ready(function() { onReady() });     function populateGroups(id) {         var groups = [];         {for group in grouplist}             {eval}                 flickr_id = grouplist[group_index].id;                 g_id = "g"+flickr_id.replace("@""_");             {/eval}             {if group_index >= (parseInt(page)-1)*groupsperpage &&                 group_index <= page*groupsperpage-1}                 {if !groupphotos[flickr_id]}                     groups[${group_index}] = '${g_id}';                     grouppage['${g_id}'] = 0;                 {else}                     grouppage['${g_id}'] = 1;                 {/if}             {/if}         {/for}         for (var group in groups) {             id = groups[group].replace("@""_");             getPhotos(id);             ajaxcount = ajaxcount+1;         }         if (ajaxcount == 0) {             getFirstNext();         }     }     function errorHandler(error) {         // alert(error); // for now         return true;     }   </script> </head> <body>     <div id="nav">         <div class="wrap">             <h1><span class="g">group</span><span class="r">r</span> -             photos in your Flickr groups</h1>             <ul>                 <li><b>groups</b></li>                 {if groupphotos.length == grouplist.length}                 <li><a href="http://${domain}/list/">list</a></li>                 {else}                 <li><span class="disabled">list</span></li>                 {/if}                 <li><a href="http://${domain}/recent/">recent</a></li>                 <li><a href="http://${domain}/faq/">faq</a></li>                 <li><a href="http://${domain}/todo/">to do</a></li>                 <li><a href="http://${domain}/changelog/">changes</a></li>                 <li class="last"><a href="http://${domain}/signout">sign out</a></li>             </ul>             <ul class="right">                 <li class="fetch"></li>                 <li class="last">signed in as ${session.nick}</li>             </ul>         </div>     </div> {if grouplist.length}     {var link_array}     {eval}         var links = Math.ceil((grouplist.length-1)/groupsperpage);         link_array = new Array(links);         for (var i=1; i <= links; i++) {             link_array[i] = i;         }     {/eval}     {var row = 0}     <div id="groups">       {for group in grouplist}         {if group_index >= (parseInt(page)-1)*groupsperpage &&             group_index <= page*groupsperpage-1}             {eval}                 flickr_id = group.id+"";                 g_id = "g"+flickr_id.replace("@""_");             {/eval}             {var groupphoto = groupphotos[flickr_id]}             {var photos = []}             {if groupphoto}                 {var photos = groupphoto.photos.photo}             {/if}         <div id="${g_id}_c" class="groupinfo">             <div class="toggle"><!-- TODO group icons --></div>             <h2 class="groupname"><a href="http://flickr.com/groups/${flickr_id}/">${group.name}</a></h2>             <div id="${g_id}_d" class="details">{if groupphoto}showing page 1 of ${groupphoto.photos.pages} (${groupphoto.photos.total} photos in total){/if}</div>         </div>             <div id="${g_id}" class="group">                 <a href="javascript:doNothing();" id="${g_id}_b"><img src="/images/blank.gif" width="10" height="75" alt="less" id="${g_id}_bi" border="0"/></a>             {eval}                 counter = new Array(ppr);                 for (var i=0; i < ppr; i++) { counter[i] = i; }             {/eval}             {for i in counter}                 {var p = photos[i]}                 {if p}                 <a href="http://flickr.com/photos/${p.owner}/${p.id}/in/pool-${flickr_id}/" id="${g_id}_${i}l"><img src="http://farm${p.farm}.static.flickr.com/${p.server}/${p.id}_${p.secret}_s.jpg" width="75" height="75" alt="${p.title}" title="'${p.title.replace(/"/g, '\'')}' by '${p.ownername.replace(/"/g, '\'')}'" id="${g_id}_${i}" border="0" /></a>                 {else}                 <a href="#" id="${g_id}_${i}l"><img src="/images/blank.gif" width="75" height="75" alt="please wait" title="" id="${g_id}_${i}" border="0" /></a>                 {/if}             {/for}             {if groupphoto && groupphoto.photos.pages > 1}                 <a href="javascript:getPhotos('${g_id}', 2)" id="${g_id}_n"><img src="/images/group_next.gif" width="10" height="75" alt="more" id="${g_id}_ni" border="0"/></a>             {else}                 <a href="javascript:doNothing();" id="${g_id}_n"><img src="/images/blank.gif" width="10" height="75" alt="more" id="${g_id}_ni" border="0"/></a>             {/if}             </div>       {/if}     {forelse}         <h2>You don't seem to be in any groups!</h2>         <p>You can find a list of groups to join <a href="http://flickr.com/groups_browse.gne">on the Flickr site</a>.</p>     {/for}     {if grouplist.length && link_array.length > 2}       <div id="link-bottom">           {if page > 1}           <a href="http://${domain}/groups/{if page > 2}page${page-1}/{/if}" class="arrows"><<</a>           {else}           <span><<</span>           {/if}           {for link in link_array}               {var start = (link-1)*groupsperpage}               {var end   = (link)*groupsperpage-1}               {if end > grouplist.length-1}                   {var end = grouplist.length-1}               {/if}               {var first = grouplist[start].name}               {var last = grouplist[end].name}               {if page == link}<b>${link}</b>{else}<a href="http://${domain}/groups/{if link > 1}page${link}/{/if}" title="${first} - ${last}">${link}</a>{/if}           {/for}           {if page < link}           <a href="http://${domain}/groups/page${page-1+2}/" class="arrows">>></a>           {else}           <span>>></span>           {/if}       </div>       {/if}     </div> {/if}     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",     "group.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <head>   <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />   <title>groupr - most interesting photos in ${group_id}</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" />   <base href="http://${husk}/" />   <script language="JavaScript" type="text/JavaScript" src="/js/jquery.js"></script>   <script language="JavaScript" type="text/JavaScript" src="/js/groupr.js"></script>   <script language="JavaScript" type="text/JavaScript">     var ppr = ${ppr};     function errorHandler(error) {         // alert(error); // for now         return true;     }   </script> </head> <body>     <div id="nav">         <div class="wrap">             <h1><span class="g">group</span><span class="r">r</span> -             photos in your Flickr groups</h1>       {if session.token}             <ul>                 <li><a href="http://${domain}/groups/">groups</a></li>                 <li><a href="http://${domain}/list/">list</a></li>                 <li><a href="http://${domain}/recent/">recent</a></li>                 <li><a href="http://${domain}/faq/">faq</a></li>                 <li><a href="http://${domain}/todo/">to do</a></li>                 <li><a href="http://${domain}/changelog/">changes</a></li>                 <li class="last"><a href="http://${domain}/signout">sign out</a></li>             </ul>             <ul class="right">                 <li class="fetch"></li>                 <li class="last">signed in as ${session.nick}</li>             </ul>       {else}           <ul class="right">               <li class="last"><b>not signed in</b></li>           </ul>       {/if}         </div>     </div>     {eval}         g_id = "g"+group_id.replace("@""_");     {/eval}     {var groupphoto = groupphotos[group_id]}     {var photos = []}     {if groupphoto}         {var photos = groupphoto.photos.photo}     {/if}     <div id="group">         <div id="${g_id}_c" class="groupinfo">             <div class="toggle"><!-- TODO group icons --></div>             <h2 class="groupname"><a href="http://flickr.com/groups/${group_id}/">${groupinfo.name}</a></h2>             <div id="${g_id}_d" class="details">showing page ${groupphoto.photos.page} of ${groupphoto.photos.pages} (${groupphoto.photos.total} photos in total) sorted by interestingness</div>         </div>             <div id="${g_id}" >       {eval}           rows = new Array(6);           columns = new Array(ppr);           {for (var i=0; i < 6; i++) { rows[i] = i; }}           {for (var i=0; i < ppr; i++) { columns[i] = i; }}       {/eval}       {for j in rows}           <img src="/images/blank.gif" width="10" height="75" alt="less" id="${g_id}_bi" border="0"/>           {for i in columns}               {var index = j*ppr+i}               {var p = photos[index]}               {if p}               <a href="http://flickr.com/photos/${p.owner}/${p.id}/in/pool-${group_id}/" id="${g_id}_${i}l"><img src="http://farm${p.farm}.static.flickr.com/${p.server}/${p.id}_${p.secret}_s.jpg" width="75" height="75" alt="${p.title}" title="'${p.title.replace(/"/g, '\'')}' by '${p.ownername.replace(/"/g, '\'')}'" id="${g_id}_${i}" border="0" /></a>               {else}               <a href="#" id="${g_id}_${i}l"><img src="/images/blank.gif" width="75" height="75" alt="please wait" title="" id="${g_id}_${i}" border="0" /></a>               {/if}           {/for}           <br>       {/for}       </div>     {if groupphoto.photos.pages > 1}       {var pages = groupphoto.photos.pages}       {if pages > 15}           {var pages = 15}       {/if}       {eval}           links = new Array(pages+1);           {for (var i=1; i <= pages; i++) { links[i] = i; }}       {/eval}       <div id="link-bottom">           {if page > 1}           <a href="http://${domain}/group/${group_id}/{if page > 2}page${page-1}/{/if}" class="arrows"><<</a>           {else}           <span><<</span>           {/if}           {for link in links}               {if page == link}<b>${link}</b>{else}<a href="http://${domain}/group/${group_id}/{if link > 1}page${link}/{/if}">${link}</a>{/if}           {/for}           {if page < link}           <a href="http://${domain}/group/${group_id}/page${page-1+2}/" class="arrows">>></a>           {else}           <span>>></span>           {/if}       </div>     {/if}     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",                 "list.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />   <title>groupr - list of your flickr groups</title>   <!-- import the zimki client-side library -->   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" />   <script language="JavaScript" type="text/JavaScript" src="http://${husk}/js/jquery.js"></script>   <script language="JavaScript" type="text/JavaScript" src="http://${husk}/js/tablesort.js"></script>   <script language="JavaScript" type="text/JavaScript">     var sortById = fdTableSort.sortNumeric;     function sortByIdPrepareData(td, innerText) {       return td.id;     }     $.getJSON('http://${domain}/json/groupr.groups.getPhotosNext', {}, getNext);     function getNext(result) {         if (result.stat != "ok") {           console.warn("error in Flickr call: "+message);           return;         }         if (result.got_group_id) {             name = result.got_group_name;             if (name.length > 19) {               name = name.substr(0, 16)+"...";             }             $('li.fetch').text("cached '"+name+"'");             $('li.fetch').css({display:"inline"});             g_id = "g"+result.got_group_id.replace("@""_");             if (result.last_added) {               $('tr#'+g_id+" td:last").attr("id", result.last_added);               $('tr#'+g_id+" td:last").text(result.offset);             } else {               $('tr#'+g_id+" td:last").attr("id""0");               $('tr#'+g_id+" td:last").text("-");             }             $.getJSON('http://${domain}/json/groupr.groups.getPhotosNext', {}, getNext);         } else {             $('li.fetch').text("cached all groups");         }     }   </script> </head> <body>     <div id="nav">         <div class="wrap">             <h1><span class="g">group</span><span class="r">r</span> -             list of your Flickr groups</h1>             <ul>                 <li><a href="/groups/">groups</a></li>                 <li><b>list</b></li>                 <li><a href="/recent/">recent</a></li>                 <li><a href="/faq/">faq</a></li>                 <li><a href="/todo/">to do</a></li>                 <li><a href="/changelog/">changes</a></li>                 <li class="last"><a href="/signout">sign out</a></li>             </ul>             <ul class="right">                 <li class="fetch"></li>                 <li class="last">signed in as ${session.nick}</li>             </ul>         </div>     </div>     <div id="groups">         {var grouplist = groups.groups.group}         {if grouplist.length > 0}             {var added = true}             {var now = new Date()}             {if groupphotos.length == grouplist.length}                 {var added = true}             {/if}             {var alt = false}         <table class="rowstyle-alt onload-zebra" width="725px;">             <tr>                 <th></th>                 <th width="340" class="sortable-text">Group Name</th>                 <th width="50" class="sortable-numeric favour-reverse">Photos</th>                 <th width="55" class="sortable-text">Privacy</th>                 <th width="55" class="sortable-text favour-reverse">Admin?</th>                 {if added}                 <th class="sortable-sortById favour-reverse">Last Photo Added</th>                 {/if}             </tr>             {for group in grouplist}                 {eval}                     flickr_id = grouplist[group_index].id;                     g_id = "g"+flickr_id.replace("@""_");                 {/eval}                 {var groupphoto = groupphotos[flickr_id]}             <tr id="${g_id}">                 <td class="nb"><img src="{if grouplist[group_index].iconserver > 0}http://farm${grouplist[group_index].iconfarm}.static.flickr.com/${grouplist[group_index].iconserver}/buddyicons/${grouplist[group_index].nsid}.jpg{else}http://www.flickr.com/images/buddyicon.jpg{/if}" width="16" height="16" /></td>                 <td><a href="http://flickr.com/groups/${flickr_id}/">${grouplist[group_index].name}</a></td>                 <td>${grouplist[group_index].photos}<!-- photo{if grouplist[group_index].photos != 1}s{/if}--></td>                 <td>{if grouplist[group_index].privacy == 1}Private{/if}{if grouplist[group_index].privacy == 2}Invite Only{/if}{if grouplist[group_index].privacy == 3}Public{/if}</td>                 <td>{if grouplist[group_index].admin > 0}Yes{else}No{/if}</td>                 {if groupphoto}                   {if grouplist[group_index].photos > 0}                 <td id="${groupphoto.last_added.getTime()}">${groupphoto.last_added.offset()}</td>                   {else}                 <td>-</td>                   {/if}                 {else}                 <td> </td>                 {/if}             </tr>             {/for}         </table>         {else}         <h2>You don't seem to be in any groups!</h2>         <p>You can find a list of groups to join <a href="http://flickr.com/groups_browse.gne">on the Flickr site</a>.</p>             </td></tr>         {/if}     </div>     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html> """,                 "recent.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />   <title>groupr - photos recently added to your groups</title>   <!-- import the zimki client-side library -->   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" />   <script language="JavaScript" type="text/JavaScript" src="http://${husk}/js/jquery.js"></script>   <script language="JavaScript" type="text/javascript">     $.getJSON('http://${domain}/json/groupr.groups.getPhotosNext', {bycount:"yes"}, getNext);     function getNext(result) {         if (result.stat != "ok") {           console.warn("error in Flickr call: "+message);           return;         }         if (result.got_group_id) {             name = result.got_group_name;             if (name.length > 19) {               name = name.substr(0, 16)+"...";             }             $('li.fetch').text("cached '"+name+"'");             $('li.fetch').css({display:"inline"});             if (result.photo) {               p = result.photo;               last = result.last_added/1000               html = '<div id="'+last+'" class="photo" style="display:none; float:left; padding:5px; text-align:center; width:80px; height:105px; overflow:hidden;">';               html += '<a href="http://flickr.com/photos/'+p.owner+'/'+p.id+'/in/pool-'+result.got_group_id+'"><img src="http://farm'+p.farm+'.static.flickr.com/'+p.server+'/'+p.id+'_'+p.secret+'_s.jpg" style="width:75px; height:75px;" width="75" height="75" border="0"';               html += 'alt="'+p.title+'" title="'+p.title.replace(/"/g, '\'')+' by '+p.ownername.replace(/"/g, '\'')+'" onload="javascript:showImage('+last+')"';               html += ' /></a><br />';               html += 'In <a href="http://flickr.com/groups/'+result.got_group_id+'/">'+result.got_group_name+'</a>'               html += '</div>'               // find the right element id               prev = new Date().getTime()/1000 // make sure everything is in seconds               last = result.last_added/1000               added = false; index = 1;               $('div.photo').each(function() {                 if (prev > last && last > this.id && !added && index <= 32) {                   $(this).before(html);                   added = true;                 }                 index++;                 prev = this.id;               });             }             $.getJSON('http://${domain}/json/groupr.groups.getPhotosNext', {bycount:"yes"}, getNext);         } else {             $('li.fetch').text("cached all groups");         }     }     function showImage(id) {         if ($('div#'+id).show() && $('div.photo:visible').length > 32) {           $('div.photo:visible:last').hide();         }     }   </script> </head> <body>     <div id="nav">         <div class="wrap">             <h1><span class="g">group</span><span class="r">r</span> -             photos recently added to your groups</h1>             <ul>                 <li><a href="/groups/">groups</a></li>                 <li><a href="/list/">list</a></li>                 <li><b>recent</b></li>                 <li><a href="/faq/">faq</a></li>                 <li><a href="/todo/">to do</a></li>                 <li><a href="/changelog/">changes</a></li>                 <li class="last"><a href="/signout">sign out</a></li>             </ul>             <ul class="right">                 <li class="fetch"></li>                 <li class="last">signed in as ${session.nick}</li>             </ul>         </div>     </div>     {var count = 0}     <div id="groups">         {for p in groupadded}             {if count < 32}<!-- TODO: paging -->             <div id="${p.dateadded}" class="photo" style="float:left; padding:5px; text-align:center; width:80px; height:105px; overflow:hidden;">               <a href="http://flickr.com/photos/${p.owner}/${p.id}/in/pool-${p.group_info.id}/"><img src="http://farm${p.farm}.static.flickr.com/${p.server}/${p.id}_${p.secret}_s.jpg" style="width:75px; height:75px;" width="75" height="75" alt="${p.title}" title="'${p.title.replace(/"/g, '\'')}' by '${p.ownername.replace(/"/g, '\'')}'" border="0" /></a><br />               In <a href="http://flickr.com/groups/${p.group_info.id}/">${p.group_info.name}</a>             </div>             {/if}             {var count = count + 1}         {forelse}             <div id="" class="photo"></div>         {/for}     </div>     <br clear="all" />     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html> """,                 "signin.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <title>groupr - log in to Flickr</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" /> </head> <body>     <div id="nav">         <div class="wrap">             <h1><span class="group">group</span><span class="r">r</span> - <a href="http://flickr.com/services/auth/?api_key=${key}&perms=read&api_sig=${shared}">authorise with Flickr</a></h1>             <ul class="right">                 <li class="last"><b>not signed in</b></li>             </ul>         </div>     </div>     <div id="faq">       <h2>groupr faqs</h2>     ${faq}     </div>     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",                 "todo.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head>   <title>groupr - todo list</title>   <link rel="stylesheet" type="text/css" href="/css/groupr.css" media="all" /> </head> <body>     <div id="nav">         <div class="wrap">         <h1><span class="group">group</span><span class="r">r</span> -  todo list</h1>         {if session.token}             <ul>                 <li><a href="/">groups</a></li>                 <li><a href="/list/">list</a></li>                 <li><a href="/recent/">recent</a></li>                 <li><a href="/faq/">faq</a></li>                 <li><b>to do</b></li>                 <li><a href="/changelog/">changes</a></li>                 <li class="last"><a href="/signout">sign out</a></li>             </ul>             <ul class="right">                 <li class="last">signed in as ${session.nick}</li>             </ul>         {else}             <ul class="right">                 <li class="last"><b>not <a href="/signin/">signed in</a></b></li>             </ul>         {/if}         </div>     </div>     <div id="todo" class="list">         <h2>todo</h2>         <pre>         - rename           - groopr? gloop? eh.         x paging           - filter by group name           - filter on each of lists, recent, groups page         - bug fixes           x long group name breaks rollover           - allow details to be shown on long group names anyway           - ajax failures shouldn't spawn alerts           x replace invalid group-as-id syntax           x handle multiple group loading correctly         - scaling           - paging paging           - group lists           - detail fetching         + list page           x list groups           x sort on update date           x copy background loading from groups page           - background loading in all pages, or using cron           - update data in real time           - allow page access even on partial         - sorting options           - groups by interestingness not date           - filter interestingness by month?         - scrolling within groups           - slide all           - scrolling/carousel?         - users           - persist data between sessions           - persistent cookie           - contacts             - groups shared with contacts             - group recommendations?             - requires job queue?         - flickr library           x split into lib-flickr-minimal app             x documentation             x refactor           - pipes-style return of image URL, src, title           - pretty URLs (flickr.getPhotoURL or somesuch)         - better internal data structures           x store update time             - sort updates on said update time           x single group store             - requires auth checks           - selective update           - based on a group list?           x calculate date added sort order           x use group details (added, private, admin)           - additional group details           x sort by the above           - group trackr integration         x todo           x include in all headers           x split out changelog         - misc           x todo/changelog CSS           x paging hover CSS           - groupr.husk.org migration (prereq for user?)           x don't show group div until images loaded           - don't show group div if no images?           - neaten group div HTML/CSS           - proper template header/footers             - username links to flickr (or prefs?)           - expose cache time?           - faq data outside code?         - speculative / clashing with flickr           - discussion details (rss)           - group admin tools           + redesign with group icons         </pre>     </div>     <div id="credit">         <div class="wrap">             <p><span class="g">group</span><span class="r">r</span> is an application by <a href="http://husk.org">Paul Mison</a>, hosted on <a href="http://www.appjet.com">appjet</a>. groupr is neither endorsed nor operated by Flickr.</p>         </div>     </div> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-5271055-5"); pageTracker._trackPageview(); </script> </body> </html>""",           "groupr.css": """/* groupr.css generated */ /* general styles */ body {   background: white;   margin: 0px;   padding: 0px;   color: #333;   font: 12px/16px "Helvetica Neue", Helvetica, Arial, sans-serif; } h1 {   font-size: 24px;   line-height: 28px;   margin:  0px;   padding: 0px 0px 5px 0px;   text-align:left; } h2 {   font-size: 18px;   padding: 0px 0px 5px 0px;   margin:  0px; } a {   color: #00f;   text-decoration:none; } a:hover {   text-decoration:underline; } a:visited {   color: #99f; } span.g {   color: #666; } span.r {   color: #999; } div.body {   padding-left: 10px; } /* navigation */ div#nav {   margin:  0px 0px 10px 0px;   padding: 0px 0px 10px 10px;   border-bottom: 1px solid #e3e3e3;   height: 40px; } .wrap {   width:735px;   margin: 0px auto;   text-align:center; } div#nav ul {   float: left;   margin:  0px;   padding: 0px;   list-style: none; } div#nav ul.right {   float: right;   text-align: right;   width: 330px; } div#nav li {   margin: 0px;   padding: 0px;   display: inline; } div#nav li:after {   padding: 10px;   content: "|"; } div#nav li.last:after {   text-align:right;   content: "" } div#nav ul.right li.fetch {   display:none; } /* div#nav ul.right li:after {   text-align:right;   content: "" } */ /* div#nav span {     color: #999; } */ ul#group-dropdown {   display: none; } div#credit {   margin-top: 10px;   padding-left: 10px;   border-top: 1px solid #e3e3e3; } /* specific styles */   /* faq */ div#faq {   margin: 0px auto;   width: 730px; } div#faq p {   width: 500px;   padding-left: 20px; } div#faq h2 {   padding-left: 0px;   width: 500px; } div#faq h3 {   padding-left: 10px;   width: 500px; }   /* todo */ div#todo {   margin: 0px auto;   width: 725px; }   /* changelog */ div#changelog {   margin: 0px auto;   width: 725px; }   /* groups */ div#groups {   width: 735px;   padding-left: 10px;   margin: 0px auto; } div#group {   width: 735px;   padding-left: 10px;   margin: 0px auto; } div.group {   margin:  2px 2px 22px 2px;   padding: 0px;   border-bottom: 1px solid #ddd; } /* -- group info */ div.groupinfo {   height: 20px;   background-color: transparent;   overflow: hidden; } h2.groupname {   float: left;   margin: 0px;   padding: 0px;   white-space: nowrap; } div.details {   float: right;   color: #999;   padding-top: 3px;   padding-right: 15px;   display: none; } div#group div.details {   display: block; } div.toggle {   width: 20px;   text-align:center;   float: left;   background-color: transparent; } div.toggle a {   font-size: 18px;   font-weight: bold; } div.toggle a:hover {   text-decoration: none; } /* -- navigation */ span.disabled {   color:#999; } div#link-bottom {   padding-top:  10px;   padding-left: 20px;   text-align: center; } div#link-bottom * {   border: 1px solid #fff;   padding: 2px 6px;   text-decoration: none; } div#link-bottom a {   border: 1px solid #999;   text-decoration: none; } div#link-bottom a.arrows {   border: 1px solid #fff; } div#link-bottom span {   color: #999; } div#link-bottom a:hover {   border: 1px solid #666;   color: #fff;   background: #00f;   text-decoration: none; } div#link-bottom a.arrows:hover {   border: 1px solid #fff;   color: #00f;   background: #fff;   text-decoration: underline; }   /* todo/changelog - needs work */ div.list pre {   font: 9px Monaco, Courier, fixed; }   /* list */ table {   margin: 0;   padding: 0;   border: 0;   border-spacing: 0;   border-collapse: collapse; } table th {   text-align: left;   padding-right: 15px;   border-bottom: 1px solid #e3e3e3;   margin-bottom: 3px; } table tr td {   margin: 0px;   padding: 2px 5px 2px 2px; } table tr.alt td.nb {   background: #fff;   padding-right: 2px; } table tr.alt {   background: #e3e3e3; }"""} };

Go Back to this app | Get plain source

Powered by AppJet on JGate
source
rendered in 12.452s