Example App: Dream Interpretation

For the purposes of demoing OpenSocial on MySpace to the SixApart Hackathon, I put together a dream interpretation application. This application is intended to demo a piece of third party functionality integrated into MySpace. People asked for the source and a brief explanation, so we'll put that in this blog post. This isn't exactly a working app--it's more of a bunch of source code that you can use to see various clumps of OpenSocial glue working. It highlights how to make requests to a third party site and combine the results with MySpace friend information.

The application profile is here:

A profile with the app installed is here:

To make this application, I took a pre-existing web site (a dream interpretation site I put together a long time ago), called http://www.enhypniomancy.com. This site has existed over the years in several different languages, on several different hosting companies, and using several different databases. Now it's written in C#, so you'll have to follow C# code to get the server stuff. If you've encountered c-style syntax before, it should be fairly easy to extrapolate that code into your server-side language of choice.

In order to work, the Dream Application needs to allow users to save dreams, interpret dreams, and request dreams on behalf of themselves and their friends. To enable this on the server-side, enhypniomancy.com exposes a set of JSON-formatted HTTP calls that encompass the following simple functionality:

  • Get dream (dream id)
  • Get dreams (user id)
  • Get friends' dreams (multiple user ids)
  • Add dream (user id, title, narrative)
  • Add interpretation (dream id, user id, interpretation)
  • Delete dream (dream id)

That functionality is available as a simple set of GET URLs. For example, the URL to get a dream is:


http://www.enhypniomancy.com/Soc.ashx?command=getdream&dreamid=6

Simple enough. For those of you familiar with ASP.NET, the .ashx file is a HttpHandler, which is a simple request/response piping mechanism--functionally similar to a Java servlet. When you look at the source, you'll see some database glue. I'm not going to release the database glue code because it isn't relevant.

Server side code (hosted on third party site)

Below is the source for the server-side HttpHandler:

code snippet 1 : Dream App Server-Side C# (hosted on www.enhypniomancy.com
<%@ WebHandler Language="C#" Class="Soc" %>

using System;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
using Enhypniomancy.DreamDomain.CollectionClasses;
using Enhypniomancy.DreamDomain.EntityClasses;


public class JSonDreamResponse
{
public string ErrorMessage;
public bool HadError;
public string ResponseMessage;
}


public class JSonDream
{

public int DreamId;
public string Title;
public string Narrative;
public string Date;
public int Interpretations;
public int UserId;

public System.Collections.Generic.IList<JSonInterp> Interps =
new System.Collections.Generic.List<JSonInterp>();
}


public class JSonInterp
{
public string Interpretation;
public int UserId;
public string InterpretationDate;
}


public class JSonFriend
{
public int UserId;
public System.Collections.Generic.IList<JSonDream> Dreams =
new System.Collections.Generic.List<JSonDream>();
}


public class JSonDreamList : JSonDreamResponse
{
public JSonDream[] Dreams;
}

public class JSonFriendDreamList : JSonDreamResponse
{
public JSonFriend[] Friends;
}


