Giter Site home page Giter Site logo

itsgoingd / clockwork-chrome Goto Github PK

View Code? Open in Web Editor NEW
413.0 12.0 29.0 2.09 MB

Clockwork - php dev tools integrated to your browser - Chrome extension

Home Page: https://underground.works/clockwork

JavaScript 32.49% CSS 35.66% HTML 31.85%
clockwork php laravel slim devtools logging debugging profiling chrome firefox

clockwork-chrome's Introduction

Archived repository

This repository contains the archived source of Clockwork Chrome up to version 3, you can find the latest version at https://github.com/underground-works/clockwork-app.


What is Clockwork?

Clockwork is a browser extension, providing tools for debugging and profiling your PHP applications, including request data, application log, database queries, routes, visualisation of application runtime and more.

Clockwork uses a server-side component, that gathers all the data and easily integrates with any PHP project, including out-of-the-box support for major frameworks.

Read more and try it out on the Clockwork website.

This repository contains the Clockwork Chrome extension.

Privacy

Installation and some updates of the Clockwork extension require privacy permissions.

Since our browsers handle a lot of personal information, we feel it's important to explain what we can do with each permission and why do we need them.

Permission to "Access your browsing activity"

Clockwork uses the webNavigation API, used for notifications about browser navigation, eg. entering URLs to address bar, clicking links, etc.

Used for the preserve log feature. When preserve log is off, we use this API to know when to clear the requests list.

Permission to "Read and modify all your data on all websites you visit"

Clockwork uses the webRequest API, used for observing HTTP requests made by the browser, including submitted data, URLs, full response content, with ability to block or modify these requests.

Used for observing incoming HTTP requests for Clockwork-enabled applications and loading Clockwork metadata from headers.

We immediately disregard any requests to non Clockwork-enabled applications, we never modify or store requests.

The extension available in the Chrome Web Store or Firefox Addons is always the latest tagged commit in this repository with no modifications.

Licence

Copyright (c) 2013 Miroslav Rigler

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

clockwork-chrome's People

Contributors

evandarwin avatar itsgoingd avatar johnnoel avatar vercoutere 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

clockwork-chrome's Issues

Special Top-level Domains

