mpviewplugins/mpdetailsviewplugin/tsrc/unittest_resources/test-src/publishplayer.js
changeset 29 8192e5b5c935
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpviewplugins/mpdetailsviewplugin/tsrc/unittest_resources/test-src/publishplayer.js	Thu May 27 12:49:57 2010 +0300
@@ -0,0 +1,1312 @@
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.contextobject = ovi.player.contextobject ||
+{};
+
+ovi.player.contextobject.getInstance = function(config) {
+    var player = {};
+    player._create = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "CREATE_PENDING");
+        }
+        // TODO: add context object to player store
+        // TODO: store options.miniview to be returned with view as HTML
+        // TODO: store other properties of options to be returned with view as JSON
+        // TODO: return uri (unique id in this player's store) in callback
+    };
+    player._cancel = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "CANCEL_PENDING");
+        }
+        // TODO: cancel pending tasks
+        // TODO: cancel Player
+    };
+    player._delete = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "DELETE_PENDING");
+        }
+        // TODO: delete object defined in options.uri from store
+    };
+    player._edit = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "EDIT_PENDING");
+        }
+        // TODO: update object defined in options.uri
+    };
+    player._pick = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "PICK_PENDING");
+        }
+        // TODO: return list of all objects, or object defined in options.uri, fork with options.view
+    };
+    player._view = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "VIEW_PENDING");
+        }
+        // TODO: return object defined in options.uri, JSON if options.view == "none", HTML if "mini"
+    };
+    ovi.player._getInstance.call(player, config);
+};
+
+ovi.player.contextobject.Player = ovi.player.contextobject.getInstance;
+/*
+ * Basic namespaces & common Player functions
+ */
+
+
+
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+
+
+
+/**
+ * A function for creating a callback chain
+ * @param {Object} options
+ * @param {Object} handler
+ * @param {Object} context
+ * @return {Function}
+ */
+ovi.player._notImplemented = function(functionId) {
+    return function() {
+        var status = ovi.player._status;
+        handler.call(context, status["NOT_IMPLEMENTED"], null);
+        handler.call(context, status[functionId + "_FAILURE"], null); // TODO: return description, too?
+    };
+};
+
+
+
+/**
+ * A method for returning an instance of a singleton
+ * @param {Object} options A configuration object for the player
+ */
+ovi.player._getInstance = function(options) {
+    // TODO: store the configuration options
+    return (function() {
+        return (this.instance ||
+        (function() {
+            this.instance = {
+                // Public interface
+                cancel: (this._cancel || ovi.player._notImplemented("CANCEL")),
+                create: (this._create || ovi.player._notImplemented("CREATE")),
+                "delete": (this._delete || ovi.player._notImplemented("DELETE")), // delete is a keyword
+                edit: (this._edit || ovi.player._notImplemented("EDIT")),
+                pick: (this._pick || ovi.player._notImplemented("PICK")),
+                show: (this._view || ovi.player._notImplemented("VIEW")), // show is the same function as view, included in API for compatibility
+                sync: (this._sync || ovi.player._notImplemented("SYNC")),
+                view: (this._view || ovi.player._notImplemented("VIEW"))
+            };
+            return this.instance;
+        }()));
+    }());
+};
+
+
+
+ovi.player._status = {
+    USER_CANCEL: "USER_CANCEL",
+    
+    CANCEL_PENDING: "CANCEL_PENDING",
+    CANCEL_FAILURE: "CANCEL_FAILURE",
+    CANCEL_SUCCESS: "CANCEL_SUCCESS",
+    
+    CREATE_PENDING: "CREATE_PENDING",
+    CREATE_FAILURE: "CREATE_FAILURE",
+    CREATE_SUCCESS: "CREATE_SUCCESS",
+    
+    DELETE_PENDING: "DELETE_PENDING",
+    DELETE_FAILURE: "DELETE_FAILURE",
+    DELETE_SUCCESS: "DELETE_SUCCESS",
+    
+    EDIT_PENDING: "EDIT_PENDING",
+    EDIT_FAILURE: "EDIT_FAILURE",
+    EDIT_SUCCESS: "EDIT_SUCCESS",
+    
+    PICK_PENDING: "PICK_PENDING",
+    PICK_FAILURE: "PICK_FAILURE",
+    PICK_SUCCESS: "PICK_SUCCESS",
+    
+    SYNC_PENDING: "SYNC_PENDING",
+    SYNC_FAILURE: "SYNC_FAILURE",
+    SYNC_SUCCESS: "SYNC_SUCCESS",
+    SYNC_CONNECT_PENDING: "SYNC_CONNECT_PENDING",
+    SYNC_CONNECT_FAILED: "SYNC_CONNECT_FAILED",
+    SYNC_CONNECT_SUCCEEDED: "SYNC_CONNECT_SUCCEEDED",
+    SYNC_LOGIN_PENDING: "SYNC_LOGIN_PENDING",
+    SYNC_LOGIN_FAILED: "SYNC_LOGIN_FAILED",
+    SYNC_LOGIN_SUCCEEDED: "SYNC_LOGIN_SUCCEEDED",
+    SYNC_UPDATE_PENDING: "SYNC_UPDATE_PENDING",
+    SYNC_UPDATE_FAILED: "SYNC_UPDATE_FAILED",
+    SYNC_UPDATE_SUCCEEDED: "SYNC_UPDATE_SUCCEEDED",
+    
+    VIEW_PENDING: "VIEW_PENDING",
+    VIEW_FAILURE: "VIEW_FAILURE",
+    VIEW_SUCCESS: "VIEW_SUCCESS",
+    
+    NOT_IMPLEMENTED: "NOT_IMPLEMENTED"
+};
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.publish = ovi.player.publish ||
+{};
+
+ovi.player.publish.getInstance = function(config) {
+    var player = {};
+    player._create = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "CREATE_PENDING");
+        }
+        // TODO: create UI
+        // TODO: get the sub-players, sync them
+    };
+    player._cancel = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "CANCEL_PENDING");
+        }
+        // TODO: cancel sub-players
+        // TODO: cancel pending tasks
+        // TODO: cancel Player
+    };
+    ovi.player._getInstance.call(player, config);
+};
+
+ovi.player.publish.Player = ovi.player.publish.getInstance;
+window["ovi"] = (window["ovi"] || {});
+ovi.player = (ovi.player || {});
+ovi.player.snc = (ovi.player.snc || {});
+ovi.player.snc.engine = (ovi.player.snc.engine || {});
+
+
+(function(){
+
+    var engine = ovi.player.snc.engine;
+	
+	
+	engine.status = {
+		SUCCESS : "SUCCESS",
+		FAILED : "FAILED",
+	}
+	
+	
+	var _callback = function(status, data, callback, context) {
+        if (typeof callback == "function") {
+            callback.call(context, status, data);
+        }
+	}
+    
+    // External functions	
+    
+    /**
+     * Publish the given object to the social networks.
+     * @param {Object} object Published data
+     * @param {String} object.text Free text
+     * @param {String} object.context.artist Artist name
+     * @param {String} object.context.title Song title
+     * @param {Array} object.networks Array of social networks ids where to publish
+     * @param {Object} [callback] Called when publish is ready.
+     * @param {Object} [context]
+     */
+    engine.publish = function(data, callback, context){
+        
+        // Just concatenate some status text from received data
+        var status = data.text;
+        
+		// Add attached objects (temporarily just append to end of text)
+        // TODO : object.type == "URI" ... etc., add as attachment when supported by SNC 		
+        if (typeof data.object != "undefined") {
+	         // Get URL to song before publishing and to status text
+            var link = data.object.content;
+            if (link.length > 0) {
+                status += " - " + link;
+            }
+        }
+		 
+        // Update to services 		
+        ovi.activities.updateStatus(status, {networkId: data.networks }, function(response){
+            
+			if (response.status == "success") {
+                _callback(engine.status.SUCCESS, null, callback, context);
+			}
+			else {
+				// TODO : Implement finer error handling
+				_callback(engine.status.FAILED, { message : "Status update failed" }, callback, context);
+			}										
+        });
+    }
+
+	/**
+	 * 
+	 * @param {Object} data
+	 * @param {Object} callback
+	 * @param {Object} context
+	 */
+    engine.cancelPublish = function(data, callback, context){
+		_callback(engine.status.SUCCESS, null, callback, context);
+    }
+    
+	
+	/**
+	 * Reload social networks from SNC backend to the local store. Use getServices() to retrieve the 
+	 * list of loaded services.
+	 * @param {Object} options Object containing Noa account information. Can be user, password or already
+	 * autheticated session token to be used. { username : "", password : "" } or { token : "" }
+	 * @param {Object} callback
+	 * @param {Object} context
+	 */
+    engine.sync = function(options, callback, context) {
+
+        // TODO : Seperate init and login from sync (we need both for publish also...)
+
+        // Do service didscovery
+		
+        // 1. Init APIs
+        requests.initOviApi(function(response){
+        
+		    if(response.status != engine.status.SUCCESS) {
+				_callback(engine.status.FAILED, response.data, callback, context);
+				return;
+			}
+		
+            // 2. Login noa     
+            requests.noaLogin(options, function(response){
+
+	            if(response.status != engine.status.SUCCESS) {
+	                _callback(engine.status.FAILED, { message: response.message }, callback, context);
+					return;
+	            }
+            
+                // 3. Service discover
+                requests.discover(function(response){
+
+                    // Store to player store
+					store.clear();
+					store.append(response.networks);
+					store.commit(); 
+					
+					// Callback function
+					if (response.status == engine.status.SUCCESS) {
+                        _callback(engine.status.OK, { networks: response.networks }, callback, context);	
+					}
+					else {
+						_callback(engine.status.FAILED, { message: response.message }, callback, context);
+					}					
+                });
+            });            
+        });
+    }		
+	
+	/**
+	 * Get list of services (social networks). Data is loaded from local 
+	 * player store. To synchronize data social networks from SNC call sync().
+	 */
+	engine.getServices = function() {
+		// Get list of services from store
+		return store.getList();
+	}
+	
+	/**
+	 * Mark service as selected / unselected. Saves the selection to the persistent 
+	 * local storage to save data over sessions.
+	 * @param {Object} id
+	 * @param {Object} enable
+	 */
+	engine.selectService = function(id, enable) {
+		// Select / unselect service in player store
+		store.select(id, enable);		
+	}
+		
+	// Player store	
+    var STORE_KEY_SERVICES = "ovi.player.snc.services";
+    var STORE_KEY_SELECTED = "ovi.player.snc.services.selected";	
+	
+	/**
+	 * Player store implementation. Persists the social networks (services) and selections 
+	 * to the HTML5 local storage, which provides over sessions caching.
+	 */
+	var store = {
+
+		_services : {},
+		_selected : {},
+				
+        /**
+         * Add new server or services to the store.
+         * @param {Object} service
+         */
+		append : function(service) {
+			if (service.length) {
+	            for(var i=0; i < service.length; i++) {
+	                var s = service[i];
+	                this._services[s.id] = s;      
+	            }
+			} else {
+				this._services[service.id] = service;
+			}
+		},
+		
+		/**
+		 * Mark service selected or unselected in the store. Also commits changes in 
+		 * selections to the store.
+		 * @param {Object} id
+		 * @param {Object} enable
+		 */
+		select : function(id, enable) {
+			this._selected[id] = enable;
+			localStorage.setItem(STORE_KEY_SELECTED, JSON.stringify(this._selected));
+		},
+		
+		/**
+		 * Clear services.
+		 */
+	    clear : function() {
+	   	    this._services = {};
+	    },
+		
+		/**
+		 * Get list (array) of services in the store.
+		 */
+		getList : function() {
+			// Convert to array
+			var res = [];
+			for(var o in this._services) {				
+				var serv = this._services[o];
+				serv.selected = typeof this._selected[o] != "undefined" ? this._selected[o] : false;
+				res.push(serv);
+			}			
+			return res;
+		},
+		
+		/**
+		 * Commit services to the store.
+		 */
+		commit : function() {
+			localStorage.setItem(STORE_KEY_SERVICES, JSON.stringify(this._services));
+		},
+		
+		/**
+		 * Retrieve services and selections in the store.
+		 */
+		load : function() {
+			this._services = JSON.parse(localStorage.getItem(STORE_KEY_SERVICES));
+			this._selected = JSON.parse(localStorage.getItem(STORE_KEY_SELECTED));
+
+			if (this._services == null) this._services = {};
+			if (this._selected == null) this._selected = {};			
+		}
+	}
+		
+	
+    // SNC Request implementation
+    var requests = {
+    
+        /**
+         *
+         * @param {Object} callback
+         */
+        initOviApi: function(callback){
+            var myincludes = "ovi.auth.noa_login, ovi.api.snc, ovi.api.activities, ovi.net.xss";
+            ovi.onReady = function(libs){
+                if (ovi.testIfLoaded(myincludes, libs)) {
+                
+                    // Successful, set environment for NCIM              
+                    ovi.config.setenv("st-account", "delegate");
+                    
+                    // Allow cross-domain scripting
+                    function xssInitCallback(data){
+                        if (data.event === "InitializeOk") {
+                            callback( { status : engine.status.SUCCESS });
+                        }
+                    }
+                    
+                    ovi.net.XssInit({
+                        back_url: "http://spb.ci.wipsl.com/ovi-api/js/ovi/net/",
+                        callback: xssInitCallback
+                    });
+                    
+                }
+            }
+            
+            ovi.include(myincludes);
+        },
+        
+        /**
+         *
+         * @param {Object} callback
+         */
+        noaLogin: function(options, callback){
+			
+			var noa = {};			
+			if (typeof options.token != "undefined") {   // This propably is not supported yet..
+				noa = { token : options.token };
+			}
+			else {
+				noa = { username : options.username, password : options.password };
+			}
+			
+			// Login
+            ovi.noa.login(noa, function(state){
+				            
+                // Callback
+                if (state.status == "success") {
+                    callback( {
+						status: engine.status.SUCCESS
+                    } );
+                }
+                else {
+					callback( {
+						status: engine.status.FAILED,
+						message: "NOA login failed - " + state.statusCode + " - " + state.status
+					} );
+                }
+            });
+        },
+        
+        /**
+         *
+         * @param {Object} callback
+         */
+        discover: function(callback){
+        
+            // Discover service available
+            ovi.snc.discoverSocialNetworks(function(response){
+            
+                if (response.status != "success") {
+                    callback( {
+						status: engine.status.FAILED,
+						message : "Service discovery failed - " + response.statusCode + " - " + response.responseText 
+                    } );
+                    return;
+                }
+				
+                var netw = response.data.network;
+                
+                // And get activated
+                ovi.snc.getActivatedSocialNetworks(function(response){                    
+
+                    if (response.status != "success") {
+	                    callback( {
+	                        status: engine.status.FAILED,
+	                        message : "Service discovery failed - " + response.statusCode + " - " + response.responseText 
+	                    } );
+												
+                        return;
+                    }
+					else if (response.statusCode == 204) { // Not an error, no active networks
+                        callback( {
+                            status: engine.status.SUCCESS,
+                            networks : [] 
+                        } );
+						
+						return;
+					}
+                    
+					var active = response.data.network;
+                    
+                    // Now we have finally the service discovery ready, create final response										
+					// Remove all not-active networks from supported networks					
+                    var results = [];					
+					for(var i=0; i < netw.length; i++) {
+						var id = netw[i].id;
+						
+						for(var j=0; j < active.length; j++) {
+							if (active[j].id == id) {								
+								// We have a winner
+								results.push({ name : netw[i].name, id : netw[i].id, maxTextInput : netw[i].maxTextInput });
+								break;
+							}
+						}
+					}					
+                    
+                    callback( { 
+					   status: engine.status.SUCCESS, 
+					   networks : results 
+                    } );                    
+                });
+            });
+        }
+    }
+    
+	// Restore data from store
+    store.load();
+    
+})();
+// Create needed namespace ovi.player.share.ui
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.share = ovi.player.share ||
+{};
+ovi.player.share.ui = ovi.player.share.ui ||
+{};
+
+/**
+ * TODO
+ * 
+ * This API tries to imitate medos framework UI controls for future compability.
+ * @param {Object} params
+ * @param {Object} target
+ */
+ovi.player.share.ui.Button = function(params, target) {
+    
+	var CLICK_EVENT = "selected";
+	var that = this;
+
+    function createHtml() {
+	    var template = '<button class="ovi_Button ovi_unselectable ovi_clip">' +
+	       params.text + '</button>';
+	
+	    var node = document.createElement("div");  // Do we need this ??
+	    node.innerHTML = template;
+		return node;		
+	}
+
+    var _root = createHtml();
+	var _button = _root.firstChild;
+ 
+    target.appendChild(_root);
+	
+	// Interface functions
+	this.addEventHandler = function(event, callback) {
+		// selected = click (comes from medos...)		
+		if (event == CLICK_EVENT) {
+            _root.addEventListener("click", function(e) { callback.call(that, e); }, that);	
+		}		 
+	} 	
+		
+    this.setProperty = function(name, value) {
+		_button[name] = value;
+	}
+	
+    this.getProperty = function(name) {
+        return _button[name];
+    }	
+}
+// Create needed namespace ovi.player.share.ui
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.share = ovi.player.share ||
+{};
+ovi.player.share.ui = ovi.player.share.ui ||
+{};
+
+/**
+ * Checkbox UI control implementation. TODO
+ * 
+ * This API tries to imitate medos framework UI controls for future compability.
+ * @param {Object} params
+ * @param {Object} target
+ */
+ovi.player.share.ui.CheckBox = function(params, target) {
+    
+	var that = this;
+
+    function createHtml() {
+		      
+        var template = '<div class="ovi_CheckBox ovi_unselectable ovi_clip">' +
+		                  '<input type="checkbox"></input>' +
+		                  '<span data-bind-text="label">' + params.label + '</span>' +
+					   '</div>';
+		
+	    var node = document.createElement("div");  // Do we need this ??
+	    node.innerHTML = template;
+		return node;		
+	}
+
+    var _root = createHtml();
+	var _checkBox = _root.firstChild.firstChild;
+ 
+    target.appendChild(_root);	
+	
+    this.setProperty = function(name, value) {
+		
+		if (name == "selected") { // Use medos property name mappings
+            _checkBox["checked"] = value;	
+		} 
+		else {
+			_checkBox = value;
+		}        
+    }
+	
+	this.getProperty = function(name) {
+		if (name == "selected") {
+			return _checkBox["checked"];
+		}
+		return _checkBox[name];
+	}
+	
+	for(var v in params) {
+		this.setProperty(v, params[v]);
+	}
+}
+// Create needed namespace ovi.player.share.ui
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+
+
+/**
+ * Context object player poc implementation
+ */
+ovi.player.contextobject = ovi.player.contextobject ||
+{};
+
+
+ovi.player.contextobject.Player = function() {
+	
+	var targetNS = this;	
+	targetNS.create = function(params) {
+		targetNS.object = params;		
+	}
+}
+
+
+
+/**
+ * Publish player poc implementation 
+ */
+ovi.player.publish = ovi.player.publish ||
+{};
+
+// Extend the namespace
+ovi.player.publish.Player = function(params) {
+
+    // TODO : MAKE THIS SINGLETON SOMEHOW ??
+    var targetNS = this;    
+
+    // Utils
+    var _id = function(id) {
+        return document.getElementById(id);
+    };
+	
+    var _addClass = function(target, className) {
+        var classes = target.className;
+        if (!classes.match(new RegExp("\\b" + className + "\\b"))) {
+            if (classes != "" && classes.substr(-1) != " ") {
+                target.className += " ";
+            }
+            target.className += className;
+        }
+    };
+    var _removeClass = function(target, className) {
+        target.className = target.className.replace(new RegExp("\\b" + className + "\\b", "g"), "");
+        //TODO: clean extra spaces?
+    
+    };
+    var _toggleClass = function(target, className) {
+        if (target.className.indexOf(className) == -1) {
+            _addClass(target, className);
+            return true;
+        } else {
+            _removeClass(target, className);
+            return false;
+        }
+        
+    };
+    
+    
+    
+    /**
+     * "State engine"
+     */
+    var _state = {
+        visible: false,
+        services: {}
+    };
+    
+    
+    
+    /**
+     * HTML templates for ui
+     */
+    var _templates = {
+        // TODO: localization
+        "share.ui": '<div class="player">\
+			<div class="panel header">Publish</div>\
+			<div class="panel panel-message">\
+				<div class="info hidden" id="comment-limit"><span id="comment-length">0</span>/<span id="comment-maxlength">0</span></div>\
+				<div class="comment" id="comment-area">\
+					<div class="hint">Write message here</div>\
+					<textarea id="message"></textarea>\
+				</div>\
+				<hr/><div class="info" id="shared-object"></div><hr/>\
+			</div>\
+			<div class="panel networks-panel"><ul class="list checked" id="available-services"></ul></div>\
+			<div class="panel panel-buttons">\
+				<ul class="list button-bar">\
+					<li id="action-ok" class="disabled"><span>OK</span></li>\
+					<li id="action-cancel"><span>Cancel</span></li>\
+				</ul>\
+			</div>\
+		</div>'
+    };
+    
+    
+    
+    /**
+     * Generic callback invoker
+     *
+     * @param {Object} notification
+     * @param {Object} data
+     * @param {Object} callback
+     * @param {Object} context
+     */
+    var _callback = function(notification, data, callback, context) {
+        if (typeof callback == "function") {
+            callback.call(context, notification, data);
+        }
+    };
+    
+    
+    
+    /**
+     * status messages
+     */
+    var _status = {
+    
+        show: "SHOW",
+        show_ok: "SHOW_SUCCEEDED",
+        show_fail: "SHOW_FAILED",
+        
+        updateobject: "UPDATEOBJECT",
+        updateobject_ok: "UPDATEOBJECT_SUCCEEDED",
+        updateobject_fail: "UPDATEOBJECT_FAILED",
+        
+        updateservices: "UPDATESERVICES",
+        updateservices_ok: "UPDATESERVICES_SUCCEEDED",
+        updateservices_fail: "UPDATESERVICES_FAILED",
+		updateservices_nonetworks : "UPDATESERVICES_NONETWORKS"
+    
+    };
+    
+    
+    
+    /**
+     * Invokes the ui for the player
+     *
+     * @param {Object} [options]
+     * @param {Object} [options.target] the dom node or id where the ui should be inserted
+     * @param {Object} [options.template] the html for the ui
+     * @param {Object} [callback]
+     * @param {Object} [context]
+     */
+    var _show = function(options, callback, context) {
+    
+        var target = (options && options.target) || _id("ovi.player.share.ui"), template = (options && options.template) || _templates["share.ui"];
+
+        // Find target node if id was given
+        if (typeof target == "string") {
+            target = _id(_target);
+        }
+        if (target) {
+            if (template) {
+                if (_state.visible && target.innerHTML.indexOf('"player"') >= 0) {
+                    // _show was already called earlier, and player is shown
+                    _callback(_status.show, {
+                        message: "Player is already visible"
+                    }, callback, context);
+                } else {
+                    target.innerHTML = template;
+                    
+                    // add handler for textarea
+                    var message = _id("message");
+                    _id("message").onkeyup = _handleMessageChange;
+                    message.style.backgroundColor = "transparent";
+                    
+                    // add handlers for buttons
+                    _id("action-ok").onclick = _handleSubmit;
+                    _id("action-cancel").onclick = _handleSubmit;
+                    
+                    // Update state
+                    _state.visible = true;
+                    _callback(_status.show_ok, null, callback, context);
+                }
+            } else {
+                _callback(_status.show_fail, {
+                    message: "Template not found",
+                    data: {
+                        template: template
+                    }
+                }, callback, context);
+            }
+        } else {
+            _callback(_status.show_fail, {
+                message: "Target container not found",
+                data: {
+                    target: target
+                }
+            }, callback, context);
+        }
+		
+		onResize();   // Initial resize
+		
+		// If we would like to show the services stored in local storage, uncommenting the following would do 
+		// the job..
+		/*
+		var services = ovi.player.share.engine.getServices();
+		targetNS.updateServices(services);*/ 		
+    };
+    
+    /**
+     * Removes all the handlers from the ui and the ui
+     *
+     * @param {Object} options
+     * @param {Object} callback
+     * @param {Object} context
+     */
+    var _teardown = function(options, callback, context) {
+        //TODO: clear handlers
+        //TODO: destroy html
+        //TODO: update status
+    };
+
+    
+    var _updateServices = function(data, callback, context) {
+    
+        var target = _id("available-services");
+        
+        if (target) {
+            if (data.length) {
+                var listToCheck = [], i;
+                // TODO: add loading animation?
+                for (i = 0; i < data.length; i++) {
+                    var id = data[i].id, label = data[i].name;
+                    if (id && label) {
+                        id = "service-" + id;
+                        listToCheck.push(id);
+                        if (!_id(id)) {
+                            // We don't have the network in our list yet, create one
+                            var item = document.createElement("LI"), checkbox = document.createElement("SPAN");
+                            checkbox.setAttribute("class", "checkbox");
+                            checkbox.appendChild(document.createTextNode(label));
+                            item.appendChild(checkbox);
+                            item.setAttribute("id", id);
+                            item.onclick = _handleServiceListClick;
+                            target.appendChild(item);
+                            // store object to the state
+							if (data[i].selected) {
+                                data[i].checked = _toggleClass(item, "checked");
+							}							
+                            _state.services[id] = data[i];
+                        }
+                    } else {
+                        _callback(_status.updateservices, {
+                            message: "Service object formatted badly",
+                            data: data[i]
+                        }, callback, context);
+                    }
+                }
+                //make a searchable string
+                listToCheck = listToCheck.join();
+                //remove obsolete networks
+                for (i = 0; i < target.childNodes.length; i++) {
+                    var node = target.childNodes[i], id = node.getAttribute("id");
+                    if (listToCheck.indexOf(id) == -1) {
+                        node.onclick = null;
+                        target.removeChild(node);
+                    }
+                    // TODO: update _state?
+                }
+				_updateServiceLimits();
+                _callback(_status.updateservices_ok, null, callback, context);				
+            } else {
+                _callback(_status.updateservices_fail, {
+                    message: "Received data was not an array",
+                    data: data
+                }, callback, context);
+            }
+        } else {
+            _callback(_status.updateservices_fail, {
+                message: "Target container not found"
+            }, callback, context);
+        }
+    };
+    
+    /**
+     * Handler for message changes
+     * @param {Object} e
+     */
+    var _handleMessageChange = function(e) {
+        _id("comment-length").innerHTML = this.value.length;
+        _checkMessageLength();
+    }
+    
+    
+    
+    /**
+     * Handler for service selection
+     * @param {Object} e
+     */
+    var _handleServiceListClick = function(e) {
+        _state.services[this.id].checked = _toggleClass(this, "checked");
+		ovi.player.snc.engine.selectService(_state.services[this.id].id, _state.services[this.id].checked);
+        _updateServiceLimits();
+    };
+    
+    
+    
+    /**
+     * Check limits and if the OK button can be clicked
+     */
+    var _updateServiceLimits = function() {
+        var bigNumber = 9999999, maxTextInput = bigNumber, min = Math.min, services = _state.services, service;
+        for (sid in services) {
+            service = services[sid];
+            if (service.checked && service.maxTextInput && service.maxTextInput > 0) {
+                maxTextInput = min(maxTextInput, service.maxTextInput);
+            }
+        }
+		
+        if (maxTextInput < bigNumber) {
+            // Reduce the link and context reserved size from input (data that will be allocated 
+			// from maxTextInput for context)
+			var contextData = _state.contextPlayer.object.data;
+            maxTextInput -= contextData.object.reservedLength;
+			     			
+            _id("comment-maxlength").innerHTML = maxTextInput;
+            _removeClass(_id("comment-limit"), "hidden");
+            //TODO: alter size of elements to make space for the limit?
+        } else {
+            _id("comment-maxlength").innerHTML = maxTextInput;
+            _addClass(_id("comment-limit"), "hidden");
+        }
+        _checkMessageLength();
+    };
+    
+    
+    
+    var _checkMessageLength = function() {
+        var length = parseInt(_id("comment-length").innerHTML), maxLength = parseInt(_id("comment-maxlength").innerHTML), message = _id("message");
+        if (length == 0) {
+            message.style.backgroundColor = "transparent";
+        } else {
+            message.style.backgroundColor = "";
+        }
+        if ((maxLength > 0) && (length > maxLength)) {
+            _addClass(_id("comment-limit"), "error");
+            _addClass(message, "error");
+        } else {
+            _removeClass(_id("comment-limit"), "error");
+            _removeClass(message, "error");
+        }
+        _updateActions();
+    };
+    
+    
+    
+    var _updateActions = function() {
+        if (_canSubmit()) {
+            _removeClass(_id("action-ok"), "disabled");
+        } else {
+            _addClass(_id("action-ok"), "disabled");
+        }
+    };
+    var _canSubmit = function() {
+        var length = parseInt(_id("comment-length").innerHTML), maxLength = parseInt(_id("comment-maxlength").innerHTML), services = _state.services, service;
+        if ((maxLength > 0) && (length > maxLength)) {
+            return false;
+        }
+        for (sid in services) {
+            service = services[sid];
+            if (service.checked && service.maxTextInput && service.maxTextInput > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    
+    var _handleSubmit = function(e) {
+        // don't accept clicks from elements with class "disabled"
+        if (/\bdisabled\b/.test(this.className)) {
+            return;
+        }
+        if (this.id == "action-ok") {
+            _addClass(this, "disabled");
+            _addClass(_id("action-cancel"), "disabled");
+            //TODO: disable testarea and service list?
+            
+            var services = _state.services, service, networks = [];
+            for (sid in services) {
+                service = services[sid];
+                if (service.checked) {
+                    networks.push(service.id);
+                }
+            }
+			
+			var contextData = _state.contextPlayer.object.data;
+			
+            var data = {
+                text: _id("message").value,				
+                object : contextData.object,
+                networks: networks
+            };
+			
+            ovi.player.snc.engine.publish(data, function(status, data) {
+				if (status == ovi.player.snc.engine.status.FAILED) {
+					alert(data.message);
+				}
+                _reset();
+            });
+            // TODO: call window.close here? teardown first? window.close in teardown?
+        } else {
+            ovi.player.snc.engine.cancelPublish(null, function(status, data) {
+                _reset();
+            });
+        }
+    };
+    
+    var _reset = function() {
+        _id("shared-object").innerHTML = "";
+        _id("message").value = "";
+		_handleMessageChange.call(_id("message"));
+        _removeClass(_id("action-cancel"), "disabled");
+		
+		// Request window to be closed
+		window.close();		
+    };
+    
+	var _sync = function(contextPlayer, handler, context) {    
+        // Sync context to UI
+        _state.contextPlayer = contextPlayer; // Store context object for publishing
+        _id("shared-object").innerHTML = _state.contextPlayer.object.data.miniview;          
+    }	
+	
+	
+	var _sncInit = function(credentials, handler, context) {
+        // Sync SNC stuff
+        ovi.player.snc.engine.sync(credentials.token, function(status, data) {
+            if (status == ovi.player.snc.engine.status.FAILED) {
+                _callback(_status.updateservices_fail, 
+				    { message: data.message }, 
+					handler, context);				    
+            }
+            else if (data.networks.length == 0) {
+                _callback(_status.updateservices_nonetworks, 
+				          { message: "No networks" }, 
+						  handler, context);
+            }
+            else {
+                var services = ovi.player.snc.engine.getServices();
+                _updateServices(services, handler, context); 
+            }               
+        } );           		
+	}
+		   
+
+
+    // Register onResize for landscape / portrait mode changes
+    window.addEventListener("resize", onResize, true);
+    
+    function onResize(e) {
+        
+        function isLandscape(){
+            return window.innerWidth > window.innerHeight;
+        }   
+		
+		function _setMode(mode) {
+            _changeMode(_id("comment-area"), mode);
+            
+            var services = _id("available-services").children;                      
+            for(var i=0; i < services.length; i++) {
+                _changeMode(services[i], mode);  
+            }           			
+		}
+		        
+        // Determine - landscape or portrait mode
+        if (isLandscape()) {		
+			_setMode("landscape");						
+        }
+        else {
+            _setMode("portrait");			
+        }       
+    }
+	
+	function _changeMode(id, mode) {
+		if (mode == "portrait") {
+            _removeClass(id, "landscape");
+			_addClass(id, "portrait");				
+		} else {
+            _removeClass(id, "portrait");
+			_addClass(id, "landscape");              			
+		}	   	
+	}
+	
+    /**
+     * Assign needed functions to the target namespace => defined public API.
+     */
+    targetNS.view = _show;
+    targetNS.teardown = _teardown;
+    targetNS.reset = _reset;    
+    
+    /**
+     * Synchronize context data from context object
+     */
+    targetNS.sync = _sync;
+    
+    /**
+     * Launch UI and initialize SNC
+     */
+    targetNS.create = function(options, handler, context) {
+        
+        // Show UI
+        _show(options, handler, context);
+        
+        // Start loading SNC networks
+        _sncInit(params.credentials, handler, context);
+    }			
+	
+	targetNS.status = _status;
+};
+// Create needed namespace ovi.player.share.ui
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.share = ovi.player.share ||
+{};
+ovi.player.share.ui = ovi.player.share.ui ||
+{};
+
+/**
+ * Label UI control implementation. TODO
+ * 
+ * This API tries to imitate medos framework UI controls for future compability.
+ * @param {Object} params
+ * @param {Object} target
+ */
+ovi.player.share.ui.Label = function(params, target) {
+    
+	var CLICK_EVENT = "selected";
+	var that = this;
+
+    function createHtml() {
+	    var template = '<span class="ovi_Label">' + params.text + '</span>';
+	
+	    var node = document.createElement("div");  // Do we need this ??
+	    node.innerHTML = template;
+		return node;		
+	}
+
+    var _root = createHtml();
+	var _label = _root.firstChild;
+ 
+    target.appendChild(_root);
+	
+    this.setProperty = function(name, value) {
+		_label[name] = value;
+	}
+	
+    this.getProperty = function(name) {
+        return _label[name];
+    }
+	
+    for(var v in params) {
+        this.setProperty(v, params[v]);
+    }
+}
+// Create needed namespace ovi.player.share.ui
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.share = ovi.player.share ||
+{};
+ovi.player.share.ui = ovi.player.share.ui ||
+{};
+
+/**
+ * Textarea UI control implementation. TODO
+ * 
+ * This API tries to imitate medos framework UI controls for future compability.
+ * @param {Object} params
+ * @param {Object} target
+ */
+ovi.player.share.ui.TextArea = function(params, target) {
+    
+	var CLICK_EVENT = "selected";
+	var that = this;
+
+    function createHtml() {
+	    var template = '<textarea class="ovi_TextInput ovi_Textarea" rows="2"></textarea>';
+	
+	    var node = document.createElement("div");  // Do we need this ??
+	    node.innerHTML = template;
+		return node;		
+	}
+
+    var _root = createHtml();
+	var _textarea = _root.firstChild;
+ 
+    target.appendChild(_root);
+	
+    this.setProperty = function(name, value) {
+		_textarea[name] = value;
+	}
+	
+	this.getProperty = function(name) {
+		return _textarea[name];
+	}
+	
+    for(var v in params) {
+        this.setProperty(v, params[v]);
+    }	
+}
+window["ovi"] = window["ovi"] ||
+{};
+ovi.player = ovi.player ||
+{};
+ovi.player.snc = ovi.player.snc ||
+{};
+
+ovi.player.snc.getInstance = function(config) {
+    var player = {};
+    player._cancel = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "CANCEL_PENDING");
+        }
+        // TODO: cancel Player
+    };
+    player._pick = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "PICK_PENDING");
+        }
+        // TODO: offer list of configured networks
+    };
+    player._view = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "VIEW_PENDING");
+        }
+        // TODO: offer miniviews for networks list
+    };
+    player._sync = function(options, handler, context) {
+        var callable = (typeof handler === "function");
+        if (callable) {
+            handler.call(context, "SYNC_PENDING");
+        }
+        // TODO: connect to the Ovi API and SNC
+    };
+    ovi.player._getInstance.call(player, config);
+};
+
+ovi.player.snc.Player = ovi.player.snc.getInstance;