blob: 1f4deea23299d741f74f37635662d808ff3f9b34 [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var widget;
/*
* WidgetPreferences, singleton class
* Calls to this object replace the legacy setPreferenceForKey/preferenceForKey methods
* Implements HTML 5 Storage API
*/
var WidgetPreferences = new function WidgetPreferences() {
/*
* the internal preferences map
*/
this.prefs = {};
/*
* the number of key/value pairs currently present in preferences
*/
this.length = 0;
/**
* Resets the length attribute
*/
this.calcLength = function () {
var x = 0;
for (var key in this.prefs) {
x++;
}
this.length = x;
return x;
};
/**
* Returns the tuple for the key
* @param key the key to return the tuple for
*/
this.key = function (n) {
var x = 0;
for (var key in this.prefs) {
if (x == n) {
return key;
}
x++;
}
};
/**
* Returns the value of a key
* @param the key to return the value for
*/
this.getItem = function (key) {
if (!this.prefs[key]) {
return null;
}
return this.prefs[key]["dvalue"];
};
/**
* Create getters and setters for a preference
* @param key the key to set
* @param value the value to set (for IE)
* @param pref the preference object to set (for IE)
*/
this.createAccessorMethods = function (key, value, pref) {
try {
eval("Widget.preferences.__defineGetter__('" + key + "', function(){return Widget.preferences.getItem('" + key + "')})");
eval("Widget.preferences.__defineSetter__('" + key + "', function(v){return Widget.preferences.setItem('" + key + "',v)})");
eval("Widget.preferences.prefs[\"" + key + "\"]=pref;");
}
catch (err) {
//
// cant use __defineGetter__ so try to setup for IE9
//
try {
eval("Object.defineProperty(Widget.preferences,'" + key + "', {get: function get() { return Widget.preferences.getItem('" + key + "');},set: function set(value) {Widget.preferences.setItem('" + key + "',value)}});");
eval("Widget.preferences.prefs[\"" + key + "\"]=pref;");
}
catch (err2) {
//
// Catch IE 8 error. See WOOKIE-44
//
eval("Widget.preferences");
//
// If eval went fine, populate with data, See WOOKIE-151
//
if (typeof Widget.preferences != "undefined") {
widget.preferences[key] = value;
widget.preferences.prefs[key] = pref;
}
}
}
};
/**
* Sets a key to a value
* @param key the key to set
* @param value the value to set
*/
this.setItem = function (key, value) {
//
// Make a preference object and set its properties
//
var pref = {};
var oldValue = null;
pref.dvalue = value;
pref.key = key;
pref.readOnly = false;
//
// Check if the preference already exists
//
var existing = this.prefs[key];
//
// If it does exist, check it isn't read-only
//
if (existing) {
oldValue = this.getItem(key);
if (existing["readOnly"] === true) {
window.DOMException.code = DOMException.NO_MODIFICATION_ALLOWED_ERR;
throw (window.DOMException);
}
} else {
//
// Setup getters and setters for the new tuple
//
this.createAccessorMethods(key, value, pref);
}
//
// Set the pref in the preferences collection
// and persist it
//
this.prefs[key] = pref;
widget.setPreferenceForKey(key, value);
this.__raiseEvent(key, value, oldValue);
//
// Recalculate the length of the preferences collection
//
this.calcLength();
};
/**
* Removes a tuple from the preferences collection
* @param key the key of the tuple to remove
*/
this.removeItem = function (key) {
this.__removeItem(key, true);
};
/**
* Internal delete function
* @param key the key of the tuple to remove
* @param fireEvent boolean; true to fire Storage Event on remove
*/
this.__removeItem = function(key, fireEvent){
var existing = (this.prefs[key]);
if (existing) {
if (existing["readOnly"] === true) {
window.DOMException.code = DOMException.NO_MODIFICATION_ALLOWED_ERR;
throw (window.DOMException);
} else {
var oldValue = this.getItem(key);
delete this.prefs[key];
widget.setPreferenceForKey(key, null);
if (fireEvent) this.__raiseEvent(key, null, oldValue);
this.calcLength();
}
}
};
/**
* Clears all tuples from the preferences collection
*/
this.clear = function () {
for (var key in this.prefs) {
try {
if(this.prefs[key]["readOnly"] === false)
this.__removeItem(key, false);
} catch (e) {
// swallow errors, as this method must never throw them according to spec.
}
}
this.__raiseEvent(null,null,null);
};
this.__raiseEvent = function(key, value, oldValue){
//
// Raise custom storage event
//
var evt;
if(document.createEvent){
evt = document.createEvent("Event");
} else { // IE before v 9
evt = document.createEventObject(window.event);
}
var evt = document.createEvent("Event");
evt.initEvent("storage", false,false);
evt.key = key;
evt.newValue = value;
evt.oldValue = oldValue;
evt.url = window.location;
evt.storageArea = widget.preferences;
//
// Dispatch to child frames
//
var frames = window.frames;
for (var i=0;i<frames.length;i++){
try{
if(document.createEvent){
frames[i].dispatchEvent(evt);
} else { // IE before v 9
frames[i].fireEvent("storage", evt);
}
} catch(err) {
if (typeof console != "undefined") {
console.warn("error dispatching storage event to child frame; most likely this is due to same-origin restrictions");
}
}
}
}
};
/**
* Widget object
*/
var Widget = {
/**
* The ID key issued by wookie
*/
instanceid_key : null,
/**
* The URL to the server-side proxy
*/
proxyUrl : null,
/**
* Method called whenever widget shared data is updated. Used by Wave feature to register its state change callback.
*/
onSharedUpdate : null,
/**
* The preferences object; instantiated below
*/
preferences: null,
/**
* Set up the widget object
*/
init: function () {
//
// This page url will be called with e.g. idkey=4j45j345jl353n5lfg09cw03f05
// so grab that key to use as authentication against the server.
// Also get the proxy address and store it.
//
var query = window.location.search.substring(1);
var pairs = query.split("&");
for (var i = 0; i < pairs.length; i++) {
var pos = pairs[i].indexOf('=');
if (pos >= 0) {
var argname = pairs[i].substring(0, pos);
//
// get the id_key and assign it to instanceid_key.
//
if (argname == "idkey") {
this.instanceid_key = pairs[i].substring(pos + 1);
}
//
// TODO - remove this & use a callback instead of having it in the URL
//
if (argname == "proxy") {
this.proxyUrl = pairs[i].substring(pos + 1);
}
}
}
//
// First, we need to refresh the security token. The initial token in the URL
// is only usable for a short while.
//
this.refreshToken(false);
//
// Instantiate a Widget Preferences object, and load all values
// Note we do this synchronously, as widgets are likely
// to ask for a handle on this as an onLoad() event
//
this.preferences = WidgetPreferences;
//
// Load preferences using AJAX
//
this.loadPreferences();
//
// Load metadata using AJAX
//
this.loadMetadata();
},
/**
* Load widget metadata from Wookie API
*/
loadMetadata: function(){
var xml_request = new XMLHttpRequest();
xml_request.open("GET", "/wookie/metadata", false);
xml_request.setRequestHeader("Authorization",this.instanceid_key);
xml_request.onreadystatechange = function()
{
if(xml_request.readyState == 4 && xml_request.status == 200){
var json = (JSON.parse(xml_request.responseText));
Widget.setMetadata(json);
}
}
xml_request.setRequestHeader("Cache-Control", "no-cache");
xml_request.send(null);
},
/**
* Load preferences from Wookie API
*/
loadPreferences: function(){
var xml_request = new XMLHttpRequest();
xml_request.open("GET", "/wookie/preferences", false);
xml_request.setRequestHeader("Authorization",this.instanceid_key);
xml_request.onreadystatechange = function()
{
if(xml_request.readyState == 4 && xml_request.status == 200){
var json = (JSON.parse(xml_request.responseText));
Widget.setPrefs(json.Preferences);
}
}
xml_request.setRequestHeader("Cache-Control", "no-cache");
xml_request.send(null);
},
/**
* Refreshes the widget security token from Wookie API
* @param async whether to refresh the token asynchronously
*/
refreshToken: function(async){
var xml_request = new XMLHttpRequest();
xml_request.open("POST", "/wookie/token", async);
xml_request.setRequestHeader("Authorization",this.instanceid_key);
xml_request.onreadystatechange = function()
{
if(xml_request.readyState == 4 && xml_request.status == 201){
var json = (JSON.parse(xml_request.responseText));
Widget.instanceid_key = json.token;
}
}
xml_request.setRequestHeader("Cache-Control", "no-cache");
xml_request.send(null);
},
/**
* Set the metadata collection for this widget instance
* @param map the map of metadata items, consisting of a key and value
*/
setMetadata: function (map) {
for (var key in map) {
Widget.setMetadataProperty(key, map[key]);
}
},
/**
* Set an individual metadata property.
* @param key the metadata key
* @param value the metadata value
*/
setMetadataProperty: function (key, value) {
//
// The height and widget metadata items use Number values
//
if (key == "width" || key == "height") {
value = Number(value);
}
//
// Set up getters and setters; note that setters must return a
// NO_MODIFICATION_ALLOWED_ERR as metadata properties are read-only
//
try {
Widget.__defineSetter__(key, function () {
window.DOMException.code = DOMException.NO_MODIFICATION_ALLOWED_ERR;
throw (window.DOMException);
});
Widget.__defineGetter__(key, function () {
return value;
});
} catch (err) {
try {
//
// cant use __defineGetter__ so try to setup for IE9
//
Object.defineProperty(Widget, key, {
set: function set() {
window.DOMException.code = DOMException.NO_MODIFICATION_ALLOWED_ERR;
throw (window.DOMException);
},
get: function get() {
return value;
}
});
} catch (err2) {
//
// catch IE8
//
eval("Widget." + key + "='" + value + "';");
}
}
},
/**
* Set the preferences collection for this widget instance
* @param map the map of preference items, consisting of key, value and readonly flag
*/
setPrefs: function (map) {
this.preferences = WidgetPreferences;
this.preferences.prefs = {};
for (var i in map) {
var obj = map[i];
var key = obj["dkey"];
//
// Define getters and setters for each preference item
//
this.preferences.createAccessorMethods(key, obj["dvalue"], obj);
//
// Add the item to preferences collection
//
eval("this.preferences.prefs[\"" + key + "\"]=obj;");
}
this.preferences.calcLength();
Widget.setMetadataProperty("preferences",this.preferences);
},
/**
* Persist a preference object in the server backend
*/
setPreferenceForKey : function (wName, wValue) {
var xml_request = new XMLHttpRequest();
xml_request.open("POST", "/wookie/preferences?name="+wName+"&value="+wValue, true);
xml_request.setRequestHeader("Authorization",this.instanceid_key);
xml_request.setRequestHeader("Cache-Control", "no-cache");
xml_request.send(null);
},
/**
* Get the instance id key for this instance
*/
getInstanceKey : function () {
return this.instanceid_key;
},
/**
* Get the URL for the server-side proxy
*/
getProxyUrl : function () {
return this.proxyUrl;
},
/**
* Convert a given URL into a proxified version
* @param url the URL to convert
*/
proxify : function (url) {
return this.proxyUrl + "?instanceid_key=" + this.instanceid_key + "&url=" + url;
},
/**
* Return Widget as the object type
*/
toString: function () {
return "[object Widget]";
}
};
//
// Utility for getting an XMLHTTP object across different browsers
//
Widget.createXMLHTTPObject = function(){
try{return new XMLHttpRequest();}catch(e){}try{return new ActiveXObject("Msxml3.XMLHTTP");}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0");}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0");}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP");}catch(e){}try{return new ActiveXObject("Microsoft.XMLHTTP");}catch(e){}return null;
}
//
// Initialize the widget object
//
Widget.init();
//
// Avoid lower/uppercase confusion for the widget object
//
widget = Widget;
//
// Add widget object to the window object
//
window.widget = Widget;