4
\$\begingroup\$

Your goal in this code golf is to compile all the Stack Exchange sites' favicons.


Getting the Domains

The domains you will be using will be:

  • *.stackexchange.com
  • stackexchange.com
  • stackoverflow.com
  • superuser.com
  • serverfault.com
  • askubuntu.com
  • mathoverflow.net
  • stackapps.com
  • stackauth.com

Everything but stackexchange.com and stackauth.com, as of now, can be gathered from /sites.

All the meta counterparts will also be included. (just prepend meta. to the beginning of any of the domains to get the meta site)

The list above shows the order of the sites. Sites without their own domain (*.stackexchange.com, i.e. codegolf.stackexchange.com) are first in the stylesheet, and are sorted alphabetically. After you've sorted the subdomain sites, the rest of the domains are in the order listed above.

Then, you can append /favicon.ico to each one of the domains you dynamically compiled and get the 32 by 32 pixel image that results from it.

Here's another list for domains that redirect to *.stackexchange.com sites:

  • askdifferent.comapple.stackexchange.com
  • arqade.comgaming.stackexchange.com
  • seasonedadvice.comcooking.stackexchange.com

Output the Images

As soon as you have all of the favicons from the sites and their meta counterparts, your code combines them. Optionally, convert them into .PNG files (you gathered .ICOs, and trying to get favicon.png doesn't work) After that, you can start combining the images into one rectangle.

There will be 10 columns, each 32 pixels in width, making a total of 320 pixels. The number of rows will match those needed to fit all the favicons, and they are 32 pixels in height.

Your code should output an image with all of the favicons you gathered as a transparent .PNG file, which is already , like seen below:


Scoring

This is and so the least bytes wins!

Leaderboard

Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

var QUESTION_ID=86753;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),e.has_more?getAnswers():process()}})}function shouldHaveHeading(e){var a=!1,r=e.body_markdown.split("\n");try{a|=/^#/.test(e.body_markdown),a|=["-","="].indexOf(r[1][0])>-1,a&=LANGUAGE_REG.test(e.body_markdown)}catch(n){}return a}function shouldHaveScore(e){var a=!1;try{a|=SIZE_REG.test(e.body_markdown.split("\n")[0])}catch(r){}return a}function getAuthorName(e){return e.owner.display_name}function process(){answers=answers.filter(shouldHaveScore).filter(shouldHaveHeading),answers.sort(function(e,a){var r=+(e.body_markdown.split("\n")[0].match(SIZE_REG)||[1/0])[0],n=+(a.body_markdown.split("\n")[0].match(SIZE_REG)||[1/0])[0];return r-n});var e={},a=1,r=null,n=1;answers.forEach(function(s){var t=s.body_markdown.split("\n")[0],o=jQuery("#answer-template").html(),l=(t.match(NUMBER_REG)[0],(t.match(SIZE_REG)||[0])[0]),c=t.match(LANGUAGE_REG)[1],i=getAuthorName(s);l!=r&&(n=a),r=l,++a,o=o.replace("{{PLACE}}",n+".").replace("{{NAME}}",i).replace("{{LANGUAGE}}",c).replace("{{SIZE}}",l).replace("{{LINK}}",s.share_link),o=jQuery(o),jQuery("#answers").append(o),e[c]=e[c]||{lang:c,user:i,size:l,link:s.share_link}});var s=[];for(var t in e)e.hasOwnProperty(t)&&s.push(e[t]);s.sort(function(e,a){return e.lang>a.lang?1:e.lang<a.lang?-1:0});for(var o=0;o<s.length;++o){var l=jQuery("#language-template").html(),t=s[o];l=l.replace("{{LANGUAGE}}",t.lang).replace("{{NAME}}",t.user).replace("{{SIZE}}",t.size).replace("{{LINK}}",t.link),l=jQuery(l),jQuery("#languages").append(l)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",answers=[],page=1;getAnswers();var SIZE_REG=/\d+(?=[^\d&]*(?:&lt;(?:s&gt;[^&]*&lt;\/s&gt;|[^&]+&gt;)[^\d&]*)*$)/,NUMBER_REG=/\d+/,LANGUAGE_REG=/^#*\s*([^,]+)/;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"><div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table></div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table></div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody></table><table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody></table>

\$\endgroup\$
27

3 Answers 3

9
\$\begingroup\$

JavaScript (ES6), 691 687 680 663 646 657 bytes

fetch(new Request('//crossorigin.me/http://stackexchange.com/sites')).then(r=>r.text(t=(d=document).body.appendChild(c=d.createElement('canvas')).getContext('2d')).then(a=>(d=new DOMParser(c.width=320).parseFromString(a,'text/html'),s=[...d.querySelectorAll('.grid-view-container>a')].map(e=>!~(h=e.href[p='slice'](7))[o='indexOf']('met')?h:h[p](5)),c.height=-~(s.length/5)*32,s.sort((a,b)=>(e=a[o](k='.s'),g=b[o](k),~e&&~g?a[l='localeCompare'](b):!~e&&!~g?a[p](e)[l](b[p](g)):g-e)).concat(s.filter(u=>!~u[o]('ka')).map(u=>'meta.'+u))).map((u,i,x,y)=>(u=`http://${u}favicon.ico`,x=i%10,y=(i-x)/10,i=new Image,i.src=u,i.onload=_=>t.drawImage(i,x*32,y*32)))))

Oof. Hopefully I can trim down the byte count.

Basically:

  • CORS request to /sites, parse DOM and scrape the site list from there using a query selector.
  • Sort first by *.stackexchange.com sites alphabetically, then by independent domain sites alphabetically.
  • Append the meta sites, filtering out meta.stackexchange.com and stackapps.com since they're both listed on /sites and are without their own meta site.
  • Asynchronously load each favicon.ico without CORS since we don't care about tainting the canvas.
  • Use each site's index i from the sorted array to place the loaded image in the correct position on the sprite-sheet.

You can right-click on the image and select "Save Image as", so I don't think I need to write more just to explicitly prompt the image for download, since it's already enough work just displaying it.

Revisions

  1. Inlining global declarations saved 2 bytes. Submission is now one loooong line.
  2. Removing () from new Image() saved another 2 bytes.
  3. Changing Math.ceil(...) to -~(...) saved 7 bytes. Doesn't apply now, but in the event that the amount of sites is a multiple of 10, it's still okay because "There are 10 columns, which is 320 pixels in width, and unlimited rows/height" regardless of the amount of sites.
  4. Saved 17 bytes by moving a global declaration and refactoring an unneeded ternary operator in a .map() function.
  5. Saved another 17 bytes by storing long repeated method names to variables.
  6. Cost 11 bytes to factor in stackexchange.com and its meta... gross.
\$\endgroup\$
18
  • \$\begingroup\$ Nothing seems to shows up when I run your code snippet and save the image... \$\endgroup\$
    – R. Kap
    Jul 29, 2016 at 18:46
  • \$\begingroup\$ @R.Kap does the sprite-sheet populate with all the favicons when viewing the program in progress? I'm running on Chrome 52 and it works perfectly. Make sure your browser supports the Request API and ES6. Check for errors in your console when running the program. If there are any, it would be helpful for me if you could share those. \$\endgroup\$ Jul 29, 2016 at 18:47
  • \$\begingroup\$ Well, I am running the snippet in Firefox and don't really know if it supports the Request API. However, I know it does support ES6 since a lot of other ES6 snippets on PPCG do work. There are no errors in the console, so t's most likely that Firefox may not support the Request API. \$\endgroup\$
    – R. Kap
    Jul 29, 2016 at 18:52
  • \$\begingroup\$ codegolf.stackexchange.com/a/86853/42091 Since you're in Firefox and there are no errors, my guess is that it's blocking "non-secure requests from HTTPS to HTTP". Just click that link and make sure your URL is HTTP instead of HTTPS when running the script. \$\endgroup\$ Jul 29, 2016 at 18:54
  • 1
    \$\begingroup\$ @Peanut I don't appreciate you editing my answer without my permission. I'm reverting it. Don't do that again. \$\endgroup\$ Jul 30, 2016 at 23:37
3
\$\begingroup\$

Python 3.5, 591 549 541 557 570 bytes:

(+16 bytes (541 -> 557) to add the ability for the final image to dynamically expand in height based on the number of favicons gathered.)

(+13 bytes (557 -> 570) since URL shorteners are apparently not allowed.)

from urllib.request import*;from PIL import Image as I;from io import*;import re
H='http://';U=urlopen;u=y=0;V=sorted(re.findall('<h2><a href="%s((?!meta).+?.stackexchange.com)">'%H,U(H+'stackexchange.com/sites?view=list').read().decode()[7167:]))+['%s.com'%i for i in'stackexchange stackoverflow superuser serverfault askubuntu mathoverflow stackapps stackauth'.split()];i=I.new("RGBA",(320,-~(len(V)*2//10)*32))
for l in V+['meta.'+i for i in V]:
 try:
  i.paste(I.open(BytesIO(U(H+l+'/favicon.ico').read())),(u,y));u+=32
  if u>319:u=0;y+=32
 except:0
i.save('O.png')

Quite long, but hey, I beat Javascript! :D This imports the modules for the following uses:

  • urllib to fetch website and image data.
  • io module for the BytesIO function to decode the fetched image data for PIL.
  • re for the use of regular expressions to parse the website data.
  • PIL to create the final image from the fetched image data.

Creates and saves a 320 x 1024 PNG image named O.png in the current working directory, with all the favicons in the order specified. I will try to golf this down more over time as much as I can, but for now, I could not be happier. :)

Final Output Image:

Final

\$\endgroup\$
3
  • \$\begingroup\$ +1 because it doesn't have duplicated Stack Overflow favicons, unlike the JavaScript version \$\endgroup\$ Jul 30, 2016 at 0:59
  • \$\begingroup\$ Got an error: SyntaxError: Unexpected keyword 'case'. Expected a closing '}' at the end of a block statement. \$\endgroup\$ Jul 30, 2016 at 3:03
  • \$\begingroup\$ @Peanut It works perfectly fine for me with Python 3.5 on Mac. \$\endgroup\$
    – R. Kap
    Jul 30, 2016 at 3:05
1
\$\begingroup\$

JavaScript, 563 bytes

a=[]
fetch`//api.stackexchange.com/sites?pagesize=999`.then(r=>r.json().then(j=>{p=j.items.forEach(i=>a.push(i.site_url+"/favicon.ico"))
s=a.sort()
g=s.filter(e=>/(?<!meta)\.stackexchange/.exec(e)).concat(["//stackexchange.com/favicon.ico"]).concat(s.filter(e=>!/meta|stackexchange/.exec(e))).concat(s.filter(e=>/meta/.exec(e)))
document.body.append(c=document.createElement`canvas`)
c.width=320
c.height=(g.length/10+0.9|0)*32
c=c.getContext`2d`
for(u in g){
var i=new Image()
i.src=g[u]
i.u=u
i.onload=(p)=>c.drawImage(b=p.target,b.u*32%320,((b.u/10)|0)*32)}}))

Generated image (as of writing this)

<img src="https://i.stack.imgur.com/jy3Ct.png"/>

Explanation (key points)


fetch`//api.stackexchange.com/sites?pagesize=999`
▸ Fetch the list of all SE sites [API docs]
-2 bytes: Uses ES6's tagged template
-3~4 bytes: It also uses a trick // that takes advantage of Stack Snippets (otherwise, you'd have to replace // with http: or even https: because of CORS weirdness)
Note: It only list the first 999 sites. While as of writing this, theres "only" 355 sites, this could reach 1000. then you'll need to add one byte: ...?pagesize="+2e9, which can go up to 2000000000! (more than that makes an invalid parameters error)


s=a.sort()
▸ Sort the list of URLs in alphabetical order (meta and normal aren't separated yet)


s.filter(e=>/(?<!meta)\.stackexchange/.exec(e))
▸ Filter all of the urls that doesn't contain meta but contains .stackexchange

.concat(["//stackexchange.com/favicon.ico"])
▸ Add stackexchange.com's favicon URL (Somehow stackexchange.com isn't on the list)

.concat(s.filter(e=>!/meta|stackexchange/.exec(e))
▸ Add all of the elements that doesn't contains meta or stackexchange
.concat(s.filter(e=>/meta/.exec(e))
▸ Add all of the elements that contains meta


c.height=(g.length/10+0.9|0)*32
▸ Set the height of the canvas to the number of urls(g.length) / 10(number of rows) approximately ceiled with +0.9|0


i.u=u
▸ Bind the URL id to the element so it can be drawn asynchronously after the onload()


c.drawImage(b=p.target,b.u*32%320,((b.u/10)|0)*32)}}))
b.u*32%320 URL id(g.u) * 32(icon size) % 320(icon size*10)
((b.u/10)|0)*32 ((URL id(g.u) / 10(number of rows)) |0(floor)) * 32(icon size)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.