Pages

Monday, August 16, 2010

Messenger Connect JavaScript API – Listing Contacts

Roadmap

This post is part of a series of related posts on how to use Windows Live Messenger Connect.

1. Using Messenger Connect REST Explorer for exploring Windows Live resources.
2. (this post) Using the Messenger Connect JavaScript API for listing your contacts.
3. Getting contacts in three different ways (API and REST).
4. Messenger Connect - Initializing Controls

Overview
In the last post Windows Live REST Explorer – Messenger Connect, we discussed a new offering from Microsoft Windows Live called Windows Live Essentials. A part of this offering includes APIs and tools that developers can use. Good developer overviews are here: Windows Live for Developers blog and your complete resource is dev.live.com.

In this post and the previous we are not interested in the new versions of Messenger or Photo Gallery, rather, we will focus on one of the APIs that allows us to talk to the Windows Live cloud. In the last post, we talked about an browser-based tool (Windows Live REST Explorer - REX) that allows you to see your Windows Live data (after signing in). In this post, we'll take the next step and work with the JavaScript API that abstracts much of repetitive working in dealing with constructing the correct requests to access resources and parsing the responses. Instead, the JavaScript API provides a familiar programmatic construct where we deal with collections, array, and types. To this end, we'll show some simple JavaScript code in a web page displaying your contacts.

The approach we'll take is to first start by downloading the SDK and using it with a web application project we create to get a successful (programmatic) sign-in to Windows Live. Then, we'll create a custom page that displays contacts. You will need to have a Windows Live ID account (___@live.com or ____@hotmail.com for example).

1. Registering An Application
2. Getting Your Computer Ready
3. Creating a New Web Application
4. Download the SDK and Add Files to the Web Application
5. Testing SignIn
6. Creating a Page That Displays Your Contacts

Registering An Application


1. To start go to dev.live.com and download the SDK and register for the Beta. If you can't get Beta access or don't want to, you can still follow along and get a sense of how the JavaScript API works. You can also work with the Interactive SDK without joining the Beta.

2. Much like Facebook Connect, there is a registration process for get a unique ID (called a client ID) that identifies your application and a key (called a secret key) that is used in token exchanges. You will get these when you register and are accepted for the Beta and then follow further instructions on the registration site: manage.dev.live.com. Shown below are two screenshots for the basic part of registering your web application (the type we are interested in here).


Registering a Web Application


Application Keys

You don't have to worry about going to live with your application. Just getting to the point where you application is registered and you can get your client ID and secret key means you are good to go.

Getting Your Computer Ready

1. If you have IIS running and it is using port 8080 you can either shut it off temporarily, OR change the port used in this tutorial to something else when following the instructions below.

2. Make sure your computer can serve up a page on the domain you registered with manage.dev.live.com. You can do this by editing your hosts file and adding an entry for the domain.

Creating a New Web Application

1. Create a new web application. For this tutorial we'll assume it is named WindowsLiveWebApplication.

2. Set the web properties of the web application as shown below, substituting your domain name where appropriate. The web properties tell Visual Studio how to act when serving up the pages on the site.

Windows Live Messenger Connect Web Application Properties
Web Application Web Properties

3. In the global.asax.cs code-behind add a session flag in the Session_Start handler as shown below.


void Session_Start(object sender, EventArgs e)
{
/* This is to ensure you have a unique sessionId for the request in the case where you keep information
* in the session state either in the implementation IAuthStoreProvider or ISessionIdProvider.
*/
Session.Add("wl_Session_started", true);
}


4. Modify the web.config file to have the following keys.


<appSettings>
<!-- Copy Client ID from http://manage.dev.live.com below for your application -->
<add key="wl_wrap_client_id" value="***PUT YOUR CLIENT ID HERE***"/>

<!-- Copy Secret Key from http://manage.dev.live.com below for your application -->
<add key="wl_wrap_client_secret" value="***PUT YOUR SECRET KEY HERE***"/>

<!-- Callback Url for your application, Register this for your app in http://manage.dev.live.com. -->
<add key="wl_wrap_client_callback" value="http://***PUT YOUR DOMAIN NAME HERE***:8080/OAuthWrapCallback.ashx"/>

