Giter Site home page Giter Site logo

tenox7 / wrp Goto Github PK

View Code? Open in Web Editor NEW
984.0 29.0 48.0 796 KB

Web Rendering Proxy: Use vintage, historical, legacy browsers on modern web

License: Apache License 2.0

Makefile 3.59% Go 81.03% Dockerfile 0.61% HTML 14.76%
web www proxy browser rendering imagemap vintage-computers legacy-browsers chrome-devtools chrome

wrp's Introduction

WRP - Web Rendering Proxy

A browser-in-browser "proxy" server that allows to use historical / vintage web browsers on the modern web. It works by rendering a web page in to a GIF or PNG image with clickable imagemap.

Internet Explorer 1.5 doing Gmail

Usage Instructions

  • Download a WRP binary and run it on a machine that will become your WRP gateway/server. This should be modern hardware, OS and Google Chrome / Chromium Browser is required to be preinstalled. Do not try to run WRP on an old machine like Windows XP or 98.
  • Make sure you have disabled firewall or open port WRP is listening on (by default 8080).
  • Point your legacy browser to http://address:port of the WRP server. Do not set or use it as a "proxy server".
  • Type a search string or a full http/https URL and click Go.
  • Adjust your screen Width/Height/Scale/Colors to fit in your old browser.
  • Scroll web page by clicking on the in-image scroll bar.
  • WRP also allows a single tall image without the vertical scrollbar and use client scrolling. To enable this, simply height H to 0 . However this should not be used with old and low spec clients. Such tall images will be very large, take a lot of memory and long time to process, especially for GIFs.
  • Do not use client browser history-back, instead use Bk button in the app.
  • You can re-capture page screenshot without reloading by using St (Stop). This is useful if page didn't render fully before screenshot is taken.
  • You can also reload and re-capture current page with Re (Reload).
  • To send keystrokes, fill K input box and press Go. There also are buttons for backspace, enter and arrow keys.
  • Prefer PNG over GIF if your browser supports it. PNG is much faster, whereas GIF requires a lot of additional processing on both client and server to encode/decode. Jpeg encoding is also quite fast.
  • GIF images are by default encoded with 216 colors, "web safe" palette. This uses an ultra fast but not very accurate color mapping algorithm. If you want better color representation switch to 256 color mode.

UI explanation

The first unnamed input box is either search (google) or URL starting with http/https

Go instructs browser to navigate to the url or perform search

Bk is History Back

St is Stop, also re-capture screenshot without refreshing page, for example if page render takes a long time or it changes periodically

Re is Reload

W is width in pixels, adjust it to get rid of horizontal scroll bar

H is height in pixels, adjust it to get rid of vertical scroll bar. It can also be set to 0 to produce one very tall image and use client scroll. This 0 size is experimental, buggy and should be used with PNG and lots of memory on a client side.

Z is zoom or scale

C is colors, for GIF images only (unused in PNG, JPG)

K is keystroke input, you can type some letters in it and when you click Go it will be typed in the remote browser.

Bs is backspace

Rt is return / enter

< ^ v > are arrow keys, typically for navigating a map, buggy.

UI Customization

WRP supports customizing it's own UI using HTML Template file. Download wrp.html place in the same directory with wrp binary customize it to your liking.

Docker

$ docker run -d -p 80:8080 tenox7/wrp

Google Cloud Run

$ gcloud run deploy --platform managed --image=gcr.io/tenox7/wrp:latest --memory=2Gi --args='-t=png','-g=1280x0x256'

Or from Gcloud Console. Use gcr.io/tenox7/wrp:latest as container image URL.

Note that unfortunately GCR forces https. Your browser support of encryption protocols and certification authorities will vary.

Azure Container Instances

$ az container create --resource-group wrp --name wrp --image gcr.io/tenox7/wrp:latest --cpu 1 --memory 2 --ports 80 --protocol tcp --os-type Linux --ip-address Public --command-line '/wrp -l :80 -t png -g 1280x0x256'

Or from the Azure Console. Use gcr.io/tenox7/wrp:latest or tenox7/wrp:latest for image name.

Fortunately ACI allows port 80 without encryption.

Flags

-l   listen address:port (default :8080)
-t   image type gif, png or jpg (default gif)
-g   image geometry, WxHxC, height can be 0 for unlimited (default 1152x600x216)
     C (number of colors) is only used for GIF
