What's New in MySpace OpenSocial 0.8 - Part 3
Activities
[
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 third 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 app 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 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 app data to accept only valid JSON
- The deprecation of PostTo in favor of opensocial.requestShareApp and opensocial.requsetSendMessage
- Activities
- App Notifications
This article, the third part in the series, will cover activities.
Picking Up Where I Left Off
In this article I will build upon the app I created in part one and further updated in part two. At the end of part two the app had the following functionality:
- Fetched and parsed all of the MySpace supported fields in an opensocial.Person object
- Used app data to persist user chosen background and text colors
- Made use of Open Canvas by incorporating opensocial.hasPermission and opensocial.requestPermission and by fetching a basic viewer person object when the viewer hasn't installed the app
- Sent app install requests and posted bulletins using requestShareApp and requestSendMessage as opposed to the deprecated PostTo functionality
In this article I'll extend the app even further to take advantage of activities. As activities can only be used from the canvas view all the action will take place there.
What is an Activity?
Activities are another way for apps to interact with MySpace users. On each MySpace home page there is a "Friend Updates" module. This module typically displays concise messages that keep you informed of what your friends are up to, such as a friend added a blog entry, uploaded photos, or became friends with another user.
Before the launch of activities the only app related entries in the feed were the "friend X has installed app Y" updates. With activities each app can define a custom message that will appear in the user's friends' Friend Updates module.
Templates
Understanding Templates
The first thing to understand is that activities use a custom template system for the messages. That means that you supply a template up front, and then at run time you supply the variables that will be inserted into the template.
Each template consists of a mandatory title and an optional body. The title can contain up to 160 visible characters while the body can contain up to 260 visible characters.
This means that you'll create a message and insert certain variables into the message. These variables will be replaced by real data when the activity is raised. Variables inside the message will be in this format: ${token_name}. A basic message with a variable might therefore look like this:
"This event was raised on: ${date}."
At run time you'd specify that ${date} be replaced with a string specifying the current date and time. So if I gave the variable $date the value "December 25th 2008" the resulting message would be:
"This event was raised on: December 25th 2008."
Data Types
In the first simple example the ${date} variable was assumed to be a string. You can, however, specify the data type of a variable. The currently available types are Literal and Person.
Literals are any string, and the variable will simply be replaced by that string. The first example showed this: ${date} was of type Literal and was simply replaced by the given string.
The other type, Person, is a little more complex. If a variable is specified as a Person, the variable will be replaced by a link when appearing in the feed. The link will go to the specified user's profile and the text of the link will be their display name. Let's say I have a template like the following, where ${friend} is of type Person:
"Just wanted to say hi to ${friend}!"
Variables of type Person require the desired user's ID, so if I passed in "6221" for the ${friend} token, I'd get something like this:
"Just wanted to say hi to Tom!"
Where the link "Tom" points to Tom's profile URL.
Reserved Variable Names
There is a list of reserved variable names which I'll list a little later, but one interesting one is ${sender}. This is actually a required variable in the title, so you'll be using it a lot. ${sender} behaves like a variable of type Person, and as such is replaced by a link to the viewer's profile and with the viewer's display name as the text. Let's change the template to the following:
"${sender} just wanted to say hi!"
If I were to raise an event with that token, the result would be:
"Chad Russell just wanted to say hi!"
However, if Tom were to raise the event, it would look like:
"Tom just wanted to say hi!"
In each case the viewer's details are used for the ${sender} variable.
Here's the list of reserved variable names and what they resolve to:
- ${subject}, which will be replaced by a link to the viewer's profile with the viewer's display name as the text
- ${subject.DisplayName}, which will be replaced by the viewer's display name
- ${subject.Id}, which will be replaced by the viewer's ID
- ${subject.ProfileUrl}, which will be replaced by the URL of the viewer's profile
- ${canvasUrl}, which will be replaced by the URL of the app's canvas
- ${variable_name.Count}, where variable_name is the name of a variable being used in the template, and is replaced by the number of aggregated items (more on aggregation later)
Aggregation
If a user has lots of friends who use apps they may get a large amount of activities showing up in their feed. Because of this, activity updates are aggregated. So if more than one friend of a user raises an activity for an app they will be aggregated into a single feed entry.
Let's look at a new template and see what it would look like if it was raised several times:
"${sender} just wanted to say hi to ${friend}!"
In this case I would specify that I want to aggregate on the ${friend} variable (I'll discuss how to pick which variable to aggregate on in the Template Editor section). The first time I raise the event I specify the ${friend} as ID "6221". The result will be:
"Chad Russell just wanted to say hi to Tom!"
If I raised the event again, but this time specified the ${friend} as ID "355953233", the result would be:
"Chad Russell just wanted to say hi to Tom and Marco!"
Raising the event a third time with a different ID, say "65815518", will result in:
"Chad Russell just wanted to say hi to Tom, Marco and @nkur!"
As you can see, the templating system will keep inserting any new variables into the message, it will also place "," and "and" into the message to stay grammatically correct.
Body and Media Items
The last two pieces of the puzzle are the body and media items. The body behaves similarly to the title, but is optional and can contain more characters. When rendered out the title will be on the first line and the body will be on the second line.
Media items are pictures that can be included with the activity message. These can be any picture on MySpace that is accessible to the viewer. You'll see later that in the app I fetch the list of photos that the viewer has uploaded and add those to the activity. A maximum of three media items are allowed and when rendered they will go on the second line. If any body text was specified along with a media item, the body text will be placed to the right of the media items.

That's the basics of how templating works in the MySpace custom activities template system, for a full list of restrictions and known issues, including what HTML markup you can use in the body, see this post on our forums:
http://developer.myspace.com/Community/forums/t/5826.aspx
Using the Template Editor
Now that we know how the template system works let's look at how to add the templates to your app. To do this you'll use the Template Editor tool. To get to this tool simply click the Templates link for the app to which you want to specify a template.

That will take you to the list of your templates for that app.

From here you may create a new template or edit an existing one. I have already created a template for my app so let's take a look at it:

Here you can see all the relevant template information. Starting at the top left under "Content" you'll see the template name, which is a unique identifier for the particular template you want to use. Below that are the Title and Body for the template, separate tabs are provided for Single Form (just one activity raised) or Aggregate Form (more than one activity raised).
On the right hand side under "Variables" you can specify the data type and test values for each variable in the template. Below that under "Media Items" are sample media items that are also used as test values.
Clicking "Preview Template" will generate a preview of the template in the bottom half of the page under "Preview". This preview will use the test data from the "Variables" and "Media Items" section. This should give you a good idea of how the activity itself will render out when the event is raised.
Also after clicking "Preview Template", in the bottom right hand side under "Sample Javascript Code", you'll see JavaScript code that can be used to raise the event. You can probably just copy and paste this code directly into your app to start raising activities, but more than likely you'll want to customize it a little bit (more on this a little later).
Don't forget to click "Save Template" once you're done with your edits, this will save the current state of your activity template for later use.
Once you're happy with your template, and the template has been saved, you can switch the template from development to live status. To do this go to the list of templates for your app and click the Publish button next to the template of choice. Click OK in the following prompt and the template will switch to Live. Note that any activities raised for templates in development won't be visible to friends.

opensocial.requestCreateActivity
Now that I've gone over what a template is and how to construct one, let's take a deeper look at the JavaScript required to raise an event and handle the callback.
Getting Valid Media Item URIs
Before we get started on raising the activity itself, I'd like to make a comment on media items. Media items are images that accompany the activity, zero to three media items can be added. To specify which media item to add you'll need the image's URI. The MySpace platform only accepts URIs that are valid API URIs, the images also have to be accessible to the viewer.
The best way to obtain the URIs is through the API itself. In the app I make a call to MyOpenSpace.DataRequest.newFetchPhotosRequest to fetch the list of photos that the viewer has access to. Each photo that's returned has a URI value, that's the value that can be used to create a media item.
Inside the init function I add this request to fetch the list of photos for the viewer:
// create the ID
var id = opensocial.IdSpec.PersonId.VIEWER;
// add the photos request to the queue
request.add(MyOpenSpace.DataRequest.newFetchPhotosRequest(id), "photos");
In the app I want to fetch the list of photos, display them to the user and attach the URI to each photo. That way when the user clicks the photo I can match the clicked photo to the correct URI.
// parses the photos response and outputs it to the page
function drawPhotos(photos){
// build up the required markup
var clear_div = '<div class="clear"></div>';
var photos_format = '<div id="photo_{0}" onclick="photoClicked';
photos_format += '(\'{1}\');" class="friend float" uri="{2}">';
photos_format += '<center><img src="{3}" />';
photos_format += '<div>{4}</div></center></div>';
var photos_div = document.getElementById("photos");
// iterate through each photo
var id, src, uri, caption;
for(var i = 0; i < photos.length; i++){
id = photos[i].getField(MyOpenSpace.Photo.Field.PHOTO_ID);
src = photos[i].getField(MyOpenSpace.Photo.Field.IMAGE_URI);
uri = photos[i].getField(MyOpenSpace.Photo.Field.PHOTO_URI);
caption = photos[i].getField(MyOpenSpace.Photo.Field.CAPTION);
if(!id || !uri) continue;
if(!caption) caption = "";
var this_photo = photos_format;
this_photo = this_photo.replace("{0}", id);
this_photo = this_photo.replace("{1}", id);
this_photo = this_photo.replace("{2}", uri);
this_photo = this_photo.replace("{3}", src);
this_photo = this_photo.replace("{4}", caption);
photos_div.innerHTML += this_photo;
}
// add the clearing div if necessary
if(photos.length > 0){
photos_div.innerHTML += clear_div;
}
}
Two different URIs are provided by the API, MyOpenSpace.Photo.Field.IMAGE_URI can be used inside the src attribute of an <img> tag, or in a similar manner to actually display the image. The MyOpenSpace.Photo.Field.PHOTO_URI field is the URI for the image on the MySpace API and is the value to use when creating media items.
In the app each photo is contained within a <div> tag, I create a custom attribute called "uri" on each of these tags and set it to the API URI from above. You'll see below how I use the uri attribute to create the media items.
Raising the Event
In order to raise an event in OpenSocial, you'll need to use opensocial.requestCreateActivity:
opensocial.requestCreateActivity = function(activity, priority, opt_callback)
activity is required to be an object of type opensocial.Activity and contains the bulk of the details for the activity being raised.
At this point priority is not yet being checked, in the future you'll be able to specify two values for this: opensocial.CreateActivityPriority.HIGH and opensocial.CreateActivityPriority.LOW. In my app I use the value HIGH.
opt_callback is a function and will be invoked once the activity popup modal has been closed.
In the app I created a function that wraps opensocial.requestCreateActivity, let's take a look at it in detail:
// wrapper for requestCreateActivity
function sendActivity(){
// the date variable is of type Literal
var date = (new Date()).toLocaleDateString();
// the action variable is also a Literal
var action = document.getElementById("activity_action").value;
// the friend varaible is of type Person, so
// parse out the ID of the selected friend
var friend = document.getElementById("activity_friend");
friend = friend.options[friend.selectedIndex].value;
// note that there is currently a bug here, the template
// does not accept an ID in the format "myspace.com:12345"
// so strip out the "myspace.com" part of the ID to leave
// just "12345"
friend = stripMySpaceDotCom(friend);
// create the parameters for the activity
var params = {};
// select the particular template here
params[opensocial.Activity.Field.TITLE_ID] = "date_message";
// use the variables from above to generate the values
// for the template variables
params[opensocial.Activity.Field.TEMPLATE_PARAMS] =
{ "date" : date, "action" : action, "friend" : friend };
// iterate through the clicked photos
if(photos_clicked.length > 0){
// if some photos were clicked create an array to
// store opensocial.MediaItem objects for each
// photo
var mediaItemArray = [];
var uri, mi, photo_div, div_id;
for(var i = 0; i < photos_clicked.length; i++){
div_id = "photo_" + photos_clicked[i];
photo_div = document.getElementById(div_id);
// media items URIs are required to be in the format
// of valid MySpace API URIs, these are supplied by
// the API, here they are saved to a custom
// "uri" attribute
uri = photo_div.attributes.uri.nodeValue;
// create the opensocial.MediaItem object
mi = opensocial.newMediaItem("image/jpeg", uri);
// add it to the array
mediaItemArray.push(mi);
}
// once done, add the array to the activity parameters
params[opensocial.Activity.Field.MEDIA_ITEMS] = mediaItemArray;
}
// create the opensocial.Activity object
var activity = opensocial.newActivity(params);
// request the event be raised, passing in the activity,
// a priority of HIGH, and specify a callback
opensocial.requestCreateActivity(activity,
opensocial.CreateActivityPriority.HIGH, activityCallback);
}
I first gather the three variable values: date, action and friend. Date is just the current date while action and friend come from the UI. I then create the parameters for the activity:
- opensocial.Activity.Field.TITLE_ID. You'll need to specify a title for each activity you want to raise. This will just be the name of the template you created as a string. This value can be found under the "Template Name" field in the Template Editor.
- opensocial.Activity.Field.TEMPLATE_PARAMS. The template parameters are required if you've defined at least one variable in your template. This will assign values to each variable and should take the form of a JSON object with key/value pairs. The key names can be found under Variables in the Template Editor. In my case the JSON object looks like the following: { "date" : date, "action" : action, "friend" : friend }
- opensocial.Activity.Field.MEDIA_ITEMS. As explained above, media items are optional and the URIs are obtained through the MySpace API. I parse out the URI value for each clicked photo by accessing the custom "uri" node on the containing div tag (photo_div.attributes.uri.nodeValue).
I then call opensocial.newActivity to create the opensocial.Activity object itself, which in turn is passed as a parameter into opensocial.requestCreateActivity.
Callback
The activity callback behaves similarly to the requestSendMessage and requestShareApp callbacks: a value of 1 is returned if the user clicked "Publish" and a 0 if the user clicked "Deny".
In the app I do some cleanup work in the callback by clearing the list of clicked photos, or if an error occurred I display an error message.
// handles the callback from requestCreateActivity
function activityCallback(response){
// first check if the activity had an error
if(!response || response.hadError() || -1 === response.getData()){
document.getElementById("activity_message").innerHTML =
"There was an error, please refresh the page and try again!";
}
else{
if(1 === response.getData()){
// the user clicked "publish"
}
else if(0 === response.getData()){
// the user clicked "cancel"
}
// otherwise, take some action, in this case
// reset the photo state
var photo;
for(var i = 0; i < photos_clicked.length; i++){
photo = document.getElementById("photo_" + photos_clicked[i]);
photo.style.backgroundColor = "white";
}
photos_clicked = [];
}
}
To help you debug issues while you're developing your app, if response.hadError() resolves to true don't forget to check the error message (response.getErrorMessage()).
Did it Work?
So the activity modal shows, a preview of your activity loads inside of it, you click "Publish" and the callback receives a value of 1. So far so good, but how do you know that the activity actually got sent? This is an asynchronous message so the calling function won't know if the message was successfully sent or not.
The best way I found to test was to view the feed for my own updates:
- Navigate to your home page
- In the Friend Updates module click "view all updates"
- In the top right corner click "View my updates"
That will show you all the updates you have sent, including app activities. If you see your activity on this page then the activity was successfully sent. Note that activities raised for apps in development, or for templates in development mode, won't show up in your friends' Friend Updates feed – only activities for live apps with live templates will show.
Full App Code Listing
You can install the app 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 app up to this point. Thanks and happy coding!
Canvas View
<style>
body
{
margin:0;
padding:0;
color:#000;
background-color:#fff;
}
img
{
border:0;
}
#outer
{
font-family:Verdana;
font-size:0.7em;
padding:5px;
}
#content
{
display:none;
}
#spinner
{
color:Red;
font-size:1.3em;
}
#friends_callout
{
display:none;
}
.divider
{
margin-bottom:5px;
border-bottom:solid 1px black;
}
.float
{
float:left;
}
.clear
{
clear:both;
}
.friend
{
border:solid 1px black;
width:80px;
height:120px;_height:130px;
margin:5px;
cursor:pointer;
overflow:hidden;
}
.friend div
{
vertical-align:top;
}
.friend img
{
width:70px;
max-height:100px;
margin-top:5px;
}
.callout
{
font-size:14px;
font-weight:bold;
}
.text
{
font-size:14px;
}
.bottom
{
padding-bottom:2px;
}
.bottom_10
{
padding-bottom:10px;
}
.hide
{
display:none;
}
.error
{
font-size:11px;
color:Red;
}
</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 id="perms" class="divider callout bottom">No access to photos? Try these: <button onclick="reqPublic();">Public Photos</button> <button onclick="reqPrivate();">Private Photos</button></div>
<div id="bulletin" class="divider callout bottom">Spread the love and send a <button onclick="sendBulletin();">Bulletin!</button></div>
<div id="activity" class="callout bottom">Let your friends know what you're up to by sending an <button onclick="showActivity();">Activity!</button></div>
<div id="activity_block" class="hide">
<p class="text">Great! You need to fill in the blanks for "friend" and "action":</p>
<p class="text">
<span id="activity_name_1">Bob</span> says hi to (friend)!<br />
On <span id="activity_date">Dec 29th</span>, <span id="activity_name_2">Bob</span> was (action)
<a href="http://profile.myspace.com/index.cfm?fuseaction=user.viewProfile&friendID=422742687">Profile Deluxe</a>
</p>
<div class="text float bottom_10">First pick a friend: </div>
<select id="activity_friend"></select>
<div class="clear"></div>
<div class="text float bottom_10">Then pick an action: </div>
<input id="activity_action" type="text" size="40" maxlength="40" value="looking at pictures" />
<div class="clear"></div>
<div class="text">
... and don't forget to add some pictures from below! (up to 3 allowed)<br />
When you're done, click <button onclick="sendActivity();">Send!</button>
<span id="activity_message" class="error"></span>
</div>
<div id="photos"></div>
<div class="clear"></div>
</div>
<div class="divider"></div>
<span id="friends_callout" class="callout">Send some friend requests!</span>
<div id="friends" class="divider"></div>
</div>
</div>
<script type="text/javascript">
// the entry point for the app, fired from the onload handler.
function init(){
// check if the app is added
is_added = isAdded();
// create the opensocial.DataRequest object
var request = opensocial.newDataRequest();
if(is_added){
// show the relevant divs
document.getElementById("perms").style.display = "block";
document.getElementById("bulletin").style.display = "block";
document.getElementById("activity").style.display = "block";
document.getElementById("friends_callout").style.display = "block";
document.getElementById("friends").style.display = "block";
// 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] = 1;
// create the idspec
var idspec = opensocial.newIdSpec(params);
// add the fetch friend request to the queue
request.add(request.newFetchPeopleRequest(idspec), "friends");
// create the ID
var id = opensocial.IdSpec.PersonId.VIEWER;
// add the photos request to the queue
request.add(MyOpenSpace.DataRequest.newFetchPhotosRequest(id), "photos");
}
else{
// if the app isn't added, just get the basic viewer
requestBasicViewer();
return;
}
// start executing the request, 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_friends = true;
var parse_viewer = true;
var parse_photos = true;
// first check if an error occurred.
if(!response){
parse_friends = false;
parse_viewer = false;
parse_photos = false;
}
else if(response.hadError()){
var friends = response.get("friends");
var viewer = response.get("viewer");
var photos = response.get("photos");
// create a locally scoped function to handle checking for errors
var checkForError = function(ri){
if(!ri){
return false;
}
else if(ri.hadError()){
if(ri.getErrorCode() === opensocial.ResponseItem.Error.INTERNAL_ERROR){
// retry the request?
}
return false;
}
return true;
}
parse_friends = checkForError(friends);
parse_viewer = checkForError(viewer);
parse_photos = checkForError(photos);
}
else{
if(!response.get("friends")) parse_friends = false;
if(!response.get("viewer")) parse_viewer = false;
if(!response.get("photos")) parse_photos = false;
}
var viewer;
if(parse_friends){
var friends = response.get("friends").getData().asArray();
drawFriends(friends);
}
if(parse_viewer){
viewer = response.get("viewer").getData();
document.getElementById("friends").style.display = "none";
}
if(parse_photos){
photos = response.get("photos").getData().asArray();
drawPhotos(photos);
}
setPermsDiv(viewer);
// 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();
}
// the global app is added flag
var is_added = null;
// return true if the app is added
// false otherwise
function isAdded(){
// the first time around the flag is null, so use the
// parameters to determine install state
if(null === is_added){
return ("1" === gadgets.views.getParams().installState + "");
}
// otherwise the flag was set directly, just return that
else{
return is_added;
}
}
function requestBasicViewer(){
// create the opensocial.DataRequest object.
var request = opensocial.newDataRequest();
// specify the viewer
var id = opensocial.IdSpec.PersonId.VIEWER;
// add the basic viewer request to the queue
request.add(request.newFetchPersonRequest(id), "viewer");
// start executing the request, call back into got_init when done.
request.send(got_init);
}
// generate the markup for how to show permissions
// based off of the user having added the app
// and what permissions they already have set
function setPermsDiv(viewer){
var inner = "";
var perms = document.getElementById("perms");
// first check if the app is added
if(!is_added){
var viewer_name = (viewer) ? " " + viewer.getDisplayName() : "";
// it's not added so no permissions have yet been set
// show both permissions, if these are clicked the add
// app modal will show first
inner = 'Welcome to Profile Deluxe';
inner += viewer_name;
inner += '!<br />Check out the app by allowing access ';
inner += 'to some of your photos: ';
inner += '<button onclick="reqPublic();">';
inner += 'Public Photos</button> ';
inner += '<button onclick="reqPrivate();">';
inner += 'Private Photos</button>';
// hide the bulletin and activity buttons as
// requestSendMessage and requestCreateActivity won't
// work when the user hasn't added the app
document.getElementById("bulletin").style.display = "none";
document.getElementById("activity_callout").style.display = "none";
document.getElementById("activity").style.display = "none";
}
else{
// permission object for public videos and photos
var pub = MyOpenSpace.Permission.VIEWER_ACCESS_TO_PUBLIC_VIDEOS_PHOTOS;
// permission object for private videos and photos
var pri = MyOpenSpace.Permission.VIEWER_ACCESS_TO_PRIVATE_VIDEOS_PHOTOS;
// check if both permissions are already granted
if(opensocial.hasPermission(pub) &&
opensocial.hasPermission(pri)){
// if so, no need to display any permission buttons
perms.style.display = "none";
}
else{
inner += "No access to photos? You can try: ";
// add the public photo permission button if necessary
if(!opensocial.hasPermission(pub)){
inner += "<button onclick='reqPublic();'>";
inner += "Public Photos</button> ";
}
// add the private photo permission button if necessary
if(!opensocial.hasPermission(pri)){
inner += "<button onclick='reqPrivate();'>";
inner += "Private Photos</button>";
}
}
}
perms.innerHTML = inner;
}
// global variable used to hold the ID of the clicked user
var friend_clicked = null;
// wrapper for requestShareApp
function friendClicked(id){
// set the global variable
friend_clicked = id;
// create the rSA message
var body = "Hey [recipient]! [sender] wants you to ";
body += "add [app]. It's way awesome!";
var title = "A fun way to view your profile!";
var params = {};
params[opensocial.Message.Field.TITLE] = title;
// create an opensocial.Message object
var reason = opensocial.newMessage(body, params);
// initiate requestShareApp
opensocial.requestShareApp(id, reason, rsaCallback);
}
// global variable used to hold the IDs of the clicked photos
var photos_clicked = [];
function photoClicked(id){
// locate the particular clicked photo
var photo = document.getElementById("photo_" + id);
// figure out if the photo is currently clicked
// or not by iterating through the photos_clicked
// array, save the index if found
var photo_exists = -1;
for(var i = 0; i < photos_clicked.length; i++){
if(photos_clicked[i] === id){
photo_exists = i;
break;
}
}
// the photo was already clicked
if(photo_exists >= 0){
// remove the photo from the list
photos_clicked.splice(i, 1);
// set the background to white
photo.style.backgroundColor = "white";
}
// the photo wasn't clicked
else{
// a maximum of 3 photos are allowed
// so take no action if 3 are already
// clicked
if(photos_clicked.length > 2) return;
// add the photo to the array
photos_clicked.push(id);
// set the photo's background to yellow
photo.style.backgroundColor = "yellow";
}
}
function reqPublic(){
var permissions = [MyOpenSpace.Permission.VIEWER_ACCESS_TO_PUBLIC_VIDEOS_PHOTOS];
var reason = "This is to allow the app to display your photos that are set to public on your home page.";
opensocial.requestPermission(permissions, reason, reqCallback);
}
function reqPrivate(){
var permissions = [MyOpenSpace.Permission.VIEWER_ACCESS_TO_PRIVATE_VIDEOS_PHOTOS];
var reason = "This is to allow the app to display your photos that are set to private on your home page.";
opensocial.requestPermission(permissions, reason, reqCallback);
}
// callback function for requestPermission
function reqCallback(response){
// if the app was previously not added, but is
// now added, reload the app
if(!is_added && "1" === gadgets.views.getParams().installState + ""){
// set the global flag
is_added = true;
// restart the app
init();
}
}
// if id starts with 'myspace.com:' it
// will be stripped out and the resulting
// value returned, otherwise return
// the id as-is
function stripMySpaceDotCom(id){
if(0 === id.indexOf("myspace.com:")){
return id.substr("myspace.com:".length);
}
else{
return id;
}
}
function showActivity(){
document.getElementById("activity").style.display = "none";
document.getElementById("activity_block").style.display = "block";
gadgets.window.adjustHeight();
}
// wrapper for requestCreateActivity
function sendActivity(){
// the date variable is of type Literal
var date = (new Date()).toLocaleDateString();
// the action variable is also a Literal
var action = document.getElementById("activity_action").value;
// the friend varaible is of type Person, so
// parse out the ID of the selected friend
var friend = document.getElementById("activity_friend");
friend = friend.options[friend.selectedIndex].value;
// note that there is currently a bug here, the template
// does not accept an ID in the format "myspace.com:12345"
// so strip out the "myspace.com" part of the ID to leave
// just "12345"
friend = stripMySpaceDotCom(friend);
// create the parameters for the activity
var params = {};
// select the particular template here
params[opensocial.Activity.Field.TITLE_ID] = "date_message";
// use the variables from above to generate the values
// for the template variables
params[opensocial.Activity.Field.TEMPLATE_PARAMS] =
{ "date" : date, "action" : action, "friend" : friend };
// iterate through the clicked photos
if(photos_clicked.length > 0){
// if some photos were clicked create an array to
// store opensocial.MediaItem objects for each
// photo
var mediaItemArray = [];
var uri, mi, photo_div, div_id;
for(var i = 0; i < photos_clicked.length; i++){
div_id = "photo_" + photos_clicked[i];
photo_div = document.getElementById(div_id);
// media items URIs are required to be in the format
// of valid MySpace API URIs, these are supplied by
// the API, here they are saved to a custom
// "uri" attribute
uri = photo_div.attributes.uri.nodeValue;
// create the opensocial.MediaItem object
mi = opensocial.newMediaItem("image/jpeg", uri);
// add it to the array
mediaItemArray.push(mi);
}
// once done, add the array to the activity parameters
params[opensocial.Activity.Field.MEDIA_ITEMS] = mediaItemArray;
}
// create the opensocial.Activity object
var activity = opensocial.newActivity(params);
// request the event be raised, passing in the activity,
// a priority of HIGH, and specify a callback
opensocial.requestCreateActivity(activity,
opensocial.CreateActivityPriority.HIGH, activityCallback);
}
// handles the callback from requestCreateActivity
function activityCallback(response){
// first check if the activity had an error
if(!response || response.hadError() || -1 === response.getData()){
document.getElementById("activity_message").innerHTML =
"There was an error, please refresh the page and try again!";
}
else{
if(1 === response.getData()){
// the user clicked "publish"
}
else if(0 === response.getData()){
// the user clicked "deny"
}
// otherwise, take some action, in this case
// reset the photo state
var photo;
for(var i = 0; i < photos_clicked.length; i++){
photo = document.getElementById("photo_" + photos_clicked[i]);
photo.style.backgroundColor = "white";
}
photos_clicked = [];
}
}
// wrapper for requestSendMessage
function sendBulletin(){
// link to the app's canvas
var app_canvas = "http://profile.myspace.com/Modules/Applications/Pages/Canvas.aspx?appId=118991";
// link to the app's icon
var img_src = "http://c3.ac-images.myspacecdn.com/images02/21/l_e2b0c1f6a7b249a2a50d9a57444c5f12.jpg";
// create the message
var body = "Try it out! A fun new way to view your profile! Click ";
body += "<a href='" + app_canvas + "'>here</a> to try it out!<br />";
body += "<center><img src='" + img_src + "' /></center>"
var title = "A fun way to view your profile!";
var params = {};
params[opensocial.Message.Field.TITLE] = title;
// specify a message type of bulletin
params[opensocial.Message.Field.TYPE] = opensocial.Message.Type.NOTIFICATION;
var message = opensocial.newMessage(body, params);
// initiate requestSendMessage
opensocial.requestSendMessage(opensocial.IdSpec.PersonId.VIEWER, message, bulletinCallback);
}
function bulletinCallback(response){
if(response && !response.hadError()){
if(1 === response.getData()){
document.getElementById("bulletin").innerHTML = "Thanks!";
}
}
}
// callback for requestShareApp
function rsaCallback(response){
if(response && !response.hadError()){
// use the global friend_clicked variable to
// grab the correct div from the DOM
var friend = document.getElementById(friend_clicked);
if(friend){
// if "Send" was clicked the response data
// will be 1, otherwise a 0
if(1 === response.getData()){
// remove that friend from the list
if(friend.parentNode){
friend.parentNode.removeChild(friend);
}
}
else{
friend.style.backgroundColor = "red";
}
}
}
}
function rntWrapper(view_name){
var supported_views = gadgets.views.getSupportedViews();
var view = null;
for(var v in supported_views){
if(view_name === supported_views[v].getName()){
view = supported_views[v];
break;
}
}
if(null !== view){
gadgets.views.requestNavigateTo(view);
}
}
// parses the friends response and outputs it to the page
function drawFriends(friends){
document.getElementById("friends_callout").style.display = "block";
var clear_div = '<div class="clear"></div>';
var friend_format = '<div id="{0}" onclick="friendClicked(\'{1}\');" class="friend float">';
friend_format += '<center><img src="{2}" /><div>{3}</div></center></div>';
var friends_div = document.getElementById("friends");
var id, image, name;
var activity_friend = document.getElementById("activity_friend");
for(var i = 0; i < friends.length; i++){
id = friends[i].getId();
image = friends[i].getField(opensocial.Person.Field.THUMBNAIL_URL);
name = friends[i].getDisplayName();
if(!id || !image || !name) continue;
friends_div.innerHTML += friend_format.replace("{0}", id).replace("{1}", id).replace("{2}", image).replace("{3}", name);
if(activity_friend){
var option = document.createElement("option");
option.value = id;
option.text = name;
activity_friend.options.add(option);
}
}
if(friends.length > 0){
friends_div.innerHTML += clear_div;
}
}
// parses the photos response and outputs it to the page
function drawPhotos(photos){
// build up the required markup
var clear_div = '<div class="clear"></div>';
var photos_format = '<div id="photo_{0}" onclick="photoClicked';
photos_format += '(\'{1}\');" class="friend float" uri="{2}">';
photos_format += '<center><img src="{3}" />';
photos_format += '<div>{4}</div></center></div>';
var photos_div = document.getElementById("photos");
// iterate through each photo
var id, src, uri, caption;
for(var i = 0; i < photos.length; i++){
id = photos[i].getField(MyOpenSpace.Photo.Field.PHOTO_ID);
src = photos[i].getField(MyOpenSpace.Photo.Field.IMAGE_URI);
uri = photos[i].getField(MyOpenSpace.Photo.Field.PHOTO_URI);
caption = photos[i].getField(MyOpenSpace.Photo.Field.CAPTION);
if(!id || !uri) continue;
if(!caption) caption = "";
var this_photo = photos_format;
this_photo = this_photo.replace("{0}", id);
this_photo = this_photo.replace("{1}", id);
this_photo = this_photo.replace("{2}", uri);
this_photo = this_photo.replace("{3}", src);
this_photo = this_photo.replace("{4}", caption);
photos_div.innerHTML += this_photo;
}
// add the clearing div if necessary
if(photos.length > 0){
photos_div.innerHTML += clear_div;
}
}
// add the app's entry point to the onload handler, this will fire when the body has finished loading.
gadgets.util.registerOnLoadHandler(init);
</script>
Home View
<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;
}
#albums div
{
margin-bottom:5px;
}
#share
{
text-align:center;
}
.album_thumb
{
float:left;
margin:4px;
cursor:pointer;
}
.album_thumb img
{
height:20px;
width:20px;
border:solid 1px black;
}
.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 class="divider">
<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 id="albums" class="divider"></div>
<div id="share">Go to <a href='javascript:void(0);' onclick='rntWrapper(gadgets.views.ViewType.CANVAS);'>the canvas</a> and share the app!</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");
// check if the app has access to any photos.
if(opensocial.hasPermission(MyOpenSpace.Permission.VIEWER_ACCESS_TO_PUBLIC_VIDEOS_PHOTOS) ||
opensocial.hasPermission(MyOpenSpace.Permission.VIEWER_ACCESS_TO_PRIVATE_VIDEOS_PHOTOS)){
// if access is allowed add the fetch albums data request to the queue.
request.add(MyOpenSpace.DataRequest.newFetchAlbumsRequest(opensocial.IdSpec.PersonId.VIEWER), "albums");
}
else{
// otherwise add a link to the canvas to try and acquire permission.
addPermsRequestUI();
}
// 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;
var parse_albums = true;
// first check if an error occurred.
if(!response){
parse_appdata = false;
parse_person = false;
parse_albums = false;
}
else if(response.hadError()){
var the_viewer = response.get("the_viewer");
var app_data = response.get("app_data");
var albums = response.get("albums");
// create a locally scoped function to handle checking for errors
var checkForError = function(ri){
if(!ri){
return false;
}
else if(ri.hadError()){
if(ri.getErrorCode() === opensocial.ResponseItem.Error.INTERNAL_ERROR){
// retry the request?
}
return false;
}
return true;
}
parse_person = checkForError(the_viewer);
parse_appdata = checkForError(app_data);
parse_albums = checkForError(albums);
}
else{
if(!response.get("the_viewer")) parse_person = false;
if(!response.get("app_data")) parse_appdata = false;
if(!response.get("albums")) parse_albums = false;
}
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();
}
if(parse_albums){
var albums = response.get("albums").getData().asArray();
drawAlbums(albums);
}
// 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();
}
function addPermsRequestUI(){
document.getElementById("albums").innerHTML =
"<div>Oops! Access to your photos are denied. Click " +
"<a href='javascript:void(0);' " +
"onclick='rntWrapper(gadgets.views.ViewType.CANVAS);'>" +
"here</a> to go to the canvas page and grant permission.</div>";
}
function rntWrapper(view_name){
var supported_views = gadgets.views.getSupportedViews();
var view = null;
for(var v in supported_views){
if(view_name === supported_views[v].getName()){
view = supported_views[v];
break;
}
}
if(null !== view){
gadgets.views.requestNavigateTo(view);
}
}
function drawAlbums(albums){
var album_format = '<div class="album_thumb" onclick="displayImage(\'{0}\');"><img src="{1}" /></div>';
var div_clear = '<div class="clear"></div>';
var albums_div = document.getElementById("albums");
for(var i = 0; i < albums.length; i++){
if(albums[i].getField(MyOpenSpace.Album.Field.PHOTO_COUNT) > 0){
albums_div.innerHTML += album_format.replace("{0}", albums[i].getField(MyOpenSpace.Album.Field.DEFAULT_IMAGE)).replace("{1}", albums[i].getField(MyOpenSpace.Album.Field.DEFAULT_IMAGE));
}
}
if(albums.length > 0){
albums_div.innerHTML += div_clear;
}
}
// 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 displayImage(src){
var data = "<img src=\"" + src + "\" />";
document.getElementById("extras_content").innerHTML = data;
document.getElementById("extras_content").style.display = "block";
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>