<!-- Channel Url for your application.-->
<add key="wl_wrap_channel_url" value="http://***PUT YOUR DOMAIN NAME HERE***:8080/channel.htm"/>
</appSettings>


5. Add a callback handler.


<system.web>
<compilation debug="true" targetFramework="4.0" />

<httpHandlers>
<add verb="*" path="OAuthWrapCallback.ashx" type="Microsoft.Live.OAuthWrapHandler, Microsoft.Live.AuthHandler"/>
</httpHandlers>
</system.web>


The plumbing here is to identify the OAuthWrapCallback.ashx page as the handler of the consent process. The page (which you don’t create but is associated with the Microsoft.Live.OAuthWrapHandler.dll) processes tokens when your site talks to the Windows Live consent service. You can create a custom callback page as described here. In that case you don’t need to reference the Microsoft.Live.OAuthWrapHandler.dll which is what you would do for non-ASP.NET sites.

Note that if you were to use IIS 7 you would have to add the handler to the system.webServer, handlers section of the web.config. But in this tutorial we are using Visual Studio to serve the page.

Download the SDK and Add Files to the Web Application

1. Go and get the SDK download. Once you download the SDK you will see that there are two files in it: Windows Live SDK\v4.1\API Toolkits\Microsoft.Live.AuthHandler.dll and Windows Live SDK 4.1\Windows Live SDK\v4.1\Channel\channel.htm.

2. In the web application you created make a reference to the AuthHandler.dll. The reference to the DLL makes the communication between your web application and the Windows Live cloud possible through the callback page.

3. Put a copy of the channel.htm file in the root of the web application you created. The channel.htm file which is used for cross domain communication when Flash isn't installed or a HTML5's cross domain post message is not available - as in older browsers.

4. In the site master page code-behind Site.Master/.cs add a reference to the AuthHandler.dll with a “using Microsoft.Live;” statement and define a property called SessionId that will be use declarative markup. Your Site.Master.cs should look like this:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Live;

namespace WindowsLiveWebApplication
{
public partial class SiteMaster : System.Web.UI.MasterPage
{
protected string SessionId
{
get
{
SessionIdProvider oauth = new SessionIdProvider();
return "wl_session_id=" + oauth.GetSessionId(HttpContext.Current);
}
}

protected void Page_Load(object sender, EventArgs e)
{

}
}
}


The SessionID property provides a unique identifier that can be used when the callback page is getting consent. Since we are not using HTTPS we need this extra step for security. The SessionID will be used to help formulate the application tag shown below.

5. In the Site.Master page you need to add several things to make the JavaScript API magic happen. Let's look at the declarative markup first. The whole page is be shown after showing the individual pieces.

First, add a loader statement which loads the JavaScript library.


<script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>


Add the namespace declaration referencing the Windows Live namespace.


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:wl="http://apis.live.net/js/2010">


Add an application tag that initializes the application as a Messenger Connect-enabled application.


<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Profiles.View, Messenger.SignIn, WL_Contacts.View"
onload="appLoaded">
</wl:app>


The first three attributes of the app tag read values out of the web.config file. The scopes attribute defines resource and access permission. The last attribute, onload, defines a handler to be executed once the application is loaded.

Add an import directive so the WebConfigurationManager class can be used above.


<%@ Import Namespace="System.Web.Configuration" %>


Add a script tag with the following JavaScript defining the appLoaded handler. In our example here it's just defines a shortcut for using the Microsoft.Live namespace and nothing else. But this is where you could put custom code that should run after the application is loaded.


<script type="text/javascript">
function appLoaded() {
Microsoft.Live.Core.Namespace.using("wl:Microsoft.Live");
}
</script>


Finally, add a wl:signin and a templated wl:userinfo control which together make signing in and showing a little information about who is signed in very easy. Also, add the wl:bar control in the footer div.

The Site.Master page so far should look like this:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="WindowsLiveWebApplication.SiteMaster" %>