-q   Jpeg image quality, default 80%
-h   headless mode, hide browser window on the server (default true)
-d   chromedp debug logging (default false)
-n   do not free maps and images after use (default false)
-ui  html template file (default "wrp.html")
-ua  user agent, override the default "headless" agent
-s   delay/sleep after page is rendered before screenshot is taken (default 2s)

Minimal Requirements

  • Server/Gateway requires modern hardware and operating system that is supported by Go language and Chrome/Chromium Browser, which must be installed.
  • Client Browser needs to support HTML FORMs and ISMAP. Typically Mosaic 2.0 would be minimum version for forms. However ISMAP was supported since 0.6B, so if you manually enter url using ?url=..., you can use the earlier version.

Troubleshooting

I can't get it to run

This program does not have a GUI and is run from the command line. After downloading, you may need to enable executable bit on Unix systems, for example:

$ cd ~/Downloads
$ chmod +x wrp-amd64-macos
$ ./wrp-amd64-macos

Websites are blocking headless browsers

This is a well known issue. WRP has some provisions to work around it, but it's a cat and mouse game. The first and foremost recommendation is to change User Agent, so that it doesn't say "headless". Add -ua="my agent" to override the default one. Obtain your regular desktop browser user agent and specify it as the flag. For example

$ wrp -ua="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"

History

  • Version 1.0 (2014) started as a cgi-bin script, adaptation of webkit2png.py and pcidade.py, blog post.
  • Version 2.0 became a stand alone http-proxy server, supporting both Linux and MacOS, another post.
  • In 2016 thanks to EFF/Certbot the whole internet migrated to HTTPS/SSL/TLS and WRP largely stopped working. Python code became unmaintainable and there was no easy way to make it work on Windows, even under WSL.
  • Version 3.0 (2019) has been rewritten in Go using Chromedp as browser-in-browser instead of http-proxy. The initial version was less than 100 lines of code.
  • Version 4.0 has been completely refactored to use mouse clicks via imagemap instead parsing a href nodes.
  • Version 4.1 added sending keystrokes in to input boxes. You can now login to Gmail. Also now runs as a Docker container and on Cloud Run/Azure Containers.
  • Version 4.5 introduces rendering whole pages in to a single tall image with client scrolling.
  • Version 4.6 adds blazing fast gif encoding by Hill Ma.

Credits

  • Uses chromedp, thanks to mvdan for dealing with my issues
  • Uses go-quantize, thanks to ericpauley for developing the missing go quantizer
  • Thanks to Jason Stevens of Fun With Virtualization for graciously hosting my rumblings
  • Thanks to claunia for help with the Python/Webkit version in the past
  • Thanks to Hill Ma for ultra fast gif encoding algorithm
  • Historical Python/Webkit versions and prior art can be seen in wrp-old repo

Legal Stuff

License: Apache 2.0
Copyright (c) 2013-2018 Antoni Sawicki
Copyright (c) 2019-2024 Google LLC

wrp's People

Contributors

claunia avatar drjosh9000 avatar girst avatar khawkins98 avatar miniupnp avatar rakslice avatar tenox7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wrp's Issues

not working on IOS 5 (iPad 1)

Web rendering proxy is running on Windows 10 Pro. I can open the webpage at port 8080 (on iPad), but after filling in a url and clicking GO, the website does not show on the iPad 1.

Firefox can't connect to proxy. How to run and test the beta?

I don't know what I'm doing wrong. I'm running Xampp/Apache on MacOS. I've created a php file called test.php which executes wrp-macos like this:

I open that URL on safari to execute wrp-macos.

On Firefox settings, I've set the proxy to be the IP of the server on port 80, where wrp-macos is running.

When I enter any URL, the browser complains that it "can't connect to the server" and that I should check my internet connection. Help?

Mouse Click instead of A HREF

Some elements on pages are clickable but are not A HREF, for instance cookie accept or some javascript buttons.

CDP Can instrument mouse clicks. Research and see if this can be used in WRP. One could imagine that it may work in ISMAP mode.

Python 3

Current code is Python 2, it would be nice to upgrade to Python 3 at some time.

Links not clickable with ISMAP=True in Netscape 1.1

I tried this proxy with Netscape 1.1N and Mosaic 2.5 through 2.7b in Slackware 2.3. It displays fine, but nothing is clickable with ISMAP=True or False. The page source appears to be the same regardless of setting.

Everything is clickable in Netscape 2.02, with ISMAP True or False.

This is with it enabled. I get no errors starting the proxy or when pages are requested and rendered.

true