public class Soc : IHttpHandler {

public void ProcessRequest (HttpContext context) {
string command = context.Request["command"];

switch(command)
{
case "getdream" :
{
GetDream(context);
break;
}
case "getdreams" :
{
GetDreams(context);
break;
}
case "getfrienddreams" :
{
GetFriendDreams(context);
break;
}
case "adddream" :
{
EnterDream(context);
break;
}
case "addinterpretation" :
{
EnterInterpretation(context);
break;
}
case "deletedream" :
{
DeleteDream(context);
break;
}
default :
{
throw new ApplicationException("Command: " + command + " is not supported");
}
}

context.Response.ContentType
= "text/json";
context.Response.Cache.SetExpires(DateTime.Now.AddYears(
1));

}

private int GetUserId(HttpContext context)
{
return int.Parse(context.Request["userid"]);
}

public void GetFriendDreams(HttpContext context)
{
try

{
string friendArray = context.Request["friendidlist"];
string[] friendIdStringArray =
friendArray.Split(
new char[]{','},StringSplitOptions.RemoveEmptyEntries);
int[] friendIdIntArray = new int[friendIdStringArray.Length];

for(int i = 0; i < friendIdStringArray.Length; i++)
{
friendIdIntArray[i]
= int.Parse(friendIdStringArray[i]);
}
SocialDreamCollection sdc
= new SocialDreamCollection();

SD.LLBLGen.Pro.ORMSupportClasses.IPredicate predicate
=

new SD.LLBLGen.Pro.ORMSupportClasses.FieldCompareRangePredicate(
Enhypniomancy.DreamDomain.HelperClasses.SocialDreamFields.UserId, friendIdIntArray);

sdc.GetMulti(predicate);
JSonFriendDreamList friendList
= new JSonFriendDreamList();
friendList.Friends
= new JSonFriend[friendIdIntArray.Length];
for (int i = 0; i < friendIdIntArray.Length; i++ )
{
friendList.Friends[i]
= new JSonFriend();
friendList.Friends[i].UserId
= friendIdIntArray[i];
}
for (int i = 0; i < sdc.Count; i++)
{
SocialDreamEntity sde
= sdc[i];
JSonDream dream
= new JSonDream();
MapJSonToSocialDream(dream, sde);

for(int j = 0; j < friendIdIntArray.Length; j++)
{
if(friendList.Friends[j].UserId == sde.UserId)
{
friendList.Friends[j].Dreams.Add(dream);
}
}
}
WriteToOutput(context,friendList);
}
catch(Exception exc)
{
WriteError(context,exc.ToString());
}
}

public void DeleteDream(HttpContext context)
{
try

{
SocialDreamEntity sde
=
new SocialDreamEntity(int.Parse(context.Request["dreamid"]));


sde.SocialDreamInterpretation.DeleteMulti();

sde.Delete();

WriteResponse(context,
"Successfully deleted dream " +
int.Parse(context.Request["dreamid"]));
}
catch(Exception exc)
{
WriteError(context,exc.ToString());
}
}

public void EnterInterpretation(HttpContext context)
{
try

{
Enhypniomancy.DreamDomain.EntityClasses.SocialDreamInterpretationEntity sde
=
new SocialDreamInterpretationEntity();

sde.DreamId
= int.Parse(context.Request["dreamid"]);
sde.InterpreterUserId
= int.Parse(context.Request["userid"]);
sde.InterpretationDate
= DateTime.Now;
sde.Interpretation
= context.Request["interpretation"];
sde.Save();

WriteResponse(context,
"Saved interpreation for dream " +
sde.DreamId
+ " by interpreter " + sde.InterpreterUserId);
}
catch (Exception exc)
{
WriteError(context, exc.ToString());
}
}

public void EnterDream(HttpContext context)
{
try

{
Enhypniomancy.DreamDomain.EntityClasses.SocialDreamEntity sde
=
new SocialDreamEntity();

int userId = GetUserId(context);
string title = context.Request["title"];
string narrative = context.Request["narrative"];

sde.DateCreated
= DateTime.Now;
sde.UserId
= userId;
sde.Narrative
= narrative;
sde.Title
= title;
sde.Save();

WriteResponse(context,
"Successfully saved dream "
+ sde.DreamId + " with title " + sde.Title + " to user " + userId);

}
catch(Exception exc)
{

WriteError(context,exc.ToString());
}
}

private void WriteToOutput(HttpContext context, object obj)
{
System.Text.StringBuilder output
= new StringBuilder();
System.Web.Script.Serialization.JavaScriptSerializer jss
=
new JavaScriptSerializer();
jss.Serialize(obj, output);
context.Response.Write(output);

}


private void WriteError(HttpContext context, string message)
{
JSonDreamResponse e
= new JSonDreamResponse();
e.ErrorMessage
= message;

e.HadError
= true;
WriteToOutput(context,e);
}

private void WriteResponse(HttpContext context,string message)
{
JSonDreamResponse jdr
= new JSonDreamResponse();
jdr.ResponseMessage
= message;
jdr.HadError
= false;
WriteToOutput(context,jdr);
}

public void GetDream(HttpContext context)
{
try

{
SocialDreamEntity sde
=
new SocialDreamEntity(int.Parse(context.Request["dreamid"]));

JSonDreamList jdl
= new JSonDreamList();
jdl.Dreams
= new JSonDream[1];
JSonDream jdr
= new JSonDream();
MapJSonToSocialDream(jdr,sde);

SocialDreamInterpretationCollection sdic
=
new SocialDreamInterpretationCollection();
sdic.GetMulti((Enhypniomancy.DreamDomain.HelperClasses.SocialDreamInterpretationFields.DreamId
==

jdr.DreamId));

foreach(SocialDreamInterpretationEntity sdie in sdic)
{
JSonInterp jsi
= new JSonInterp();
jsi.Interpretation
= sdie.Interpretation;
jsi.InterpretationDate
= sdie.InterpretationDate.ToShortDateString();
jsi.UserId
= sdie.InterpreterUserId;
jdr.Interps.Add(jsi);
}

jdl.Dreams[
0] = jdr;

WriteToOutput(context,jdl);

}
catch(Exception exc)
{
WriteError(context,exc.ToString());
}
}
private void MapJSonToSocialDream(JSonDream dream, SocialDreamEntity sde)
{
dream.Date
= sde.DateCreated.ToShortDateString();
dream.Narrative
= sde.Narrative;
dream.DreamId
= sde.DreamId;
dream.Title
= sde.Title;
dream.UserId
= sde.UserId;
dream.Interpretations
= sde.SocialDreamInterpretation.Count;
}

public void GetDreams(HttpContext context)
{
try

{
int userId = int.Parse(context.Request["userid"]);

Enhypniomancy.DreamDomain.CollectionClasses.SocialDreamCollection sdc
=
new SocialDreamCollection();

SD.LLBLGen.Pro.ORMSupportClasses.ISortExpression ise
=
new SD.LLBLGen.Pro.ORMSupportClasses.SortExpression(
(Enhypniomancy.DreamDomain.HelperClasses.SocialDreamFields.DateCreated
|

SD.LLBLGen.Pro.ORMSupportClasses.SortOperator.Descending));

sdc.GetMulti(
(Enhypniomancy.DreamDomain.HelperClasses.SocialDreamFields.UserId
== userId),15, ise);
JSonDreamList jdl
= new JSonDreamList();
jdl.Dreams
= new JSonDream[sdc.Count];

for(int i = 0; i < sdc.Count; i++)
{
JSonDream dream
= new JSonDream();
SocialDreamEntity sde
= sdc[i];
MapJSonToSocialDream(dream, sde);

jdl.Dreams[i]
= dream;
}

WriteToOutput(context, jdl);

}
catch(Exception exc)
{
WriteError(context,exc.ToString());
}
}


public bool IsReusable {
get {
return false;
}
}

}

