What's New in MySpace OpenSocial 0.8 - Part 1
opensocial.Person and JSON application data
[
Part 1 |
Part 2 |
Part 3]
Published January 8, 2008 by Chad Russell.
The spec for OpenSocial 0.8 has been finalized and MySpace’s implementation has been released. There are some key benefits that 0.8 adds over 0.7 on the MySpace platform. The JavaScript container more closely conforms to the spec and some MySpace specific extensions have been pulled into the OpenSocial namespace. The result of this is that your 0.8 app will port much more easily to other platforms than your 0.7 app did. The app data API has been cleaned up and made more useful.
There are also important new features and capabilities on the MySpace platform that are only available to 0.8 apps:
- Open Canvas allows your app to be functional for users even before they have added the app. Learn more about Open Canvas.
- Custom App Activities allow 0.8 apps to post activities into user Friend Updates.
- App Notifications will allow apps to message their users directly, and will only be available on 0.8.
Introduction
This is the first in a series of articles intended to demonstrate the new functionality available in the OpenSocial 0.8 container on MySpace. My assumptions going into this article are:
- You're familiar with OpenSocial.
- You have an application written in 0.7 that you want to port to 0.8, or are interested in the latest and greatest features of OpenSocial.
- You have knowledge of JavaScript, HTML and CSS.
In this series I'll walk you through the process of creating an application from start to finish with emphasis on the six major improvements that distinguish version 0.8 from 0.7 on MySpace:
- Enhancements to the opensocial.Person object
- Our new permission model, dubbed Open Canvas, that makes extensive use of opensocial.hasPermission and opensocial.requestPermission
- Updates to application data to accept only valid JSON
- The deprecation of PostTo in favor of opensocial.requestShareApp and opensocial.requsetSendMessage
- Activities
- App Notifications
Application Creation
Actual application creation hasn't changed much, so this will be brief. The only difference between creating a 0.7 application and a 0.8 application is the version drop down selection found on the Edit App Information tab.
Figure 1 – The OpenSocial Version drop down
So make sure you have 0.8 selected in the drop down when you save.
Coding Your Application
Purpose of the Application
The application I'm creating will essentially display the profile on the home page. Not terribly useful I know, but it suits my needs as I'll be able to pull in lots of information, including all of the profile data, the friends list and picture albums as the application grows throughout the three articles.
opensocial.Person
The first thing I'll do is get as much information on the viewer as possible by using the new opensocial.Person object.
The process of requesting a person object has changed only slightly. Instead of using the enums opensocial.DataRequest.PersonId.VIEWER and opensocial.DataRequest.PersonId.OWNER, you'll use opensocial.IdSpec.PersonId.VIEWER and opensocial.IdSpec.PersonId.OWNER respectively.
The other difference is which Person fields are supported. We've eliminated most of the MyOpenSpace extended fields to more closely match the OpenSocial spec. In the code below I show how to use the new enum in addition to the Person fields we support. The fields array contains all supported Person fields supported by MySpace.
// the entry point for the app, fired from the onload handler.
function init(){
// create the opensocial.DataRequest object.
var request = opensocial.newDataRequest();
// create a list of Person fields to fetch,
// in this case, include all supported fields.
var fields = [opensocial.Person.Field.ABOUT_ME,
opensocial.Person.Field.AGE,
opensocial.Person.Field.BODY_TYPE,
opensocial.Person.Field.BOOKS,
opensocial.Person.Field.CHILDREN,
opensocial.Person.Field.CURRENT_LOCATION,
opensocial.Person.Field.DATE_OF_BIRTH,
opensocial.Person.Field.DRINKER,
opensocial.Person.Field.ETHNICITY,
opensocial.Person.Field.GENDER,
opensocial.Person.Field.HAS_APP,
opensocial.Person.Field.HEROES,
opensocial.Person.Field.ID,
opensocial.Person.Field.INTERESTS,
opensocial.Person.Field.JOBS,
opensocial.Person.Field.LOOKING_FOR,
opensocial.Person.Field.MOVIES,
opensocial.Person.Field.MUSIC,
opensocial.Person.Field.NAME,
opensocial.Person.Field.NETWORK_PRESENCE,
opensocial.Person.Field.NICKNAME,
opensocial.Person.Field.PROFILE_SONG,
opensocial.Person.Field.PROFILE_URL,
opensocial.Person.Field.RELATIONSHIP_STATUS,
opensocial.Person.Field.RELIGION,
opensocial.Person.Field.SEXUAL_ORIENTATION,
opensocial.Person.Field.SMOKER,
opensocial.Person.Field.STATUS,
opensocial.Person.Field.THUMBNAIL_URL,
opensocial.Person.Field.TV_SHOWS,
opensocial.Person.Field.URLS,
MyOpenSpace.Person.Field.MEDIUM_IMAGE,
MyOpenSpace.Person.Field.LARGE_IMAGE];
// add the supported fields to the parameter list.
var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = fields;
// add the fetch person request to the queue.
request.add(request.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER, params), "the_viewer");
// start executing the requests, call back into got_init when done.
request.send(got_init);
}
Most of the changes introduced in 0.8 have to do with how to parse out all the data from the Person object. In 0.7 on the MySpace platform we typically returned more data to you than you asked for, so in 0.8 we try to return only what you requested. If you request relationship status and movies you should only get relationship status and movies in the response. Note that id, profile URL, thumbnail URL and name are always returned.
We also typically returned most Person fields back to you as strings in 0.7. In 0.8 there are three ways in which we return data: strings, objects and enums. In the code below I demonstrate how to parse all three types of data from a Person object.
// parses the fields of the current owner and outputs it to the page
function drawPerson(){
// thumbnail url is a string.
document.getElementById("image").src =
person.getField(opensocial.Person.Field.THUMBNAIL_URL);
// id is a string of format 'myspace.com:6221'.
document.getElementById("image").alt = person.getId();
// status is a string.
document.getElementById("headline").innerHTML =
"\"" + person.getField(opensocial.Person.Field.STATUS) + "\"";
var details_div = document.getElementById("details");
// getDisplayName is a shortcut, this data is also found
// under the name field:
// var name =
// person.getField(opensocial.Person.Field.NAME);
// details_div.innerHTML =
// name.getField(opensocial.Name.Field.UNSTRUCTURED);
details_div.innerHTML = person.getDisplayName() + "<br/>";
details_div.innerHTML +=
person.getField(opensocial.Person.Field.AGE) + " years old<br/>";
// the date of birth field is a JavaScript Date object.
var dob = person.getField(opensocial.Person.Field.DATE_OF_BIRTH);
details_div.innerHTML += "Born: " + dob.toDateString() + "<br/>";
// current location is an opensocial.Address object.
var current_location =
person.getField(opensocial.Person.Field.CURRENT_LOCATION);
details_div.innerHTML +=
current_location.getField(opensocial.Address.Field.REGION);
details_div.innerHTML += " ";
details_div.innerHTML +=
current_location.getField(opensocial.Address.Field.POSTAL_CODE);
details_div.innerHTML += " ";
details_div.innerHTML +=
current_location.getField(opensocial.Address.Field.COUNTRY);
details_div.innerHTML += "<br/>";
// gender is an opensocial.Enum.
var gender = person.getField(opensocial.Person.Field.GENDER);
details_div.innerHTML += gender.getDisplayValue() + "<br/>";
// network presence is an opensocial.Enum.
var network_presence = person.getField(opensocial.Person.Field.NETWORK_PRESENCE);
details_div.innerHTML += network_presence.getDisplayValue() + " ";
// match the enum key to determine the online/offline status.
if(network_presence.getKey() === opensocial.Enum.Presence.ONLINE){
details_div.innerHTML += ":-)";
}
else if(network_presence.getKey() === opensocial.Enum.Presence.OFFLINE){
details_div.innerHTML += ":-(";
}
}
Strings
THUMBNAIL_URL, STATUS, ID and other strings should be familiar to you. They are all still strings and haven't changed much in terms of parsing the data out of the response. One difference to note is that ID now has the prefix "myspace:", so if your ID was “6221” in 0.7, it'll be “myspace:6221” in 0.8.
Objects
Next is the DATE_OF_BIRTH field and is returned as an object of type Date, which is a built-in class in JavaScript.
>p>I then parse out CURRENT_LOCATION. This field returns an object of type opensocial.Address. Most objects in OpenSocial behave the way Person and Address behave: they have a list of fields and those fields are accessed via a call to getField. Here I get the Address object from the Person object by calling getField on the Person. I then in turn call getField on the Address object to extract the data from it.
Other types of objects that are returned are:
- opensocial.BodyType from the field opensocial.Person.Field.BODY_TYPE
- opensocial.Name from the field opensocial.Person.Field.NAME
- opensocial.Organization from the field opensocial.Person.Field.JOBS
- opensocial.Url from the fields MyOpenSpace.Person.Field.MEDIUM_IMAGE and MyOpenSpace.Person.Field.LARGE_IMAGE.
Enums
The last type of data type is the enum. Enums are returned as opensocial.Enum objects. Some examples are: GENDER, NETWORK_PRESENCE and SMOKER.
There are two bits of information returned with an enum: the display value and the key. The display value can be any text the container decides and is just used for display purposes. The key will match up to the corresponding enum, in this case opensocial.Enum.Presence, and is used for comparisons like the one I make above. So the above code might output "online :-)".
JSON application data
Application data is a way to store data using a key/value pair that is specific to an application. In this application I'm using it to save custom style data. The user is able to select from a list of background and text colors, and that selection is then saved in application data. When the application loads I pull that information back out and style the application accordingly.
There are three main differences with application data between 0.7 and 0.8 on MySpace. Two are fairly minor and one fairly major. The first minor change again has to do with the enum used to specify an ID. In 0.8 the concept of "viewer friends" and "owner friends" has been replaced by the opensocial.IdSpec object. With an IdSpec you need to specify two things: the user ID and the network distance.
// create the parameters for the idspec to send into the fetch application data request.
params = {};
params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER;
params[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0;
// create the idspec.
var idspec = opensocial.newIdSpec(params);
The user ID will be either opensocial.IdSpec.PersonId.VIEWER or opensocial.IdSpec.PersonId.OWNER. A NETWORK_DISTANCE of 0 refers to the owner or viewer directly. A NETWORK_DISTANCE of 1 would mean the viewer's or owner's friends, 2 would be the viewer's or owner's friend's friends, and so on. MySpace only supports a 0 or 1, so that means only the owner, viewer or their friends. In the code above I'm creating an IdSpec that specifies the viewer.Another minor change has to do with how the returned data is HTML escaped.
// create the parameters for the fetch app data request, the response is HTML
// escaped by default, but let's set it explicitly here.
params = {};
params[opensocial.DataRequest.DataRequestFields.ESCAPE_TYPE] = opensocial.EscapeType.HTML_ESCAPE;
// add the fetch app data request to the queue.
request.add(request.newFetchPersonAppDataRequest(idspec, "style", params), "app_data");
HTML_ESCAPE is the default and will cause any strings in the response to be HTML escaped. It's highly recommended to use HTML_ESCAPE to avoid any script injection exploits in your app, something your users would definitely not be happy about.
The one major change is the enforcement of valid JSON when storing and fetching application data. That means when you update application data the value you pass in must be valid JSON. When you fetch application data, the response will be a JavaScript object created from the JSON.
So what exactly is valid JSON, you ask? For the nitty-gritty details see json.org, which defines the JSON specification. Essentially it boils down to three things:
- Objects in JavaScript are typically valid JSON, especially if they contain things like strings, numbers, arrays and other objects.
- A string can be valid JSON if it can be converted into an object using eval() or gadgets.json.parse(), so something like this would count: "{'background':'blue','text':'green'}".
- A regular old string is not valid JSON, but it is if you wrap an extra set of quotes around it.
- var str1 = "'json string'"; // str1 is valid json
- var str2 = '"json string"'; // str2 is valid json
- var str3 = "json string"; // str3 is not valid json
- var str4 = 'json string'; // str4 is not valid json
Let's look at some code. In the application I create an object named Style anduse this object to track the current style values.
var Style = function(background, text){
this._style.background = background;
this._style.text = text;
};
Style.prototype = {
_style:{
background:null,
text:null
},
getBackground:function(){
return this._style.background;
},
getText:function(){
return this._style.text;
},
setBackground:function(background, update){
document.body.style.backgroundColor = background;
this._style.background = background;
if(update){
this._updateStyle();
}
},
setText:function(text, update){
document.body.style.color = text;
this._style.text = text;
if(update){
this._updateStyle();
}
},
_updateStyle:function(){
var request = opensocial.newDataRequest();
request.add(request.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, "style", this._style));
request.send();
}
};
Style contains another object, _style. This is valid JSON and is used as a parameter that is passed into newUpdatePersonAppDataRequest.
request.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, "style", this._style)
The object is passed in directly, which really simplifies things. When I then fetch the app data the response contains the same object.
// the callback function for the initial data requests,
// 'response' is the opensocial.DataResponse object.
function got_init(response){
// retrieve the opensocial.ResponseItem object.
var ri = response.get("app_data");
// pull out the app data response,
// this is indexed by user ID.
var app_data_map = ri.getData();
// use the viewer ID to access the correct object,
// this is indexed by the app data key.
var app_data = app_data_map[gadgets.views.getParams().viewerId];
// the actual data we want is indexed by the key
// "style", so let's get the Style object.
// this whole process would normally be done by doing:
// var style = response.get("app_data").getData()[
// gadgets.views.getParams().viewerId].style;
// but is broken up here for the sake of
// explanation and demonstration.
var style_data = app_data.style;
// set the colors to the saved versions if any exist.
if(style_data && style_data.background){
style.setBackground(style_data.background, false);
style.setText(style_data.text, false);
}
}
So the flow becomes: pass object in when updating, get object back when fetching. No more ugly string parsing or fighting with the eval function.
A Note on Checking for Errors
I typically have close to a dozen apps installed on my home and profile pages and I have noticed that I often get JavaScript errors from apps. I think the main cause of this is that some application developers don't expect errors to occur; but in reality, errors can and do happen, and you should be prepared for them.
OpenSocial provides several functions to deal with errors. In the application I check for errors. If an error is found, I don't try and parse the response for data.
// create flags to use so we don't try to parse a response that had an error.
var parse_person = true;
var parse_appdata = true;
// first check if an error occurred.
if(!response){
parse_appdata = false;
parse_person = false;
}
else if(response.hadError()){
var the_viewer = response.get("the_viewer");
var app_data = response.get("app_data");
if(!the_viewer){
parse_person = false;
}
else if(the_viewer.hadError()){
parse_person = false;
if(the_viewer.getErrorCode() === opensocial.ResponseItem.Error.INTERNAL_ERROR){
// retry the request?
}
}
if(!app_data){
parse_appdata = false;
}
else if(app_data.hadError()){
parse_appdata = false;
if(app_data.getErrorCode() === opensocial.ResponseItem.Error.INTERNAL_ERROR){
// retry the request?
}
}
}
First I use opensocial.DataResponse.hadError which will tell me if any of my requests failed. If an error did occur I attempt to determine which request failed. If only one failed I can still parse the response from the other one.
To do this I use opensocial.ResponseItem.hadError on both response items. From there I set a flag that prevents the application from parsing that particular response. If I detect that an INTERNAL_ERROR occurred, I may retry that request. An INTERNAL_ERROR can happen due to intermittent issues such as network congestion.
This approach has two benefits:
- The code only accesses objects when they are present, avoiding JavaScript errors.
- You can sometimes recover from the error by retrying the request. Just make sure to maintain a retry count so you don't enter an infinite loop here, which I've seen as well.
Full Application Code Listing
You can install the application in its current state from here: http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&friendid=422742687 Below you'll find the full code listing for the application up to this point. I'll be adding onto this application in future articles. Thanks and happy coding!
Example: Complete Application Source
<style>
body
{
margin:0;
padding:0;
color:#000;
background-color:#fff;
}
img
{
border:0;
}
#outer
{
width:280px;
font-family:Verdana;
font-size:0.7em;
padding:5px;
}
.image_links a
{
cursor:pointer;
color:Red;
}
#headline
{
font-size:1.1em;
font-weight:bold;
}
#image
{
padding:5px;
}
#content
{
display:none;
}
#spinner
{
color:Red;
font-size:1.3em;
}
#extras_content
{
display:none;
margin:10px 5px;
padding:5px;
border:solid 1px black;
}
#extras div
{
margin-bottom:10px;
}
.divider
{
margin-bottom:5px;
border-bottom:solid 1px black;
}
.float
{
float:left;
}
.clear
{
clear:both;
}
.image_links
{
padding-bottom:5px;
}
.color_label
{
margin:6px;
}
.color_box
{
width:20px;
height:20px;
border:solid 1px black;
margin:3px;
cursor:pointer;
}
.blue
{
background-color:#09f;
}
.pink
{
background-color:#fcf;
}
.white
{
background-color:#fff;
}
.green
{
background-color:#6c0;
}
.black
{
background-color:#000;
}
.orange
{
background-color:#f90;
}
.red
{
background-color:#c03;
}
.purple
{
background-color:#96f;
}
</style>
<div id="outer">
<div id="spinner">
<img src="http://x.myspacecdn.com/modules/common/static/img/loadercircles.gif" />
Loading...
</div>
<div id="content">
<div class="divider">
<div id="headline" class="divider"></div>
<div class="float">
<center class="image_links">
<a href="javascript:launchProfile();"><img id="image" src="http://x.myspacecdn.com/images/no_pic.gif" alt="" /></a><br />
<a href="javascript:showExtraContent(MyOpenSpace.Person.Field.MEDIUM_IMAGE);">[M]</a>
<a href="javascript:showExtraContent(MyOpenSpace.Person.Field.LARGE_IMAGE);">[L]</a>
</center>
</div>
<div id="details"></div>
<div class="clear"></div>
</div>
<div id="extras" class="divider">
<div>
<a href="javascript:showExtraContent(opensocial.Person.Field.ABOUT_ME);">About Me</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.BODY_TYPE);">Body Type</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.BOOKS);">Books</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.CHILDREN);">Children</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.DRINKER);">Drinker</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.ETHNICITY);">Ethnicity</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.HEROES);">Heroes</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.INTERESTS);">Interests</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.JOBS);">Jobs</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.LOOKING_FOR);">Looking For</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.MOVIES);">Movies</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.MUSIC);">Music</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.PROFILE_SONG);">Profile Song</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.RELATIONSHIP_STATUS);">Relationship Status</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.RELIGION);">Religion</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.SEXUAL_ORIENTATION);">Sexual Orientation</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.SMOKER);">Smoker</a> |
<a href="javascript:showExtraContent(opensocial.Person.Field.TV_SHOWS);">TV Shows</a>
</div>
<div id="extras_content"></div>
</div>
<div>
<div class="float color_label">Background:</div>
<div class="float color_box blue" onclick="pickColor('bg', '09f');"></div>
<div class="float color_box pink" onclick="pickColor('bg', 'fcf');"></div>
<div class="float color_box white" onclick="pickColor('bg', 'fff');"></div>
<div class="float color_box green" onclick="pickColor('bg', '6c0');"></div>
<div class="clear"></div>
<div class="float color_label">Text:</div>
<div class="float color_box black" onclick="pickColor('txt', '000');"></div>
<div class="float color_box orange" onclick="pickColor('txt', 'f90');"></div>
<div class="float color_box red" onclick="pickColor('txt', 'c03');"></div>
<div class="float color_box purple" onclick="pickColor('txt', '96f');"></div>
<div class="clear"></div>
</div>
</div>
</div>
<script>
// the entry point for the app, fired from the onload handler.
function init(){
// create the opensocial.DataRequest object.
var request = opensocial.newDataRequest();
// create a list of Person fields to fetch,
// in this case, include all supported fields.
var fields = [opensocial.Person.Field.ABOUT_ME,
opensocial.Person.Field.AGE,
opensocial.Person.Field.BODY_TYPE,
opensocial.Person.Field.BOOKS,
opensocial.Person.Field.CHILDREN,
opensocial.Person.Field.CURRENT_LOCATION,
opensocial.Person.Field.DATE_OF_BIRTH,
opensocial.Person.Field.DRINKER,
opensocial.Person.Field.ETHNICITY,
opensocial.Person.Field.GENDER,
opensocial.Person.Field.HAS_APP,
opensocial.Person.Field.HEROES,
opensocial.Person.Field.ID,
opensocial.Person.Field.INTERESTS,
opensocial.Person.Field.JOBS,
opensocial.Person.Field.LOOKING_FOR,
opensocial.Person.Field.MOVIES,
opensocial.Person.Field.MUSIC,
opensocial.Person.Field.NAME,
opensocial.Person.Field.NETWORK_PRESENCE,
opensocial.Person.Field.NICKNAME,
opensocial.Person.Field.PROFILE_SONG,
opensocial.Person.Field.PROFILE_URL,
opensocial.Person.Field.RELATIONSHIP_STATUS,
opensocial.Person.Field.RELIGION,
opensocial.Person.Field.SEXUAL_ORIENTATION,
opensocial.Person.Field.SMOKER,
opensocial.Person.Field.STATUS,
opensocial.Person.Field.THUMBNAIL_URL,
opensocial.Person.Field.TV_SHOWS,
opensocial.Person.Field.URLS,
MyOpenSpace.Person.Field.MEDIUM_IMAGE,
MyOpenSpace.Person.Field.LARGE_IMAGE];
// add the supported fields to the parameter list.
var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = fields;
// add the fetch person request to the queue.
request.add(request.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER, params), "the_viewer");
// create the parameters for the idspec to send into the fetch app data request.
params = {};
params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER;
params[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0;
// create the idspec.
var idspec = opensocial.newIdSpec(params);
// create the parameters for the fetch app data request, the response is HTML
// escaped by default, but let's set it explicitly here.
params = {};
params[opensocial.DataRequest.DataRequestFields.ESCAPE_TYPE] = opensocial.EscapeType.HTML_ESCAPE;
// add the fetch app data request to the queue.
request.add(request.newFetchPersonAppDataRequest(idspec, "style", params), "app_data");
// start executing the requests, call back into got_init when done.
request.send(got_init);
}
// the callback function for the initial data requests, 'response' is the opensocial.DataResponse object.
function got_init(response){
// create flags to use so we don't try to parse a response that had an error.
var parse_person = true;
var parse_appdata = true;
// first check if an error occurred.
if(!response){
parse_appdata = false;
parse_person = false;
}
else if(response.hadError()){
var the_viewer = response.get("the_viewer");
var app_data = response.get("app_data");
if(!the_viewer){
parse_person = false;
}
else if(the_viewer.hadError()){
parse_person = false;
if(the_viewer.getErrorCode() === opensocial.ResponseItem.Error.INTERNAL_ERROR){
// retry the request?
}
}
if(!app_data){
parse_appdata = false;
}
else if(app_data.hadError()){
parse_appdata = false;
if(app_data.getErrorCode() === opensocial.ResponseItem.Error.INTERNAL_ERROR){
// retry the request?
}
}
}
if(parse_appdata){
// retrieve the opensocial.ResponseItem object.
var ri = response.get("app_data");
// pull out the app data response, this is indexed by user ID.
var app_data_map = ri.getData();
// use the viewer ID to access the correct object, this is indexed by the app data key.
var app_data = app_data_map[gadgets.views.getParams().viewerId];
// the actual data we want is indexed by the key "style", so let's get the Style object.
// this whole process would normally be done by doing:
// var style = response.get("app_data").getData()[gadgets.views.getParams().viewerId].style;
// but is broken up here for the sake of explanation and demonstration.
var style_data = app_data.style;
// set the colors to the saved versions if any exist.
if(style_data && style_data.background){
style.setBackground(style_data.background, false);
style.setText(style_data.text, false);
}
}
if(parse_person){
// get the opensocial.Person object from the other opensocial.ResponseItem and save it to a global variable.
person = response.get("the_viewer").getData();
drawPerson();
}
// hide the spinner and show the content now that the DOM has been populated.
document.getElementById("spinner").style.display = "none";
document.getElementById("content").style.display = "block";
// make the iframe fit the new content.
gadgets.window.adjustHeight();
}
// parses the fields of the current owner and outputs it to the page
function drawPerson(){
// thumbnail url is a string.
document.getElementById("image").src =
person.getField(opensocial.Person.Field.THUMBNAIL_URL);
// id is a string of format 'myspace.com:6221'.
document.getElementById("image").alt = person.getId();
// status is a string.
document.getElementById("headline").innerHTML =
"\"" + person.getField(opensocial.Person.Field.STATUS) + "\"";
var details_div = document.getElementById("details");
// getDisplayName is a shortcut, this data is also found
// under the name field:
// var name =
// person.getField(opensocial.Person.Field.NAME);
// details_div.innerHTML =
// name.getField(opensocial.Name.Field.UNSTRUCTURED);
details_div.innerHTML = person.getDisplayName() + "<br/>";
details_div.innerHTML +=
person.getField(opensocial.Person.Field.AGE) + " years old<br/>";
// the date of birth field is a JavaScript Date object.
var dob = person.getField(opensocial.Person.Field.DATE_OF_BIRTH);
details_div.innerHTML += "Born: " + dob.toDateString() + "<br/>";
// current location is an opensocial.Address object.
var current_location =
person.getField(opensocial.Person.Field.CURRENT_LOCATION);
details_div.innerHTML +=
current_location.getField(opensocial.Address.Field.REGION);
details_div.innerHTML += " ";
details_div.innerHTML +=
current_location.getField(opensocial.Address.Field.POSTAL_CODE);
details_div.innerHTML += " ";
details_div.innerHTML +=
current_location.getField(opensocial.Address.Field.COUNTRY);
details_div.innerHTML += "<br/>";
// gender is an opensocial.Enum.
var gender = person.getField(opensocial.Person.Field.GENDER);
details_div.innerHTML += gender.getDisplayValue() + "<br/>";
// network presence is an opensocial.Enum.
var network_presence = person.getField(opensocial.Person.Field.NETWORK_PRESENCE);
details_div.innerHTML += network_presence.getDisplayValue() + " ";
// match the enum key to determine the online/offline status.
if(network_presence.getKey() === opensocial.Enum.Presence.ONLINE){
details_div.innerHTML += ":-)";
}
else if(network_presence.getKey() === opensocial.Enum.Presence.OFFLINE){
details_div.innerHTML += ":-(";
}
}
function showExtraContent(field){
var extras_content = document.getElementById("extras_content");
extras_content.style.display = "block";
// handle the different possible fields here.
var data = person.getField(field);
// if it's a string we can output it directly.
if(data && String !== data.constructor){
// if it's an array grab the first element.
if(Array === data.constructor){
if(data.length > 0){
data = data[0];
}
else{
data = null;
}
}
else{
// if it's an enum get the display value.
if(data.getDisplayValue){
data = data.getDisplayValue();
if(data.length > 0){
data = data[0];
}
}
// if it's an object get the sub fields.
else if(data.getField){
if(data.getField(opensocial.Url.Field.ADDRESS)){
if(data.getField(opensocial.Url.Field.TYPE).indexOf("image") > 0){
data = "<img src=\"" + data.getField(opensocial.Url.Field.ADDRESS) + "\" />";
}
else{
data = data.getField(opensocial.Url.Field.ADDRESS);
}
}
else if(data.getField(opensocial.BodyType.Field.BUILD)){
data = data.getField(opensocial.BodyType.Field.BUILD);
}
}
else{
data = null;
}
}
}
if(null === person ||
"undefined" === typeof(data) ||
null === data ||
"" === data){
extras_content.innerHTML = "Oops! No data!";
}
else{
extras_content.innerHTML = data;
}
// make the iframe fit the new content.
gadgets.window.adjustHeight();
}
function launchProfile(){
if(null !== person){
// profile url is a string.
window.open(person.getField(opensocial.Person.Field.PROFILE_URL));
}
}
function pickColor(type, color){
color = "#" + color;
switch (type){
case "bg":
style.setBackground(color, true);
break;
case "txt":
style.setText(color, true);
break;
}
}
var Style = function(background, text){
this._style.background = background;
this._style.text = text;
};
Style.prototype = {
_style:{
background:null,
text:null
},
getBackground:function(){
return this._style.background;
},
getText:function(){
return this._style.text;
},
setBackground:function(background, update){
document.body.style.backgroundColor = background;
this._style.background = background;
if(update){
this._updateStyle();
}
},
setText:function(text, update){
document.body.style.color = text;
this._style.text = text;
if(update){
this._updateStyle();
}
},
_updateStyle:function(){
var request = opensocial.newDataRequest();
request.add(request.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, "style", this._style));
request.send();
}
};
var person = null;
var style = new Style(document.body.style.backgroundColor, document.body.style.color);
// add the app's entry point to the onload handler, this will fire when the body has finished loading.
gadgets.util.registerOnLoadHandler(init);
</script>