mpviewplugins/mpdetailsviewplugin/tsrc/unittest_resources/test-src/publishplayer.js
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 27 May 2010 12:49:57 +0300
changeset 29 8192e5b5c935
permissions -rw-r--r--
Revision: 201019 Kit: 2010121

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;