<%@ Import Namespace="System.Web.Configuration" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:wl="http://apis.live.net/js/2010">
<head id="Head1" runat="server">
<script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>
<title></title>
<link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
<asp:ContentPlaceHolder ID="HeadContent" runat="server">
</asp:ContentPlaceHolder>
<script type="text/javascript">
function appLoaded() {
Microsoft.Live.Core.Namespace.using("wl:Microsoft.Live");
}
</script>
</head>
<body>
<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Profiles.View, Messenger.SignIn, WL_Contacts.View"
onload="appLoaded">
</wl:app>

<form id="Form1" runat="server">
<div class="page">
<div class="header">
<div class="title">
<h1>
My Windows Live Web Application
</h1>
</div>
<div class="loginDisplay">
<wl:signin id="mysignin" style='float:right'></wl:signin>
<!-- Display user first name after signed in -->
<wl:userinfo>
<div id="userInfoTemplate" class="sys-template">
<div id='profileElement' class='LiveConnectProfile'>
<div class='LiveConnectProfileTextFrame' style='float:right; margin-top:-2px'>
<span id='profileName' class='LiveConnectProfileName' style='position:relative; color:white' sys:innertext="{binding profile.firstName, convert={{new Function('a'\, 'return a?\'Hello \'+a:\'\'')}} }">
</span>
</div>
</div>
</div>
</wl:userinfo>
</div>
<div class="clear hideSkiplink">
<asp:Menu ID="NavigationMenu" runat="server" CssClass="menu" EnableViewState="false"
IncludeStyleBlock="false" Orientation="Horizontal">
<Items>
<asp:MenuItem NavigateUrl="~/Default.aspx" Text="Home" />
<asp:MenuItem NavigateUrl="~/About.aspx" Text="About" />
</Items>
</asp:Menu>
</div>
</div>
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
<div class="clear">
</div>
</div>
<div class="footer">
<wl:bar></wl:bar>
</div>
</form>
</body>
</html>


Testing SignIn

To run the application press F5 or CTRL + F5 (no debug). The Visual Studio ASP.NET Development Server is used to serve up the page.

Windows Live Messenger Connect SignIn Page
SignIn Page Before Signing In

Notice the messenger bar widget at the bottom of the page. When you sign in it becomes active and provides useful Messenger features like chatting in the window. When you sign in the page should change UI to reflect your information.

Windows Live Messenger Connect - After Sign In
SignIn Page After Signing In

Creating a Page That Displays Your Contacts

In the previous sections, if all went well, you got a successful sign in to Windows Live. In this section, we show a simple approach for listing contacts of the user that signs in. This approach could be extended easily to include methods for importing and exporting contacts or a dashboard for managing contacts.

What we show here is similar to what is discussed in this video that shows working with contacts. The video walks through a sample project given at the http://code.msdn.microsoft.com/messengerconnect site.

1. Create a new web form (page) called contacts.aspx that derives from the Site master page. We are going to using the Microsoft Ajax preview library (to use the DataView control part of preview releases of Microsoft Ajax Library) and the jQuery library. Add the following to the header content div:


<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0911/start.debug.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" ></script>


2. The next step is think about the programming pattern a bit. The Messenger Connect JavaScript API is designed so that you structure your data requests as asynchronous operations in a sort of cascade-fashion. For example, the wl:signin control we put on the page will call a SignedInCallback handler on sign-in which kicks off a cascade of subsequent calls (the full code sample below contains all the code). The cascade of function calls looks like this:


