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...