# Configuration options:
PORT      = 8090
WIDTH     = 800
HEIGHT    = 1500
ISMAP     = True # ISMAP=True is Server side for Mosaic 1.1 and up. HTML 3.2 supports Client side maps (ISMAP=False)
WAIT      = 1  # sleep for 1 second to allow javascript renders
QUALITY   = 75 # For JPEG: image quality 0-100; For PNG: sets compression level (leftmost digit 0 fastest, 9 best)
AUTOWIDTH = False # Check for browser width using javascript
FORMAT    = "GIF" # AUTO = GIF for mac OS, JPG for rest; PNG, GIF, JPG as supported values.

# PythonMagick configuration options
MK_MONOCHROME = False # Convert the render to a black and white dithered image
MK_GRAYSCALE  = False # Convert the render to a grayscal dithered image
MK_COLORS     = 256   # Reduce number of colors in the image. 0 for not reducing. Less than 256 works in grayscale also.
MK_DITHER     = False # Dither the image to reduce size. GIFs will always be dithered. Ignored if MK_COLORS is not set.

Real HTTP Proxy Mode

historical facts:

  • the whole internet quacks https since 2016 thanks to eff and google
  • https was first introduced in 1994 by friends of jwz at netscape
  • http connect was introduced in 1999 by http 1.1
  • a growing number of clickable elements on web pages arent a href links

meaning:

  1. browsers prior to 1994 don't support https, they will probably error when trying to enter https://...
  2. browsers past 1994 will have https but there was no http connect until 1999, so how do they proxy https requests? via http? ftp? gopher? smoke signals?
  3. browsers past 1999 will have http1.1 connect and will demand ssl cert on connection, can you generate a cert that will satisfy ca shipped ages ago? or should you convert everything to http (aka sslstrip)
  4. how do you proxy non href links, eg close a cookie popup window, move a map, fill a form, select a dropdown or play some flash/html5 games?

Could not connect to display?

I installed a fresh copy of Ubuntu Server 18.10 64bit, and ran THESE commands:
sudo apt install sslstrip
sudo apt install python-pyqt5.qtwebki
git clone https://github.com/tenox7/wrp.git
But when I run it, it does this:
vmware_1LkP4sWLnf
I am running this in a VM.

Firefox portal detection crash

If firefox is expecting WRP when it loads, crashes on first request:

claunia@zeus ~/Development/wrp $ ./wrp.py 
Web Rendering Proxy v1.4 serving at port: 8080
>>> URL request... http://detectportal.firefox.com/success.txt
127.0.0.1 - - [09/May/2017 01:02:42] "GET http://detectportal.firefox.com/success.txt HTTP/1.1" 200 -
Traceback (most recent call last):
  File "./wrp.py", line 424, in __main_qt
    image = renderer.render(WebkitRenderer.req_url)
  File "./wrp.py", line 132, in render
    image = helper.render(url)
  File "./wrp.py", line 242, in render
    % (WebkitRenderer.req_url, web_url))
  File "/usr/lib/python2.7/socket.py", line 328, in write
    self.flush()
  File "/usr/lib/python2.7/socket.py", line 307, in flush
    self._sock.sendall(view[write_offset:write_offset+buffer_size])
socket.error: [Errno 32] Broken pipe
Aborted (core dumped)

Image Processing Time

I just tried this yesterday on Win7 to internet browse on an old Mac Performa. I was curious if it's normal for wrp to take minutes to process an image before it gets sent to the Mac? For example going to msn.com took minutes. Doing a google search seems to take around a minute. Anyways, great job on putting this together! Seems to be the only viable way that i've found to use the internet browsers on these old Macs.

linux env

hi
I tried installing on some different linux versions and it always fails
Can you share on what distribution and version you installed it?
version of openssl also?

[Bug] [v4.1] Scroll function only works one time

System: MacOS 10.11.6
Browser: Safari 11.1.2, Firefox 67 64-Bit

To reproduce:
1: Visit http://localhost:8080/?url=https://github.com/tenox7/wrp/&w=1152&h=600&s=1.00&c=256 as linked in the footer
2: scroll down one screen/page by clicking beneath vertical 'throbber'
3: try to continue another time (or to scroll back up to the top)

Result:
4: The same image reloads each time

Expected result:
4: A new image representing the new scroll position should appear

