CINXE.COM
April | 2012 | code.flickr.com
<!DOCTYPE html> <!--[if IE 6]> <html id="ie6" lang="en"> <![endif]--> <!--[if IE 7]> <html id="ie7" lang="en"> <![endif]--> <!--[if IE 8]> <html id="ie8" lang="en"> <![endif]--> <!--[if !(IE 6) | !(IE 7) | !(IE 8) ]><!--> <html lang="en"> <!--<![endif]--> <!-- generated in 0.283 seconds 43763 bytes batcached for 300 seconds --> <head><script type="text/javascript" src="https://web-static.archive.org/_static/js/bundle-playback.js?v=7YQSqjSh" charset="utf-8"></script> <script type="text/javascript" src="https://web-static.archive.org/_static/js/wombat.js?v=txqj7nKC" charset="utf-8"></script> <script>window.RufflePlayer=window.RufflePlayer||{};window.RufflePlayer.config={"autoplay":"on","unmuteOverlay":"hidden"};</script> <script type="text/javascript" src="https://web-static.archive.org/_static/js/ruffle/ruffle.js"></script> <script type="text/javascript"> __wm.init("https://web.archive.org/web"); __wm.wombat("http://code.flickr.net:80/2012/04/","20130504183413","https://web.archive.org/","web","https://web-static.archive.org/_static/", "1367692453"); </script> <link rel="stylesheet" type="text/css" href="https://web-static.archive.org/_static/css/banner-styles.css?v=p7PEIJWi" /> <link rel="stylesheet" type="text/css" href="https://web-static.archive.org/_static/css/iconochive.css?v=3PDvdIFv" /> <!-- End Wayback Rewrite JS Include --> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width"/> <title>April | 2012 | code.flickr.com</title> <link rel="profile" href="http://gmpg.org/xfn/11"/> <link rel="stylesheet" type="text/css" media="all" href="https://web.archive.org/web/20130504183413cs_/http://s2.wp.com/wp-content/themes/vip/flickr-code/style.css?m=1345671377g"/> <link rel="pingback" href="http://code.flickr.net/xmlrpc.php"/> <!--[if lt IE 9]> <script src="http://s2.wp.com/wp-content/themes/pub/twentyeleven/js/html5.js?m=1354160568g" type="text/javascript"></script> <![endif]--> <script src="https://web.archive.org/web/20130504183413js_/http://r-login.wordpress.com/remote-login.php?action=js&host=code.flickr.net&id=39034126&t=1367692452&back=code.flickr.net%2F2012%2F04%2F" type="text/javascript"></script> <script type="text/javascript"> /* <![CDATA[ */ if ( 'function' === typeof WPRemoteLogin ) { document.cookie = "wordpress_test_cookie=test; path=/"; if ( document.cookie.match( /(;|^)\s*wordpress_test_cookie\=/ ) ) { WPRemoteLogin(); } } /* ]]> */ </script> <link rel="alternate" type="application/rss+xml" title="code.flickr.com » Feed" href="https://web.archive.org/web/20130504183413/http://code.flickr.net/feed/"/> <link rel="alternate" type="application/rss+xml" title="code.flickr.com » Comments Feed" href="https://web.archive.org/web/20130504183413/http://code.flickr.net/comments/feed/"/> <script type="text/javascript"> /* <![CDATA[ */ function addLoadEvent(func){var oldonload=window.onload;if(typeof window.onload!='function'){window.onload=func;}else{window.onload=function(){oldonload();func();}}} /* ]]> */ </script> <link rel="stylesheet" id="all-css-0" href="https://web.archive.org/web/20130504183413cs_/http://s1.wp.com/_static/??-eJydjkkOwjAMRS9EsJhadYE4SxrcJI2TWI3bqrenILFiEGJlff+nZ8PMyuQkmATiqJhG61MB8gEL9CisTVCPtDWlbOA9zrmI6kj7AYoshC+sOIyrkMcWZF4XCxJOmL7Tk2foyJswrM0V/1GrmU2Ov77u9OCTfc5Pl9wRLOVW0x24xPPuUNX7qm6aU38DFuV8+Q==" type="text/css" media="all"/> <script type="text/javascript" src="https://web.archive.org/web/20130504183413js_/http://s1.wp.com/_static/??-eJyFzUEKgCAQBdALZSKRu+gsYkOMpZkzFXX6CmoRRMHAX/zHH7lEgcH2UwMk3XHjBGm9IneUyS8gPLbJMNzQDoEh8OniQOyByLRv7fMNhhlh+WUOOBrbiQSE27la+0oVWpeqULp0O6cpS2E="></script> <link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://flickrcode.wordpress.com/xmlrpc.php?rsd"/> <link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://flickrcode.wordpress.com/wp-includes/wlwmanifest.xml"/> <meta name="generator" content="WordPress.com"/> <link rel="shortcut icon" type="image/x-icon" href="https://web.archive.org/web/20130504183413im_/http://1.gravatar.com/blavatar/341946154e8a7e5497473810e7ef560c?s=16" sizes="16x16"/> <link rel="icon" type="image/x-icon" href="https://web.archive.org/web/20130504183413im_/http://1.gravatar.com/blavatar/341946154e8a7e5497473810e7ef560c?s=16" sizes="16x16"/> <link rel="apple-touch-icon-precomposed" href="https://web.archive.org/web/20130504183413im_/http://0.gravatar.com/blavatar/8b1d73fba9c0d02a3e78929d8cecfd82?s=114"/> <link rel="openid.server" href="http://flickrcode.wordpress.com/?openidserver=1"/> <link rel="openid.delegate" href="http://flickrcode.wordpress.com/"/> <link rel="search" type="application/opensearchdescription+xml" href="https://web.archive.org/web/20130504183413/http://code.flickr.net/osd.xml" title="code.flickr.com"/> <link rel="search" type="application/opensearchdescription+xml" href="https://web.archive.org/web/20130504183413/http://wordpress.com/opensearch.xml" title="WordPress.com"/> <style> /* <![CDATA[ */ /* Block: reblog */ .reblog-from img { margin: 0 10px 0 0; vertical-align: middle; padding: 0; border: 0; } .reblogger-note img.avatar { float: left; padding: 0; border: 0; } .reblogger-note-content { margin: 0 0 20px; } .reblog-post .wpcom-enhanced-excerpt-content { border-left: 3px solid #eee; padding-left: 15px; } .reblog-post ul.thumb-list { display: block; list-style: none; margin: 2px 0; padding: 0; clear: both; } .reblog-post ul.thumb-list li { display: inline; margin: 0; padding: 0 1px; border: 0; } .reblog-post ul.thumb-list li a { margin: 0; padding: 0; border: 0; } .reblog-post ul.thumb-list li img { margin: 0; padding: 0; border: 0; } .reblog-post .wpcom-enhanced-excerpt { clear: both; } .reblog-post .wpcom-enhanced-excerpt address, .reblog-post .wpcom-enhanced-excerpt li, .reblog-post .wpcom-enhanced-excerpt h1, .reblog-post .wpcom-enhanced-excerpt h2, .reblog-post .wpcom-enhanced-excerpt h3, .reblog-post .wpcom-enhanced-excerpt h4, .reblog-post .wpcom-enhanced-excerpt h5, .reblog-post .wpcom-enhanced-excerpt h6, .reblog-post .wpcom-enhanced-excerpt p { font-size: 100% !important; } .reblog-post .wpcom-enhanced-excerpt blockquote, .reblog-post .wpcom-enhanced-excerpt pre, .reblog-post .wpcom-enhanced-excerpt code, .reblog-post .wpcom-enhanced-excerpt q { font-size: 98% !important; } /* ]]> */ </style> <meta name="application-name" content="code.flickr.com"/><meta name="msapplication-window" content="width=device-width;height=device-height"/><meta name="msapplication-task" content="name=Subscribe;action-uri=http://code.flickr.net/feed/;icon-uri=http://1.gravatar.com/blavatar/341946154e8a7e5497473810e7ef560c?s=16"/> <style type="text/css" id="twentyeleven-header-css"> #site-title, #site-description { position: absolute !important; clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ clip: rect(1px, 1px, 1px, 1px); } </style> <style id="syntaxhighlighteranchor"></style> <link rel="stylesheet" id="custom-css-css" type="text/css" href="https://web.archive.org/web/20130504183413cs_/http://s1.wp.com/?custom-css=1&csblog=2DMyG&cscache=6&csrev=103"/> </head> <body class="archive date typekit-enabled two-column right-sidebar highlander-enabled highlander-light"> <div id="page" class="hfeed"> <header id="branding" role="banner"> <hgroup> <h1 id="site-title"><span><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/" title="code.flickr.com" rel="home">code.flickr.com</a></span></h1> <h2 id="site-description"></h2> </hgroup> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/"> <img src="https://web.archive.org/web/20130504183413im_/http://flickrcode.files.wordpress.com/2012/09/code-flickr-com-drawn-header-grey-large.png" width="1000" height="157" alt=""/> </a> <div class="only-search with-image"> <form method="get" id="searchform" action="https://web.archive.org/web/20130504183413/http://code.flickr.net/"> <label for="s" class="assistive-text">Search</label> <input type="text" class="field" name="s" id="s" placeholder="Search"/> <input type="submit" class="submit" name="submit" id="searchsubmit" value="Search"/> </form> </div> <nav id="access" role="navigation"> <h3 class="assistive-text">Main menu</h3> <div class="skip-link"><a class="assistive-text" href="#content" title="Skip to primary content">Skip to primary content</a></div> <div class="skip-link"><a class="assistive-text" href="#secondary" title="Skip to secondary content">Skip to secondary content</a></div> <div class="menu-menu-container"><ul id="menu-menu" class="menu"><li id="menu-item-2084" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2084"><a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/">Flickr</a></li> <li id="menu-item-2085" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2085"><a href="https://web.archive.org/web/20130504183413/http://blog.flickr.net/">Flickr Blog</a></li> <li id="menu-item-2250" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2250"><a href="https://web.archive.org/web/20130504183413/http://twitter.com/flickr">@flickr</a></li> <li id="menu-item-2086" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2086"><a href="https://web.archive.org/web/20130504183413/http://twitter.com/flickrapi">@flickrapi</a></li> <li id="menu-item-2087" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2087"><a href="https://web.archive.org/web/20130504183413/http://developer.flickr.com/">Developer Guidelines</a></li> <li id="menu-item-2088" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2088"><a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/services/api/">API</a></li> <li id="menu-item-2089" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2089"><a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/jobs/">Jobs</a></li> </ul></div> </nav><!-- #access --> </header><!-- #branding --> <div id="main"> <section id="primary"> <div id="content" role="main"> <header class="page-header"> <h1 class="page-title"> Monthly Archives: <span>April 2012</span> </h1> </header> <article id="post-1773" class="post-1773 post type-post status-publish format-standard hentry category-uncategorized tag-css3 tag-fileapi tag-html5 tag-javascript tag-uploader tag-uploadr"> <header class="entry-header"> <h1 class="entry-title"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/04/25/raising-the-bar-on-web-uploads/" rel="bookmark">Raising the bar on web uploads</a></h1> <div class="entry-meta"> <span class="sep">Posted on </span><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/04/25/raising-the-bar-on-web-uploads/" title="6:05 pm" rel="bookmark"><time class="entry-date" datetime="2012-04-25T18:05:44+00:00">April 25, 2012</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="https://web.archive.org/web/20130504183413/http://code.flickr.net/author/scottschiller/" title="View all posts by scottschiller" rel="author">scottschiller</a></span></span> </div><!-- .entry-meta --> </header><!-- .entry-header --> <div class="entry-content"> <p>With over seven billion photos uploaded since day one, it’s safe to say that uploading is an important part of the Flickr experience.</p> <p>There are numerous ways to get photos onto Flickr, but the native web-based one at <code>flickr.com/photos/upload/</code> is especially important as it typically accounts for a majority of uploads to the site.</p> <h2>A brief history of Flickr “Web Uploadrs”</h2> <div> <div style="float:right;display:inline;width:320px;padding:1em 1em 0;"> <a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/photos/schill/7112862941/in/set-72157629533547504/" title="Flickr "Flashy" Uploadr UI (2008) vs. Basic Uploadr UI"><img src="https://web.archive.org/web/20130504183413im_/http://farm6.staticflickr.com/5117/7112862941_f383735379_n.jpg" width="320" height="128" title="Flickr "Flashy" Uploadr UI (2008) vs. Basic Uploadr UI"/></a> <p style="font-family:helvetica, arial, sans-serif;font-size:.9em;color:#666;margin:0;text-align:center;">Flickr “Flashy” Uploadr UI (2008) vs. Basic Uploadr UI</p> </p></div> <p>Earlier versions of Flickr’s web-based upload UI used a simple <code><form></code> with six file inputs, and no more. As the site grew in scale, the native web upload experience had to scale to match. In early 2008, an <a href="https://web.archive.org/web/20130504183413/http://code.flickr.com/blog/2008/04/22/making-a-better-flickr-web-uploadr-or-web-browsers-arent-good-at-uploading-files-by-themselves/">HTML/Flash hybrid upgrade</a> added support for batch file selection, allowing up to several gigabytes of files to be uploaded in one session. This was a much-needed step in the right direction.</p> </div> <p>The “flashy” uploader does one thing – sending lots of files – fast, and reliably. However, it was not designed to tackle the other tasks one often performs on photos including adding and editing of metadata, sorting and organizing. As a result, “upload and organize” has traditionally been reinforced as two separate actions on Flickr when using the web-based UI.</p> <h2>The new (mostly-HTML5-based) shiny</h2> <p>Thanks to HTML5-based features in newer browsers, we have been able to build a new uploader that’s pretty slick, and is more desktop application-like than ever before; it brings us closer to the idea of a one-stop “upload and organize” experience. At the same time, the UI also retains common web conventions and has a distinct Flickr feel to it. We think the result is a pretty good mix, combining some of the best parts of both.</p> <p>As feedback from a group of beta testers have confirmed, it can also be <i>deceivingly</i> fast.</p> <p style="text-align:center;margin:0;"><a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/photos/schill/6964585290/sizes/o/in/photostream/"><img src="https://web.archive.org/web/20130504183413im_/http://farm9.staticflickr.com/8020/6964585290_6163268b0a.jpg" title="Screenshot: The new (2012) Flickr Web Upload UI (click for full-size)"/></a></p> <p style="font-family:helvetica, arial, sans-serif;color:#666;font-size:90%;text-align:center;margin:0;">The new Flickr Web Uploader. It’s powerful, it’s got a dark background, and it’s <i>fast</i>.</p> <h2 style="margin-top:1em;">Features: An Overview</h2> <p>Here are a few fun things the new uploader does:</p> <ul> <li> <p>Drag and drop batches of files from your OS. Where present and supported, EXIF thumbnails are shown in the UI almost immediately.</p> </li> <li> <p>Fluid photo “grid” shows photo thumbnails, allows larger, lightbox-style previews, inline editing of description/title and rotation.</p> </li> <li> <p>Mouse and keyboard-based grid selection and rearrange functionality similar to that of desktops.</p> </li> <li> <p>“Editor panel” shows state of current selection, provides powerful batch editing features (title + description, adding of tags, people, sets, license, privacy etc.)</p> </li> <li> <p>“Info” mode shows overlay icons on grid items, allowing for a quick overview of pending edits (privacy, people, tags etc.)</p> </li> <li> <p>Auto-retry and recovery cases for dropped / lost connection cases</p> </li> </ul> <h2 style="margin-top:1em;">Technical Bits</h2> <p>A small book could probably be written on the process, prototypes and technology decisions made during the development of this uploader, but we’ll save the gory details for a couple of in-depth blog posts which will highlight specific parts of the UI. In the meantime, here are some notes on the tech used:</p> <ul> <li> <p><b>HTML5 File APIs</b></p> <p>Modern browser file APIs make up the core of file handling functionality, including drag-and-dropping of files right into the browser. <code>FileReader</code>-type APIs allow access to data from disk, enabling things like EXIF thumbnail parsing and retrieval where supported. EXIF parsing is almost instantaneous and thumbnails are hugely valuable, of course, in prompting users’ editing decisions.</p> <p>(For browsers without the relevant file APIs, a Flash-based fallback is used in which case file drag-and-drop is not supported, and EXIF thumb previews are not implemented.)</p> </li> <li> <p><b>CSS3</b></p> <p>Thanks to growing support across newer browsers, we’ve been able to produce a modern design that takes advantage of CSS-based gradients to achieve visual goals that would have traditionally required external images, and occasionally, hacks or shims in our HTML and JavaScript.</p> <p>CSS3′s <code>border-radius</code>, <code>text-shadow</code> and <code>box-shadow</code> are also featured nicely in this new design, alongside visual <code>transform</code> effects such as <code>rotate</code>, <code>zoom</code> and <code>scale</code>. Eagle-eyed users of newer Webkit builds such as Chrome Canary may even see a little use of <code>filter</code> with <code>blur</code> here and there.</p> <p>CSS transitions are also featured extensively in the new uploader, a notable shift away from animation sequences which would traditionally have been calculated and rendered by JavaScript. Good candidates for transitions include the expanding or collapsing of a menu section, or a background color fade when a text area is focused, for example.</p> <p>While triggering transitions and/or transforms can be a little quirky depending on the current “state” of the element (for example, an element just added to the DOM may need a moment to settle and be rendered before transitioning,) the advantage of using CSS vs. JS for “enhancement”-style UI effects like these is absolutely clear.</p> </li> <li> <p><b>YUI3</b></p> <p>Thanks to <a href="https://web.archive.org/web/20130504183413/http://yuilibrary.com/">YUI3</a>, the new Flickr Uploader is a highly-modularized, component-based application. The editr module itself is comprised of about 35 sub-modules, following YUI’s standard module pattern. In Flickr’s case, modules are defined as being JavaScript, CSS or string (i.e., language translation) components. This compartmentalization approach reduces the overall complexity of code, encourages extensibility and allows developers to work on features within a specific scope.</p> </li> </ul> <h2>A sneak peek: Screencast (Beta Version)</h2> <p>At time of writing, the new uploader is being gradually rolled out to the masses. For those who haven’t seen it yet, here’s a <a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/photos/schill/6928227556/in/set-72157629533547504/?likes_hd=1">demo screencast of an earlier beta version</a> showing some of the interactions for common upload and editing use cases. (Best viewed full-screen, and with “HD” on.) The video gives an idea of what the experience is like, but it’s best seen in person. We’ve really had a lot of fun building this one.</p> <p class="undersized-image-container"> <object type="application/x-shockwave-flash" width="500" height="281" data="https://web.archive.org/web/20130504183413im_/http://www.flickr.com/apps/video/stewart.swf?v=1.161" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> <param name="flashvars" value="photo_id=6928227556&photo_secret=11b73352d1&flickr_show_info_box=true"></param><param name="movie" value="http://www.flickr.com/apps/video/stewart.swf?v=1.161"></param><param name="bgcolor" value="#000000"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="opaque"></param><embed type="application/x-shockwave-flash" src="https://web.archive.org/web/20130504183413oe_/http://www.flickr.com/apps/video/stewart.swf?v=1.161" bgcolor="#000000" allowfullscreen="true" flashvars="photo_id=6928227556&photo_secret=11b73352d1&flickr_show_info_box=true" wmode="opaque" height="281" width="500"></embed></object></p> <div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"></div> </div><!-- .entry-content --> <footer class="entry-meta"> <span class="cat-links"> <span class="entry-utility-prep entry-utility-prep-cat-links">Posted in</span> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/uncategorized/" title="View all posts in Uncategorized" rel="category tag">Uncategorized</a> </span> <span class="sep"> | </span> <span class="tag-links"> <span class="entry-utility-prep entry-utility-prep-tag-links">Tagged</span> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/css3/" rel="tag">css3</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/fileapi/" rel="tag">fileapi</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/html5/" rel="tag">html5</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/javascript/" rel="tag">javascript</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/uploader/" rel="tag">uploader</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/uploadr/" rel="tag">uploadr</a> </span> </footer><!-- .entry-meta --> </article><!-- #post-1773 --> <article id="post-1748" class="post-1748 post type-post status-publish format-standard hentry category-uncategorized tag-aviary tag-cors tag-flash tag-html5"> <header class="entry-header"> <h1 class="entry-title"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/04/18/building-an-html5-photo-editor/" rel="bookmark">Building an HTML5 Photo Editor</a></h1> <div class="entry-meta"> <span class="sep">Posted on </span><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/04/18/building-an-html5-photo-editor/" title="11:16 pm" rel="bookmark"><time class="entry-date" datetime="2012-04-18T23:16:35+00:00">April 18, 2012</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="https://web.archive.org/web/20130504183413/http://code.flickr.net/author/standardpixel/" title="View all posts by Eric Gelinas" rel="author">Eric Gelinas</a></span></span> </div><!-- .entry-meta --> </header><!-- .entry-header --> <div class="entry-content"> <p style="padding:6px 8px;background:#f8f8f8;"> <a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/photos/35998464@N07/"><img src="https://web.archive.org/web/20130504183413im_/http://farm5.staticflickr.com/4008/buddyicons/35998464@N07.jpg?1263791643#35998464@N07" style="float:left;margin-right:8px;" width="48px" height="48px"></a>Introducing guest blogger, <b>Ari Fuchs</b>. He is a Lead API Engineer and Developer Evangelist at <b><a href="https://web.archive.org/web/20130504183413/http://aviary.com/">Aviary</a></b>. He has spent the last 3 years building out Aviary’s internal and external facing APIs, and is now working with partners to bring Aviary’s tools to the masses. He also did a lot of work to bring the Aviary editor to Flickr. <a href="https://web.archive.org/web/20130504183413/https://twitter.com/arifuchs">Follow him on Twitter</a> and send him a nice message to make him feel better about his <a href="https://web.archive.org/web/20130504183413/http://www.flickr.com/photos/35998464@N07/5193379076/in/photostream">stolen bike</a>. Now, on to his post… </p> <p>At Aviary, we’ve been passionate about photos since day one. It’s been five years since we released our first creative tool, Phoenix, a powerful, free Flash-based photo editor. Phoenix offered functionality on par with Adobe Photoshop 5 and a price point that opened its usage to anyone with an internet connection. As amateur photographers worldwide began trying their hand at editing, we watched our product join the ranks of a small number of companies working to democratize the process of photo editing for the first time.</p> <p>Around two years ago we began rethinking the future of our tool set. While our original tools offered incredible functionality, they did have a learning curve which meant that the average person couldn’t just sit down and begin editing without investing time to become familiar with the tools. We wanted to build a powerful editor that anyone could use.</p> <p>Because we were rebuilding the editor from the ground up, we took the opportunity to switch from a Flash based solution to one built using HTML5 technologies. We saw this as an opportunity to build on a growing standard, and to support the most platforms.</p> <p>In fall of 2010 we released our HTML5 photo editor which has evolved into the product we’re proud to share with you today.</p> <h2>Widget Encapsulation</h2> <p>During our initial foray into the online editor space, we took a straightforward approach by having API users launch our editor in a new page or window. This simplified integrations and allowed us to own the editing experience.</p> <p>When we rebuilt our editor in JavaScript, we took the opportunity to re-architect our API as well. Our first big change was making the editor embeddable. This meant that third party developers could load the editor on their own sites, maintaining user engagement while controlling their experience. We built out customization options that allowed the site owner to decide which tools appeared in the editor. A real estate site, for example, might not want its users adding mustache stickers to appliances in photos.</p> <p>Our editor, unlike many rich HTML widgets, does not require an iframe and is truly embedded into a hosting webpage. This posed many challenges during development, but the result is a more seamless, lightweight integration. </p> <p class="undersized-image-container"><img src="https://web.archive.org/web/20130504183413im_/http://farm6.staticflickr.com/5080/6945711452_34af520b41.jpg" width="500" height="392" alt="Aviary embeded in Flickr" border="0"/></p> <h2>Constructor API</h2> <p>When we rebuilt our API, we took a leap by assuming that web developers integrating our editor would have experience with other JavaScript libraries and plugins. We built our API to use a Constructor method that accepts a configuration object to allow for the aforementioned tool customization. The configuration object is also used to configure callbacks, image URLs, language settings, etc., and allows us to continue building out our API without losing backwards compatibility.</p> <h2>Simplifying the Save Process</h2> <p>Saving image data is always a challenge in the browser, and can require various cross-browser workarounds. An obvious method would be to initiate a form post to the server and include the base64 image data in a hidden field. This breaks in Safari, where form fields have an undocumented value length limit. We worked around this by switching to an ajax post with the appropriate CORS headers to get around cross domain issues. In browsers that don’t support CORS, we fall back to the form post method.</p> <p>To hide this complexity from the developer, we’ve abstracted the save process completely. When a user saves an edited image, we temporarily save the image data to our own servers and return a public URL so the host application can download the image to their own.</p> <h2>High Resolution Photos</h2> <p>One of the coolest features of our editor is the high resolution image support — that being said, it certainly has a number of challenges. There’s the practical issue of limited real estate in the browser (keep an eye out for updates addressing this in the near future), as well as performance issues that are harder to quantify. Even in Flash based tools, the size of the image you can edit in the browser is limited by a number of gating factors: hardware specs, number of running processes, etc. To get around these client limitations, we’ve set a configurable maxSize on the editor and added a configuration field for an original-resolution version of the image to be edited: hiresUrl.</p> <p>When a hiresUrl is supplied, every user edit action is logged. On save, the aptly named “actionlist” is sent to our server along with the hiresUrl. When it hits our render farm, the actionlist is replayed on the high resolution image, and the final results are returned to the host site via a new hiresUrl.</p> <pre> { "metadata": { "imageorigsize": [ 800, 530 ] }, "actionlist": [ { "action": "setfeathereditsize", "width": 800, "height": 530 }, { "action": "flatten" }, { "action": "redeye", "radius": 5, "pointlist": [ [545, 183], [546,183], [547,182], [548,181], [548,179], [548,177], [547,177], [545,177], [544,177], [543,177], [542,177], [541,179], [541,181], [541,183], [542,184] ] }, { "action": "redeye", "radius": 5, "pointlist": [ [481, 191], [481,193], [481,195], [482,196], [483,197], [484,198], [485,197], [485,196], [485,193], [485,190], [485,189], [485,188], [484,188], [482,188], [480,189], [480,190], [480, 191] ] }, { "action": "sharpen", "value": 21.69312, "flatten": true } ] } </pre> <p>As a side note, we maintain feature parity across all of our platforms (mobile included) by prototyping new tools and filters in the JavaScript first, and then porting them to C for our render farm and Android, and then to Objective-C for our iPhone SDK. By maintaining feature parity and synchronizing output across platforms, we’re able to ensure that users get the edits they expect on their high resolution photos, and we keep the door open for future server-side support for our mobile SDKs where the original photo might not be stored on the device.</p> <h2>Tools and Libraries</h2> <p>We use some pretty awesome tools to help us maintain cross-browser compatibility.</p> <h2><a href="https://web.archive.org/web/20130504183413/http://lesscss.org/">LESS CSS</a></h2> <p>We moved a lot of the cross-browser concerns to build-time with LESS and a library of mix-ins inspired initially by Twitter Bootstrap, though the final result is wholly our own. LESS’s color math and variables let us achieve a textured and rounded look and feel while minimizing complexity during development.</p> <p><pre> /* LESS */ .avpw_inset_button_group { #gradient > .vertical(lighten(@conveyorBelt, 4%), darken(@conveyorBelt, 1%)); .box-shadow(inset 0 0 4px darken(@conveyorBelt, 20%)); .border-radius(8px); } /* EXPANDED */ .avpw_inset_button_group { background-color: #2a2a2a; background-repeat: repeat-x; background-image: -khtml-gradient(linear, left top, left bottom, from(#383838), to(#2a2a2a)); background-image: -moz-linear-gradient(top, #383838, #2a2a2a); background-image: -ms-linear-gradient(top, #383838, #2a2a2a); background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #383838), color-stop(100%, #2a2a2a)); background-image: -webkit-linear-gradient(top, #383838, #2a2a2a); background-image: -o-linear-gradient(top, #383838, #2a2a2a); background-image: linear-gradient(top, #383838, #2a2a2a); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#383838', endColorstr='#2a2a2a', GradientType=0); -webkit-box-shadow: inset 0 0 4px #000000; -moz-box-shadow: inset 0 0 4px #000000; box-shadow: inset 0 0 4px #000000; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; } </pre> </p> <h2><a href="https://web.archive.org/web/20130504183413/https://developer.mozilla.org/en/CSS">CSS3</a></h2> <p>With CSS3, we’ve just about managed a complete break from the DHTML effects of the past. The new UI uses CSS3 transitions and transforms wherever possible to remain future-proof.</p> <h2>Flash</h2> <p>Yes, our editor does indeed have a Flash fallback for browsers that lack certain HTML5 features (namely <a href="https://web.archive.org/web/20130504183413/https://developer.mozilla.org/en/HTML/Element/canvas">canvas</a>). We initially built the editor as a move away from Flash, but because of the legacy IE7 and IE8 userbases on our larger partner sites, we had to go back and rebuild certain components in Flash to support those browsers.</p> <p>We’ve architected the editor so that Flash is only being used where necessary. Some tools, such as draw, have been completely rebuilt in Flash; for others, like effects, the bitmap data is being exported and manipulated in JavaScript (using a reverse implementation of pibeca). This allows for code reuse, and enables us to build new features faster with more backwards compatibility.</p> <h2>Future</h2> <p>While the feedback for our editor has been overwhelmingly fantastic, we’re continuing to work hard building out new tools and features, and performance enhancements to our existing set.</p> <div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"></div> </div><!-- .entry-content --> <footer class="entry-meta"> <span class="cat-links"> <span class="entry-utility-prep entry-utility-prep-cat-links">Posted in</span> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/uncategorized/" title="View all posts in Uncategorized" rel="category tag">Uncategorized</a> </span> <span class="sep"> | </span> <span class="tag-links"> <span class="entry-utility-prep entry-utility-prep-tag-links">Tagged</span> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/aviary/" rel="tag">aviary</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/cors/" rel="tag">CORS</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/flash/" rel="tag">flash</a>, <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/tag/html5/" rel="tag">html5</a> </span> </footer><!-- .entry-meta --> </article><!-- #post-1748 --> </div><!-- #content --> </section><!-- #primary --> <div id="secondary" class="widget-area" role="complementary"> <aside id="search-2" class="widget widget_search"> <form method="get" id="searchform" action="https://web.archive.org/web/20130504183413/http://code.flickr.net/"> <label for="s" class="assistive-text">Search</label> <input type="text" class="field" name="s" id="s" placeholder="Search"/> <input type="submit" class="submit" name="submit" id="searchsubmit" value="Search"/> </form> </aside> <aside id="recent-posts-2" class="widget widget_recent_entries"> <h3 class="widget-title">Recent Posts</h3> <ul> <li> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2013/03/26/using-redis-as-a-secondary-index-for-mysql/" title="Using Redis as a Secondary Index for MySQL">Using Redis as a Secondary Index for MySQL</a> </li> <li> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/12/21/redis-global-locks-redux/" title="Redis Global Locks Redux">Redis Global Locks Redux</a> </li> <li> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/12/12/highly-available-real-time-notifications/" title="Highly Available Real Time Push Notifications and You">Highly Available Real Time Push Notifications and You</a> </li> <li> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/12/10/drag-n-drop/" title="Avoiding Dragons: A Practical Guide to Drag ’n’ Drop">Avoiding Dragons: A Practical Guide to Drag ’n’ Drop</a> </li> <li> <a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/10/26/flickr-at-sf-web-performance/" title="Flickr at SF Web Performance">Flickr at SF Web Performance</a> </li> </ul> </aside><aside id="archives-2" class="widget widget_archive"><h3 class="widget-title">Archives</h3> <ul> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2013/03/" title="March 2013">March 2013</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/12/" title="December 2012">December 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/10/" title="October 2012">October 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/07/" title="July 2012">July 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/06/" title="June 2012">June 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/05/" title="May 2012">May 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/04/" title="April 2012">April 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/02/" title="February 2012">February 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2012/01/" title="January 2012">January 2012</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/12/" title="December 2011">December 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/10/" title="October 2011">October 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/09/" title="September 2011">September 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/08/" title="August 2011">August 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/07/" title="July 2011">July 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/06/" title="June 2011">June 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/03/" title="March 2011">March 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/02/" title="February 2011">February 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2011/01/" title="January 2011">January 2011</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/11/" title="November 2010">November 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/10/" title="October 2010">October 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/09/" title="September 2010">September 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/08/" title="August 2010">August 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/07/" title="July 2010">July 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/05/" title="May 2010">May 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/04/" title="April 2010">April 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/03/" title="March 2010">March 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/02/" title="February 2010">February 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2010/01/" title="January 2010">January 2010</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/12/" title="December 2009">December 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/11/" title="November 2009">November 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/10/" title="October 2009">October 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/09/" title="September 2009">September 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/07/" title="July 2009">July 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/06/" title="June 2009">June 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/05/" title="May 2009">May 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/04/" title="April 2009">April 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/03/" title="March 2009">March 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/02/" title="February 2009">February 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2009/01/" title="January 2009">January 2009</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/12/" title="December 2008">December 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/11/" title="November 2008">November 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/10/" title="October 2008">October 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/09/" title="September 2008">September 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/08/" title="August 2008">August 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/07/" title="July 2008">July 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/06/" title="June 2008">June 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/05/" title="May 2008">May 2008</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/2008/04/" title="April 2008">April 2008</a></li> </ul> </aside><aside id="categories-2" class="widget widget_categories"><h3 class="widget-title">Categories</h3> <ul> <li class="cat-item cat-item-564792"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/change-log/" title="View all posts filed under changelog">changelog</a> </li> <li class="cat-item cat-item-5784"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/event/" title="View all posts filed under event">event</a> </li> <li class="cat-item cat-item-29160"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/geo/" title="All things geo related">geo</a> </li> <li class="cat-item cat-item-34412"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/kittens/" title="View all posts filed under kittens">kittens</a> </li> <li class="cat-item cat-item-171"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/meta/" title="View all posts filed under meta">meta</a> </li> <li class="cat-item cat-item-1"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/uncategorized/" title="View all posts filed under Uncategorized">Uncategorized</a> </li> <li class="cat-item cat-item-249276"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/uploadr/" title="View all posts filed under uploadr">uploadr</a> </li> <li class="cat-item cat-item-830560"><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/category/xulrunner/" title="View all posts filed under xulrunner">xulrunner</a> </li> </ul> </aside><aside id="meta-2" class="widget widget_meta"><h3 class="widget-title">Meta</h3> <ul> <li><a href="https://web.archive.org/web/20130504183413/http://wordpress.com/signup/?ref=wplogin">Register</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://flickrcode.wordpress.com/wp-login.php">Log in</a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/feed/" title="Syndicate this site using RSS 2.0">Entries <abbr title="Really Simple Syndication">RSS</abbr></a></li> <li><a href="https://web.archive.org/web/20130504183413/http://code.flickr.net/comments/feed/" title="The latest comments to all posts in RSS">Comments <abbr title="Really Simple Syndication">RSS</abbr></a></li> Powered by <a href="https://web.archive.org/web/20130504183413/http://vip.wordpress.com/" rel="generator nofollow" class="powered-by-wpcom">WordPress.com VIP</a> </ul> </aside> </div><!-- #secondary .widget-area --> </div><!-- #main --> <footer id="colophon" role="contentinfo"> <div id="site-generator"> Theme: Twenty Eleven <span class="sep"> | </span> Powered by <a href="https://web.archive.org/web/20130504183413/http://vip.wordpress.com/" rel="generator nofollow" class="powered-by-wpcom">WordPress.com VIP</a> </div> </footer><!-- #colophon --> </div><!-- #page --> <script type="text/javascript"> var _qevents = _qevents || [], wpcomQuantcastData = {"qacct":"p-18-mFEk4J448M","labels":",language.en,type.wpcom,vip.flickrcode"}; function wpcomQuantcastPixel( labels, options ) { var i, defaults = wpcomQuantcastData, data = { event: 'ajax' }; labels = labels || ''; options = options || {}; if ( typeof labels != 'string' ) options = labels; for ( i in defaults ) { data[i] = defaults[i]; } for ( i in options ) { data[i] = options[i]; } if ( data.labels ) { data.labels += ',' + labels; } else { data.labels = labels; } _qevents.push( data ); }; (function() {var elem = document.createElement('script');elem.src = (document.location.protocol == "https:" ? "https://web.archive.org/web/20130504183413/https://secure" : "https://web.archive.org/web/20130504183413/http://edge") + ".quantserve.com/quant.js";elem.async = true;elem.type = "text/javascript";var scpt = document.getElementsByTagName('script')[0];scpt.parentNode.insertBefore(elem, scpt); })(); _qevents.push( wpcomQuantcastData ); </script> <noscript><div style="display: none;"><img src="//web.archive.org/web/20130504183413im_/http://pixel.quantserve.com/pixel/p-18-mFEk4J448M.gif?labels=%2Clanguage.en%2Ctype.wpcom%2Cvip.flickrcode" height="1" width="1" alt=""/></div></noscript> <script type="text/javascript" src="//web.archive.org/web/20130504183413js_/http://0.gravatar.com/js/gprofiles.js?ver=201318ac"></script> <script type="text/javascript"> /* <![CDATA[ */ var WPGroHo = {"my_hash":""}; /* ]]> */ </script> <script type="text/javascript" src="https://web.archive.org/web/20130504183413js_/http://s0.wp.com/wp-content/mu-plugins/gravatar-hovercards/wpgroho.js?m=1351637563g"></script> <script>jQuery(document).ready(function($){ Gravatar.profile_cb = function( h, d ) { WPGroHo.syncProfileData( h, d ); }; Gravatar.my_hash = WPGroHo.my_hash; Gravatar.init( 'body', '#wp-admin-bar-my-account' ); });</script> <div style="display:none"> </div> <script type="text/javascript" src="https://web.archive.org/web/20130504183413js_/http://s0.wp.com/wp-content/js/devicepx.js?m=1354656609g"></script> <script type="text/javascript"> // <![CDATA[ (function() { try{ if ( window.external &&'msIsSiteMode' in window.external) { if (window.external.msIsSiteMode()) { var jl = document.createElement('script'); jl.type='text/javascript'; jl.async=true; jl.src='/wp-content/plugins/ie-sitemode/custom-jumplist.php'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(jl, s); } } }catch(e){} })(); // ]]> </script><script src="https://web.archive.org/web/20130504183413js_/http://s.stats.wordpress.com/w.js?21" type="text/javascript"></script> <script type="text/javascript"> st_go({'blog':'39034126','v':'wpcom','tz':'0','user_id':'0','subd':'flickrcode'}); function st_vt() {var x=document.createElement("img");x.src="https://web.archive.org/web/20130504183413/http://stats.wordpress.com/g.gif?blog=39034126&v=wpcomvt&tz=0&user_id=0&subd=flickrcode&rand="+Math.random();} ex_go({'crypt':'UE40eW5QN0p8M2Y/RE1BNmNJfGhxNCVxUDExYmtXRThKbHcwXTdETWI1alhvb1oseHImN101ZFpEakVpYjlQYVFLYzBaVHRtPz0wXS9bM1lKdVZKQS1NTGJmdUM0ZHJxVkdbSmJHXy45cWp6UEwwZUM/ZjMuP3ZrOFF6SE1vRUxvMzhIeSthUVgvMyZnWiVoX3lNLCtxLmEmaXd8Y1AxZmlyJS5nRCs5Mzl6RmkzeHg5OWUzdUIyMW5VemZMU09iLmFTXXVUNk1jS3clbk10cHdaQ0FwcUZXNUs3W0tSLTY0dXx0M1pBdUJbQmJzVGxQVk04SXY0NjZWVmI1cGo='}); addLoadEvent(function(){linktracker_init('39034126',0);}); </script> <noscript><img src="https://web.archive.org/web/20130504183413im_/http://stats.wordpress.com/b.gif?v=noscript" style="height:0px;width:0px;overflow:hidden" alt=""/></noscript> </body> </html><!-- FILE ARCHIVED ON 18:34:13 May 04, 2013 AND RETRIEVED FROM THE INTERNET ARCHIVE ON 07:50:19 Mar 03, 2025. JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. SECTION 108(a)(3)). --> <!-- playback timings (ms): captures_list: 3.04 exclusion.robots: 2.227 exclusion.robots.policy: 0.03 esindex: 0.013 cdx.remote: 19.76 LoadShardBlock: 104.38 (3) PetaboxLoader3.datanode: 95.886 (4) PetaboxLoader3.resolve: 408.894 (2) load_resource: 423.13 -->