Git Product home page Git Product logo

dcintrospect's Introduction

DCIntrospect

Twitter: @patr

Our commercial apps: domesticcat.com.au

Introspect is small set of tools for iOS that aid in debugging user interfaces built with UIKit. It's especially useful for UI layouts that are dynamically created or can change during runtime, or for tuning performance by finding non-opaque views or views that are re-drawing unnecessarily. It's designed for use in the iPhone simulator, but can also be used on a device.

Introspect Demo Image

It uses keyboard shortcuts to handle starting, ending and other commands. It can also be invoked via an app-wide UIGestureRecognizer if it is to be used on the device.

Features:

  • Simple to setup and use
  • Controlled via app-wide keyboard commands
  • Highlighting of view frames
  • Displays a views origin & size, including distances to edges of main window
  • Move and resize view frames during runtime using shortcut keys
  • Logging of properties of a view, including subclass properties, actions and targets (see below for an example)
  • Logging of accessibility properties — useful for UI automation scripts
  • Manually call setNeedsDisplay, setNeedsLayout and reloadData (for UITableView)
  • Highlight all view outlines
  • Highlight all views that are non-opaque
  • Shows warning for views that are positioned on non-integer origins (will cause blurriness when drawn)
  • Print a views hierarchy to console (via private method recursiveDescription) to console

Usage

Before you start make sure the DEBUG environment variable is set. DCIntrospect will not run without that set to prevent it being left in for production use.

Add the DCIntrospect class files to your project, add the QuartzCore framework if needed. To start:

[window makeKeyAndDisplay]

// always call after makeKeyAndDisplay.
#if TARGET_IPHONE_SIMULATOR
    [[DCIntrospect sharedIntrospector] start];
#endif

The #if to target the simulator is not required but is a good idea to further prevent leaving it on in production code.

Once setup, simply push the space bar to invoke the introspect or then start clicking on views to get info. You can also tap and drag around the interface.

A a small demo app is included to test it out.

Selected keyboard shortcuts

  • Start/Stop: spacebar
  • Help: ?
  • Print properties and actions of selected view to console: p
  • Print accessibility properties and actions of selected view to console: a
  • Toggle all view outlines: o
  • Toggle highlighting non-opaque views: O
  • Nudge view left, right, up & down: 4 6 8 2 (use the numeric pad) or ← → ↑ ↓
  • Print out the selected views' new frame to console after nudge/resize: 0
  • Print selected views recursive description to console: v

Logging selected views properties

Pushing p will log out the available properties about the selected view. DCIntrospect will try to make sense of the values it can and show more useful info. An example from a UIButton:

** UIRoundedRectButton : UIButton : UIControl : UIView : UIResponder : NSObject **

** UIView properties **
    tag: 1
    frame: {{21, 331}, {278, 37}} | bounds: {{0, 0}, {278, 37}} | center: {160, 349.5}
    transform: [1, 0, 0, 1, 0, 0]
    autoresizingMask: UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin
    autoresizesSubviews: YES
    contentMode: UIViewContentModeScaleToFill | contentStretch: {{0, 0}, {1, 1}} backgroundColor: nil
    alpha: 1.00 | opaque: NO | hidden: NO | clips to bounds: NO |
    clearsContextBeforeDrawing: YES
    userInteractionEnabled: YES | multipleTouchEnabled: NO
    gestureRecognizers: nil

** UIRoundedRectButton properties **

** Targets & Actions **
    target: <DCIntrospectDemoViewController: 0x4c8c0e0> action: buttonTapped:

Customizing Key Bindings

Edit the file DCIntrospectSettings.h to change key bindings. You might want to change the key bindings if your using a laptop/wireless keyboard for development.

License

Made available under the MIT License.

Collaboration

If you have any feature requests/bugfixes etc. feel free to help out and send a pull request, or create a new issue.

dcintrospect's People

Contributors

0xced avatar atty303 avatar chrismiles avatar domesticcatsoftware avatar jessedc avatar johnezang avatar megastep avatar molescat avatar myell0w 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  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

dcintrospect's Issues

DEBUGGER instruction requires: Not 64-bit mode

I'm not sure if this is an issue with the library or my XCode installation, but my builds have been failing recently due to the DEBUGGER instruction on line 624 of DCIntrospect.m.