function SignedInCallback() { contactsLoaded();}
function contactsLoaded() { populateTable(); }
function populateTable() { contactProfile(); //populate the table with basic contact info }
function contactProfile() { //get specific info for each contact}


What's tricky about this is getting the right information to the UI at the right time. In the contactProfile function the particular piece of data returns and must be assigned to a named UI element after the table has already been rendered. (There is probably a better way of structuring this code, but on first pass this code seems to be what the API suggests we do.)

3. In this example, we decided to try and stick to using all client script instead of resorting a server-side control. We loaded the Microsoft Ajax library previously to use a DataView control to display data. The markup for the DataView looks like this:


<tbody id="contactList" class="sys-template">
<tr>
<td>{{firstName}} {{lastName}}</td>
<td>{{location}}</td>
<td><div sys:if="{{cid}}">
<img sys:if="{{thumbnailImageLink}}" width="40px" sys:src="{{thumbnailImageLink}}" />
<div sys:if="{{cid}}">says </div>
<span sys:id="{{cid}}" class="emp"></span>
</div>
</td>
</tr>
</tbody>


The items in the {{ }} will be determined in the populateTable function.

4. Contacts (collection) are loaded using a View which helps show only a specified number of items from a collection at any given time. You can then advance or go back through the collection and the JavaScript API will handle to queries to Windows Live to get the data. It's an easy way to move through data.


function contactsLoaded(dataLoadCompletedEventArgs) {
contactsCollection = dataLoadCompletedEventArgs.get_data();
contactsView = contactsCollection.get_view(); // get the view
contactsView.set_pageSize(pageSize); // set the page size
populateTable(contactsView); // pass the view to the function that iterates through and displays contacts
}


5. Finally, when you put it all together and sign in you get something like the screen shot shown below.

Windows Live Messenger Connect Contacts

The full code sample is shown below.


<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Contacts.aspx.cs" Inherits="WindowsLiveWebApplication.Contacts" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0911/start.debug.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" ></script>
<script type="text/javascript">
var dvContactList;
var dataContext;
var contactsView;
var totalNumContacts;
var pageSize = 5;
jQuery.noConflict();

Microsoft.Live.Core.Loader.onReady(function () {
Sys.require([Sys.components.dataView], function () {
dvContactList = $create(Sys.UI.DataView, {}, {}, {}, $get('contactList'));
});
});

function SignedOutCallback() {
// Clear the DataView
dvContactList.set_data([]);
$get('contactTable').className = 'hidden';
$get('prevButton').className = 'hidden';
$get('nextButton').className = 'hidden';

}
function SignedInCallback(signInCompletedEventArgs) {
if (signInCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Failed to sign in: " + signInCompletedEventArgs.get_resultCode());
return;
}
dataContext = Microsoft.Live.App.get_dataContext();
dataContext.loadAll(Microsoft.Live.DataType.contacts, contactsLoaded);
}

function contactsLoaded(dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Contacts failed to load...!");
return;
}

contactsCollection = dataLoadCompletedEventArgs.get_data();
totalNumContacts = contactsCollection.items.length; // not used, but could be
contactsView = contactsCollection.get_view();
contactsView.set_pageSize(pageSize);

setButtons(contactsView);
$get('contactTable').className = 'visible';
$get('prevButton').className = 'visible';
$get('nextButton').className = 'visible';
populateTable(contactsView);
}

function setButtons(contactsView) {
contactsView.hasNextPage(function (hasNext) {
if (!hasNext) {
$get('nextButton').disabled = true;
}
else {
$get('nextButton').disabled = false;
}
});
if (!contactsView.hasPreviousPage()) {
$get('prevButton').disabled = true;
}
else {
$get('prevButton').disabled = false;
}
}

function populateTable(contactsView) {
var contactsJSON = [];
for (var i = 0; i < contactsView.get_data().length; i++) {
var contact = contactsView.get_data()[i];
var windowsLiveUser = (contact.get_isFriend() === undefined) ? false : true;
var cid = (contact.get_cid() === undefined) ? null : contact.get_cid();
var thumbnailImageLink = (contact.get_thumbnailImageLink() === undefined) ? null : contact.get_thumbnailImageLink();
contactsJSON.push({ firstName: contact.get_firstName(),
lastName: contact.get_lastName(),
location: (((contact.get_locations()[0]) === undefined) ? "" : (contact.get_locations()[0]).get_city()),
windowsLiveUser: windowsLiveUser,
cid: cid,
thumbnailImageLink: thumbnailImageLink
});
if (windowsLiveUser) {
contact.loadProfiles(contactProfile);
}

}
dvContactList.set_data(contactsJSON);
}

function contactProfile(dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Profile for contact failed to load...!");
return;
}
var profileCollection = dataLoadCompletedEventArgs.get_data();
var cid = profileCollection.get_profile().get_cid()
var statusMessageLink = profileCollection.items[0].get_statusMessageLink();
var statusText = "";
if (statusMessageLink !== undefined) {
var statusCollection = new Microsoft.Live.Services.StatusCollection(statusMessageLink);
statusCollection.load(function (dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
log("Couldn't load status.");
return;
}
else {
var statusCollection = dataLoadCompletedEventArgs.get_data();
var statusMessage = statusCollection.get_statusMessage();
if (statusMessage) {
statusText = statusMessage.get_statusText();
jQuery('#' + cid.toLowerCase()).text(statusText);
}
}
});
}
}