================== Logs ===================
Last login: Fri Jul 12 18:44:29 on ttys000
You have new mail.
$ cd Downloads/
MacBook-Pro:Downloads jarlath$ ./wrp-macos
2019/07/12 18:53:57 Web Rendering Proxy Version 4.1
2019/07/12 18:53:57 Starting WRP http server on :8080
2019/07/12 18:54:00 [::1]:57170 Page Request for / []
2019/07/12 18:54:00 WrpReq from Form: &{U: W:1152 H:600 S:1 C:256 X:0 Y:0 K: B:false}
2019/07/12 18:54:02 [::1]:57171 Page Request for / [url=https://github.com/tenox7/wrp/&w=1152&h=600&s=1.00&c=256]
2019/07/12 18:54:02 WrpReq from Form: &{U:https://github.com/tenox7/wrp/ W:1152 H:600 S:1 C:256 X:0 Y:0 K: B:false}
2019/07/12 18:54:02 [::1]:57171 Processing Capture Request for https://github.com/tenox7/wrp/
2019/07/12 18:54:08 [::1]:57171 Landed on: https://github.com/tenox7/wrp/
2019/07/12 18:54:09 [::1]:57171 Encoded GIF image: /img/0712.gif, Size: 131KB, Colors: 256
2019/07/12 18:54:09 [::1]:57171 Done with caputure for https://github.com/tenox7/wrp/
2019/07/12 18:54:09 [::1]:57171 IMG Request for /img/0712.gif
2019/07/12 18:54:41 [::1]:57171 ISMAP Request for /map/0712.map [1144,146]
2019/07/12 18:54:41 [::1]:57171 WrpReq from ISMAP: {U:https://github.com/tenox7/wrp/ W:1152 H:600 S:1 C:256 X:1144 Y:146 K: B:false}
2019/07/12 18:54:41 [::1]:57171 Mouse Click 1144,146
2019/07/12 18:54:44 [::1]:57171 Landed on: https://github.com/tenox7/wrp/
2019/07/12 18:54:46 [::1]:57171 Encoded GIF image: /img/7010.gif, Size: 55KB, Colors: 256
2019/07/12 18:54:46 [::1]:57171 Done with caputure for https://github.com/tenox7/wrp/
2019/07/12 18:54:46 [::1]:57171 IMG Request for /img/7010.gif
2019/07/12 18:54:47 [::1]:57171 ISMAP Request for /map/7010.map [1144,255]
2019/07/12 18:54:47 [::1]:57171 WrpReq from ISMAP: {U:https://github.com/tenox7/wrp/ W:1152 H:600 S:1 C:256 X:1144 Y:255 K: B:false}
2019/07/12 18:54:47 [::1]:57171 Mouse Click 1144,255
2019/07/12 18:54:50 [::1]:57171 Landed on: https://github.com/tenox7/wrp/
2019/07/12 18:54:51 [::1]:57171 Encoded GIF image: /img/1374.gif, Size: 56KB, Colors: 256
2019/07/12 18:54:51 [::1]:57171 Done with caputure for https://github.com/tenox7/wrp/
2019/07/12 18:54:52 [::1]:57171 IMG Request for /img/1374.gif

better color quantization

Currently attempts to reduce number of colors from default 256 result in garbled image. Research alternative gif.Encode implementations.

Need help with building a hosted proxy

Boring life story: I made a project that lets you browse archive.org's way back machine on old browsers. http://theoldnet.com

People are using it and cool sites are bubbling to the top. It's legit fun and I'm pretty happy with it.

Want: I want to offer up the work you've done BUT allow users a configuration/setup free solution to browse modern web pages on old systems.

Requirement: A similar experience with http://theoldnet.com. A user goes there, it loads up no problem (because no modern js/css). They type in their destination, they get a compatible response back. No need to enter browser proxy settings. No need to stand up their own server.

Ask: I'm not very experienced with python so although I'm sure what I'm asking is a not that much work, I don't know how to implement it off hand.

The end goal would be a python process that I could server through an nginx proxy. It takes an address as a param. Then does the heavy lifting you wrote. Then returns the response. And repeat.

ImportError: No module named Foundation

Hi, I've tried this on macOS High Sierra and got this error:

Traceback (most recent call last):
File "wrp.py", line 516, in
import Foundation
ImportError: No module named Foundation

What should I do? There is not even instructions on how to run this. Thanks

[More testing needed] Non-HTML files should be returned as is

>>> done: wrp-595.jpg [12 kb]...
>>> URL request... http://detectportal.firefox.com/success.txt
127.0.0.1 - - [09/May/2017 01:06:47] "GET http://detectportal.firefox.com/success.txt HTTP/1.1" 200 -
>>> done: wrp-547.jpg [12 kb]...
>>> URL request... http://detectportal.firefox.com/success.txt
127.0.0.1 - - [09/May/2017 01:06:51] "GET http://detectportal.firefox.com/success.txt HTTP/1.1" 200 -
>>> done: wrp-805.jpg [12 kb]...
>>> URL request... http://detectportal.firefox.com/success.txt
127.0.0.1 - - [09/May/2017 01:06:56] "GET http://detectportal.firefox.com/success.txt HTTP/1.1" 200 -
>>> done: wrp-632.jpg [12 kb]...
>>> URL request... http://detectportal.firefox.com/success.txt
127.0.0.1 - - [09/May/2017 01:07:00] "GET http://detectportal.firefox.com/success.txt HTTP/1.1" 200 -
>>> done: wrp-673.jpg [12 kb]...
>>> URL request... http://detectportal.firefox.com/success.txt
127.0.0.1 - - [09/May/2017 01:07:04] "GET http://detectportal.firefox.com/success.txt HTTP/1.1" 200 -
>>> done: wrp-686.jpg [12 kb]...
>>> URL request... http://detectportal.firefox.com/success.txt

Maybe when #7 is finished we can process images, but return rest as-is

HTTPS support

Some pages will stop, or are not, supporting HTTP anymore. WRP should handle the HTTPS protocol itself for browsers that predate the adequate SSL/TLS versions like MSIE 1.5 and Mosaic.

Support for multi client

Currently WRP is a "personal proxy" and only handles one client at a time.

Implement multi client mode. Either spawn new chrome/context for each client (with timeout) or new potentially just a new tab within same chrome instance.

cpu usage

I tried wrp 3.0 beta on Raspberry Pi 3 (Raspbian Stretch) and when idle wrp uses about 10% cpu. Is there anything that can be done to lower this?

Great program by the way :)

Not sure how this is supposed to work - bug?

I've been attempting to get wrp-cocoa.py working on my Mac but I'm having some problems.

Launching it and then loading http://localhost:8080 results in:

./wrp-cocoa.py:99: UninitializedDeallocWarning: leaking an uninitialized object of type NSURL
  nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(url)
2016-07-13 13:45:32.286 Python[3159:73840] <type 'exceptions.NameError'>: global name 'url' is not defined

OK, maybe I need to specify a URL. But loading localhost:8080/http://cnn.com results in:

>>> request for url: /http://cnn.com
./wrp-cocoa.py:99: UninitializedDeallocWarning: leaking an uninitialized object of type NSURL
  nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(url)
2016-07-13 13:46:43.133 Python[3208:75329] <type 'exceptions.NameError'>: global name 'url' is not defined

The URL appears to be prefixed with a /. And then of course it's still complaining that "url" is undefined, here:

nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(url)

Should "url" be "rurl"? Even if you change that, I still can't figure out how to correctly pass a URL that isn't prefixed with a slash.

Character conversion problem

$ xvfb-run -s "-screen 0 1024x768x24" ./wrp.py

... snip ...

>>> URL request... http://main.system7today.com/softwareindex/productivity.html
10.0.2.2 - - [25/Jul/2018 17:07:19] "GET http://main.system7today.com/softwareindex/productivity.html HTTP/1.0" 200 -
Traceback (most recent call last):
  File "./wrp.py", line 446, in __main_qt
    image = renderer.render(WebkitRenderer.req_url)
  File "./wrp.py", line 146, in render
    image = helper.render(url)
  File "./wrp.py", line 287, in render
    % (xmin, ymin, xmax, ymax, turl, turl))
  File "/usr/lib/python2.7/socket.py", line 320, in write
    data = str(data) # XXX Should really reject non-string non-buffers
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 101: ordinal not in range(128)
Aborted

Running under Debian 9.5.

Potentially relevant env't vars:

LANG=en_CA.UTF-8
LANGUAGE=en_CA:en
TERM=xterm

Under these conditions on this system sys.getdefaultencoding() returns "ascii".

Encoding the unicode before passing it to httpout.write at wrp.py:287 makes the error go away.

Mac OS X El Capitan forces use of HTTPS by default.

From #4:

2017-01-10 10:03:06.475 Python[1118:25855] App Transport Security has clocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file. ... something went wrong 2: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.

Context Canceled using latest version on RPi3

I tried it on a Dosbox with WFWG 3.11 & IE5, iPadOS 13 with Safari, and RPi3 with Chromium, they all end with the same message: context cancelled.

I am running the wrp-arm-Linux version on RPi3. What am I doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.