I receive 2 (duplicate) errors which say "Instruction requires: Not 64-bit mode".

Linking with MapKit framework overflows stack with textInputTraitsProperties

This is a very similar issue to the one discussed in #22. To reproduce, open the demo application and link against the MapKit.framework. Build and run. As soon as you try to type into a text field, the recursive bug happens again, this time with a UIFieldInput object. Here's a gdb trace from my own application which I caught setting a breakpoint and waiting for the recursion to happen:

(gdb) bt
#0  UITextInputTraits_valueForKey (self=0x6085600, _cmd=0x515edb4, key=0x5c70ce0) at DCIntrospect.m:79
#1  0x0000b421 in UITextInputTraits_valueForKey (self=0x6085600, _cmd=0x515edb4, key=0x5c70ce0) at DCIntrospect.m:87
#2  0x0000b421 in UITextInputTraits_valueForKey (self=0x6085600, _cmd=0x515edb4, key=0x5c70ce0) at DCIntrospect.m:87
#3  0x05cb56bf in -[NSObject(UIAccessibilitySafeCategory) safeValueForKey:] ()
#4  0x05c218f6 in -[UIWebDocumentViewAccessibility(SafeCategory) _accessibilityDocumentView] ()
#5  0x05c2122b in -[UIWebDocumentViewAccessibility(SafeCategory) _accessibilityRootObject] ()
#6  0x05c211ad in -[UIWebDocumentViewAccessibility(SafeCategory) accessibilityElementCount] ()
#7  0x05c191d4 in -[UITextFieldAccessibility(SafeCategory) _accessibilityCountAccessibleChildren:] ()
#8  0x05c19a5c in -[UITextFieldAccessibility(SafeCategory) _updateButtons] ()
#9  0x000ebcab in -[UITextField layoutSubviews] ()
#10 0x00d90a5a in -[CALayer layoutSublayers] ()
#11 0x00d92ddc in CALayerLayoutIfNeeded ()
#12 0x00d380b4 in CA::Context::commit_transaction ()
#13 0x00d39294 in CA::Transaction::commit ()
#14 0x00d3946d in CA::Transaction::observer_callback ()
#15 0x010cf89b in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#16 0x010646e7 in __CFRunLoopDoObservers ()
#17 0x0102d1d7 in __CFRunLoopRun ()
#18 0x0102c840 in CFRunLoopRunSpecific ()
#19 0x0102c761 in CFRunLoopRunInMode ()
#20 0x013261c4 in GSEventRunModal ()
#21 0x01326289 in GSEventRun ()
#22 0x0005dc93 in UIApplicationMain ()
#23 0x000027ff in main (argc=1, argv=0xbfffe818) at main.m:6
(gdb) f 2
#2  0x0000b421 in UITextInputTraits_valueForKey (self=0x6085600, _cmd=0x515edb4, key=0x5c70ce0) at DCIntrospect.m:87
87          return valueForKey(self, _cmd, key);
(gdb) po key
_webView
(gdb) po _cmd
0x515edb4 does not appear to point to a valid object.
(gdb) po self
<UIFieldEditor: 0x6085600; frame = (0 0; 173 28); text = ''; opaque = NO; layer = <UIWebLayer: 0xac21c00>>
(gdb) 

Presumably the swizzling trick that was done for UITextField has to be done for the undocumented/private UIFieldEditor, but I don't know how to do that. Any help?

Simulator only

It might be cool to update the readme to say you can add it with the following:

#ifdef TARGET_IPHONE_SIMULATOR
    [[DCIntrospect sharedIntrospector] start];
#endif

Kinda handy. Just a thought.

Space key non-functional after backgrounding

After going into the background and foreground again, the space key isn't attached to DCIntrospect anymore.
No big deal but I wanted to let you know.

Thanks for an awesome debugging tool, it's so great it should be included in the Simulator by Apple from scratch!

  • Ortwin

Support for iPad?

Has anyone got this working for iPad? I'm getting only a black screen with DCIntrospect on it.

No further development

DCIntrospect is so cool but outdated. I created a version that now supports ARC, will feature Localization soon and various other bugfixes. If you want to contribute to actively develop this utility it would be awesome if you could contribute to DCIntrospect-ARC

