[gnome-maps] Add Check-in dialog



commit fec9ef1f9191665fefbb076115a33b1886060234
Author: Damián Nohales <damiannohales gmail com>
Date:   Mon Jun 2 14:48:49 2014 -0300

    Add Check-in dialog
    
    https://bugzilla.gnome.org/show_bug.cgi?id=731113

 data/gnome-maps.css                  |    4 +
 data/org.gnome.maps.gschema.xml.in   |   20 ++
 po/POTFILES.in                       |    4 +
 src/account-row.ui                   |   76 ++++++
 src/accountListBox.js                |   85 ++++++
 src/check-in-dialog.ui               |  465 ++++++++++++++++++++++++++++++++++
 src/checkIn.js                       |   41 +++
 src/checkInDialog.js                 |  299 ++++++++++++++++++++++
 src/gnome-maps.data.gresource.xml    |    4 +
 src/gnome-maps.js.gresource.xml      |    4 +
 src/social-place-more-results-row.ui |   22 ++
 src/social-place-row.ui              |   44 ++++
 src/socialPlaceListBox.js            |  110 ++++++++
 src/socialPlaceMatcher.js            |  126 +++++++++
 14 files changed, 1304 insertions(+), 0 deletions(-)
---
diff --git a/data/gnome-maps.css b/data/gnome-maps.css
index 42d6087..cf14d80 100644
--- a/data/gnome-maps.css
+++ b/data/gnome-maps.css
@@ -85,3 +85,7 @@
 .bubble-content {
     font-size: small;
 }
+
+.maps-check-in GtkTextView {
+    font-size: large;
+}
diff --git a/data/org.gnome.maps.gschema.xml.in b/data/org.gnome.maps.gschema.xml.in
index d118207..96df8c4 100644
--- a/data/org.gnome.maps.gschema.xml.in
+++ b/data/org.gnome.maps.gschema.xml.in
@@ -25,5 +25,25 @@
       <_summary>Number of recent places to store</_summary>
       <_description>Number of recently visited places to store.</_description>
     </key>
+    <key name="checkin-facebook-privacy" type="s">
+      <default>"EVERYONE"</default>
+      <_summary>Facebook check-in privacy setting</_summary>
+      <_description>Latest used Facebook check-in privacy setting. Possible values are: EVERYONE, 
FRIENDS_OF_FRIENDS, ALL_FRIENDS or SELF.</_description>
+    </key>
+    <key name="checkin-foursquare-privacy" type="s">
+      <default>"public"</default>
+      <_summary>Foursquare check-in privacy setting</_summary>
+      <_description>Latest used Foursquare check-in privacy setting. Possible values are: public, followers 
or private.</_description>
+    </key>
+    <key name="checkin-foursquare-broadcast-facebook" type="b">
+      <default>false</default>
+      <_summary>Foursquare check-in Facebook broadcasting</_summary>
+      <_description>Indicates if Foursquare should broadcast the check-in as a post in the Facebook account 
associated with the Foursquare account.</_description>
+    </key>
+    <key name="checkin-foursquare-broadcast-twitter" type="b">
+      <default>false</default>
+      <_summary>Foursquare check-in Twitter broadcasting</_summary>
+      <_description>Indicates if Foursquare should broadcast the check-in as a tweet in the Twitter account 
associated with the Foursquare account.</_description>
+    </key>
   </schema>
 </schemalist>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9417e87..44a76de 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,6 +4,9 @@ data/org.gnome.Maps.desktop.in.in
 data/org.gnome.maps.gschema.xml.in
 src/application.js
 [type: gettext/glade]src/app-menu.ui
+[type: gettext/glade]src/check-in-dialog.ui
+src/checkIn.js
+src/checkInDialog.js
 [type: gettext/glade]src/context-menu.ui
 src/geoclue.js
 src/mainWindow.js
@@ -15,5 +18,6 @@ src/searchResultBubble.js
 [type: gettext/glade]src/share-dialog.ui
 src/sidebar.js
 [type: gettext/glade]src/sidebar.ui
+[type: gettext/glade]src/social-place-more-results-row.ui
 [type: gettext/glade]src/user-location-bubble.ui
 src/utils.js