The handler uses the Microsoft ASP.NET AJAX Framework to serialize objects into JSON. You'll notice the complete lack of security. People could do anything on behalf of anyone else by munging requests. For this to be a secure application, I would enable OAuth support on the server side. As soon as I have that done I'll update this blog post, but this article is really focused more on the client--JavaScript using the OpenSocial API.

OpenSocial JavaScript code on MySpace

Now on to the JavaScript! I'm not trying to wow anyone here with my JavaScript abilities. I do like my little 'renderView' system, which takes a set of divs and switches them on or off depending on what view you want to render, but it's been done better, I'm sure. Unfortunately (or fortunately, depending on your leanings), the app only works on FireFox. If I get it working on IE I'll send along an update. Below is the entirety of the application (you could cut and paste this into an app of your own and it would work):

working app 1 : Dream App JavaScript Source
<style type="text/css">

body
{
margin
: 0px;
background-color
: black;

}

#ui
{
background-position
: bottom;
background-color
: #000000;

width
: 430px;
height
: 430px;
color
: #FFFFFF;

background-image
:
url(http://www.enhypniomancy.com/images/DreamsTitle.jpg)
;
background-repeat
: no-repeat;
}


#nav
{
background-color
: #000066;
border-bottom-color
: #FF0000;

border-bottom-width
: thin;
border-bottom-style
: dashed;
}


a
{
color
: #FF00FF;
cursor
: pointer;

}

a:active
{
color
: #FF0000;
}


a:hover
{
background-color
: #00FFFF;
}

.view_Visible
{

width
: 100%;
height
: 100%;
display
: inline;

word-wrap
: normal;
}

.view_Invisible
{
display
: none;

}

#canvas
{
overflow
: auto;
border
: thin solid #800000;

height
: 85%;
margin
: 15px;
padding
: 5px;

}

#Text2
{
height
: 177px;
width
: 373px;

}

textarea
{
height
: 68px;
width
: 375px;

}

span
{
font-size
: small;
}


pre
{
font-family
: Arial;
font-size
: small;

font-style
: italic;
}
</style>
<div id="ui">

<div id="nav">
<a title="Dreams" onclick="renderView('dreams')">Dreams</a>

|
<a title="Enter Dream" onclick="renderView('newdream');">Enter Dream</a>
|
<a onclick="renderView('friends')">Friends' Dreams</a>

|
<a onclick="renderView('search');">Search</a>
|
<a onclick="renderView('help');">Help!</a>

</div>
<div id="canvas">
<div id="dreams" class="view_Invisible">

<div id="lastDreams">
</div>
</div>
<div id="friends" class="view_Invisible">

Friends
</div>
<div id="view" class="view_Invisible">
View dream
</div>

<div id="interpret" class="view_Invisible">
Interpret dream
<br/>
<p>

Title:
<span id="interpTitle"></span>
<br/>
Narrative:
<br/>

<span id="interpNarrative"></span>
</p>
<p>
Your interpretation:
<br/>

<textarea id="interpInput">
</textarea>
<br/>
<input type="button" value="Submit your interpretation"
onclick
="addInterp()" id="interpButton"/>

</p>
</div>
<div id="search" class="view_Invisible">

Search
</div>
<div id="help" class="view_Visible">
Welcome to the land of dream interpretations!
<br/>

<br/>
You can use this application to journal your dreams,
view your friends' dreams,
and interpret your friends' dreams.
<br/>
<br/>
You can also search through your and your friends' dreams.
Ever wonder if themes
are floating between you and your friends' dreams? Hmm?
<br/>

<br/>
Thought so.
</div>
<div id="newdream" class="view_Invisible">

Enter a new dream into your journal:
<br/>
Title:
<br/>
<input id="dreamTitle" type="text"/>

<br/>
Narrative:
<br/>
<textarea id="dreamNarrative">
</textarea>

<br/>
<br/>
<input id="button_EnterDream" type="button"
value
="Enter dream..." onclick="enterDream()"/>

</div>
<div id="waiting" class="view_Invisible">
<blink>

Working...
</blink>
</div>
</div>
<div id="error" class="view_Invisible">

Errors:
<br/>
</div>
</div>
<script type="text/javascript">


//####Helper functions####



function showError(errorMessage){
renderView(
"error");
var errorDiv = document.getElementById("error");
errorDiv.innerHTML
+= errorMessage + "<br />";
}


//####Globals and class definitions####




User.prototype.getDisplayHtml
= function(){
var response = "<img width=\"45px\" src=\"" +
this.UserThumbnail + "\" /><br /><a href=\"" +
this.UserProfileURL + "\">" +
this.UserName + "</a><br />";
return response;
}



var currentUserId = 0;
var friendIdList = new Array();
var friendDictionary = {};
var currentUser;
var friendList = {};
var viewerId;

function User(userId, userName, userThumnail, userProfileURL){
this.UserId = userId;
this.UserName = userName;
this.UserThumbnail = userThumnail;
this.UserProfileURL = userProfileURL;


}

function init(){
renderView(
"working");
populateUserData();
}

function populateUserData(){
//1: Create the DataRequest.

var dataRequest = opensocial.newDataRequest();

//2: Prepare RequestItems to be added to the DataRequest
var friendRequest =
dataRequest.newFetchPeopleRequest(
opensocial.DataRequest.Group.OWNER_FRIENDS);
var personRequest =
dataRequest.newFetchPersonRequest(
opensocial.DataRequest.PersonId.OWNER);
var viewerRequest =
dataRequest.newFetchPersonRequest(
opensocial.DataRequest.PersonId.VIEWER);
//3: Add the RequestItems to the DataRequest.

dataRequest.add(friendRequest);
dataRequest.add(personRequest);

//4: Send it along, and expect a function called responseCallback(dataResponse) to be executed when it's done.
dataRequest.send(populateUserData_Callback);
}

function populateUserData_Callback(dataResponse){

var errorHappened = dataResponse.hadError();
if (!errorHappened) {
var friendsData =
dataResponse.get(
opensocial.DataRequest.Group.OWNER_FRIENDS).getData();
var ownerData =
dataResponse.get(
opensocial.DataRequest.PersonId.OWNER).getData();

var viewer =
dataResponse.get(
opensocial.DataRequest.PersonId.VIEWER);

if (viewer != null) {
var viewerData =
dataResponse.get(
opensocial.DataRequest.PersonId.VIEWER).getData();
viewerId
=
viewerData.getField(opensocial.Person.Field.ID);
}
else {
viewerId
= ownerData.getField(opensocial.Person.Field.ID);
}
var ownerName =
ownerData.getField(opensocial.Person.Field.NAME);
var ownerId =
ownerData.getField(opensocial.Person.Field.ID);
var ownerURL =
ownerData.getField(opensocial.Person.Field.PROFILE_URL);
var ownerImage =
ownerData.getField(opensocial.Person.Field.THUMBNAIL_URL);

var ownerObj = new User(ownerId, ownerName, ownerImage, ownerURL);
currentUser
= ownerObj;
currentUserId
= ownerId;

friendsData.each(
function(friendData){
var friendName =
friendData.getField(opensocial.Person.Field.NAME);
var friendThumbnailUrl =
friendData.getField(opensocial.Person.Field.THUMBNAIL_URL);
var friendId =
friendData.getField(opensocial.Person.Field.ID);
var friendURL =
friendData.getField(opensocial.Person.Field.PROFILE_URL);

var friendObj =
new User(friendId, friendName, friendThumbnailUrl, friendURL);
friendDictionary[friendId]
= friendObj;
friendIdList.push(friendId);
});

//Let's shove the current user into the "friendDictionary" so we can look him/her up without silly if login

friendDictionary[currentUserId] = currentUser;


renderView(
"help");
}
else {
showError(
"Error on dataresponse");
}

}


//End OpenSocial integration point

//View management code

//Views assume that there is a <div> whose ID corresponds to the name of the view
var currentView = "dreams";
var views = new Array("dreams", "friends", "search", "help", "newdream", "view", "interpret", "waiting");

function switchOnWaitingView(message){

for (var i = 0; i < views.length; i++) {
var view = views[i];
document.getElementById(view).setAttribute(
"class", "view_Invisible");
}

var waitingDiv = document.getElementById("waiting")
waitingDiv.innerHTML
= "<blink>" + message + "</blink>";
waitingDiv.setAttribute(
"class", "view_Visible");
}

function switchOffWaitingView(){
var waitingDiv = document.getElementById("waiting")

waitingDiv.setAttribute(
"class", "view_Invisible");

for (var i = 0; i < views.length; i++) {
var view = views[i];
if (view == currentView)
document.getElementById(view).setAttribute(
"class", "view_Visible");
}

}


function renderView(name){
currentView
= name;

for (var i = 0; i < views.length; i++) {
var view = views[i];
if (view == name) {
document.getElementById(view)
.setAttribute(
"class", "view_Visible");
}
else {
document.getElementById(view)
.setAttribute(
"class", "view_Invisible");
}
}


//For a specific initialize function for a view, add it to the below. Some views do not

//require initialization because they do not grab data.
switch (name) {
case "dreams":
initializeView_dreams();
break;
case "friends":
initializeView_frienddreams();
break;
case "newdream":
initializeView_newdream();
break;
}


}



function initializeView_frienddreams(){
var intArray = '';
for (var i = 0; i < friendIdList.length; i++) {
intArray
+= friendIdList[i] + ",";
}
var finalIntArray = intArray.substr(0, intArray.length - 1);
var params = {};
params.friendidlist
= intArray;

getResponse(
"getfrienddreams",
initializeView_frienddreams_Callback, params);
}

function initializeView_frienddreams_Callback(response){
var friendDreamDiv = document.getElementById("friends");
friendDreamDiv.innerHTML
= "The following friends have posted dreams.";

var numPosted = 0;

for (var i = 0; i < response.Friends.size(); i++) {
friendDreamDiv.innerHTML
+= "<p>";
var dreamer = friendDictionary[response.Friends[i].UserId];

if (dreamer != null && response.Friends[i].Dreams.size() > 0)
{
friendDreamDiv.innerHTML
+=
dreamer.getDisplayHtml()
+ "<br />";

for (var j = 0; j < response.Friends[i].Dreams.size(); j++) {
friendDreamDiv.innerHTML
+=
renderDream(response.Friends[i].Dreams[j]);
}
numPosted
++;
}

}


if (numPosted == 0) {
friendDreamDiv.innerHTML
= "<br />None of your friends have posted dreams--encourage them to do so! Here's a list of them--go to their profiles and remind them :)<br/>";

for (var i = 0; i < friendIdList.length; i++) {
friendDreamDiv.innerHTML
+=
friendDictionary[friendIdList[i]].getDisplayHtml();
}
}



}

function initializeView_dreams(){
var params = {};
params.userid
= currentUserId;

getResponse(
"getdreams", initializeView_dreams_Callback, params);
}

function initializeView_dreams_Callback(response){
var numDreams = response.Dreams.size();
var dreamDiv = document.getElementById("lastDreams");

dreamDiv.innerHTML
= "";

if (numDreams == 0) {
dreamDiv.innerHTML
+= "<br />"
+ currentUser.UserName + " has no dreams in the system.<br />";
}
else
if (numDreams == 1) {
dreamDiv.innerHTML
+=
"<br />Displaying 1 dream of "
+ currentUser.UserName + "<br />";
}
else {
dreamDiv.innerHTML
+=
"<br />Displaying " + numDreams + " dream of "
+ currentUser.UserName + "<br />";
}

for (var i = 0; i < numDreams; i++) {
var dream = response.Dreams[i];
dreamDiv.innerHTML
+= renderDream(dream);
}
}

function renderDream(dream){

var response = "<span id=\"" + dream.DreamId + "\">" +

dream.Date
+
" : " +
dream.Title
+
" : " +

dream.Interpretations
+
" Interps " +
"(<a onclick=\"deleteDream(" +

dream.DreamId
+
")\">del</a>/<a onclick=\"newInterp(" +
dream.DreamId
+

")\">interp</a>/<a onclick=\"viewDream(" +
dream.DreamId
+
")\">view</a>)</span><br />";

return response;
}


function viewDream(dreamId){

renderView(
"view");
var params = {};
params.dreamid
= dreamId;
getResponse(
"getdream", viewDream_Callback, params);
}

function viewDream_Callback(response){
var viewDiv = document.getElementById("view");
viewDiv.innerHTML
= "";
viewDiv.innerHTML
+=
"<img width=\"200\" src=\"http://www.enhypniomancy.com/images/tiny_logo.jpg\" /><br />";

var dreamerHtml =
friendDictionary[response.Dreams[
0].UserId].getDisplayHtml();

viewDiv.innerHTML
+= dreamerHtml;
viewDiv.innerHTML
+= "Date: " + response.Dreams[0].Date + "<br />";
viewDiv.innerHTML
+= "Title: " + response.Dreams[0].Title + "<br />";
viewDiv.innerHTML
+= "Narrative: <br/>";
viewDiv.innerHTML
+= "<p><i> " + response.Dreams[0].Narrative + "</i></p>";

if (response.Dreams[0].Interps.size() == 0) {
viewDiv.innerHTML
+= "<p><a onclick=\"newInterp(" + response.Dreams[0].DreamId + ")\">No interpretations yet! Click here to interpret this dream.</a></p>";
}
else {
viewDiv.innerHTML
+= "<img width=\"200\" src=\"http://www.enhypniomancy.com/images/post_interpretation.jpg\" />";

for (var i = 0; i < response.Dreams[0].Interps.size(); i++) {
var dreamInterpreter =
friendDictionary[response.Dreams[
0].Interps[i].UserId];

if (dreamInterpreter != null) //If we have an interpretation without an interpreter, they are probably not friends any more.

{
viewDiv.innerHTML
+= "<p>";
viewDiv.innerHTML
+= dreamInterpreter.getDisplayHtml() + "<br />";
viewDiv.innerHTML
+= "Date: " + response.Dreams[0].Interps[i].InterpretationDate + "<br />";
viewDiv.innerHTML
+= "Interpretation: <br />";
viewDiv.innerHTML
+= "<i>" + response.Dreams[0].Interps[i].Interpretation + "</i>";
viewDiv.innerHTML
+= "</p>";

}
}

}

}

function newInterp(dreamId){
var params = {};
params.dreamid
= dreamId;

getResponse(
"getdream", newInterp_Callback, params);
}

function newInterp_Callback(response){
renderView(
"interpret");

document.getElementById(
"interpInput").value = "";
document.getElementById(
"interpTitle")
.innerHTML
= response.Dreams[0].Title;
document.getElementById(
"interpNarrative")
.innerHTML
= "<i>" + response.Dreams[0].Narrative + "</i>";

document.getElementById(
"interpButton")
.setAttribute(
"onclick", "addInterp(" + response.Dreams[0].DreamId + ")");
document.getElementById(
"interpButton").value =
"Click to add interpretation";


}

function addInterp(dreamId){
document.getElementById(
"interpButton").value = "Submitting...";

var interpText = document.getElementById("interpInput").value;

var params = {};
params.dreamid
= dreamId;
params.userid
= viewerId;
params.interpretation
= interpText;
tempdreamid
= dreamId;
getResponse(
"addinterpretation", addInterp_Callback, params);

}

var tempdreamid = 0;

function addInterp_Callback(response){
document.getElementById(
"interpButton")
.value
= "Interpretation added. Click to see the dream.";
document.getElementById(
"interpButton")
.setAttribute(
"onclick", "viewDream(" + tempdreamid + ")");
}

function deleteDream(dreamId){
if (confirm("Are you sure you wish to delete this dream?")) {
var params = {};
params.dreamid
= dreamId;
getResponse(
"deletedream", deleteDream_Callback, params);
}
}

function deleteDream_Callback(response){
renderView(
"dreams");
}



function trace(message){


document.getElementById(
"messageDiv").innerHTML += message + "<br />";
}

function startThisUp(){
//var dataRequest = opensocial.newDataRequest();

renderView("dreams");

alert(
"hi!");

trace(
"Starting...");

var id = document.getElementById("dreamIdText").value;
trace(
"Getting dream with id " + id);


trace(
"Hello world called");
}

function helloWorldSuccess(response){
trace(
"Success callback");
}

function helloWorldFailure(response){
trace(
"fail callback");

}



function initializeView_newdream(){
document.getElementById(
"dreamTitle").value = "";
document.getElementById(
"dreamNarrative").value = "";
document.getElementById(
"button_EnterDream")
.value
= "Click to enter dream";
document.getElementById(
"button_EnterDream")
.disabled
= false;
}

function enterDream(){
var titleText = document.getElementById("dreamTitle").value;
var narrativeText = document.getElementById("dreamNarrative").value;

var button = document.getElementById("button_EnterDream");
button.disabled
= true;
button.value
= "Submitting...";

var params = {};
params.userid
= viewerId;
params.title
= titleText;
params.narrative
= narrativeText;

getResponse(
"adddream", enterDream_Callback, params);


}

function enterDream_Callback(response){
renderView(
"dreams");
}


function getResponse(command, callback, params){
params.random
= Math.random();
var url = "http://www.enhypniomancy.com/Soc.ashx?command=" + command;

for (var param in params) {
url
+= "&" + param + "=" + encodeURIComponent(params[param]);
}

var url2 = url;
os_params
= {};




switchOnWaitingView(
"Executing makeRequest for external url and waiting for resopnse...");
opensocial.makeRequest(url2, makeRequest_Callback, os_params);

function makeRequest_Callback(data){
var jsonedResponseObject = data.responseText.evalJSON();

if (jsonedResponseObject.HadError == true) {
showError(
"Error talking to server: "
+ jsonedResponseObject.ErrorMessage);
}

switchOffWaitingView();
callback(jsonedResponseObject);
}
}




init();

</script>

Let's call out some important pieces. When you start up the app, it calls the init() function, which in turn grabs the OpenSocial-centric data necessary for the application to work. It needs the data of the Owner (the user who installed the app) and the Owner's friends (the friends of the user who installed the app). Below is a call-out of the code that does all the OpenSocial data munging. You'll notice that I map the OpenSocial Person objects to my own User object--I actually built this entire application without OpenSocial, and took about an hour to integrate. I think that's a good practice and also shows the strength of the platform, which is that it is very much just another JavaScript library you import, while you focus on writing your app in the exact way you want.

code snippet 2 : Initialization Calls for Owner and Owner's Friends
var currentUserId = 0;
var friendIdList = new Array();

var friendDictionary = {};
var currentUser;
var friendList = {};
var viewerId;


function User(userId, userName, userThumnail, userProfileURL)
{
this.UserId = userId;
this.UserName = userName;
this.UserThumbnail = userThumnail;
this.UserProfileURL = userProfileURL;


}


function init()
{
renderView(
"working");
populateUserData();
}

function populateUserData()
{
//1: Create the DataRequest.
var dataRequest = opensocial.newDataRequest();

//2: Prepare RequestItems to be added to the DataRequest

var friendRequest = dataRequest.newFetchPeopleRequest(opensocial.DataRequest.Group.OWNER_FRIENDS);
var personRequest = dataRequest.newFetchPersonRequest(opensocial.DataRequest.PersonId.OWNER);
var viewerRequest = dataRequest.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER);
//3: Add the RequestItems to the DataRequest.

dataRequest.add(friendRequest);
dataRequest.add(personRequest);

//4: Send it along, and expect a function called responseCallback(dataResponse) to be executed when it's done.
dataRequest.send(populateUserData_Callback);
}

