Google Maps Search, Knockoutjs.com - Twitter client example

http://knockoutjs.com/examples/twitter.html

by Alex Azuero

HTML

<link rel="stylesheet" href="http://knockoutjs.com/examples/resources/twitterExample.css">
<script src="http://knockoutjs.com/examples/resources/twitterApi.js"></script>
<script src="http://github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.js"></script>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Twitter Location Search</title>


<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
        
            <script type="text/javascript" >
/**
 * A distance widget that will display a circle that can be resized and will
 * provide the radius in km.
 *
 * @param {Object} opt_options Options such as map, position etc.
 *
 * @constructor
 */
function DistanceWidget(opt_options) {
  var options = opt_options || {};

  this.setValues(options);

  if (!this.get('position')) {
    this.set('position', map.getCenter());
  }

  // Add a marker to the page at the map center or specified position
  var marker = new google.maps.Marker({
    draggable: true,
    editable: true,
    title: 'Move me!'
  });

  marker.bindTo('map', this);
  marker.bindTo('zIndex', this);
  marker.bindTo('position', this);
  marker.bindTo('icon', this);

  // Create a new radius widget
  var radiusWidget = new RadiusWidget(options['distance'] || 50);

  // Bind the radius widget properties.
  radiusWidget.bindTo('center', this, 'position');
  radiusWidget.bindTo('map', this);
  radiusWidget.bindTo('zIndex', marker);
  radiusWidget.bindTo('maxDistance', this);
  radiusWidget.bindTo('minDistance', this);
  radiusWidget.bindTo('color', this);
  radiusWidget.bindTo('activeColor', this);
  radiusWidget.bindTo('sizerIcon', this);
  radiusWidget.bindTo('activeSizerIcon', this);

  // Bind to the radius widget distance property
  this.bindTo('distance', radiusWidget);
  // Bind to the radius widget bounds property
 ...

CSS

body { font-family: arial; font-size: 14px; }

.liveExample { padding: 1em; background-color: #ddd; border: 8px solid #fff; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
.liveExample select[multiple] { width: 100%; height: 8em; }
.liveExample h2 { margin-top: 0.4em; font-weight: bold; font-size: 1.2em; }

.liveExample select { height: 1.7em; }
.liveExample button { height: 2em; }


li { list-style-type: disc; margin-left: 20px; }
body {
  margin: 0;
  padding: 0;
  font: 12px Helvetica, Arial, Sans-serif;
  background: #ddeef6;
  padding: 0.5%;
}

#search {
  background: #59b;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  -moz-box-shadow: 2px 2px 5px rgba(88, 88, 88, 0.5);
  -webkit-box-shadow: 2px 2px 5px rgba(88, 88, 88, 0.5);
  margin-bottom: 10px;
  padding: 10px;
}

#q {
  background: #fff;
  margin: 0;
  padding: 8px 10px;
  border: 1px solid #eee;
  outline-width: 0;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  font-size: 18px;
  width: 230px;
}

#btn {
  padding: 8px 10px;
  border: 1px solid #eee;
  outline-width: 0;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  font-size: 15px;
  background: #fff;
  cursor: pointer;
  color: orange;
}

#search label {
  color: #fff;
  padding: 8px 10px;
  font-size: 18px;
}

#map-wrapper, #results-wrapper {
  padding: 1%;
  background: #fff;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  -moz-box-shadow: 2px 2px 5px rgba(88, 88, 88, 0.5);
  -webkit-box-shadow: 2px 2px 5px rgba(88, 88, 88, 0.5);
}

/*.has-cols #map-wrapper,
.has-cols #results-wrapper {
  width: 47.5%;
}

.has-cols #map-wrapper {
  float: left;
}

.has-cols #results-wrapper {
  float: right;
}*/

#results-wrapper {
 display: none;
}

#map {
  height: 500px;
}

#results {
  padding: 2px;
  max-height:...

JavaScript

// The view model holds all the state we're working with. It also has methods that can edit it, and it uses
// computed observables to calculate more state in terms of the underlying data
// --
// The view (i.e., the HTML UI) binds to this using data-bind attributes, so it always stays up-to-date with
// the view model, even though the view model does not know or care about any view that binds to it
var savedLists = [
    { name: "Celebrities", userNames: ['JohnCleese', 'MCHammer', 'StephenFry', 'algore', 'StevenSanderson']},
    { name: "Microsoft people", userNames: ['BillGates', 'shanselman', 'ScottGu']},
    { name: "Tech pundits", userNames: ['c_Sco', 'Scobleizer', 'LeoLaporte', 'techcrunch', 'BoingBoing', 'timoreilly', 'codinghorror']}
];
 
var TwitterListModel = function(lists, selectedList) {
    this.savedLists = ko.observableArray(lists);
    this.editingList = {
        name: ko.observable(selectedList),
        userNames: ko.observableArray()
    };
    this.userNameToAdd = ko.observable("");
    this.currentTweets = ko.observableArray([])
 
    this.findSavedList = function(name) {
        var lists = this.savedLists();
        return ko.utils.arrayFirst(lists, function(list) {
            return list.name === name;
        });
    };
 
    this.addUser = function() {
        if (this.userNameToAdd() && this.userNameToAddIsValid()) {
            this.editingList.userNames.push(this.userNameToAdd());
            this.userNameToAdd("");
        }
    };
 
    this.removeUser = function(userName) { 
        this.editingList.userNames.remove(userName) 
    }.bind(this);
 
    this.saveChanges = function() {
        var saveAs = prompt("Save as", this.editingList.name());
        if (saveAs) {
            var dataToSave = this.editingList.userNames().slice(0);
            var existingSavedList = this.findSavedList(saveAs);
            if (existingSavedList) existingSavedList.userNames = dataToSave; // Overwrite existing list
            else...