If you want to work on it form pull request or write me a message, that I can add you to the team.
Let's get DCIntrospect up to date and make it even better 👍

Alpha limits

It is possible to use the + and - keys to set the alpha of a view to something like 1.20 or -0.20.

Change Readme

TARGET_IPHONE_SIMULATOR is defined on the device, but set to false.

So instead of

ifdef TARGET_IPHONE_SIMULATOR

[[DCIntrospect sharedIntrospector] start];

endif

correct is:

if TARGET_IPHONE_SIMULATOR

[[DCIntrospect sharedIntrospector] start];

endif

Showing help inside demo crashes program

Just grabbed a copy of DCIntrospect. I tried compiling the demo example and run it on Xcode 4.0.2 with the iPhone 4.3 simulator. The example shows and I press the space bar to enter the introspection mode. Everything seems to work fine, but pressing ? to show the help screen crashes the application. The crash sometimes happens immediately, other times it seems to take a few seconds to trigger, but the backtrace is always the same:

#0  0x00000000 in ?? ()
#1  0x00003d71 in UITextInputTraits_valueForKey (self=0x8866800, _cmd=0x38c62b2, key=0x4f6f760) at DCIntrospect.m:87
#2  0x04b676bf in -[NSObject(UIAccessibilitySafeCategory) safeValueForKey:] ()
#3  0x04f22377 in -[UIWebDocumentViewAccessibility(SafeCategory) webView:didFinishLoadForFrame:] ()
#4  0x00ebbc7d in __invoking___ ()
#5  0x00ebbb51 in -[NSInvocation invoke] ()
#6  0x00ee9858 in -[NSInvocation invokeWithTarget:] ()
#7  0x00ebca04 in ___forwarding___ ()
#8  0x00ebc522 in __forwarding_prep_0___ ()
#9  0x00ebbc7d in __invoking___ ()
#10 0x00ebbb51 in -[NSInvocation invoke] ()
#11 0x036cf140 in HandleDelegateSource ()
#12 0x00f2c8ff in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#13 0x00e8a88b in __CFRunLoopDoSources0 ()
#14 0x00e89d86 in __CFRunLoopRun ()
#15 0x00e89840 in CFRunLoopRunSpecific ()
#16 0x00e89761 in CFRunLoopRunInMode ()
#17 0x01a921c4 in GSEventRunModal ()
#18 0x01a92289 in GSEventRun ()
#19 0x001abc93 in UIApplicationMain ()
#20 0x00002bc9 in main (argc=1, argv=0xbfffe758) at main.m:14

Cannot fully disable DCIntrospect

It would be really nice if DCIntrospect could be fully disabled at compile time without removing the code from Xcode or performing other Xcode setting gymnastics.

We wrap DCIntrospect starting with:

if USE_DCINTROSPECT == 1

[[DCIntrospect sharedIntrospector] start];

endif