function getPage(direction) {
switch (direction) {
case 'next':
contactsView.nextPage(function (evtArgs) {
if (evtArgs.get_resultCode() === Microsoft.Live.AsyncResultCode.success) {
populateTable(contactsView);
}
});
break;
case 'prev':
if (contactsView.hasPreviousPage()) {
contactsView.previousPage();
populateTable(contactsView);
}
break;
}
setButtons(contactsView);
}

</script>
<style type="text/css">
table thead tr td
{
font-weight: bold;
}
table tr td { text-align: center; width: 250px; height: 40px;}
table.hidden, input.hidden
{
display: none;
}
table.visible, input.visible
{
display:inline;
}
span.emp { font-style: italic}
</style>

</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h1>
Contacts DataView</h1>
<wl:signin onsignin="SignedInCallback" onsignout="SignedOutCallback" signedouttext="Sign In To View Contacts">
</wl:signin>
<input id="prevButton" type="button" onclick="getPage('prev')" value="Prev Page" class="hidden" />
<input id="nextButton" type="button" onclick="getPage('next')" value="Next Page" class="hidden"/>


<table id="contactTable" class="hidden">
<thead>
<tr>
<td>First/Last Name</td>
<td>City</td>
<td>Windows Live User?</td>
</tr>
</thead>
<tbody id="contactList" class="sys-template">
<tr>
<td>{{firstName}} {{lastName}}</td>
<td>{{location}}</td>
<td><div sys:if="{{cid}}">
<img sys:if="{{thumbnailImageLink}}" width="40px" sys:src="{{thumbnailImageLink}}" />
<div sys:if="{{cid}}">says </div>
<span sys:id="{{cid}}" class="emp"></span>
</div>
</td>
</tr>
</tbody>
</table>

</asp:Content>

Sunday, August 15, 2010

The Shape of Water (La forma dell’Aqua)

The Shape of Water - Andrea Camilleri
The Shape of Water (2002) is a detective story (il giallo, romanzi polizieschi) in the Inspector Montalbano series by Andrea Camilleri (1925 – ). La forma dell’acqua (1994) is the Italian title. The story is set in the fictional town of Vigàta in Sicily in the fictional province of Montelusa. Salvo Montalbano, a police inspector (commissario di polizia), investigates a suspicious but apparently natural death. Montalbano does get to the bottom of it and along the way we are treated to good food, witty dialogue, a cast of characters, and a sampling of the grit, grime and beauty of Sicilian life (or at least a version in Camilleri’s mind).

In Vigàta things seemingly never get done and are so controlled by corrupt officials that people accept this as the state of affairs, occasionally making a deal or play to better their own situation. In the middle of the book, a leftist journalist explains how “in Sicily, and in the province of Montelusa in particular, mutatis mutandis – or zara zabara – to say it in Sicilian – things never budged, even when there was a storm on a horizon. He quoted, with obvious facility, the prince of Salina’s famous statement about changing everything in order to change nothing…” The prince of Salina being the main character in Il Gattopardo.

The Italian phrasing of the statement is this “zara zabara per dirla in dialetto o mutatis mutandis per dirla in latino, le cose nell’isola, e nella provincia di Montelusa in particolare, non si cataminavano mai, magari se il barometro segnava tempesta. Citò, ed ebbe gioco facile, la frase saliniana del cangiar tutto per non cangiare niente...

The story’s title is revealed when Signora Luparello is speaking to Montalbano and advising him on the next steps in the investigation: “That is up to you to discover, if you so desire. Or else you can stop at the shape they’ve given the water.

The image shown below is the Italian version front cover. In copertina: Gonna a pieghe di Allen Jones. Collezione privata, Brescia.
La forma dell'acqua - Andrea Camilleri

Coming Soon… in Fremont