function populateUserData_Callback(dataResponse)
{

var errorHappened = dataResponse.hadError();
if(!errorHappened)
{
var friendsData = dataResponse.get(opensocial.DataRequest.Group.OWNER_FRIENDS).getData();
var ownerData = dataResponse.get(opensocial.DataRequest.PersonId.OWNER).getData();

var viewer = dataResponse.get(opensocial.DataRequest.PersonId.VIEWER);

if (viewer != null) {
var viewerData = dataResponse.get(opensocial.DataRequest.PersonId.VIEWER).getData();
viewerId
= viewerData.getField(opensocial.Person.Field.ID);
}
else{
viewerId
= ownerData.getField(opensocial.Person.Field.ID);
}
var ownerName = ownerData.getField(opensocial.Person.Field.NAME);
var ownerId = ownerData.getField(opensocial.Person.Field.ID);
var ownerURL = ownerData.getField(opensocial.Person.Field.PROFILE_URL);
var ownerImage = ownerData.getField(opensocial.Person.Field.THUMBNAIL_URL);

var ownerObj = new User(ownerId,ownerName,ownerImage,ownerURL);
currentUser
= ownerObj;
currentUserId
= ownerId;

friendsData.each(
function(friendData) {
var friendName = friendData.getField(opensocial.Person.Field.NAME);
var friendThumbnailUrl = friendData.getField(opensocial.Person.Field.THUMBNAIL_URL);
var friendId = friendData.getField(opensocial.Person.Field.ID);
var friendURL = friendData.getField(opensocial.Person.Field.PROFILE_URL);

var friendObj = new User(friendId,friendName,friendThumbnailUrl,friendURL);
friendDictionary[friendId]
= friendObj;
friendIdList.push(friendId);
}
);

//Let's shove the current user into the "friendDictionary" so we can look him/her up without silly if login

friendDictionary[currentUserId] = currentUser;


renderView(
"help");
}
else
{
showError(
"Error on dataresponse");
}

}