And even with this code disabled DCIntrospect still interfers with the Keyboard animations (see issue #9). It seems there are side effects to just including code in project (I imagine there's a +load somewhere loading code).

iPad support

I am working on a universal iPhone/iPad app and DCIntrospect is great for debugging and tweaking on the iPhone side of things, however it seems to completely not work so far on the iPad side. I haven't exactly looked too closely at what might be triggering this, but it would be great to have full iPad support.

Feature Request: Enter GDB

I'm not really sure if this is possible, but it would be amazing if there was a key to break and enter gdb so you could adjust stuff like background color and such or do other tweaking.

valueForKey "Too many arguments to function call, expected 0, have 3"

Due to alleged errors with the valueForKey method such as "Too many arguments to function call, expected 0, have 3", around this line:

IMP valueForKey = (IMP)[objc_getAssociatedObject([self class], originalValueForKeyIMPKey) pointerValue];

XCode 5.1.1 refuses to compile this under iOS SDK 8.2 targeting iOS 7.0, after installing DCIntrospect 0.0.2 with cocoapods 0.37.2.

Any ideas?

Add the ability to tag and display tags of views.

To make it easier to read debug logs, add this category and make it so that when you tap on a view, it displays the tag name along with the class name. Also, the debug output should display these tags.

e.g.

[myview setNametag:@"My main view"]

when you tap on it, DCIntrospect should display "UIView - My main view {{0,0},{320,568}}" on the top status bar. Without this, you would only see "UIView" which is not helpful when you have many views and subviews!

Category code:

@interface UIView (NameTags)

  • (NSString *)nametag;
  • (void)setNametag:(NSString *)theNametag;
  • (NSString *)objectIdentifier;
    @EnD

@implementation UIView (NameTags)

  • (NSString *)nametag {
    return (NSString *)objc_getAssociatedObject(self, @selector(nametag));
    }

// Nametag setter

  • (void)setNametag:(NSString *)theNametag {
    objc_setAssociatedObject(self, @selector(nametag), theNametag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

// Return 'Class description : hex memory address'

  • (NSString *)objectIdentifier {
    return [NSString stringWithFormat:@"%@:0x%0x", self.class.description, (int) self];
    }

@EnD

Add

To make it easier to read debug logs, add this category and make it so that when you tap on a view, it displays the tag name along with the class name. Also, the debug output should display these tags.

e.g.

[myview setNametag:@"My main view"]

when you tap on it, DCIntrospect should display "UIView - My main view {{0,0},{320,568}}" on the top status bar. Without this, you would only see "UIView" which is not helpful when you have many views and subviews!

Category code:

@interface UIView (NameTags)

  • (NSString *)nametag;
  • (void)setNametag:(NSString *)theNametag;
  • (NSString *)objectIdentifier;
    @EnD

@implementation UIView (NameTags)

  • (NSString *)nametag {
    return (NSString *)objc_getAssociatedObject(self, @selector(nametag));
    }

// Nametag setter

  • (void)setNametag:(NSString *)theNametag {
    objc_setAssociatedObject(self, @selector(nametag), theNametag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

// Return 'Class description : hex memory address'

  • (NSString *)objectIdentifier {
    return [NSString stringWithFormat:@"%@:0x%0x", self.class.description, (int) self];
    }

@EnD

ARC

... this would have been exactly what I was looking for ... but all my projects are with ARC :-(
Do you plan to make the modification?

IFDEF example in readme is wrong

TARGET_IPHONE_SIMULATOR is always defined, but its value is set to 1 or 0.

The correct way to do this is as follows:

if TARGET_IPHONE_SIMULATOR

[[DCIntrospect sharedIntrospector] start];

endif

e.g. using IF instead of IFDEF

The ApplicationAccessibilityEnabled hack may turn out to be a very bad idea

It seems that the hack enabling accessibility without the accessibility inspector in the simulator has very bad unintended side effects.

Setting the ApplicationAccessibilityEnabled preference to true in ~/Library/Application Support/iPhone Simulator/5.0/Library/Preferences/com.apple.Accessibility.plist will change the behavior of UITableView instances for all apps in the simulator in a very unexpected way.

The data source method - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; and the delegate method - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; will be called for all index paths, even for cells which are not visible when the app is totally idle! Deleting the ApplicationAccessibilityEnabled preference restores the expected behavior of UITableView.

Here are the relevant backtraces.

#0  -[TableViewController tableView:cellForRowAtIndexPath:] ()
#1  0x00444e0f in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] ()
#2  0x004455d6 in -[UITableView(UITableViewInternal) _createPreparedCellForRowAtIndexPath:] ()
#3  0x0c96c0ad in -[UITableViewAccessibility(Accessibility) accessibilityCellForRowAtIndexPath:] ()
#4  0x0c97f60b in -[UITableViewCellAccessibilityElement tableViewCell] ()
#5  0x0c98012a in -[UITableViewCellAccessibilityElement isAccessibilityElement] ()
#6  0x0b774e24 in _appendChildrenToArrayStartingAtIndex ()
#7  0x0b774d3d in _addAXElementsToArrayFromObject ()
#8  0x0b774851 in _appendVendedAXElementsFromUIElements ()
#9  0x0b774774 in _createAXUIElementsFromUIElements ()
#10 0x0b773258 in _copyParameterizedAttributeValueCallback ()
#11 0x0b7aeea6 in _AXXMIGCopyParameterizedAttributeValue ()
#12 0x0b7a9853 in _XCopyParameterizedAttributeValue ()
#13 0x0b7b1e66 in mshMIGPerform ()
#14 0x014e61c5 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ ()
#15 0x0144b022 in __CFRunLoopDoSource1 ()
#16 0x0144990a in __CFRunLoopRun ()
#17 0x01448db4 in CFRunLoopRunSpecific ()
#18 0x01448ccb in CFRunLoopRunInMode ()
#19 0x01c07879 in GSEventRunModal ()
#20 0x01c0793e in GSEventRun ()
#21 0x003aba9b in UIApplicationMain ()
#0  -[TableViewController tableView:willDisplayCell:forRowAtIndexPath:] ()
#1  0x004454fe in -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] ()
#2  0x004455d6 in -[UITableView(UITableViewInternal) _createPreparedCellForRowAtIndexPath:] ()
#3  0x0c9db0ad in -[UITableViewAccessibility(Accessibility) accessibilityCellForRowAtIndexPath:] ()
#4  0x0c9ee60b in -[UITableViewCellAccessibilityElement tableViewCell] ()
#5  0x0c9ef12a in -[UITableViewCellAccessibilityElement isAccessibilityElement] ()
#6  0x0ca88e24 in _appendChildrenToArrayStartingAtIndex ()
#7  0x0ca88d3d in _addAXElementsToArrayFromObject ()
#8  0x0ca88851 in _appendVendedAXElementsFromUIElements ()
#9  0x0ca88774 in _createAXUIElementsFromUIElements ()
#10 0x0ca87258 in _copyParameterizedAttributeValueCallback ()
#11 0x0cac2ea6 in _AXXMIGCopyParameterizedAttributeValue ()
#12 0x0cabd853 in _XCopyParameterizedAttributeValue ()
#13 0x0cac5e66 in mshMIGPerform ()
#14 0x014e61c5 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ ()
#15 0x0144b022 in __CFRunLoopDoSource1 ()
#16 0x0144990a in __CFRunLoopRun ()
#17 0x01448db4 in CFRunLoopRunSpecific ()
#18 0x01448ccb in CFRunLoopRunInMode ()
#19 0x01c07879 in GSEventRunModal ()
#20 0x01c0793e in GSEventRun ()
#21 0x003aba9b in UIApplicationMain ()

Can anybody else confirm this?

Unable to bring up introspector when UITextField has focus

I've had a dig through the code and am unable to think of a clean way to bring up the introspector when a UITextField currently has focus.

I've found a hacky way, whereby the introspector is invoked after a delay, though this also hides the keyboard when the introspector is brought up, which is a problem in some views as some of them listen to keyboard show / hide notifications.

Thoughts?

Btw, this is a great tool, and I use it all the time when adjusting views created programatically. Keep up the good work!

DCIntrospect stops work

Hi,
at first let me thank you for great tools. While using it I found issue. Step to reproduce:

  1. Start app on simulator with DCIntrospect.
  2. Press Space to enable DCIntrospect - it works.
  3. I use FBConnect in my app to login user. So when FB connect launch Safari for login and then returns to app - DCIntrospect stops work.

Regars

error: unknown register name 'eax' in asm

DCIntrospect.m Line 624:
Expanded from macro 'DEBUGGER' from the following:

    else if ([string isEqualToString:kDCIntrospectKeysEnterGDB])
    {
        UIView *view = self.currentView;
        view.tag = view.tag;    // suppress the xcode warning about an unused variable.
        NSLog(@"DCIntrospect: access current view using local 'view' variable.");
        DEBUGGER;
        return NO;
    }

Keyboard issues

I've noticed when DCIntrospect is enabled, the keyboard will not animate in correctly. Also sometimes settings becomeFirstResponder doesn't work or does work and the keyboard is not visible.

Not a huge deal, just noticing.

Infinite value not supported

It seems that DCIntrospect does not support infinite value. I made a mistake with a UIView which frame had an infinite value in the frame, and when I was trying to use the framework my app seemed to freeze...

Feature Request: Accessibility Info

It would be great to in one of the log options (or perhaps a new one) to log the accessibility info. This would make writing automated view tests much easier.

Feature Request: Traversal History

It would be great if when you press page up to look at a superview, if you could press page down and back to where you were before. Obviously pressing page down before pressing page up isn't really useful because you don't know which view is intended.

This feature would make things easier if you accidentally went to far and wanted to go back down.

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.