Coming Soon - Fremont
We walk by Coming Soon [a retail pop-up installation space] a couple of times a week as we roll down the hill into the center of the universe, aka Fremont. From what we’ve read the space is inspired by pop-up art stores in New York City. We don’t know what pop-up art store means exactly - I still call contemporary art, modern art and receive stern looks from those in the [k]now - but we like to stop and gaze at the installations. They bring a smile to nostre faccie. The current show (and pictured above) is called Of Paper and Light by artist Jesse Brown which features cut-out paper sculptures.

Sunday, August 8, 2010

The Periodic Table – Primo Levi

The Periodic Table Front Cover
The Periodic Table (1975) is a collection of fascinating stories by the Italian chemist and novelist Primo Levi (1919 – 1987). The collection, titled in Italian as Il sistema periodico, was translated into English in 1985. Each of the 21 chapters is based on an element that sometimes playing a figurative role in the story, but most often, playing a literal role. The majority of the stories are based on events in Levi’s life. In chapter 10, Gold, Levi is captured by the Fascists. In chapter 11, Cerium, Levi is in Auschwitz. How the elements gold and cerium figure into the stories is Levi’s narrative magic. Another important work by Levi is Se questo è un uomo (If This is a Man) which describes his 11 months in the Auschwitz and more generally the human condition in all its extremes.

The periodic table is the organizional scheme of how we understand elements and their relationships. It’s clever and in hindsight it makes sense, that Levi, a chemist, chose this title of the collection as such and structured each story around an element. Throughout the book it’s clear that Levi is a probing mind, a detective in matters of life and science, and in a battle with nature to unlock its secrets. To Levi, “incomprehensible matter” must be examined, writing that “[w]e must never feel disarmed: nature is immense and complex, but it is not impermeable to the intelligence; we must circle around it, pierce and probe it, look for the opening or make it.” There are many passages in the book where battle metaphors are used.

In the stories, there are many instances when Levi starts with an observation or fact about life or chemistry and generalizes it in a matter-of-fact way to a quasi-mediation on life. A good example is from chapter 3, Zinc, where Levi goes from a specific chemical behavior of zinc to wondering about the Fascist mentality:

The course notes contained a detail which at first reading had escaped me, namely, that the so tender and delicate zinc, so yielding to acid which gulps it down in a single mouthful, behaves, however, in a very different fashion when it is very pure: then it obstinately resists the attack. One could draw from this two conflicting philosophical conclusions: the praise of purity, which protects from evil like a coat of mail; the praise of impurity, which gives rise to changes, in other words, life. I discarded the first, disgustingly moralistic, and I lingered to consider the second, which I found more congenial. In order for the wheel to turn, for life to be lived, impurities are needed, and the impurities of impurities in the soil, too, as is known, if it is to be fertile. Dissension, diversity, the grain of salt and mustard are needed: Fascism does
not want them, forbids them, and that’s why you’re not a Fascist; it wants everybody to be the same, and you are not.

One aspect that you get a sense of reading these stories is Levi’s love of the origin of words and their meaning. The collection starts out in Chapter 1, Argon, with Levi relating what he knows of his ancestors, part of the larger Jewish community in Piedmont first arriving there in 1500. In the backstory, Levi introduces many Jewish-Piedmontese terms from his childhood. His ancestors to him were like argon, a noble gas, inert in that they never really mixed with other elements/society – or at least seemed relegated to the margins of society. Another example of his love of words is from chapter 6, Nickel, where Levi writes:

The entrails of the earth swarm with gnomes, kobolds (cobalt!), nickel, German “little demon” or “sprite,” and from which we derive the word nickel, creatures who can be generous and let you find a treasure beneath the tip of your pickax, or deceive you and bedazzle you, making modest pyrites glitter like gold, or disgusting zinc in the garb of tin: and in fact, many are the minerals who names have roots that signify ‘deception, fraud, bedazzlement.’
The collection ends with the story that seems to have been the nucleus for Levi’s writing career. He alludes that that story was “dreamed in an hour and place when my life was not worth much” – probably while imprisoned. It is the story of a carbon atom and this story is perhaps the most beloved of the 21 stories. This story appeared in The Oxford Book of Modern Science Writing and is what led me originally to The Periodic Table.
A Visual Representation of the The Periodic Table - Il sistema periodico