I'm running a development environment on a Vagrant/VirtualBox VM and using Pow's port proxying (http://www.pow.cx) to proxy calls to http://www.domain.dev on to http://127.0.0.1:8080.

I've tested using my local IP, localhost, and 127.0.0.1 and they all work as expected, but domain.dev doesn't. The top-level .dev domain has the http headers, creates the json files, but doesn't show anything in the plugin window. I'm assuming it's something on the plugin side and not the server-side.. but I could be wrong.

Hide Clockwork Tab

Would it be possible to hide the Clockwork tab in the developer tools if we're currently not working with the library? I know the React developer tools has this feature in their extension.

Nevertheless, it would be a good feature for this extension.

resize columns

When looking at the clockwork tab, I'm not able to resize the columns for the (Path,Method,Status,Time) Most specifically the time columns is cutting off the ms string.

Error After Adding Service Provider And Alias Using Laravel 5.3

ErrorException in Builder.php line 1277:
count(): Parameter must be an array or an object that implements Countable
in Builder.php line 1277
at HandleExceptions->handleError('2', 'count(): Parameter must be an array or an object that implements Countable', 'C:\xampp\htdocs\tutorania\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php', '1277', array('query' => object(Builder), 'originalWhereCount' => '1'))
at count(null) in Builder.php line 1277
at Builder->shouldNestWheresForScope(object(Builder), '1') in Builder.php line 1235
at Builder->callScope(object(Closure)) in Builder.php line 1262
at Builder->applyScopes() in Builder.php line 325
at Builder->get(array('*')) in Builder.php line 297
at Builder->first() in Model.php line 3561
at Model->__call('first', array()) in Model.php line 3573
at Model::__callStatic('first', array()) in AppServiceProvider.php line 57
at AppServiceProvider->App\Providers{closure}(object(View))
at call_user_func_array(object(Closure), array(object(View))) in Dispatcher.php line 221
at Dispatcher->fire('composing: front.index_view', array(object(View))) in Factory.php line 519
at Factory->callComposer(object(View)) in View.php line 118
at View->renderContents() in View.php line 85
at View->render() in Response.php line 45
at Response->setContent(object(View)) in Response.php line 201
at Response->__construct(object(View)) in Router.php line 1028
at Router->prepareResponse(object(Request), object(View)) in Router.php line 653
at Router->Illuminate\Routing{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing{closure}(object(Request)) in SubstituteBindings.php line 41
at SubstituteBindings->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in VerifyCsrfToken.php line 65
at VerifyCsrfToken->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in AddQueuedCookiesToResponse.php line 37
at AddQueuedCookiesToResponse->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in EncryptCookies.php line 59
at EncryptCookies->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in Pipeline.php line 104
at Pipeline->then(object(Closure)) in Router.php line 655
at Router->runRouteWithinStack(object(Route), object(Request)) in Router.php line 629
at Router->dispatchToRoute(object(Request)) in Router.php line 607
at Router->dispatch(object(Request)) in Kernel.php line 268
at Kernel->Illuminate\Foundation\Http{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing{closure}(object(Request)) in ShareErrorsFromSession.php line 49
at ShareErrorsFromSession->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in StartSession.php line 64
at StartSession->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in CheckForMaintenanceMode.php line 46
at CheckForMaintenanceMode->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in ClockworkMiddleware.php line 29
at ClockworkMiddleware->handle(object(Request), object(Closure)) in Pipeline.php line 137
at Pipeline->Illuminate\Pipeline{closure}(object(Request)) in Pipeline.php line 33
at Pipeline->Illuminate\Routing{closure}(object(Request)) in Pipeline.php line 104
at Pipeline->then(object(Closure)) in Kernel.php line 150
at Kernel->sendRequestThroughRouter(object(Request)) in Kernel.php line 117
at Kernel->handle(object(Request)) in index.php line 53
at require_once('C:\xampp\htdocs\tutorania\public\index.php') in index.php line 18

X-Clockwork-Requested-With header for lean usage

I would like to suggest one small enhancement for the Chrome extension. Is it possible to implement an ability to add custom X-Clockwork-Requested-With header to each request to a server when extension is used? In this case I can run server-side part only when it is required by custom header in a request. Does it makes sense?

Mouse scroll not working

After updating the extension to the new version the mouse wheel doesn't scroll the Clockwork windows anymore.

I can still scroll by dragging the scrollbar up and down and by clicking the up and down arrows on the scrollbar ends, but the wheel does nothing(nor does the two-finger touchpad scrolling). Same thing for the horizontal scrolling, SHIFT+Mouse wheel does nothing.

Environment:

  • Google Chrome 61.0.3163.100 64bit
  • Clockwork extension 2.0
  • Windows 10 Pro 1703 64bit
  • Mouse: Logitech M500(highly recommend it)

Also, after updating the composer package from ^1.14 to ^2.0 the new version warning didn't disappear:
image

Clockwork works but extension doesn't display anything, just empty...

Hi there!

First of all thanks for doing this great stuff!

I have installed extension in developer mode and it seems working in dev tools. But when I enable server side part, I don't see any results in the Clockwork tab. I have opened background page console and see all ajax requests created by Clockwork, all of them receive proper JSON generated by server side library.

Looks like $scope.setActive(requestId); doesn't work as expected....

Is there any particular instructions how can I troubleshoot it?

ide (VSCode, phpstorm..etc) uri scheme implementation

please make these text link to IDE quick open scheme. or make this configurable at least.

image

vscode://file/home/workspace/app/http/controllers/BastAuthController.php:58

Then we can quick go'to programming workspace and that's so great!

explain:

vscode://file/{full-path-to-file}:{line-number}

Empty panels in Incognito

When used in incognito mode, it will sometimes have empty panels or the initial page you opened dev tools on. However, when not in incognito the extension works perfectly fine.

X-Clockwork-Path escapes query part of an URL

There is a bug with X-Clockwork-Path header. If I pass a path with query part it will be escaped. For instance I send following header:

/some/path/to/scrip.php?param1=someparam&item=

And instead of

/some/path/to/scrip.php?param1=someparam&item=23293842.23422.23423

My site receives following as $_SERVER['REQUEST_URI']

/some/path/to/scrip.php%3Fparam1=someparam&item=23293842.23422.23423

Enhancement Request

Either have the ability to pause traffic or stop the focusing on new entries.

I have background tasks that manage the session and when troubleshooting something from the main call, it loses focus when one of the background events occur. I hope that makes sense.

Thanks!

Wrong url for request data

Hi!

For one page in my application I now see the error "Server returned an invalid JSON.".
When I update code extension

			try {
				data = JSON.parse(xhr.responseText)
			} catch (e) {
				return callback({ error: 'Server returned an invalid JSON from. Request url: '+ message.url })
			}

The message was:
Server returned an invalid JSON. url:http://dev.mydevdomain.ru/__clockwork/#date=2018-03-011519997504-2179-575785715

I suppose the problem in url's with anchor "http://dev.mydevdomain.ru/#date=2018-03-01"

MissingMandatoryParametersException

I'm getting this error when loading a page:

Some mandatory parameters are missing ("id") to generate
a URL for route "get /__clockwork/{id}"

Not sure how to fix this. Clockwork works just fine for every other page.

clockwork requests data but don't display

Hello .. I'm developing Lavarel API (using Dingo)

When I call in browser /dev/path/server/public/api/wps, I see in apache access log request to /dev/path/server/public/__clockwork/1480681244.3731.1135877235
In browser I see JSON result, but in clockwork see nothing

Any Idea why?

Thanks for help


raw JSON:
 {"id":"1480681244.3731.1135877235","version":1,"time":1480681244.285,"method":"GET","uri":"\/dev\/path\/server\/public\/api\/wps","headers":{"host":["192.168.100.222"],"connection":["keep-alive"],"cache-control":["max-age=0"],"upgrade-insecure-requests":["1"],"user-agent":["Mozilla\/5.0 (Windows NT 10.0; WOW64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/54.0.2840.87 Safari\/537.36"],"accept":["text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,*\/*;q=0.8"],"accept-encoding":["gzip, deflate, sdch"],"accept-language":["cs-CZ,cs;q=0.8"],"cookie":["adminer_version=0; PHPSESSID=mhbu892vv1t0p1t0amlguncsk1; nette-browser=0169ngb2qx; _ga=GA1.1.1482508004.1477558523; _gat=1; XSRF-TOKEN=eyJpdiI6IklHMmxLRHpNQW1pTERQS08zbjRqalE9PSIsInZhbHVlIjoiUlNRRGt6K3JUMmZiMTZtNlo4WjZUWDE4blF1TWpLZ3pRNnp4ZWVLVFZiRDVUejFuMWR5T2g1UHI4MlpTNnUzSW9oQVdEbXRibmVuNjNkTkNhMGlCVVE9PSIsIm1hYyI6ImQwNzI4ZDdmZGE0YzM5MzA2YTk3YTdhYmIxNjczNWE1ZGE3YmMyNmY5M2VjMzMxZDllODBiN2FkYzBlYmM3NDAifQ%3D%3D; laravel_session=eyJpdiI6ImpYRnpjcUU2TEgwXC9ONFZwdDUzOGlnPT0iLCJ2YWx1ZSI6IisxRDQ1UHpIVjBJeEtkQzRhcWtyRCtsR0x0eCs1S0N5anJ1TkxQeHdLeW1XbmRkNGhWdW1QTHM1NUtkNW5jR1J2eWpQTmI3WlRNU1lpajFtRlNwbmp3PT0iLCJtYWMiOiIzMzVkNDJhNzU2NTZmZWRlNTMzNTBkZDczMDZkZmFmYmEyY2Y3NDhkZGY2YjNhM2E1MzVjYTQ4YWY3Y2JkNzk5In0%3D"]},"controller":null,"getData":[],"postData":[],"sessionData":[],"cookies":{"adminer_version":"0","PHPSESSID":"mhbu892vv1t0p1t0amlguncsk1","nette-browser":"0169ngb2qx","_ga":"GA1.1.1482508004.1477558523","_gat":"1","XSRF-TOKEN":"eyJpdiI6IklHMmxLRHpNQW1pTERQS08zbjRqalE9PSIsInZhbHVlIjoiUlNRRGt6K3JUMmZiMTZtNlo4WjZUWDE4blF1TWpLZ3pRNnp4ZWVLVFZiRDVUejFuMWR5T2g1UHI4MlpTNnUzSW9oQVdEbXRibmVuNjNkTkNhMGlCVVE9PSIsIm1hYyI6ImQwNzI4ZDdmZGE0YzM5MzA2YTk3YTdhYmIxNjczNWE1ZGE3YmMyNmY5M2VjMzMxZDllODBiN2FkYzBlYmM3NDAifQ==","laravel_session":"eyJpdiI6ImpYRnpjcUU2TEgwXC9ONFZwdDUzOGlnPT0iLCJ2YWx1ZSI6IisxRDQ1UHpIVjBJeEtkQzRhcWtyRCtsR0x0eCs1S0N5anJ1TkxQeHdLeW1XbmRkNGhWdW1QTHM1NUtkNW5jR1J2eWpQTmI3WlRNU1lpajFtRlNwbmp3PT0iLCJtYWMiOiIzMzVkNDJhNzU2NTZmZWRlNTMzNTBkZDczMDZkZmFmYmEyY2Y3NDhkZGY2YjNhM2E1MzVjYTQ4YWY3Y2JkNzk5In0="},"responseTime":1480681244.4441,"responseStatus":200,"databaseQueries":[{"query":"SELECT * FROM w01_work_packages","duration":14.15,"connection":"mysql","file":"\\app\\Http\\Controllers\\WorkPackageController.php","line":12,"model":"App\\WorkPackage"},{"query":"SELECT * FROM w01_work_packagesWHEREw01_work_packages.w01_work_package_idin ('1')","duration":0.49,"connection":"mysql","file":"\\app\\Http\\Controllers\\WorkPackageController.php","line":12,"model":"App\\WorkPackage"},{"query":"SELECT * FROMw02_package_typesWHEREw02_package_types.w02_package_type_idin ('1')","duration":0.47,"connection":"mysql","file":"\\app\\Http\\Controllers\\WorkPackageController.php","line":12,"model":"App\\WorkPackageType"}],"timelineData":{"total":{"start":1480681244.285,"end":1480681244.4462,"duration":161.20886802673,"description":"Total execution time.","data":[]},"initialisation":{"start":1480681244.285,"end":1480681244.3537,"duration":68.652868270874,"description":"Application initialisation.","data":[]},"boot":{"start":1480681244.3537,"end":1480681244.3976,"duration":43.899059295654,"description":"Framework booting.","data":[]},"run":{"start":1480681244.3537,"end":1480681244.4462,"duration":92.555046081543,"description":"Framework running.","data":[]}},"log":[{"message":"Message text.","context":"[]","level":"debug","time":1480681244.4089,"file":"\\app\\Http\\Controllers\\WorkPackageController.php","line":11}],"routes":[],"emailsData":[],"viewsData":[],"userData":null,"responseDuration":159.09600257874,"databaseDuration":15.11}

Text search through messages in Log tab

When messages are too much in Log tab, it's difficult to search specific message without removing lots of debug code or adding IFs into the code. Sometimes IFs are also to complicated for getting specific debug info.
Search bar would be great!

Error loading request metadata. Server returned an error response.

It says "loading request metadata. returned an error response."
How can I see the error itself?

// PHP page
<?php

require_once __DIR__.'/../vendor/autoload.php';

$clockwork = Clockwork\Support\Vanilla\Clockwork::init([ 'api' => '/temp/server.php' ]);
$clockwork->requestProcessed();

API endpoint

<?php

require_once __DIR__.'/../vendor/autoload.php';

$clockwork = Clockwork\Support\Vanilla\Clockwork::init();
$clockwork->returnMetadata();

Logging

Hi. First of all my compliments. This is really awesome. I love the ease to check your sql queries!

There is a log tab in the chrome extension dev tools. However I found no info about this. Is there something like a log method?

Possible enhancement: prettyprint JSON

First - this is a great extension/library and works really well with Laravel 4 - thanks!

It would be great if the log window recognised JSON and formatted it appropriately (i.e. colours, newlines, indentation etc.):

image

Cheers,

Dan

Database result rows support

It would be nice, if I can add the result rows to databaseQueries, so I can see the database result in Clockwork.
Same for Firefox extension and WebUI.

Visual distinction of Log levels

It's difficult to distinguish by sight messages' levels in Log tab.
For example, warnings and debugs have the same appearance, when warnings could be marked in the log table with badge or icon.

Installed on Laravel, not getting info on dev tools

Hi,

I installed it on Laravel 5.2, follow each step:
1.- Composer installation
2.- Added Provider on app.php
3.- Added middleware
4.- publish config/clockwork.php (explicitly enabled, enable=>true)

I'm getting headers:

X-Clockwork-Id:1458977332.2753.1683523069
X-Clockwork-Version:1.11.1

But when I hit a route, I don't see any info on Clockwork tab.

What else can I check?

Thanks.

This site requires authentication

Just installed the latest version of Clockwork, the Web Interface and Firefox Extension is working fine, however the Chrome Extension always gives me a "This site requires authentication." and no data. Please see screenshot attached.

Clockwork Package & Chrome Extension are both v3.1.1.
Chrome version is 70.0.3538.110.

clockwork

Add context menu option: Copy url

I would like to be able to right click a request in the request list and copy the absolute url to the clipboard. The url should include scheme, hostname, port and everything else needed to be able to paste it into the address field of a new tab to navigate to the page (assuming it was a GET request).

No ability to clear the panels

Hi.

With the Chrome extension, there is no ability to clear the content. I think this is related to a change in Chrome as it seems the ability to clear the content of the panels has moved from the panel container to each panel. Not all panels have a clear option and I think this is what is now missing from Clockwork.

I hope that makes some sense.

Regards,

Richard.

Not working for me

Hello! I'm trying to use this extension in a way that's not very supported right now - I'm trying to write a library for Clojure, which will provide backend for this extension.

I had basic implementation working a month or so ago, and I feel like I remember it working. But then during last month everything stopped working. My backend still returns three headers:

X-Clockwork-Version: 3.1.2
X-Clockwork-Path: /__clockwork/
X-Clockwork-Id: bfc701e4-b2e3-4d47-8133-983bb7f96856

but nothing is showing up in extension (and it's not making a request to server for a given id). I've looked at changes in 3.1.2 and it doesn't feel like it should affect anything, but clearly something has changed.

Do you have any ideas/pointers on what to do to make it work?

Dark theme

It would be nice to have dark theme support

Visual enhancements

Currently request tab looks a bit messy. All groups are placed too close to each other. It would be better if we could separate groups by adding some margin between them. Something like this:

clockwork

Makes sense?

Demands auth option

Hello mod,

clockwork is a great debug tool,
but the only thing i want which it was not provided is that clockwork has no option to set a key into header or get argument to make auth.

Clarify new permissions

First off: thanks for making this. It's really handy!

I got a popup warning in Chrome today noting that the Clockwork extension required new permissions (access to my browsing history), and it prompted me to accept them or remove the extension.

I did some digging and found that the new permissions are because it's now using the webNavigation API.

Because abuse by Chrome extensions is something that happens, perhaps it would be beneficial to add a security overview note to the README, or a separate SECURITY.md file, explaining what permissions the extension uses, and why.

I'd be happy to help out with that (though I can't answer those two questions currently).

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.