diff --git a/src/account-row.ui b/src/account-row.ui
new file mode 100644
index 0000000..60afd7c
--- /dev/null
+++ b/src/account-row.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <template class="Gjs_AccountRow" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkGrid" id="grid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="margin_start">10</property>
+        <property name="margin_end">10</property>
+        <property name="margin_top">10</property>
+        <property name="margin_bottom">10</property>
+        <property name="column_spacing">12</property>
+        <child>
+          <object class="GtkGrid" id="labelsGrid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <child>
+              <object class="GtkLabel" id="providerLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="identityLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="providerImage">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="pixel_size">32</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="attentionNeededImage">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="icon_name">dialog-warning-symbolic</property>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/accountListBox.js b/src/accountListBox.js
new file mode 100644
index 0000000..f1a96a2
--- /dev/null
+++ b/src/accountListBox.js
@@ -0,0 +1,85 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2014 Damián Nohales
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Damián Nohales <damiannohales gmail com>
+ */
+
+const Gio = imports.gi.Gio;
+const Goa = imports.gi.Goa;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+
+const Application = imports.application;
+
+const AccountRow = new Lang.Class({
+    Name: 'AccountRow',
+    Extends: Gtk.ListBoxRow,
+    Template: 'resource:///org/gnome/maps/account-row.ui',
+    InternalChildren: [ 'providerLabel',
+                        'identityLabel',
+                        'providerImage',
+                        'attentionNeededImage' ],
+
+    _init: function(params) {
+        this.account = params.account;
+        delete params.account;
+
+        this.parent(params);
+
+        let account = this.account.get_account();
+
+        this._providerLabel.label = account.provider_name;
+        this._identityLabel.label = account.presentation_identity;
+        this._providerImage.gicon = Gio.Icon.new_for_string(account.provider_icon);
+        this._attentionNeededImage.visible = account.attention_needed;
+    }
+});
+
+const AccountListBox = new Lang.Class({
+    Name: 'AccountListBox',
+    Extends: Gtk.ListBox,
+    Signals: {
+        'account-selected': { param_types: [Goa.Object] }
+    },
+
+    _init: function(params) {
+        params.activate_on_single_click = true;
+        this.parent(params);
+
+        Application.checkInManager.connect('accounts-refreshed', this.refresh.bind(this));
+
+        this.connect('row-activated', (function(list, row) {
+            this.emit('account-selected', row.account);
+        }).bind(this));
+
+        this.refresh();
+    },
+
+    refresh: function() {
+        let accounts = Application.checkInManager.accounts;
+
+        this.forall(function(row) {
+            row.destroy();
+        });
+
+        accounts.forEach((function(account) {
+            this.add(new AccountRow({ account: account }));
+        }).bind(this));
+    }
+});
diff --git a/src/check-in-dialog.ui b/src/check-in-dialog.ui
new file mode 100644
index 0000000..819ead9
--- /dev/null
+++ b/src/check-in-dialog.ui
@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <template class="Gjs_CheckInDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="type">popup</property>
+    <property name="type_hint">dialog</property>
+    <property name="width_request">500</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="contentArea">
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="homogeneous">True</property>
+            <property name="transition_type">crossfade</property>
+            <child>
+              <object class="GtkGrid" id="loadingGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkSpinner" id="loadingSpinner">
+                    <property name="height_request">32</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="active">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">loading</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="accountGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_start">15</property>
+                <property name="margin_end">15</property>
+                <property name="margin_top">15</property>
+                <property name="margin_bottom">15</property>
+                <child>
+                  <object class="GtkFrame" id="accountFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">in</property>
+                    <child type="label_item">
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">account</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="placeGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_start">15</property>
+                <property name="margin_end">15</property>
+                <property name="margin_top">15</property>
+                <property name="margin_bottom">15</property>
+                <child>
+                  <object class="GtkInfoBar" id="placeNotFoundInfoBar">
+                    <property name="visible">True</property>
+                    <property name="app_paintable">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_bottom">5</property>
+                    <property name="hexpand">True</property>
+                    <property name="message_type">warning</property>
+                    <child internal-child="action_area">
+                      <object class="GtkButtonBox" id="placeNotFoundInfoBarActionArea">
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <property name="layout_style">end</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child internal-child="content_area">
+                      <object class="GtkBox" id="placeNotFoundInfoBarContentArea">
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkImage" id="placeNotFoundInfoBarImage">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_start">6</property>
+                            <property name="icon_name">dialog-warning-symbolic</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="placeNotFoundLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="wrap">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="placeScrolledWindow">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="shadow_type">in</property>
+                    <property name="min_content_height">300</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">place</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="messageGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_start">15</property>
+                <property name="margin_end">15</property>
+                <property name="margin_top">8</property>
+                <property name="margin_bottom">10</property>
+                <property name="row_spacing">8</property>
+                <child>
+                  <object class="GtkGrid" id="messageInfoGrid">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="column_spacing">5</property>
+                    <child>
+                      <object class="GtkImage" id="messageInfoAccountImage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="pixel_size">32</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="messageInfoLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="margin_start">5</property>
+                        <property name="hexpand">True</property>
+                        <property name="use_markup">True</property>
+                        <property name="wrap">True</property>
+                        <property name="xalign">0</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="messageTextViewFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTextView" id="messageTextView">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="pixels_above_lines">10</property>
+                        <property name="wrap_mode">word-char</property>
+                        <property name="left_margin">10</property>
+                        <property name="right_margin">15</property>
+                        <property name="input_hints">GTK_INPUT_HINT_SPELLCHECK | 
GTK_INPUT_HINT_NONE</property>
+                      </object>
+                    </child>
+                    <child type="label_item">
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid" id="messageOptionsGrid">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkGrid" id="facebookOptionsGrid">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="column_spacing">10</property>
+                        <child>
+                          <object class="GtkLabel" id="facebookOptionsPrivacyLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Visibility</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBox" id="facebookOptionsPrivacyComboBox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hexpand">True</property>
+                            <property name="model">facebookOptionsPrivacyListStore</property>
+                            <property name="active">0</property>
+                            <property name="id_column">0</property>
+                            <child>
+                              <object class="GtkCellRendererText" 
id="facebookOptionsPrivacyComboBoxCellRendererText"/>
+                              <attributes>
+                                <attribute name="text">1</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkGrid" id="foursquareOptionsGrid">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="row_spacing">8</property>
+                        <child>
+                          <object class="GtkGrid" id="foursquareOptionsPrivacyGrid">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hexpand">True</property>
+                            <property name="column_spacing">10</property>
+                            <child>
+                              <object class="GtkLabel" id="foursquareOptionsPrivacyLabel">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Visibility</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkComboBox" id="foursquareOptionsPrivacyComboBox">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="hexpand">True</property>
+                                <property name="model">foursquareOptionsPrivacyListStore</property>
+                                <property name="active">0</property>
+                                <property name="id_column">0</property>
+                                <child>
+                                  <object class="GtkCellRendererText" 
id="foursquareOptionsPrivacyComboBoxCellRendererText"/>
+                                  <attributes>
+                                    <attribute name="text">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">0</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="foursquareOptionsBroadcastFacebookCheckButton">
+                            <property name="label" translatable="yes">Post on Facebook</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="halign">start</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="foursquareOptionsBroadcastTwitterCheckButton">
+                            <property name="label" translatable="yes">Post on Twitter</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="halign">start</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">message</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child type="titlebar">
+      <object class="GtkHeaderBar" id="headerBar">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="show-close-button">False</property>
+        <child>
+          <object class="GtkButton" id="cancelButton">
+            <property name="label" translatable="yes">_Cancel</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="use_underline">True</property>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="okButton">
+            <property name="label" translatable="yes">C_heck in</property>
+            <property name="visible">False</property>
+            <property name="can_focus">True</property>
+            <property name="use_underline">True</property>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <style>
+      <class name="maps-check-in"/>
+    </style>
+  </template>
+  <object class="GtkListStore" id="facebookOptionsPrivacyListStore">
+    <columns>
+      <!-- column-name id -->
+      <column type="gchararray"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0">EVERYONE</col>
+        <col id="1" translatable="yes">Everyone</col>
+      </row>
+      <row>
+        <col id="0">FRIENDS_OF_FRIENDS</col>
+        <col id="1" translatable="yes">Friends of friends</col>
+      </row>
+      <row>
+        <col id="0">ALL_FRIENDS</col>
+        <col id="1" translatable="yes">Just friends</col>
+      </row>
+      <row>
+        <col id="0">SELF</col>
+        <col id="1" translatable="yes">Just me</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="foursquareOptionsPrivacyListStore">
+    <columns>
+      <!-- column-name id -->
+      <column type="gchararray"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0">public</col>
+        <col id="1" translatable="yes">Public</col>
+      </row>
+      <row>
+        <col id="0">followers</col>
+        <col id="1" translatable="yes">Followers</col>
+      </row>
+      <row>
+        <col id="0">private</col>
+        <col id="1" translatable="yes">Private</col>
+      </row>
+    </data>
+  </object>
+</interface>
diff --git a/src/checkIn.js b/src/checkIn.js
index 8d2ede3..88e9f0c 100644
--- a/src/checkIn.js
+++ b/src/checkIn.js
@@ -22,8 +22,11 @@
 
 const GObject = imports.gi.GObject;
 const Goa = imports.gi.Goa;
