Giter VIP home page Giter VIP logo

gmaps-autocomplete-rails's Introduction

Gmaps autocomplete for Rails

Extracted from gmaps-autocomplete and improved markedly and then packaged for use with Rails as an asset gem :)

General info

The script is now compiled from Coffeescript and allows you to have multiple fields linked to multiple instances of the GmapsCompleter class on the same page.

I recommend that you also check out: google maps and RoR

Upgrading

Version 1.3+ now comes only with a class based GmapsCompleter. The old static GmapsCompleter container, used in version 1.2 (and below) has been deprecated.

Please upgrade your configuration functionality as demonstrated in the usage/config instructions below.

Time for a redo!?

In my own humble opinion, this library is in dire need to be completely redone. I would love if someone out there was brave enough to refactor (or better redo from scratch!) this library. The time has come.... Thanks ;)

Install

Update gem dependencies

In your project Gemfile

gem 'jquery-rails'
gem 'gmaps-autocomplete-rails'

Or to always use the most recent version, use `gem 'gmaps-autocomplete-rails', github: "kristianmandrup/gmaps-autocomplete-rails"``

Then run bundle install;)

Packed and ready for use with the Asset pipeline :)

Update javascript dependencies

Add to javascript manifest file, fx app/assets/javascripts/application.js

//= require jquery_ujs
//= require gmaps-auto-complete

Update style dependencies

Add to stylesheets manifest file, fx app/assets/stylesheets/application.css

*= require gmaps-auto-complete

Include the google maps script before application.js, fx in your layout file:

application.html.erb

<script async defer type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
<%= javascript_include_tag "application" %>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>

Note also that the autocomplete script currently depends on jQuery 1.6+. Please feel free to remove this dependency with a pull request :)

Using Google API keys

Google API keys

<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
  type="text/javascript"></script>

Note: Google apparently now requires use of the HTTPS protocol. This is enforced to ensure that your key (and location?) is not intercepted. Better safe than sorry ;)

Customization

If you want to customize the gem to use another autocomplete "engine" than the default (assumed to be jquery-ui), then fork the gem and change the following in the gmaps-auto-complete.coffee script to suit your needs and compile to js.

# around line 237
    autocompleteOpts = $.extend true, defaultAutocompleteOpts, autocompleteOpts

    $(@inputField).autocomplete(autocompleteOpts)

    # triggered when user presses a key in the address box
    $(@inputField).bind 'keydown', @keyDownHandler
    # autocomplete_init

  keyDownHandler: (event) =>
    if (event.keyCode == 13)
      @geocodeLookup 'address', $(@inputField).val(), true
      # ensures dropdown disappears when enter is pressed
      $(@inputField).autocomplete "disable"
    else
      # re-enable if previously disabled above
      $(@inputField).autocomplete "enable"

PS: I'm not sure if this is the only place that requires changes...

Initialize

Add functionality that is executed after the document (and all scripts) have been fully loaded. Example:

mypage.js

jQuery(function() {
  var completer;

  completer = new GmapsCompleter({
    inputField: '#gmaps-input-address',
    errorField: '#gmaps-error'
  });

  completer.autoCompleteInit({
    country: "us"
  });
});

or using Coffeescript

mypage.js.coffee

jQuery ->
    completer = new GmapsCompleter
        inputField: '#gmaps-input-address'
        errorField: '#gmaps-error'

    completer.autoCompleteInit
        country: "us"

Configuration options

The constructor function can take a configuration option hash that can configure the specific workings of the GmapsCompleter. It uses the following defaults:

{
  mapElem: "#gmaps-canvas", 
  zoomLevel: 2, 
  mapType: google.maps.MapTypeId.ROADMAP,
  pos: [51.751724, -1.255284],
  inputField: '#gmaps-input-address',
  errorField: '#gmaps-error',
  debugOn: false
};

The following default methods can be replaced by configuration:

  • positionOutputter
  • updateUI
  • updateMap

These methods are used to control how the gmaps data is used to update the UI on the page, such as the position output and map position update. Customize these needed.

The default logic (taken from GmapsCompleterDefaultAssist) is:

  defaultUpdateMap: (geometry) -> 
    map     = @map
    marker  = @marker

    map.fitBounds(geometry.viewport) if map
    marker.setPosition(geometry.location) if marker

  # fill in the UI elements with new position data
  defaultUpdateUI: (address, latLng) ->
    $(@inputField).autocomplete 'close'

    @debug 'country', @country

    updateAdr = address.replace ', ' + @country, ''
    updateAdr = address

    @debug 'updateAdr', updateAdr

    $(@inputField).val updateAdr
    @positionOutputter latLng

  defaultPositionOutputter: (latLng) ->
    $('#gmaps-output-latitude').html latLng.lat()
    $('#gmaps-output-longitude').html latLng.lng()

The default update UI logic removes the country from the address displayed.

autoCompleteInit

The function autoCompleteInit, called on an instance of GmapsCompleter, can takes an option hash. Currently only region, country and autocomplete can be used.

Example:

autoCompleteInit({region: 'DK', country: 'Denmark'});

Will make searches in the DK region and remove ', Denmark' from the result.

Note: Not sure if this is still useful with the new instance based approach!?

Parameter autocomplete allows to configure JQuery autocomplete widget

Example:

autoCompleteInit({
  region: 'DK', 
  country: 'Denmark',
  autocomplete: {
    minLength: 4,
    position: {
      my: "center top",
      at: "center bottom"
    }
  }
});

Assist object

The options hash for the constructor can now take an assist object as an argument. The assist object can be a class or a simple object containing the following:

  options:
    mapElem: '#gmaps-canvas'
    zoomLevel: 2
    mapType: google.maps.MapTypeId.ROADMAP
    pos: [0, 0]
    inputField: '#gmaps-input-address'
    errorField: '#gmaps-error'
    debugOn: true  
  
  # update marker and map
  updateMap: (geometry) ->

  # fill in the UI elements with new position data
  updateUI: (address, latLng) ->

  # display current position
  positionOutputter: (latLng) ->

  # optionally also message functions (see below)

If you use a class you can use Coffeescript extends (see http://coffeescript.org/#classes) to subclass the default implementation. You can then optionally use super to call the base implementation.

Example:

class MyCompleterAssist extends GmapsCompleterDefaultAssist
  updateUI: (address, latLng) ->
    console.log "Doing my own thang!"
    // ...

    super (address, latLng)

Usage (config):

    completer = new GmapsCompleter
        inputField: '#gmaps-my-input-address'
        assist: MyCompleterAssist

Usage with Rails form helpers

Simple form example:

= simple_form_for(@post) do |f|
    = f.input :address, :input_html =>{:id => 'gmaps-input-address'}, :placeholder => 'Start typing a place...'

Examples

See spec/test-gmaps-auto-coffee.html for an example page using this "plugin". Note that if you set mapElem to null or leave out that element on the page, the autocomplete will function without attempting to update the map :)

This is very useful in scenarios where you want to geolocate the address without displaying on the map.

Advanced Customization

Some of the prime candidate functions for customization are:

  • updateUi
  • updateMap

Here the default simple updateUI implementation:

updateUi: function( address, latLng ) {
  $(this.inputField).autocomplete("close");
  $(this.inputField).val(address);

  this.positionOutputter(latLng);
}

Let's enrich the autocomplete fields with a jobs count for "the area" for each address.

updateUi: function( address, latLng ) {
  $(this.inputField).autocomplete("close");

  var jobsCount = $.ajax(url: 'jobs/search?address=' + address + '&type=count');

  $(this.inputField).val(address + ' (' + jobsCount + ' jobs)');
}

Note that you can encapsulate this kind of customization using the assist option and an Assist object/class as demonstrated above.

Customizing messages

The following message functions can be customized, either by passing in the options hash, overriding directly on the GmapsCompleter object or even by using the Assist object/class.

  • geocodeErrorMsg: function()
  • invalidAddressMsg: function(value)
  • noAddressFoundMsg: function()

Example:

GmapsCompleter.prototype.geocodeErrorMsg = function() {
  "Geocode error!"
}

Here, ensuring that ALL instances of GmapsCompleter will use this functionality as the baseline (since overriding the prototype function).

Localizing messages

For localization/internationalization support, you could customize your Assist object constructor to set the current locale and then have your custom xMsg functions use this locale to display the localized message.

Formtastic example

For formtastic something like:

= semantic_form_for @search do |f|
  = f.input :address, placeholder: 'find address'
  %span#address_error

Or,

<%= semantic_form_for(@search) do |f| %>
	<%= f.input :pickupAddress, :as => :string, :label => "House/Apt Number and Street", :input_html => { :id => "gmaps-input-address", :style => "width:350px; font-size:14px", :placeholder => "Start typing an address or location" } %>
	...

And matching configuration in your javascript:

$(document).ready(function() { 
  var completer;
  completer = new GmapsCompleter({inputField: 'form#search #address', errorField: 'form#search #address_error'});
  completer.autoCompleteInit({region: 'DK'});
});

Tips

To avoid loading google maps script on all pages, either use turbolinks or alternatively conditionally load it depending on whether the page needs it. For this you could use a simple Page model, something like this:

class Page
  include ::ActiveModel::Model

  attr_accessor :name, :type, :mode

  def map?
    mode && mode.to_s =~ /location/
  end

Then use the Page in the controller

class PropertiesController < BaseController
  def show
    @name = params[:id]
    @mode = params[:mode] || 'gallery'
    @page = Page.new name: :property, mode: @mode
  end
end

Then use page instance to have fine-grained control over how to display the page!

<% if @page.map? %>
  <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js"></script>
<% end %>
<%= javascript_include_tag "application" %>

This could fx be integrated into your page layouts (layout files) or similar.

Alternatively perhaps use RequireJS via the requirejs-rails gem, and load it "AMD" style, and then use a HTML data attribute to set if the page should load the google map script or not. There are many ways to achieve this...

Enjoy!

Troubleshooting

Uncaught ReferenceError: google is not defined

You must remember to include google maps in your html page before gmaps-autocomplete (see application.html.erb above).

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js"></script>

Dropdown transparency

Some developers have experiences transparency issues for the autocomplete dropdown. This is a CSS issue which can be fixed as noted in #14

.ui-autocomplete {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1000;
  float: left;
  display: none;
  min-width: 160px;
  _width: 160px;
  padding: 4px 0;
  margin: 2px 0 0 0;
  list-style: none;
  background-color: #ffffff;
  border-color: #ccc;
  border-color: rgba(0, 0, 0, 0.2);
  border-style: solid;
  border-width: 1px;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
  -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
  -webkit-background-clip: padding-box;
  -moz-background-clip: padding;
  background-clip: padding-box;
  *border-right-width: 2px;
  *border-bottom-width: 2px;

  .ui-menu-item > a.ui-corner-all {
    display: block;
    padding: 3px 15px;
    clear: both;
    font-weight: normal;
    line-height: 18px;
    color: #555555;
    white-space: nowrap;

    &.ui-state-focus, &.ui-state-active {
      color: #ffffff;
      text-decoration: none;
      background-color: #0088cc;
      border-radius: 0px;
      -webkit-border-radius: 0px;
      -moz-border-radius: 0px;
      background-image: none;
    }
  }
}

TODO

  • even better class based functionality encapsulation
  • possibly remove autoCompleteInit ??

Please help out with suggestions and improvements etc!

Contributing to gmaps-autocomplete-rails

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
  • Fork the project.
  • Start a feature/bugfix branch.
  • Commit and push until you are happy with your contribution.
  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright

Copyright (c) 2012 Kristian Mandrup. See LICENSE.txt for further details.

gmaps-autocomplete-rails's People

Contributors

angelsystem avatar chalmagean avatar cpursley avatar kristianmandrup avatar martinechtner avatar nlight avatar pdsteele 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gmaps-autocomplete-rails's Issues

How to use Google Places API Key?

Hi, I read the documentation but cannot find a way to use a Google API Key (to increase the number of requests), is there a way to do it?

I know I could add key param inside the URL

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=XXXX&amp;sensor=false"></script>

But is that safe to embed an API Key on the client side?

Multiple autocomplete fields on the same page does not autocomplete correctly

Hi,

First, I want to say thanks for writing this great gem. It makes using gmaps autocomplete much easier, and gives me better results than other solutions.

I am currently trying to get multiple autocomplete fields on the same page, and by initializing gmaps-autocomplete with different input-field ids, it does work as intended (with respect to showing autocomplete results).

However, if you choose a result and autocomplete, it will also autocomplete all fields that occur after the chosen field (e.g. autocompleting address field 1 will fill address field 2 and 3 with the same results, autocompleting address field 2 will fill address field 3 with the same results, and autocompleting address 3 will result in the expected behavior).

Uncaught ReferenceError and TypeError

Following the readme, I have encountered two errors when trying to use this gem.

Uncaught ReferenceError: google is not defined
and
Uncaught TypeError: GmapsCompleterDefaultAssist is not a function

More detail:

Uncaught ReferenceError: google is not defined
(anonymous function) @ gmaps-auto-complete.self-4f972ec52feb2af3169e444e2e9d9c84e6798f29ae76241fc5346c6baccd0a9f.js?body=1:242
(anonymous function) @ gmaps-auto-complete.self-4f972ec52feb2af3169e444e2e9d9c84e6798f29ae76241fc5346c6baccd0a9f.js?body=1:293
Uncaught TypeError: GmapsCompleterDefaultAssist is not a function
GmapsCompleter.init @ gmaps-auto-complete.self-4f972ec52feb2af3169e444e2e9d9c84e6798f29ae76241fc5346c6baccd0a9f.js?body=1:58
GmapsCompleter @ gmaps-auto-complete.self-4f972ec52feb2af3169e444e2e9d9c84e6798f29ae76241fc5346c6baccd0a9f.js?body=1:42
(anonymous function) @ gmaps_address_complete.self-05bb90d5989795f4e2417891ae404cf8de01fc8974d3a31665afe857b7954355.js?body=:4
fire @ jquery.self-c64a74367bda6ef8b860f19e74df08927ca99d2be2ac934e9e92d5fd361e0da4.js?body=1:3233
self.fireWith @ jquery.self-c64a74367bda6ef8b860f19e74df08927ca99d2be2ac934e9e92d5fd361e0da4.js?body=1:3363
jQuery.extend.ready @ jquery.self-c64a74367bda6ef8b860f19e74df08927ca99d2be2ac934e9e92d5fd361e0da4.js?body=1:3583
completed @ jquery.self-c64a74367bda6ef8b860f19e74df08927ca99d2be2ac934e9e92d5fd361e0da4.js?body=1:3618

Mistake in README.md

The README.md file states

//= require gmaps-auto-complete

but this should be

//= require gmaps-autocomplete

Uncaught TypeError: Object #<HTMLInputElement> has no method 'geocodeLookup'

third line throws an error...

  GmapsCompleter.prototype.keyDownHandler = function(event) {
    if (event.keyCode === 13) {
      this.geocodeLookup('address', $(this.inputField).val(), true);
      return $(this.inputField).autocomplete("disable");
    } else {
      return $(this.inputField).autocomplete("enable");
    }
  };

  return GmapsCompleter;

i'm using coffee script

jQuery ->
  temp = new GmapsCompleter
    inputField: '#ride_s',
    errorField: '#ride_s_error_code'
  temp.autoCompleteInit
    debugOn: true
  return

why does this happen?

debug info

<exception>: TypeError
event: jQuery.Event
this: input#ride_s.string optional ui-autocomplete-input
accept: ""
accessKey: ""
align: ""
alt: ""
attributes: NamedNodeMap
autocomplete: "off"
autofocus: false
baseURI: "http://localhost:3000/ru/rides/new"
checked: false
childElementCount: 0
childNodes: NodeList[0]
children: HTMLCollection[0]
classList: DOMTokenList
className: "string optional ui-autocomplete-input"
clientHeight: 28
clientLeft: 1
clientTop: 1
clientWidth: 218
contentEditable: "inherit"
dataset: DOMStringMap
defaultChecked: false
defaultValue: ""
dir: ""
dirName: ""
disabled: false
draggable: false
files: null
firstChild: null
firstElementChild: null
form: form#new_ride.simple_form form-horizontal
formAction: ""
formEnctype: ""
formMethod: ""
formNoValidate: false
formTarget: ""
height: 0
hidden: false
id: "ride_s"
incremental: false
indeterminate: false
innerHTML: ""
innerText: ""
isContentEditable: false
jQuery110205148881799541414: 32
labels: NodeList[1]
lang: ""
lastChild: null
lastElementChild: null
list: null
localName: "input"
max: ""
maxLength: 524288
min: ""
multiple: false
name: "ride[s]"
namespaceURI: "http://www.w3.org/1999/xhtml"
nextElementSibling: null
nextSibling: null
nodeName: "INPUT"
nodeType: 1
nodeValue: null
offsetHeight: 30
offsetLeft: 315
offsetParent: body.rides new
offsetTop: 208
offsetWidth: 220
onabort: null
onbeforecopy: null
onbeforecut: null
onbeforepaste: null
onblur: null
onchange: null
onclick: null
oncontextmenu: null
oncopy: null
oncut: null
ondblclick: null
ondrag: null
ondragend: null
ondragenter: null
ondragleave: null
ondragover: null
ondragstart: null
ondrop: null
onerror: null
onfocus: null
oninput: null
oninvalid: null
onkeydown: null
onkeypress: null
onkeyup: null
onload: null
onmousedown: null
onmousemove: null
onmouseout: null
onmouseover: null
onmouseup: null
onmousewheel: null
onpaste: null
onreset: null
onscroll: null
onsearch: null
onselect: null
onselectstart: null
onsubmit: null
onwebkitfullscreenchange: null
onwebkitfullscreenerror: null
onwebkitspeechchange: null
outerHTML: "<input class="string optional ui-autocomplete-input" id="ride_s" name="ride[s]" type="text" autocomplete="off">"
outerText: ""
ownerDocument: document
parentElement: div.controls
parentNode: div.controls
pattern: ""
placeholder: ""
prefix: null
previousElementSibling: span.ui-helper-hidden-accessible
previousSibling: span.ui-helper-hidden-accessible
readOnly: false
required: false
scrollHeight: 20
scrollLeft: 0
scrollTop: 0
scrollWidth: 206
selectionDirection: "none"
selectionEnd: 14
selectionStart: 14
size: 20
spellcheck: true
src: ""
step: ""
style: CSSStyleDeclaration
tabIndex: 0
tagName: "INPUT"
textContent: ""
title: ""
translate: true
type: "text"
useMap: ""
validationMessage: ""
validity: ValidityState
value: "Москва, Россия"
valueAsDate: null
valueAsNumber: NaN
webkitEntries: EntryArray
webkitGrammar: false
webkitPseudo: ""
webkitShadowRoot: null
webkitSpeech: false
webkitdirectory: false
webkitdropzone: ""
width: 0
willValidate: true
__proto__: HTMLInputElement

Gem not working with latest gem versions

After a gem update through bundle update, the autocomplete stopped working. I have upgraded from 0.1.4 to 2.0.0

Any upgrade guide available ? The homepage only mentions the 1.2.0 -> 1.3.0 migration.

I don't have any error in the JS (I believe) but the <ul> tag with autocomplete results doesn't get populated

JS debug output

called with opts Object { inputField="#company_employee_meeting_place",  pos=[2],  errorField="#company_employee_meeting_place_errors",  more...}
gmaps-a...d415.js (line 120)
final completerAssist undefined
gmaps-a...d415.js (line 120)
defaultOptions Object { mapElem="#gmaps-canvas",  zoomLevel=2,  mapType="roadmap",  more...}
gmaps-a...d415.js (line 120)
options after merge with defaults Object { mapElem="#map",  zoomLevel=14,  mapType="roadmap",  more...}
gmaps-a...d415.js (line 120)
lat,lng (44.620909, 4.389862999999991) { lat=function(),  lng=function(),  toString=function(),  more...}
gmaps-a...d415.js (line 120)
map options Object { zoom=14,  center=(44.620909, 4.389862999999991),  mapTypeId="roadmap",  more...}
gmaps-a...d415.js (line 120)
mapElem <div id="map" class="col-xs-8">
gmaps-a...d415.js (line 120)
region undefined

file name of gmaps-autocomplete.js is wrong

Sorry if this issue might be stupid, but I'm quite new to rails.

I can't get it to run, and after investigating into the issue I think that the file 'vendor/assets/gmaps-autocomplete.js' is missing in the gem.

The file seems to be named 'gmaps-auto-complete.js'

README outdated and referring to the wrong gem

There are several mistakes in the README. I suppose it's outdated but it should be fixed because the errors we run into might discourage people from using this gem.

  1. The single most important one is that gem 'gmaps-autocomplete-rails isn't loading your gem. We must refer to your repository as in
gem 'gmaps-autocomplete-rails', github: "kristianmandrup/gmaps-autocomplete-rails"
  1. jquery-ui-rails reached version 5.0.0 and must be loaded with
//= require jquery-ui

instead of require jquery.ui.all

  1. This must be added to include the stylesheets for the autocompleter

app/assets/stylesheets/application.css

*= require jquery-ui

As a side note, I had to //= require gmaps-auto-complete.js with the .js suffix because I disabled coffescript in the Gemfile.

Error load pipeline asset

No work assets pipeline in rails 4.

couldn't find file 'gmaps-auto-complete'
(in app/assets/javascripts/application.js:16)

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.