The above code initializes the app. It loads information about the Owner, the Viewer, and the Owner's friends. It then maps them to a custom User object and stores the users as a collection that can be accessed by the rest of the application in a manner that is decoupled from OpenSocial-specific calls.

The above code is one half of the OpenSocial interaction in this app. The other half uses the OpenSocial client-to-third-party-server API call: opensocial.makeRequest(). In the below code I'm using a wrapper around makeRequest, so as far as my code is concerned I'm making a simple AJAX call. (In fact, before I integrated OpenSocial into this app I was using Prototype's AJAX method.)

code snippet 3 : makeRequest() wrapper
function getResponse(command,callback,params)
{
params.random
= Math.random();
var url = "http://www.enhypniomancy.com/Soc.ashx?command=" + command;

for(var param in params)
{
url
+= "&" + param + "=" + encodeURIComponent(params[param]);
}

var url2 = url;
os_params
= {};




switchOnWaitingView(
"Executing makeRequest for external url and waiting for resopnse...");
opensocial.makeRequest(url2,makeRequest_Callback,os_params);

function makeRequest_Callback(data)
{
var jsonedResponseObject = data.responseText.evalJSON();

if(jsonedResponseObject.HadError == true)
{
showError(
"Error talking to server: " + jsonedResponseObject.ErrorMessage);
}

switchOffWaitingView();
callback(jsonedResponseObject);
}
}

The above code allows you to pass a bucket of parameters that will be translated into a single opensocial.makeRequest() call. Following is an example of code that calls it:

code snippet 4 : Javascript that calls makeRequest wrapper
function initializeView_newdream()
{
document.getElementById(
"dreamTitle").value = "";
document.getElementById(
"dreamNarrative").value = "";
document.getElementById(
"button_EnterDream").value = "Click to enter dream";
document.getElementById(
"button_EnterDream").disabled = false;
}

function enterDream()
{
var titleText = document.getElementById("dreamTitle").value;
var narrativeText = document.getElementById("dreamNarrative").value;

var button = document.getElementById("button_EnterDream");
button.disabled
= true;
button.value
= "Submitting...";

var params = {};
params.userid
= viewerId;
params.title
= titleText;
params.narrative
= narrativeText;

getResponse(
"adddream",enterDream_Callback,params);


}

function enterDream_Callback(response)
{
renderView(
"dreams");
}

What you will see under the covers when you invoke this code is a request that goes to http://api.msappsapce.com/relay.proxy. This will then return the response from your own site. The rest of the app is custom UI. Put it all together, and you have yourself a dream interpretation app!

Published Sunday, February 24, 2008 1:53 AM by Chris
Filed under:

Comments

# re: Example App: Dream Interpretation

Thursday, March 06, 2008 5:00 AM by Muhammad

Thanks for writing this article its great help

# re: Example App: Dream Interpretation

Wednesday, June 04, 2008 4:47 PM by savelady2

wat do my dreams say?

# re: Example App: Dream Interpretation

Tuesday, March 03, 2009 1:20 AM by uncle tron

Thank you, I understand how it works now.

Powered by Community Server (Commercial Edition), by Telligent Systems