+const Gtk = imports.gi.Gtk;
 const Lang = imports.lang;
+const _ = imports.gettext.gettext;
 
+const CheckInDialog = imports.checkInDialog;
 const FacebookBackend = imports.socialService.facebookBackend;
 const FoursquareBackend = imports.socialService.foursquareBackend;
 
@@ -116,6 +119,44 @@ const CheckInManager = new Lang.Class({
     findPlaces: function(account, latitude, longitude, distance, callback, cancellable) {
         this._getBackend(account)
             .findPlaces(this._getAuthorizer(account), latitude, longitude, distance, callback, cancellable);
+    },
+
+    showCheckInDialog: function(parentWindow, place, matchPlace) {
+        let dialog = new CheckInDialog.CheckInDialog({ transient_for: parentWindow,
+                                                       matchPlace: matchPlace,
+                                                       place: place });
+        let response = dialog.run();
+        dialog.destroy();
+
+        let message = null;
+
+        switch (response) {
+        case CheckInDialog.Response.FAILURE_NO_PLACES:
+            if (matchPlace)
+                /* Translators: %s is the place name that user wanted to check-in */
+                message = _("Cannot find \"%s\" in the social service").format(place.name);
+            else
+                message = _("Cannot find a suitable place to check-in in this location");
+            break;
+        case CheckInDialog.Response.FAILURE_GET_PLACES:
+            if (dialog.error.code === 401)
+                message = _("Credentials have expired, please open Online Accounts to sign in and enable 
this account");
+            else
+                message = dialog.error.message;
+            break;
+        }
+
+        if (message) {
+            let messageDialog = new Gtk.MessageDialog({ transient_for: parentWindow,
+                                                        destroy_with_parent: true,
+                                                        message_type: Gtk.MessageType.ERROR,
+                                                        buttons: Gtk.ButtonsType.OK,
+                                                        modal: true,
+                                                        text: _("An error has occurred"),
+                                                        secondary_text: message });
+            messageDialog.run();
+            messageDialog.destroy();
+        }
     }
 });
 
diff --git a/src/checkInDialog.js b/src/checkInDialog.js
new file mode 100644
index 0000000..fe76689
--- /dev/null
+++ b/src/checkInDialog.js
@@ -0,0 +1,299 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2014 Damián Nohales
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Damián Nohales <damiannohales gmail com>
+ */
+
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const _ = imports.gettext.gettext;
+
+const AccountListBox = imports.accountListBox;
+const Application = imports.application;
+const CheckIn = imports.checkIn;
+const SocialPlaceListBox = imports.socialPlaceListBox;
+const SocialPlaceMatcher = imports.socialPlaceMatcher;
+
+const Response = {
+    SUCCESS: 0,
+    CANCELLED: 1,
+    FAILURE_NO_PLACES: 2,
+    FAILURE_GET_PLACES: 3,
+    FAILURE_ACCOUNT_DISABLED: 4,
+    FAILURE_CHECKIN_DISABLED: 5
+};
+
+const CheckInDialog = new Lang.Class({
+    Name: 'CheckInDialog',
+    Extends: Gtk.Dialog,
+    Template: 'resource:///org/gnome/maps/check-in-dialog.ui',
+    InternalChildren: [ 'cancelButton',
+                        'okButton',
+                        'stack',
+                        'accountFrame',
+                        'placeScrolledWindow',
+                        'placeNotFoundInfoBar',
+                        'placeNotFoundLabel',
+                        'messageInfoLabel',
+                        'messageInfoAccountImage',
+                        'messageTextView',
+                        'facebookOptionsGrid',
+                        'facebookOptionsPrivacyComboBox',
+                        'foursquareOptionsGrid',
+                        'foursquareOptionsPrivacyComboBox',
+                        'foursquareOptionsBroadcastFacebookCheckButton',
+                        'foursquareOptionsBroadcastTwitterCheckButton' ],
+
+    _init: function(params) {
+        this._place = params.place;
+        delete params.place;
+
+        this._matchPlace = params.matchPlace;
+        delete params.matchPlace;
+
+        // This is a construct-only property and cannot be set by GtkBuilder
+        params.use_header_bar = true;
+
+        this.parent(params);
+
+        this._account = null;
+        this._checkIn = new CheckIn.CheckIn();
+        this.error = null;
+
+        this._cancellable = new Gio.Cancellable();
+        this._cancellable.connect((function() {
+            this.response(Response.CANCELLED);
+        }).bind(this));
+
+        this.connect('delete-event', (function() {
+            this._cancellable.cancel();
+        }).bind(this));
+
+        Application.checkInManager.connect('accounts-refreshed', this._onAccountRefreshed.bind(this));
+
+        this._initHeaderBar();
+        this._initWidgets();
+    },
+
+    _initHeaderBar: function() {
+        this._cancelButton.connect('clicked', (function() {
+            this._cancellable.cancel();
+        }).bind(this));
+
+        this._okButton.connect('clicked', (function() {
+            this._startCheckInStep();
+        }).bind(this));
+    },
+
+    _initWidgets: function() {
+        // Limitations in Gjs means we can't do this in UI files yet
+        this._accountListBox = new AccountListBox.AccountListBox({ visible: true });
+        this._accountFrame.add(this._accountListBox);
+
+        this._placeListBox = new SocialPlaceListBox.SocialPlaceListBox({ visible: true });
+        this._placeScrolledWindow.add(this._placeListBox);
+
+        Application.settings.bind('checkin-facebook-privacy',
+                                  this._facebookOptionsPrivacyComboBox,
+                                  'active_id', Gio.SettingsBindFlags.DEFAULT);
+
+        Application.settings.bind('checkin-foursquare-privacy',
+                                  this._foursquareOptionsPrivacyComboBox,
+                                  'active_id', Gio.SettingsBindFlags.DEFAULT);
+
+        Application.settings.bind('checkin-foursquare-broadcast-facebook',
+                                  this._foursquareOptionsBroadcastFacebookCheckButton,
+                                  'active', Gio.SettingsBindFlags.DEFAULT);
+
+        Application.settings.bind('checkin-foursquare-broadcast-twitter',
+                                  this._foursquareOptionsBroadcastTwitterCheckButton,
+                                  'active', Gio.SettingsBindFlags.DEFAULT);
+
+        this._accountListBox.connect('account-selected', (function(list, account) {
+            this._account = account;
+            this._startPlaceStep();
+        }).bind(this));
+
+        this._placeListBox.connect('place-selected', (function(list, place) {
+            this._checkIn.place = place;
+            this._startMessageStep();
+        }).bind(this));
+    },
+
+    vfunc_show: function() {
+        this._startup();
+        this.parent();
+    },
+
+    _startup: function() {
+        let accounts = Application.checkInManager.accounts;
+
+        if (accounts.length > 1)
+            this._startAccountStep();
+        else if (accounts.length === 1) {
+            this._account = Application.checkInManager.accounts[0];
+            this._startPlaceStep();
+        } else {
+            Mainloop.idle_add((function() {
+                this.response(Response.FAILURE_CHECKIN_DISABLED);
+            }).bind(this));
+        }
+    },
+
+    _onAccountRefreshed: function() {
+        let accounts = Application.checkInManager.accounts;
+
+        if (!Application.checkInManager.hasCheckIn)
+            this.response(Response.FAILURE_CHECKIN_DISABLED);
+        else if (this._account) {
+            for (let i in accounts) {
+                let account = accounts[i];
+                if (this._account.get_account().id === accounts[i].get_account().id)
+                    return;
+            }
+
+            this.response(Response.FAILURE_ACCOUNT_DISABLED);
+        }
+    },
+
+    _startAccountStep: function() {
+        this.set_title(_("Select an account"));
+        this._stack.set_visible_child_name('account');
+    },
+
+    _startPlaceStep: function() {
+        this.set_title(_("Loading"));
+        this._stack.set_visible_child_name('loading');
+
+        Application.checkInManager.findPlaces(this._account,
+                                              this._place.location.latitude,
+                                              this._place.location.longitude,
+                                              100,
+                                              this._onFindPlacesFinished.bind(this),
+                                              this._cancellable);
+    },
+
+    _onFindPlacesFinished: function(account, places, error) {
+        if (!error) {
+            if (places.length === 0) {
+                this.response(Response.FAILURE_NO_PLACES);
+                return;
+            }
+
+            let matches = SocialPlaceMatcher.match(this._place, places);
+
+            if (matches.exactMatches.length === 1 && this._matchPlace) {
+                this._checkIn.place = matches.exactMatches[0];
+                this._startMessageStep();
+            } else {
+                this.set_title(_("Select a place"));
+                this._placeListBox.matches = matches;
+
+                if (this._matchPlace) {
+                    if (this._account.get_account().provider_type === 'facebook')
+                        this._placeNotFoundLabel.label = _("Maps cannot find the place to check in to with 
Facebook. Please select one from this list.");
+                    else if (this._account.get_account().provider_type === 'foursquare')
+                        this._placeNotFoundLabel.label = _("Maps cannot find the place to check in to with 
Foursquare. Please select one from this list.");
+                } else
+                    this._placeNotFoundInfoBar.hide();
+
+                this._stack.set_visible_child_name('place');
+            }
+        } else {
+            this.error = error;
+            this.response(Response.FAILURE_GET_PLACES);
+        }
+    },
+
+    _startMessageStep: function() {
+        /* Translators: %s is the name of the place to check in.
+         */
+        this.set_title(_("Check in to %s").format(this._checkIn.place.name));
+        this._stack.set_visible_child_name('message');
+
+        this._messageTextView.grab_focus();
+        this._okButton.show();
+
+        let account = this._account.get_account();
+
+        /* Translators: %s is the name of the place to check in.
+         */
+        let labelMessageInfo = _("Write an optional message to check in to %s.");
+        this._messageInfoLabel.label = labelMessageInfo.format('<a 
href="%s">%s</a>'.format(this._checkIn.place.link,
+                                                                                            
this._checkIn.place.name));
+        this._messageInfoAccountImage.gicon = Gio.Icon.new_for_string(account.provider_icon);
+
+        let optionsGrids = { 'facebook': this._facebookOptionsGrid,
+                             'foursquare': this._foursquareOptionsGrid };
+
+        for (let provider in optionsGrids)
+            if (provider === account.provider_type)
+                optionsGrids[provider].show();
+            else
+                optionsGrids[provider].hide();
+    },
+
+    _startCheckInStep: function() {
+        this.set_title(_("Loading"));
+        this._stack.set_visible_child_name('loading');
+
+        this._okButton.hide();
+
+        let message = this._messageTextView.buffer.text;
+        let privacy;
+
+        if (this._account.get_account().provider_type === 'facebook')
+            privacy = this._facebookOptionsPrivacyComboBox.active_id;
+        else if (this._account.get_account().provider_type === 'foursquare')
+            privacy = this._foursquareOptionsPrivacyComboBox.active_id;
+
+        let broadcastFacebook = this._foursquareOptionsBroadcastFacebookCheckButton.active;
+        let broadcastTwitter = this._foursquareOptionsBroadcastTwitterCheckButton.active;
+
+        this._checkIn.message = message;
+        this._checkIn.privacy = privacy;
+        this._checkIn.broadcastFacebook = broadcastFacebook;
+        this._checkIn.broadcastTwitter = broadcastTwitter;
+
+        Application.checkInManager.performCheckIn(this._account,
+                                                  this._checkIn,
+                                                  this._onPerformCheckInFinished.bind(this),
+                                                  this._cancellable);
+    },
+
+    _onPerformCheckInFinished: function (account, data, error) {
+        if (!error)
+            this.response(Response.SUCCESS);
+        else {
+            let messageDialog = new Gtk.MessageDialog({ transient_for: this,
+                                                        destroy_with_parent: true,
+                                                        message_type: Gtk.MessageType.ERROR,
+                                                        buttons: Gtk.ButtonsType.OK,
+                                                        modal: true,
+                                                        text: _("An error has occurred"),
+                                                        secondary_text: error.message });
+            messageDialog.run();
+            messageDialog.destroy();
+
+            this._startMessageStep();
+        }
+    }
+});
diff --git a/src/gnome-maps.data.gresource.xml b/src/gnome-maps.data.gresource.xml
index b19a625..9638968 100644
--- a/src/gnome-maps.data.gresource.xml
+++ b/src/gnome-maps.data.gresource.xml
@@ -17,6 +17,10 @@
     <file preprocess="xml-stripblanks">search-result-bubble.ui</file>
     <file preprocess="xml-stripblanks">share-dialog.ui</file>
     <file preprocess="xml-stripblanks">user-location-bubble.ui</file>
+    <file preprocess="xml-stripblanks">check-in-dialog.ui</file>
+    <file preprocess="xml-stripblanks">account-row.ui</file>
+    <file preprocess="xml-stripblanks">social-place-row.ui</file>
+    <file preprocess="xml-stripblanks">social-place-more-results-row.ui</file>
     <file alias="application.css">../data/gnome-maps.css</file>
     <file alias="maptype-aerial.png">../data/media/maptype-aerial.png</file>
     <file alias="maptype-street.png">../data/media/maptype-street.png</file>
diff --git a/src/gnome-maps.js.gresource.xml b/src/gnome-maps.js.gresource.xml
index 9d7c4bf..07ffff1 100644
--- a/src/gnome-maps.js.gresource.xml
+++ b/src/gnome-maps.js.gresource.xml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/maps">
+    <file>accountListBox.js</file>
     <file>application.js</file>
     <file>checkIn.js</file>
+    <file>checkInDialog.js</file>
     <file>config.js</file>
     <file>contextMenu.js</file>
     <file>epaf.js</file>
@@ -38,6 +40,8 @@
     <file>turnPointMarker.js</file>
     <file>settings.js</file>
     <file>sidebar.js</file>
+    <file>socialPlaceListBox.js</file>
+    <file>socialPlaceMatcher.js</file>
     <file>userLocationMarker.js</file>
     <file>userLocationBubble.js</file>
     <file>socialService/facebookBackend.js</file>
diff --git a/src/social-place-more-results-row.ui b/src/social-place-more-results-row.ui
new file mode 100644
index 0000000..2dcca7f
--- /dev/null
+++ b/src/social-place-more-results-row.ui
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <template class="Gjs_SocialPlaceMoreResultsRow" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkLabel" id="label">
+        <property name="label" translatable="yes">Show more results</property>
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="halign">start</property>
+        <property name="margin_start">10</property>
+        <property name="margin_end">10</property>
+        <property name="margin_top">10</property>
+        <property name="margin_bottom">10</property>
+        <attributes>
+          <attribute name="foreground" value="#777777" />
+        </attributes>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/social-place-row.ui b/src/social-place-row.ui
new file mode 100644
index 0000000..623c92a
--- /dev/null
+++ b/src/social-place-row.ui
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <template class="Gjs_SocialPlaceRow" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkGrid" id="grid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="margin_start">10</property>
+        <property name="margin_end">10</property>
+        <property name="margin_top">10</property>
+        <property name="margin_bottom">10</property>
+        <property name="row_spacing">4</property>
+        <child>
+          <object class="GtkLabel" id="nameLabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="wrap">True</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="categoryLabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/socialPlaceListBox.js b/src/socialPlaceListBox.js
new file mode 100644
index 0000000..c9e744d
--- /dev/null
+++ b/src/socialPlaceListBox.js
@@ -0,0 +1,110 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2014 Damián Nohales
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Damián Nohales <damiannohales gmail com>
+ */
+
+const GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const SocialPlaceRow = new Lang.Class({
+    Name: 'SocialPlaceRow',
+    Extends: Gtk.ListBoxRow,
+    Template: 'resource:///org/gnome/maps/social-place-row.ui',
+    InternalChildren: [ 'nameLabel',
+                        'categoryLabel' ],
+
+    _init: function(params) {
+        this.place = params.place;
+        delete params.place;
+
+        this.parent(params);
+
+        this._nameLabel.label = this.place.name;
+        if (this.place.category)
+            this._categoryLabel.label = this.place.category;
+        else
+            this._categoryLabel.visible = false;
+    }
+});
+
+const SocialPlaceMoreResultsRow = new Lang.Class({
+    Name: 'SocialPlaceMoreResultsRow',
+    Extends: Gtk.ListBoxRow,
+    Template: 'resource:///org/gnome/maps/social-place-more-results-row.ui'
+});
+
+const SocialPlaceListBox = new Lang.Class({
+    Name: 'SocialPlaceListBox',
+    Extends: Gtk.ListBox,
+    Signals: {
+        'place-selected': { param_types: [GObject.TYPE_OBJECT] }
+    },
+
+    _init: function(params) {
+        params.activate_on_single_click = true;
+        this.parent(params);
+
+        this.connect('row-activated', (function(list, row) {
+            if (!row.place) {
+                // "Show more results" row activated
+                row.destroy();
+                this._showBadMatches();
+            } else
+                this.emit('place-selected', row.place);
+        }).bind(this));
+    },
+
+    get matches() {
+        return this._matches;
+    },
+
+    set matches(matches) {
+        this.forall(function(row) {
+            row.destroy();
+        });
+
+        this._matches = matches;
+
+        if (this._matches.exactMatches.length +
+            this._matches.goodMatches.length === 0) {
+            this._showBadMatches();
+        } else {
+            this._matches.exactMatches.forEach(this._addPlace.bind(this));
+            this._matches.goodMatches.forEach(this._addPlace.bind(this));
+
+            if (this._matches.badMatches.length > 0)
+                this._addMoreResults();
+        }
+    },
+
+    _showBadMatches: function() {
+        this._matches.badMatches.forEach(this._addPlace.bind(this));
+    },
+
+    _addPlace: function(place) {
+        this.add(new SocialPlaceRow({ place: place }));
+    },
+
+    _addMoreResults: function() {
+        this.add(new SocialPlaceMoreResultsRow({}));
+    }
+});
diff --git a/src/socialPlaceMatcher.js b/src/socialPlaceMatcher.js
new file mode 100644
index 0000000..767d756
--- /dev/null
+++ b/src/socialPlaceMatcher.js
@@ -0,0 +1,126 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2014 Damián Nohales
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Damián Nohales <damiannohales gmail com>
+ */
+
+/**
+ * The Levenshtein distance is a string comparison algorithm defined
+ * as the minimal number of characters you have to replace, insert or
+ * delete to transform string a into string b.
+ * We use it to compare the similarities of two place names.
+ *
+ * http://en.wikipedia.org/wiki/Levenshtein_distance
+ */
+function _getLevenshteinDistance(a, b) {
+    let i;
+    let j;
+    let d = [];
+
+    for (i = 0; i <= a.length; i++) {
+        d[i] = [];
+        for (j = 0; j <= b.length; j++)
+            d[i][j] = 0;
+    }
+
+    for (i = 1; i <= a.length; i++)
+        d[i][0] = i;
+
+    for (j = 1; j <= b.length; j++)
+        d[0][j] = j;
+
+    for (j = 1; j <= b.length; j++)
+        for (i = 1; i <= a.length; i++)
+            if (a[i] === b[j])
+                d[i][j] = d[i-1][j-1];
+            else
+                d[i][j] = Math.min(d[i-1][j] + 1,
+                                   d[i][j-1] + 1,
+                                   d[i-1][j-1] + 1);
+
+    return d[a.length][b.length];
+}
+
+function _normalize(name) {
+    return name.toLowerCase().trim().replace(/ +(?= )/g,'');
+}
+
+function _getPlacesLevenshteinDistance(geoPlace, socialPlace) {
+    let a = geoPlace.name;
+    let b = socialPlace.name;
+
+    return _getLevenshteinDistance(_normalize(a), _normalize(b));
+}
+
+/**
+ * Returns: 0 for bad match, 1 for good match and 2
+ * exact match.
+ */
+function _getKindOfMatch(geoPlace, socialPlace) {
+    let distance = geoPlace.location.get_distance_from(socialPlace.location);
+    let levenshtein = _getPlacesLevenshteinDistance(geoPlace, socialPlace);
+
+    if (distance < 0.01 && levenshtein <= 2)
+        return 2;
+    else if (distance < 0.03 && levenshtein <= 5)
+        return 1;
+    else
+        return 0;
+}
+
+function match(geoPlace, socialPlaces) {
+    let result = {
+        exactMatches: [],
+        goodMatches: [],
+        badMatches: []
+    };
+
+    socialPlaces.sort(function(a, b) {
+        let da = geoPlace.location.get_distance_from(a.location);
+        let db = geoPlace.location.get_distance_from(b.location);
+
+        if (da > db)
+            return 1;
+        else if (da < db)
+            return -1;
+
+        return 0;
+    });
+
+    socialPlaces.forEach(function(place) {
+        let match = _getKindOfMatch(geoPlace, place);
+
+        switch (match)
+        {
+        case 0:
+            result.badMatches.push(place);
+            break;
+
+        case 1:
+            result.goodMatches.push(place);
+            break;
+
+        case 2:
+            result.exactMatches.push(place);
+            break;
+        }
+    });
+
+    return result;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]