<commit_before>/*
 *     (C) Copyright 2012 Universidad Politécnica de Madrid
 *
 *     This file is part of Wirecloud Platform.
 *
 *     Wirecloud Platform is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Wirecloud is distributed in the hope that it will be useful, but WITHOUT
 *     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 *     License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Wirecloud Platform.  If not, see
 *     <http://www.gnu.org/licenses/>.
 *
 */

/*global gettext, StyledElements, Wirecloud*/

(function () {

    "use strict";

    /**
     * Simple Description Constructor
     *
     */
    var SimpleDescription = function SimpleDescription(tutorial, options) {
        this.options = options;
        this.element = options.elem;
        this.tutorial = tutorial;
        this.layer = tutorial.msgLayer;
        this.last = false;
        this.pos = options.pos;
        this.title = options.title;
        this.nextButtonText = gettext('Next');
        if (options.nextButtonText) {
            this.nextButtonText = options.nextButtonText;
        }

        Wirecloud.ui.WindowMenu.call(this, this.title, 'simpleDescription');

        this.windowContent.innerHTML = options.msg;

        this.nextButton = new StyledElements.StyledButton({
            'title': gettext("next"),
            'class': 'nextButton btn-primary',
            'text': gettext(this.nextButtonText),
        });

        this.nextButton.insertInto(this.windowBottom);

        // Cancel button
        this.cancelButton = new StyledElements.StyledButton({
            'title': gettext("Cancel"),
            'class': 'cancelButton',
            'text': gettext("Cancel"),
        });
        this.cancelButton.insertInto(this.windowBottom);
        this.cancelButton.addEventListener('click', this._closeListener);

        this.layer.appendChild(this.htmlElement);

        this.wrapperElement = this.htmlElement;
    };
    SimpleDescription.prototype = new Wirecloud.ui.WindowMenu();

    SimpleDescription.prototype._closeListener = function _closeListener(e) {
        Wirecloud.ui.WindowMenu.prototype._closeListener.call(this, e);
        this.tutorial.destroy();
    };

    /**
     * get style position.
     */
    SimpleDescription.prototype.getStylePosition = function getStylePosition() {
        var coordinates;
        coordinates = {
            posX: parseInt(this.htmlElement.style.left, 10),
            posY: parseInt(this.htmlElement.style.top, 10)
        };
        return coordinates;
    };

    /**
     * set position.
     */
    SimpleDescription.prototype.setPosition = function setPosition(coordinates) {
        this.htmlElement.style.left = coordinates.posX + 'px';
        this.htmlElement.style.top = coordinates.posY + 'px';
    };

    /**
     * set this SimpleDescription as the last one, don't need next button.
     */
    SimpleDescription.prototype.setLast = function setLast(buttonLabel, optionalHandler) {
        this.last = true;
        Wirecloud.Utils.removeFromParent(this.nextButton.wrapperElement);
        if (buttonLabel == null) {
            buttonLabel = gettext('Close');
        }
        this.cancelButton.setLabel(gettext(buttonLabel));
        if (optionalHandler != null) {
            this.cancelButton.removeEventListener('click', this._closeListener);
            this.cancelButton.addEventListener('click', optionalHandler.bind(this));
        }
    };

    /**
     * set next handler
     */
    SimpleDescription.prototype.setNext = function setNext() {
        this.nextButton.addEventListener('click', function () {
            if (this.element != null) {
                this.element.removeClassName('tuto_highlight');
            }
            this.tutorial.nextStep();
        }.bind(this));
    };

    /**
     * activate this step
     */
    var _activate = function _activate() {
        this.htmlElement.classList.add("activeStep");
        if (typeof this.element === 'function') {
            this.element = this.element();
            this.tutorial.setControlLayer(this.element, true);
            this.element.classList.add('tuto_highlight');
        } else {
            this.tutorial.resetControlLayer(false);
        }
        if (this.element != null) {
            pos = this.element.getBoundingClientRect();
            switch (this.pos) {
                case 'up':
                    this.htmlElement.style.top = (pos.top - this.htmlElement.offsetHeight - 20) + 'px';
                    break;
                case 'right':
                    this.htmlElement.style.left = (pos.right + 20) + 'px';
                    break;
                case 'left':
                    this.htmlElement.style.left = (pos.left - this.htmlElement.offsetWidth - 20) + 'px';
                    break;
                default:
                case 'down':
                    this.htmlElement.style.top = (pos.bottom + 20) + 'px';
                    break;
            }
        } else {
            this.htmlElement.style.top = ((window.innerHeight / 2) - (this.htmlElement.offsetHeight / 2)) + 'px';
            this.htmlElement.style.left = ((window.innerWidth / 2) - (this.htmlElement.offsetWidth / 2)) + 'px';
        }
        this.nextButton.focus();
    };

     /**
     * activate this step
     */
    SimpleDescription.prototype.activate = function activate() {
        if (this.options.asynchronous) {
            this.element(_activate.bind(this));
        } else {
            _activate.call(this);
        }
    };

    /*************************************************************************
     * Make SimpleDescription public
     *************************************************************************/
    Wirecloud.ui.Tutorial.SimpleDescription = SimpleDescription;

})();
<commit_msg>Fix bug in tutorial code<commit_after>/*
 *     (C) Copyright 2012 Universidad Politécnica de Madrid
 *
 *     This file is part of Wirecloud Platform.
 *
 *     Wirecloud Platform is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Wirecloud is distributed in the hope that it will be useful, but WITHOUT
 *     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 *     License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Wirecloud Platform.  If not, see
 *     <http://www.gnu.org/licenses/>.
 *
 */

/*global gettext, StyledElements, Wirecloud*/

(function () {

    "use strict";

    /**
     * Simple Description Constructor
     *
     */
    var SimpleDescription = function SimpleDescription(tutorial, options) {
        this.options = options;
        this.element = options.elem;
        this.tutorial = tutorial;
        this.layer = tutorial.msgLayer;
        this.last = false;
        this.pos = options.pos;
        this.title = options.title;
        this.nextButtonText = gettext('Next');
        if (options.nextButtonText) {
            this.nextButtonText = options.nextButtonText;
        }

        Wirecloud.ui.WindowMenu.call(this, this.title, 'simpleDescription');

        this.windowContent.innerHTML = options.msg;

        this.nextButton = new StyledElements.StyledButton({
            'title': gettext("next"),
            'class': 'nextButton btn-primary',
            'text': gettext(this.nextButtonText),
        });

        this.nextButton.insertInto(this.windowBottom);

        // Cancel button
        this.cancelButton = new StyledElements.StyledButton({
            'title': gettext("Cancel"),
            'class': 'cancelButton',
            'text': gettext("Cancel"),
        });
        this.cancelButton.insertInto(this.windowBottom);
        this.cancelButton.addEventListener('click', this._closeListener);

        this.layer.appendChild(this.htmlElement);

        this.wrapperElement = this.htmlElement;
    };
    SimpleDescription.prototype = new Wirecloud.ui.WindowMenu();

    SimpleDescription.prototype._closeListener = function _closeListener(e) {
        Wirecloud.ui.WindowMenu.prototype._closeListener.call(this, e);
        this.tutorial.destroy();
    };

    /**
     * get style position.
     */
    SimpleDescription.prototype.getStylePosition = function getStylePosition() {
        var coordinates;
        coordinates = {
            posX: parseInt(this.htmlElement.style.left, 10),
            posY: parseInt(this.htmlElement.style.top, 10)
        };
        return coordinates;
    };

    /**
     * set position.
     */
    SimpleDescription.prototype.setPosition = function setPosition(coordinates) {
        this.htmlElement.style.left = coordinates.posX + 'px';
        this.htmlElement.style.top = coordinates.posY + 'px';
    };

    /**
     * set this SimpleDescription as the last one, don't need next button.
     */
    SimpleDescription.prototype.setLast = function setLast(buttonLabel, optionalHandler) {
        this.last = true;
        Wirecloud.Utils.removeFromParent(this.nextButton.wrapperElement);
        if (buttonLabel == null) {
            buttonLabel = gettext('Close');
        }
        this.cancelButton.setLabel(gettext(buttonLabel));
        if (optionalHandler != null) {
            this.cancelButton.removeEventListener('click', this._closeListener);
            this.cancelButton.addEventListener('click', optionalHandler.bind(this));
        }
    };

    /**
     * set next handler
     */
    SimpleDescription.prototype.setNext = function setNext() {
        this.nextButton.addEventListener('click', function () {
            if (this.element != null) {
                this.element.removeClassName('tuto_highlight');
            }
            this.tutorial.nextStep();
        }.bind(this));
    };

    /**
     * activate this step
     */
    var _activate = function _activate() {
        this.htmlElement.classList.add("activeStep");
        if (typeof this.element === 'function') {
            this.element = this.element();
            this.tutorial.setControlLayer(this.element, true);
            this.element.classList.add('tuto_highlight');
        } else {
            this.tutorial.resetControlLayer(false);
        }
        if (this.element != null) {
            var pos = this.element.getBoundingClientRect();
            switch (this.pos) {
                case 'up':
                    this.htmlElement.style.top = (pos.top - this.htmlElement.offsetHeight - 20) + 'px';
                    break;
                case 'right':
                    this.htmlElement.style.left = (pos.right + 20) + 'px';
                    break;
                case 'left':
                    this.htmlElement.style.left = (pos.left - this.htmlElement.offsetWidth - 20) + 'px';
                    break;
                default:
                case 'down':
                    this.htmlElement.style.top = (pos.bottom + 20) + 'px';
                    break;
            }
        } else {
            this.htmlElement.style.top = ((window.innerHeight / 2) - (this.htmlElement.offsetHeight / 2)) + 'px';
            this.htmlElement.style.left = ((window.innerWidth / 2) - (this.htmlElement.offsetWidth / 2)) + 'px';
        }
        this.nextButton.focus();
    };

     /**
     * activate this step
     */
    SimpleDescription.prototype.activate = function activate() {
        if (this.options.asynchronous) {
            this.element(_activate.bind(this));
        } else {
            _activate.call(this);
        }
    };

    /*************************************************************************
     * Make SimpleDescription public
     *************************************************************************/
    Wirecloud.ui.Tutorial.SimpleDescription = SimpleDescription;

})();
<|endoftext|>


<commit_before>window.livedesk.init = function() {
		var self = this,
			contentPath = self.contentPath === undefined? '': self.contentPath;
		if (typeof jQuery == 'undefined') {
			self.loadScript('http://code.jquery.com/jquery-1.7.2.min.js', function(){
				if (typeof $.gizmo == 'undefined') {
					self.loadScript(contentPath+'gizmo.js', function(){
						self.startLoading();
					})
				}
			})
		} else {
			if (typeof $.gizmo == 'undefined') {
				self.loadScript(contentPath+'gizmo.js', function(){
					self.startLoading();
				})
			} else {
				self.startLoading();
			}
		}
	};

window.livedesk.loadScript = function (src, callback) {
		var script = document.createElement("script")
		script.type = "text/javascript";
		if (script.readyState) { //IE
			script.onreadystatechange = function () {
				if (script.readyState == "loaded" || script.readyState == "complete") {
					script.onreadystatechange = null;
					callback();
				}
			};
		} else { //Others
			script.onload = function () {
				callback();
			};
		}
		script.src = src;
		document.getElementsByTagName("head")[0].appendChild(script);
	};
window.livedesk.startLoading = function() {
		var
		User = $.gizmo.Model.extend({}),
/*		PostType = $.gizmo.Model.extend({}),
		AuthorPerson = $.gizmo.Model.extend({}),
		PostBlog = $.gizmo.Model.extend({}),
		Author = $.gizmo.Model.extend({}),
*/
		Post = $.gizmo.Model.extend
		({
			defaults:
			{
				Creator: User
			},
			services: {
				'flickr': true,
				'google': true,
				'twitter': true,
				'facebook': true,
				'youtube': true
			},
			/**
			* Get css class based on type
			*
			* @return {string}
			*/
			getClass: function() {
				switch (this.get('Type').Key) {
					case 'wrapup':
						return 'wrapup';
						break;

					case 'quote':
						return 'quotation';
						break;

					case 'advertisement':
						return 'advertisement';
						break;

					default:
						if (this.isService()) {
							return 'service';
						}

						return 'tw';
				}
			},
			/**
			* Test if post is from service
			*
			* @return {bool}
			*/
			isService: function() {
				return this.get('AuthorName') in this.services;
			},

			/**
			* Test if post is quote
			*
			* @return {bool}
			*/
			isQuote: function() {
				return this.getClass() == 'quotation';
			},
			twitter: {
				link: {
					anchor: function(str)
					{
						return str.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/g, function(m)
						{
							m = m.link(m);
							m = m.replace('href="','target="_blank" href="');
							return m;
						});
					},
					user: function(str)
					{
						return str.replace(/[@]+[A-Za-z0-9-_]+/g, function(us)
						{
							var username = us.replace("@","");

							us = us.link("http://twitter.com/"+username);
							us = us.replace('href="','target="_blank" onclick="loadProfile(\''+username+'\');return(false);"  href="');
							return us;
						});
					},
					tag: function(str)
					{
						return str.replace(/[#]+[A-Za-z0-9-_]+/g, function(t)
						{
							var tag = t.replace(" #"," %23");
							t = t.link("http://summize.com/search?q="+tag);
							t = t.replace('href="','target="_blank" href="');
							return t;
						});
					},
					all: function(str)
					{
						str = this.anchor(str);
						str = this.user(str);
						str = this.tag(str);
						return str;
					}
				}
			}
		}),
		AutoCollection = $.gizmo.Collection.extend({
			timeInterval: 10000,
			idInterval: 0,
			_latestCId: 0,
			setIdInterval: function(fn){
				this.idInterval = setInterval(fn, this.timeInterval);
				return this;
			},
			getMaximumCid: function(data){
				for(i=0, count=data.list.length; i<count; i++) {
					var CId = parseInt(data.list[i].get('CId'))
					if( !isNaN(CId) && (this._latestCId < CId) )
						this._latestCId = CId;
				}
			},
			auto: function(){
				var self = this, requestOptions = {data: {'startEx.cId': this._latestCId}, headers: { 'X-Filter': 'CId'}};
				if(this._latestCId === 0) delete requestOptions.data;
				$.gizmo.Collection.prototype.sync.call(this,requestOptions).done(function(data){
					self.getMaximumCid(self.parse(data));
				});
				return this;
			},
			pause: function(){
				var self = this;
				clearInterval(self.idInterval);
				return this;
			},
			sync: function(){
				var self = this;
				this.auto().pause().setIdInterval(function(){self.auto();});
			}
		});
		Posts = AutoCollection.extend({
			model: Post
		}),

		Blog = $.gizmo.Model.extend
		({
			defaults:
			{
				Post: Posts,
				PostPublished: Posts,
				PostUnpublished: Posts
			}
		});

		var i=0,
		PostItemView = $.gizmo.View.extend
		({
			init: function()
			{
				var self = this;
				self.post
					.on('read', self.render, self)
					.on('update', function(evt, data){
						if(isOnly(data, 'CId')) {
							self.post.sync(); //.xfilter(self.xfilter)
						}
						else
							self.render(evt, data);
					})
					.on('delete', self.remove, self)
					.sync();
			},
			remove: function()
			{
				var self = this;
				self.tightkNots();
				self.el.remove();
				return self;
			},
			tightkNots: function()
			{
				if(this.next !== undefined)
					this.next.prev = this.prev;
				if(this.prev !== undefined)
					this.prev.next = this.next;
			},
			render: function()
			{
				var self = this, order = parseFloat(self.post.get('Order'));
				if ( !isNaN(self.order) && (order != self.order)) {
					var actions = { prev: 'insertBefore', next: 'insertAfter' }, ways = { prev: 1, next: -1}, anti = { prev: 'next', next: 'prev'}
					for( var dir = (self.order - order > 0)? 'next': 'prev', cursor=self[dir];
						(cursor[dir] !== undefined) && ( cursor[dir].order*ways[dir] < order*ways[dir] );
						cursor = cursor[dir]
					);
					var other = cursor[dir];
					if(other !== undefined)
						other[anti[dir]] = self;
					cursor[dir] = self;
					self.tightkNots();
					self[dir] = other;
					self[anti[dir]] = cursor;
					self.el[actions[dir]](cursor.el);
				}
				self.order = order;
				var content = self.post.get('Content');

				var style= '';
				if (self.post.getClass() == 'wrapup') {
					style += ' open';
				}
				if (self.post.isService()) {
					style += ' ' + self.post.get('AuthorName');

                                        var meta = JSON.parse(self.post.get('Meta'));
                                        var annotation = '';
                                        if( meta.annotation) {
                                            for (var i=0; i < meta.annotation.length; i++ ) {
                                                if(meta.annotation[i]) {
                                                    annotation += meta.annotation[i];
                                                }
                                            }
                                        }

					if (self.post.get('AuthorName') == 'flickr') {
						var paddedContent = '' + content + '';
						var jqo = $(paddedContent);
						jqo.find('img').attr('src', jqo.find('a').attr('href'));
						content = jqo.html();
					} else if (self.post.get('AuthorName') == 'twitter') {
						content = self.post.twitter.link.all(content);
					}

                                        content = annotation + content;

				}
				var template = '
' + content + '
';
				self.setElement( template );
			}
		}),
		TimelineView = $.gizmo.View.extend
		({
			el: '#livedesk-root',
			events:
			{
				'[uberclick="ceva-click-shucar"]': {
					'click': 'altceva'
				}
			},
			init: function()
			{
				var self = this;
				if($.type(self.url) === 'string')
					self.blog = new Blog(self.url.replace('my/',''));
				self.blog.on('read', function()
				{
					self.blog.get('PostPublished').on('read', self.render, self).xfilter('CId').sync();
				})
				.sync();
			},
			addOne: function(model)
			{
				current = new PostItemView({post: model, _parent: this});
				this.el.prepend(current.el);
				current.next = this._latest;
				if( this._latest !== undefined )
					this._latest.prev = current;
				this._latest = current;
			},
			addAll: function(data)
			{
				if ( data === undefined)
					data = this.blog.get('PostPublished')._list;
				var next = this._latest, current, model, i = data.length;
				while(i--) {
					this.addOne(data[i]);
				}
			},
			render: function(evt, data)
			{
				if ( data === undefined)
					data = this.blog.get('PostPublished')._list;
				var next = this._latest, current, model, i = data.length;
				while(i--) {
					this.addOne(data[i]);
				}
			}

		});
		window.livedesk.TimelineView = TimelineView;
		window.livedesk.callback();
	};
window.livedesk.init();<commit_msg>NR-192 : All elements of the liveblog must be linkable<commit_after>window.livedesk.init = function() {
		var self = this,
			contentPath = self.contentPath === undefined? '': self.contentPath;
		if (typeof jQuery == 'undefined') {
			self.loadScript('http://code.jquery.com/jquery-1.7.2.min.js', function(){
				if (typeof $.gizmo == 'undefined') {
					self.loadScript(contentPath+'gizmo.js', function(){
						self.startLoading();
					})
				}
			})
		} else {
			if (typeof $.gizmo == 'undefined') {
				self.loadScript(contentPath+'gizmo.js', function(){
					self.startLoading();
				})
			} else {
				self.startLoading();
			}
		}
	};

window.livedesk.loadScript = function (src, callback) {
		var script = document.createElement("script")
		script.type = "text/javascript";
		if (script.readyState) { //IE
			script.onreadystatechange = function () {
				if (script.readyState == "loaded" || script.readyState == "complete") {
					script.onreadystatechange = null;
					callback();
				}
			};
		} else { //Others
			script.onload = function () {
				callback();
			};
		}
		script.src = src;
		document.getElementsByTagName("head")[0].appendChild(script);
	};
window.livedesk.startLoading = function() {
		var
		User = $.gizmo.Model.extend({}),
/*		PostType = $.gizmo.Model.extend({}),
		AuthorPerson = $.gizmo.Model.extend({}),
		PostBlog = $.gizmo.Model.extend({}),
		Author = $.gizmo.Model.extend({}),
*/
		Post = $.gizmo.Model.extend
		({
			defaults:
			{
				Creator: User
			},
			services: {
				'flickr': true,
				'google': true,
				'twitter': true,
				'facebook': true,
				'youtube': true
			},
			/**
			* Get css class based on type
			*
			* @return {string}
			*/
			getClass: function() {
				switch (this.get('Type').Key) {
					case 'wrapup':
						return 'wrapup';
						break;

					case 'quote':
						return 'quotation';
						break;

					case 'advertisement':
						return 'advertisement';
						break;

					default:
						if (this.isService()) {
							return 'service';
						}

						return 'tw';
				}
			},
			/**
			* Test if post is from service
			*
			* @return {bool}
			*/
			isService: function() {
				return this.get('AuthorName') in this.services;
			},

			/**
			* Test if post is quote
			*
			* @return {bool}
			*/
			isQuote: function() {
				return this.getClass() == 'quotation';
			},
			twitter: {
				link: {
					anchor: function(str)
					{
						return str.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/g, function(m)
						{
							m = m.link(m);
							m = m.replace('href="','target="_blank" href="');
							return m;
						});
					},
					user: function(str)
					{
						return str.replace(/[@]+[A-Za-z0-9-_]+/g, function(us)
						{
							var username = us.replace("@","");

							us = us.link("http://twitter.com/"+username);
							us = us.replace('href="','target="_blank" onclick="loadProfile(\''+username+'\');return(false);"  href="');
							return us;
						});
					},
					tag: function(str)
					{
						return str.replace(/[#]+[A-Za-z0-9-_]+/g, function(t)
						{
							var tag = t.replace(" #"," %23");
							t = t.link("http://summize.com/search?q="+tag);
							t = t.replace('href="','target="_blank" href="');
							return t;
						});
					},
					all: function(str)
					{
						str = this.anchor(str);
						str = this.user(str);
						str = this.tag(str);
						return str;
					}
				}
			}
		}),
		AutoCollection = $.gizmo.Collection.extend({
			timeInterval: 10000,
			idInterval: 0,
			_latestCId: 0,
			setIdInterval: function(fn){
				this.idInterval = setInterval(fn, this.timeInterval);
				return this;
			},
			getMaximumCid: function(data){
				for(i=0, count=data.list.length; i<count; i++) {
					var CId = parseInt(data.list[i].get('CId'))
					if( !isNaN(CId) && (this._latestCId < CId) )
						this._latestCId = CId;
				}
			},
			auto: function(){
				var self = this, requestOptions = {data: {'startEx.cId': this._latestCId}, headers: {'X-Filter': 'CId'}};
				if(this._latestCId === 0) delete requestOptions.data;
				$.gizmo.Collection.prototype.sync.call(this,requestOptions).done(function(data){
					self.getMaximumCid(self.parse(data));
				});
				return this;
			},
			pause: function(){
				var self = this;
				clearInterval(self.idInterval);
				return this;
			},
			sync: function(){
				var self = this;
				this.auto().pause().setIdInterval(function(){self.auto();});
			}
		});
		Posts = AutoCollection.extend({
			model: Post
		}),

		Blog = $.gizmo.Model.extend
		({
			defaults:
			{
				Post: Posts,
				PostPublished: Posts,
				PostUnpublished: Posts
			}
		});

		var i=0,
		PostItemView = $.gizmo.View.extend
		({
			init: function()
			{
				var self = this;
				self.post
					.on('read', self.render, self)
					.on('update', function(evt, data){
						if(isOnly(data, 'CId')) {
							self.post.sync(); //.xfilter(self.xfilter)
						}
						else
							self.render(evt, data);
					})
					.on('delete', self.remove, self)
					.sync();
			},
			remove: function()
			{
				var self = this;
				self.tightkNots();
				self.el.remove();
				return self;
			},
			tightkNots: function()
			{
				if(this.next !== undefined)
					this.next.prev = this.prev;
				if(this.prev !== undefined)
					this.prev.next = this.next;
			},
			render: function()
			{
				var self = this, order = parseFloat(self.post.get('Order'));
				if ( !isNaN(self.order) && (order != self.order)) {
					var actions = {prev: 'insertBefore', next: 'insertAfter'}, ways = {prev: 1, next: -1}, anti = {prev: 'next', next: 'prev'}
					for( var dir = (self.order - order > 0)? 'next': 'prev', cursor=self[dir];
						(cursor[dir] !== undefined) && ( cursor[dir].order*ways[dir] < order*ways[dir] );
						cursor = cursor[dir]
					);
					var other = cursor[dir];
					if(other !== undefined)
						other[anti[dir]] = self;
					cursor[dir] = self;
					self.tightkNots();
					self[dir] = other;
					self[anti[dir]] = cursor;
					self.el[actions[dir]](cursor.el);
				}
				self.order = order;
				var content = self.post.get('Content');

				var style= '';
				if (self.post.getClass() == 'wrapup') {
					style += ' open';
				}
				if (self.post.isService()) {
					style += ' ' + self.post.get('AuthorName');

                                        var meta = JSON.parse(self.post.get('Meta'));
                                        var annotation = '';
                                        if( meta.annotation) {
                                            for (var i=0; i < meta.annotation.length; i++ ) {
                                                if(meta.annotation[i]) {
                                                    annotation += meta.annotation[i];
                                                }
                                            }
                                        }

					if (self.post.get('AuthorName') == 'flickr') {
						var paddedContent = '' + content + '';
						var jqo = $(paddedContent);
						jqo.find('img').attr('src', jqo.find('a').attr('href'));
						content = jqo.html();
					} else if (self.post.get('AuthorName') == 'twitter') {
						content = self.post.twitter.link.all(content);
					}

                                        content = annotation + content;

				}
                                var postId = self.post.get('Id');
                                var blogTitle = self._parent.blog.get('Title');
                                blogTitle = blogTitle.replace(/ /g, '-');
				var template ='' + '
' + content + '
';
				self.setElement( template );
			}
		}),
		TimelineView = $.gizmo.View.extend
		({
			el: '#livedesk-root',
			events:
			{
				'[uberclick="ceva-click-shucar"]': {
					'click': 'altceva'
				}
			},
			init: function()
			{
				var self = this;
				if($.type(self.url) === 'string')
					self.blog = new Blog(self.url.replace('my/',''));
				self.blog.on('read', function()
				{
					self.blog.get('PostPublished').on('read', self.render, self).xfilter('CId').sync();
				})
				.sync();
			},
			addOne: function(model)
			{
				current = new PostItemView({post: model, _parent: this});
				this.el.prepend(current.el);
				current.next = this._latest;
				if( this._latest !== undefined )
					this._latest.prev = current;
				this._latest = current;
			},
			addAll: function(data)
			{
				if ( data === undefined)
					data = this.blog.get('PostPublished')._list;
				var next = this._latest, current, model, i = data.length;
				while(i--) {
					this.addOne(data[i]);
				}
			},
			render: function(evt, data)
			{
				if ( data === undefined)
					data = this.blog.get('PostPublished')._list;
				var next = this._latest, current, model, i = data.length;
				while(i--) {
					this.addOne(data[i]);
				}
			}

		});
		window.livedesk.TimelineView = TimelineView;
		window.livedesk.callback();
	};
window.livedesk.init();
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Amadeus s.a.s.
 * 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.
 */

Aria.classDefinition({
    $classpath : "test.aria.widgets.container.dialog.dynamicZIndex.DynamicZIndexRobotBase",
    $extends : "aria.jsunit.RobotTestCase",
    $dependencies : ["aria.popups.PopupManager", "aria.utils.Dom"],
    $constructor : function () {
        this.$RobotTestCase.constructor.call(this);
        this.data = {
            nonModalDialog1Visible: true,
            nonModalDialog1X: 16,
            nonModalDialog1Y: 25,
            nonModalDialog1Width: 304,
            nonModalDialog1Height: 223,
            nonModalDialog1Center: false,
            nonModalDialog2Visible: true,
            nonModalDialog2X: 106,
            nonModalDialog2Y: 70,
            nonModalDialog2Width: 403,
            nonModalDialog2Height: 232,
            nonModalDialog2Center: false,
            nonModalDialog3Visible: true,
            nonModalDialog3X: 25,
            nonModalDialog3Y: 205,
            nonModalDialog3Width: 502,
            nonModalDialog3Height: 223,
            nonModalDialog3Center: false
        };
        this.setTestEnv({
            template: "test.aria.widgets.container.dialog.dynamicZIndex.DynamicZIndexTestTpl",
            data : this.data
        });
    },
    $prototype : {

        assertDialogOrder : function (expectedOrder) {
            var dialogOrder = this.getDialogOrder();
            this.assertJsonEquals(dialogOrder, expectedOrder);
        },

        getDialogOrder : function () {
            var counts = {
                "nonModalDialog1" : 0,
                "nonModalDialog2" : 0,
                "nonModalDialog3" : 0
            };
            this.incrementPopup(counts, 124, 223); // 1, 2 or 3
            this.incrementPopup(counts, 124, 115); // 1 or 2
            this.incrementPopup(counts, 430, 223); // 2 or 3
            this.incrementPopup(counts, 34, 223); // 1 or 3
            return ["nonModalDialog1", "nonModalDialog2", "nonModalDialog3"].sort(function (a, b) {
                var res = counts[b] - counts[a];
                if (res === 0) {
                    throw new Error("Same score for 2 popups: " + a + " and " + b);
                }
                return res;
            });
        },

        incrementPopup : function (counts, x, y) {
            var popup = this.getPopupNameFromElement(this.testDocument.elementFromPoint(x, y));
            counts[popup]++;
            if (isNaN(counts[popup])) {
                throw new Error("Unexpected popup: " + popup);
            }
        },

        getPopupFromElement : function (element) {
            var popups = aria.popups.PopupManager.openedPopups;
            var foundPopup;
            for (var i = 0, l = popups.length; i < l; i++) {
                var curPopup = popups[i];
                var curPopupAncestor = aria.utils.Dom.isAncestor(element, curPopup.domElement);
                if (curPopupAncestor) {
                    foundPopup = curPopup;
                    break;
                }
            }
            return foundPopup;
        },

        getPopupNameFromElement: function (element) {
            var foundPopup = this.getPopupFromElement(element);
            if (foundPopup) {
                var dialogTitle = this.getElementsByClassName(foundPopup.domElement, "xDialog_title");
                if (dialogTitle.length > 0) {
                    return dialogTitle[0].innerHTML;
                }
                return "unknown";
            }
            return "none";
        },

        waitForZIndexChange : function (cb) {
            var document = this.testDocument;
            var initialTopMostPopup = this.getPopupNameFromElement(document.elementFromPoint(124, 223));
            this.waitFor({
                condition: function () {
                    return initialTopMostPopup !== this.getPopupNameFromElement(document.elementFromPoint(124, 223));
                },
                callback: cb
            });
        }
    }
});
<commit_msg>fix/test Making DynamicZIndexClickRobotTestCase more reliable<commit_after>/*
 * Copyright 2015 Amadeus s.a.s.
 * 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.
 */

Aria.classDefinition({
    $classpath : "test.aria.widgets.container.dialog.dynamicZIndex.DynamicZIndexRobotBase",
    $extends : "aria.jsunit.RobotTestCase",
    $dependencies : ["aria.popups.PopupManager", "aria.utils.Dom"],
    $constructor : function () {
        this.$RobotTestCase.constructor.call(this);
        this.data = {
            nonModalDialog1Visible: true,
            nonModalDialog1X: 16,
            nonModalDialog1Y: 25,
            nonModalDialog1Width: 304,
            nonModalDialog1Height: 223,
            nonModalDialog1Center: false,
            nonModalDialog2Visible: true,
            nonModalDialog2X: 106,
            nonModalDialog2Y: 70,
            nonModalDialog2Width: 403,
            nonModalDialog2Height: 232,
            nonModalDialog2Center: false,
            nonModalDialog3Visible: true,
            nonModalDialog3X: 25,
            nonModalDialog3Y: 205,
            nonModalDialog3Width: 502,
            nonModalDialog3Height: 223,
            nonModalDialog3Center: false
        };
        this.setTestEnv({
            template: "test.aria.widgets.container.dialog.dynamicZIndex.DynamicZIndexTestTpl",
            data : this.data
        });
    },
    $prototype : {

        assertDialogOrder : function (expectedOrder) {
            var dialogOrder = this.getDialogOrder();
            this.assertJsonEquals(dialogOrder, expectedOrder);
        },

        getDialogOrder : function () {
            var counts = {
                "nonModalDialog1" : 0,
                "nonModalDialog2" : 0,
                "nonModalDialog3" : 0
            };
            this.incrementPopup(counts, 124, 223); // 1, 2 or 3
            this.incrementPopup(counts, 124, 115); // 1 or 2
            this.incrementPopup(counts, 430, 223); // 2 or 3
            this.incrementPopup(counts, 34, 223); // 1 or 3
            return ["nonModalDialog1", "nonModalDialog2", "nonModalDialog3"].sort(function (a, b) {
                var res = counts[b] - counts[a];
                if (res === 0) {
                    throw new Error("Same score for 2 popups: " + a + " and " + b);
                }
                return res;
            });
        },

        incrementPopup : function (counts, x, y) {
            var popup = this.getPopupNameFromElement(this.testDocument.elementFromPoint(x, y));
            counts[popup]++;
            if (isNaN(counts[popup])) {
                throw new Error("Unexpected popup: " + popup);
            }
        },

        getPopupFromElement : function (element) {
            var popups = aria.popups.PopupManager.openedPopups;
            var foundPopup;
            for (var i = 0, l = popups.length; i < l; i++) {
                var curPopup = popups[i];
                var curPopupAncestor = aria.utils.Dom.isAncestor(element, curPopup.domElement);
                if (curPopupAncestor) {
                    foundPopup = curPopup;
                    break;
                }
            }
            return foundPopup;
        },

        getPopupNameFromElement: function (element) {
            var foundPopup = this.getPopupFromElement(element);
            if (foundPopup) {
                var dialogTitle = this.getElementsByClassName(foundPopup.domElement, "xDialog_title");
                if (dialogTitle.length > 0) {
                    return dialogTitle[0].innerHTML;
                }
                return "unknown";
            }
            return "none";
        },

        waitForZIndexChange : function (cb) {
            var document = this.testDocument;
            var initialTopMostPopup = this.getPopupNameFromElement(document.elementFromPoint(124, 223));
            this.waitFor({
                condition: function () {
                    var newTopMostPopup = this.getPopupNameFromElement(document.elementFromPoint(124, 223));
                    // depending on how the click is done by the robot, there can be the beginning of a drag
                    // in which case an overlay is displayed on top of everything for a short time, during which
                    // newTopMostPopup = "none"
                    return initialTopMostPopup !== newTopMostPopup && newTopMostPopup !== "none";
                },
                callback: cb
            });
        }
    }
});
<|endoftext|>


<commit_before>var util = require("./ixputil"),
pack = util.pack;

var QTDIR = 0x80;
var Qid = ["i1:type", "i4:ver", "i8:path"];
var lastquid = 0;

var packets = {
    100: {name: "Tversion", fmt: ["i4:size", "i1:type", "i2:tag", "i4:msize", "S2:version"]},
    101: {name: "Rversion", fmt: ["i4:size", "i1:type", "i2:tag", "i4:msize", "S2:version"]},
    102: {name: "Tauth", fmt: ["i4:size", "i1:type", "i2:tag", "S2:uname", "S2:aname"]},
    104: {name: "Tattach", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i4:afid", "S2:uname", "S2:aname"]},
    105: {name: "Rattach", fmt: ["i4:size", "i1:type", "i2:tag", "b13:qid"]},
    107: {name: "Rerror", fmt: ["i4:size", "i1:type", "i2:tag", "S2:ename"]},
    108: {name: "Tflush", fmt: ["i4:size", "i1:type", "i2:tag", "i2:oldtag"]},
    109: {name: "Rflush", fmt: ["i4:size", "i1:type", "i2:tag"]},
    110: {name: "Twalk", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i4:newfid", "i2:nwname", "R:wname"]},
    111: {name: "Rwalk", fmt: ["i4:size", "i1:type", "i2:tag", "i2:nqid", "R:qids"]},
    112: {name: "Topen", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i1:mode"]},
    113: {name: "Ropen", fmt: ["i4:size", "i1:type", "i2:tag", "b13:qid", "i4:iounit"]},
    114: {name: "Tcreate", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "S2:name", "i4:perm", "i1:mode"]},
    116: {name: "Tread", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i8:offset", "i4:count"]},
    117: {name: "Rread", fmt: ["i4:size", "i1:type", "i2:tag", "S4:data"]},
    118: {name: "Twrite", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i8:offset", "S4:data"]},
    119: {name: "Rwrite", fmt: ["i4:size", "i1:type", "i2:tag", "i4:count"]},
    120: {name: "Tclunk", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"]},
    121: {name: "Rclunk", fmt: ["i4:size", "i1:type", "i2:tag"]},
    122: {name: "Tremove", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"]},
    124: {name: "Tstat", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"]},
    125: {name: "Rstat", fmt: ["i4:size", "i1:type", "i2:tag", "S2:stat"]},
    126: {name: "Twstat", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "S2:stat"]}
};

module.exports.packets = packets;

var msgtype = {};
for (var p in packets) {
    //console.log(""+p+" "+JSON.stringify(packets[p].name+" -> "+packets[p].fmt));
    module.exports[packets[p].name] = p;
    msgtype[packets[p].name] = p;
}

module.exports.Service = {
    tree: {},
    fids: [],
    answer: function(p) {
        if (this.verbose) { console.log(p); }
        if (packets[p.type] !== undefined) {
            var handler = packets[p.type].name;
            if (this[handler] !== undefined) {
              var response = this[handler](p);
              if (this.verbose) {console.log(response);}
              return response;
            }
         }
        var err = this.error9p(p.tag, "unimplemented type="+p.type);
        if (this.verbose) {console.log(err);}
        return err;
    },

    error9p: function(tag, msg) {
        return this.send9p({type:msgtype.Rerror, tag:tag, ename:msg});
    },
    send9p: function(p){
        return p;
    },
    Tauth: function(p){
        return this.error9p(p.tag, "no auth necessary");
    },

    Tversion: function(p){
        p.type = msgtype.Rversion;
        return this.send9p(p);
    },

    Tattach: function(p){
        if (this.fids[p.fid] !== undefined) return this.error9p(p.tag, "fid already in use");
        this.fids[p.fid] = { f: this.tree, open: false};
        return this.send9p({type: msgtype.Rattach, tag: p.tag, qid: pack(this.tree.qid, Qid)});
    },

    Twalk: function(p){
        if(this.fids[p.fid] === undefined) return this.error9p(p.tag, "fid not in use");
        if(this.fids[p.newfid] !== undefined) return this.error9p(p.tag, "fid already in use");
        var f = this.fids[p.fid];
        if (p.nwname === 0) {
          this.fids[p.newfid] = this.fids[p.fid];
          return this.send9p({type:msgtype.Rwalk, tag:p.tag, nqid:0});
        }
        if (p.nwname === 1) {
          var nf = f.f.lookup(p.wname[0]);
          this.fids[p.newfid] = {f: nf, open:false};
          //what if lookup failed? catch exception, return this.error9p(p.tag, "not found");
          return this.send9p({type:msgtype.Rwalk, tag:p.tag, nqid:1, qids:[nf.qid]});
        }
        return this.error9p(p.tag, "Can't do plaid!"); //should be "walk multiple steps not yet implemented"
    },

    Topen: function(p){
        var node = this.fids[p.fid];
        if (node === undefined) { return this.error9p(p.tag, "no such fid"); }
        //will ignore node.f.open for now
        var reason = must_deny_access(node.f, p.mode);
        if (!reason) {
          if (isDir(node.f)) { node.f.bloc = node.f.nloc = 0;}
          node.open=true;
         //console.log(node);
          return this.send9p({type:msgtype.Ropen, tag:p.tag, qid: pack(node.f.qid, Qid), iounit:0});
        } else {
            return this.error9p(p.tag, "permission denied: "+reason);
        }
    },

    Tread: function(p){
        var node = this.fids[p.fid];
        if (node === undefined) return this.error9p(p.tag, "no such fid");
        if (node.open !== true) return this.error9p(p.tag, "fid not open");
        if (node.f.qid.type & QTDIR) return read_dirent(this, p, node);
        if (node.f.read === undefined) return this.error9p(p.tag, "permission denied");
        return read_file(this, p, node);
    },
    ////
    Tclunk: function(p){
        if (this.fids[p.fid] === undefined) return this.error9p(p.tag, "fid not in use");
        delete this.fids[p.fid];
        return this.send9p({type:msgtype.Rclunk, tag:p.tag});
    }
};

var must_deny_access = function(f, mode) {
  var reason;
  if (isDir(f)) { return (mode & 3) ? "directories are read only" : false; }
  if (mode & 3 === 3) { return "OEXEC not implemented";}
  if (mode & 3 && f.write === undefined) { return "read only";}
  return false;
};

var read_file = function(service, packet, node) {
  var data = node.f.read(packet.offset, packet.count);
  return service.send9p({type:msgtype.Rread, tag:packet.tag, count:5, data:data});
};

var read_dirent = function(service, packet, f) {
  //sends one dirent at a time, provided packet.offset===f.bloc and the dirent fits into packet.count
  var reply = {type:msgtype.Rread, tag:packet.tag};
  if (packet.offset === 0) f.bloc = f.nloc = 0;
  if (packet.offset != f.bloc) return service.error9p(packet.tag, "seek in directory illegal "+
    packet.offset +"!=" +f.bloc);
  if (packet.count < 2) return service.error9p(packet.tag, "read too short");
  if (f.nloc >= f.f.nchildren.length) {
    reply.data = "";
    return service.send9p(reply);
  }
  var child = f.f.nchildren[f.nloc];
  var s = exports.dirent(child);
  if (packet.count < s.length) {
    reply.data = s.substring(0,2);
    return service.send9p(reply);
  }
  f.bloc += s.length;
  f.nloc++;
  reply.data = s;
  return service.send9p(reply);
};

// here (for now) is dirent
exports.dirent = function(f){
  var fmt= ["i2:type", "i4:dev", "b13:qid", "i4:mode", "i4:atime", "i4:mtime", "i8:length", "S2:name", "S2:uid", "S2:gid", "S2:muid"];
  var now = new Date().getTime() / 1000;
  var s = { type: 0, dev: 0, qid: pack(f.qid, Qid), mode: 0, atime: now, mtime: now, length: 0,
    name: f.name, uid: "js", gid: "js", muid: "js"};
  if(f.qid.type & QTDIR) { s.mode |= 0111; s.mode += 0x80000000; }
  var packed = pack(s, fmt);
  var size = pack({ a:packed.length },["i2:a"]);
  return size + packed;
};

// filesystem funcs
var mkfile = function(path, open, read, write, close) {
    var f, parent, n;
    parent = this.lookup(path, true);
    path = path.split('/');
    n = path[path.length - 1];
    f = {
        name: n,
        parent: parent,
        qid: {type: 0, ver: 0, path: ++lastquid},
        open: open, read: read, write: write, close: close};
    parent.children[n] = f;
    parent.nchildren.push(f);
    return f;
};

var mkdir = function(path){
    var f, parent, name;
    parent = this.lookup(path, true);
    path = path.split('/');
    n = path[path.length-1];

    f = {
        mkfile: mkfile,
        mkdir: mkdir,
        lookup: lookup,
        name: n, parent: parent.name, children: {}, nchildren: [],
        qid: {type: QTDIR, ver: 0, path: ++lastquid}};
    parent.children[n] = f;
    parent.nchildren.push(f);

    return f;
};

function lookup(path, getParent) {
    var f, s, n, x;

    f = this;
    s = path.split('/');
    for(x in s){
        if(getParent && x == s.length - 1)
            break;
        n = s[x];
        if(n === "" || n == ".")
            continue;
        if(n == "..")
            f = f.parent;
        else {
            f = f.children[n];
            if(f === undefined)
                throw path + " not found";
        }
    }
    return f;
}

var isDir = exports.isDir = function(i){
    return (i.qid.type === QTDIR);
};
var isFile = exports.isFile = function(i){
    return (i.qid.type !== QTDIR);
};

module.exports.mkroot = function(){
    return {
        name: "/",
        children: {},
        nchildren: [],
        qid: {type: QTDIR, ver: 0, path: 0},
        mkfile: mkfile,
        mkdir: mkdir,
        lookup: lookup
    };
};
<commit_msg>require a fid for attach<commit_after>var util = require("./ixputil"),
pack = util.pack;

var QTDIR = 0x80;
var Qid = ["i1:type", "i4:ver", "i8:path"];
var lastquid = 0;

var packets = {
    100: {name: "Tversion", fmt: ["i4:size", "i1:type", "i2:tag", "i4:msize", "S2:version"]},
    101: {name: "Rversion", fmt: ["i4:size", "i1:type", "i2:tag", "i4:msize", "S2:version"]},
    102: {name: "Tauth", fmt: ["i4:size", "i1:type", "i2:tag", "S2:uname", "S2:aname"]},
    104: {name: "Tattach", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i4:afid", "S2:uname", "S2:aname"]},
    105: {name: "Rattach", fmt: ["i4:size", "i1:type", "i2:tag", "b13:qid"]},
    107: {name: "Rerror", fmt: ["i4:size", "i1:type", "i2:tag", "S2:ename"]},
    108: {name: "Tflush", fmt: ["i4:size", "i1:type", "i2:tag", "i2:oldtag"]},
    109: {name: "Rflush", fmt: ["i4:size", "i1:type", "i2:tag"]},
    110: {name: "Twalk", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i4:newfid", "i2:nwname", "R:wname"]},
    111: {name: "Rwalk", fmt: ["i4:size", "i1:type", "i2:tag", "i2:nqid", "R:qids"]},
    112: {name: "Topen", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i1:mode"]},
    113: {name: "Ropen", fmt: ["i4:size", "i1:type", "i2:tag", "b13:qid", "i4:iounit"]},
    114: {name: "Tcreate", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "S2:name", "i4:perm", "i1:mode"]},
    116: {name: "Tread", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i8:offset", "i4:count"]},
    117: {name: "Rread", fmt: ["i4:size", "i1:type", "i2:tag", "S4:data"]},
    118: {name: "Twrite", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "i8:offset", "S4:data"]},
    119: {name: "Rwrite", fmt: ["i4:size", "i1:type", "i2:tag", "i4:count"]},
    120: {name: "Tclunk", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"]},
    121: {name: "Rclunk", fmt: ["i4:size", "i1:type", "i2:tag"]},
    122: {name: "Tremove", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"]},
    124: {name: "Tstat", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid"]},
    125: {name: "Rstat", fmt: ["i4:size", "i1:type", "i2:tag", "S2:stat"]},
    126: {name: "Twstat", fmt: ["i4:size", "i1:type", "i2:tag", "i4:fid", "S2:stat"]}
};

module.exports.packets = packets;

var msgtype = {};
for (var p in packets) {
    //console.log(""+p+" "+JSON.stringify(packets[p].name+" -> "+packets[p].fmt));
    module.exports[packets[p].name] = p;
    msgtype[packets[p].name] = p;
}

module.exports.Service = {
    tree: {},
    fids: [],
    answer: function(p) {
        if (this.verbose) { console.log(p); }
        if (packets[p.type] !== undefined) {
            var handler = packets[p.type].name;
            if (this[handler] !== undefined) {
              var response = this[handler](p);
              if (this.verbose) {console.log(response);}
              return response;
            }
         }
        var err = this.error9p(p.tag, "unimplemented type="+p.type);
        if (this.verbose) {console.log(err);}
        return err;
    },

    error9p: function(tag, msg) {
        return this.send9p({type:msgtype.Rerror, tag:tag, ename:msg});
    },
    send9p: function(p){
        return p;
    },
    Tauth: function(p){
        return this.error9p(p.tag, "no auth necessary");
    },

    Tversion: function(p){
        p.type = msgtype.Rversion;
        return this.send9p(p);
    },

    Tattach: function(p){
        if (p.fid === undefined) return this.error9p(p.tag, "attach requires a fid");
        if (this.fids[p.fid] !== undefined) return this.error9p(p.tag, "fid already in use");
        this.fids[p.fid] = { f: this.tree, open: false};
        return this.send9p({type: msgtype.Rattach, tag: p.tag, qid: pack(this.tree.qid, Qid)});
    },

    Twalk: function(p){
        if(this.fids[p.fid] === undefined) return this.error9p(p.tag, "fid not in use");
        if(this.fids[p.newfid] !== undefined) return this.error9p(p.tag, "fid already in use");
        var f = this.fids[p.fid];
        if (p.nwname === 0) {
          this.fids[p.newfid] = this.fids[p.fid];
          return this.send9p({type:msgtype.Rwalk, tag:p.tag, nqid:0});
        }
        if (p.nwname === 1) {
          var nf = f.f.lookup(p.wname[0]);
          this.fids[p.newfid] = {f: nf, open:false};
          //what if lookup failed? catch exception, return this.error9p(p.tag, "not found");
          return this.send9p({type:msgtype.Rwalk, tag:p.tag, nqid:1, qids:[nf.qid]});
        }
        return this.error9p(p.tag, "Can't do plaid!"); //should be "walk multiple steps not yet implemented"
    },

    Topen: function(p){
        var node = this.fids[p.fid];
        if (node === undefined) { return this.error9p(p.tag, "no such fid"); }
        //will ignore node.f.open for now
        var reason = must_deny_access(node.f, p.mode);
        if (!reason) {
          if (isDir(node.f)) { node.f.bloc = node.f.nloc = 0;}
          node.open=true;
         //console.log(node);
          return this.send9p({type:msgtype.Ropen, tag:p.tag, qid: pack(node.f.qid, Qid), iounit:0});
        } else {
            return this.error9p(p.tag, "permission denied: "+reason);
        }
    },

    Tread: function(p){
        var node = this.fids[p.fid];
        if (node === undefined) return this.error9p(p.tag, "no such fid");
        if (node.open !== true) return this.error9p(p.tag, "fid not open");
        if (node.f.qid.type & QTDIR) return read_dirent(this, p, node);
        if (node.f.read === undefined) return this.error9p(p.tag, "permission denied");
        return read_file(this, p, node);
    },
    ////
    Tclunk: function(p){
        if (this.fids[p.fid] === undefined) return this.error9p(p.tag, "fid not in use");
        delete this.fids[p.fid];
        return this.send9p({type:msgtype.Rclunk, tag:p.tag});
    }
};

var must_deny_access = function(f, mode) {
  var reason;
  if (isDir(f)) { return (mode & 3) ? "directories are read only" : false; }
  if (mode & 3 === 3) { return "OEXEC not implemented";}
  if (mode & 3 && f.write === undefined) { return "read only";}
  return false;
};

var read_file = function(service, packet, node) {
  var data = node.f.read(packet.offset, packet.count);
  return service.send9p({type:msgtype.Rread, tag:packet.tag, count:5, data:data});
};

var read_dirent = function(service, packet, f) {
  //sends one dirent at a time, provided packet.offset===f.bloc and the dirent fits into packet.count
  var reply = {type:msgtype.Rread, tag:packet.tag};
  if (packet.offset === 0) f.bloc = f.nloc = 0;
  if (packet.offset != f.bloc) return service.error9p(packet.tag, "seek in directory illegal "+
    packet.offset +"!=" +f.bloc);
  if (packet.count < 2) return service.error9p(packet.tag, "read too short");
  if (f.nloc >= f.f.nchildren.length) {
    reply.data = "";
    return service.send9p(reply);
  }
  var child = f.f.nchildren[f.nloc];
  var s = exports.dirent(child);
  if (packet.count < s.length) {
    reply.data = s.substring(0,2);
    return service.send9p(reply);
  }
  f.bloc += s.length;
  f.nloc++;
  reply.data = s;
  return service.send9p(reply);
};

// here (for now) is dirent
exports.dirent = function(f){
  var fmt= ["i2:type", "i4:dev", "b13:qid", "i4:mode", "i4:atime", "i4:mtime", "i8:length", "S2:name", "S2:uid", "S2:gid", "S2:muid"];
  var now = new Date().getTime() / 1000;
  var s = { type: 0, dev: 0, qid: pack(f.qid, Qid), mode: 0, atime: now, mtime: now, length: 0,
    name: f.name, uid: "js", gid: "js", muid: "js"};
  if(f.qid.type & QTDIR) { s.mode |= 0111; s.mode += 0x80000000; }
  var packed = pack(s, fmt);
  var size = pack({ a:packed.length },["i2:a"]);
  return size + packed;
};

// filesystem funcs
var mkfile = function(path, open, read, write, close) {
    var f, parent, n;
    parent = this.lookup(path, true);
    path = path.split('/');
    n = path[path.length - 1];
    f = {
        name: n,
        parent: parent,
        qid: {type: 0, ver: 0, path: ++lastquid},
        open: open, read: read, write: write, close: close};
    parent.children[n] = f;
    parent.nchildren.push(f);
    return f;
};

var mkdir = function(path){
    var f, parent, name;
    parent = this.lookup(path, true);
    path = path.split('/');
    n = path[path.length-1];

    f = {
        mkfile: mkfile,
        mkdir: mkdir,
        lookup: lookup,
        name: n, parent: parent.name, children: {}, nchildren: [],
        qid: {type: QTDIR, ver: 0, path: ++lastquid}};
    parent.children[n] = f;
    parent.nchildren.push(f);

    return f;
};

function lookup(path, getParent) {
    var f, s, n, x;

    f = this;
    s = path.split('/');
    for(x in s){
        if(getParent && x == s.length - 1)
            break;
        n = s[x];
        if(n === "" || n == ".")
            continue;
        if(n == "..")
            f = f.parent;
        else {
            f = f.children[n];
            if(f === undefined)
                throw path + " not found";
        }
    }
    return f;
}

var isDir = exports.isDir = function(i){
    return (i.qid.type === QTDIR);
};
var isFile = exports.isFile = function(i){
    return (i.qid.type !== QTDIR);
};

module.exports.mkroot = function(){
    return {
        name: "/",
        children: {},
        nchildren: [],
        qid: {type: QTDIR, ver: 0, path: 0},
        mkfile: mkfile,
        mkdir: mkdir,
        lookup: lookup
    };
};
<|endoftext|>


<commit_before>CKEDITOR.plugins.add('splitquote', {
    hidpi: true,
    icons: 'splitquote',
    init: function(editor) {

        editor.addCommand('splitQuote', {
            exec: function(editor) {
                var helper, quote, text, range, cursorNode, cursorNodeLength, cursorNodeOffset;

                // is the cursor position within a quote (otrs-style)?
                quote = editor.elementPath().contains('div', false, true);
                if (quote !== null && quote.hasAttribute('type') && quote.getAttribute('type') === 'cite') {

                    // Special handling:
                    // If the cursor is at the end of a line, the
 of this line is still behind
                    // the cursor. If the quote is now splitted, it splits before the
.
                    // We try to find out, if the cursor is at the end of a line just before a

                    // and than move the cursor behind the
, which at the end moves the cursor
                    // to the beginning of the next line.
                    // Splitting the quote there will keep the
 at the end of the previous line and
                    // wil not create an unnecessary empty new line after the split position.
                    // This will still not work correctly if a formatting ends at the end of line,
                    // like e.g.
blablatest
blabla

                    // get current cursor position, container node, length and offset
                    range = editor.getSelection().getRanges()[0];
                    cursorNode = range.startContainer;
                    cursorNodeLength = cursorNode.getLength();
                    cursorNodeOffset = range.startOffset;

                    // if cursor position is at the end of text node and next element is

                    if (cursorNodeOffset === cursorNodeLength && cursorNode.hasNext() && cursorNode.getNext().getName() === 'br') {
                        range = editor.createRange();
                        // move cursor position behind the next element (= before the next next ;-) )
                        range.setStart(cursorNode.getNext().getNext(), 0);
                        range.setEnd(cursorNode.getNext().getNext(), 0);
                        editor.getSelection().selectRanges([range]);
                    }

                    // create a helper element to insert at cursor position
                    helper = CKEDITOR.dom.element.createFromHtml('
');
                    editor.insertElement(helper);
                    // break quote at the position of the helper element
                    helper.breakParent(quote);
                    // create a second helper element with only a text node
                    text = CKEDITOR.dom.element.createFromHtml(' ');
                    // and insert after the first part of the quote
                    text.insertAfter(quote);
                    // now create a range and move cursor to the position of the text helper element
                    range = new CKEDITOR.dom.range(editor.document);
                    range.moveToElementEditablePosition(text, true);
                    editor.getSelection().selectRanges([range]);
                }
            }
        });

        editor.ui.addButton('SplitQuote', {
            label: Core.Config.Get('RichText.Lang.SplitQuote') || 'Split Quote',
            command: 'splitQuote',
            toolbar: 'insert'
        });

        editor.setKeystroke(CKEDITOR.CTRL + CKEDITOR.SHIFT + 13, 'splitQuote'); // CTRL+SHIFT+Return
    }
});<commit_msg>Fixed broken SplitQuote functionality for CKEditor. Splitting a quote now also works in empty rows (with no text).<commit_after>CKEDITOR.plugins.add('splitquote', {
    hidpi: true,
    icons: 'splitquote',
    init: function(editor) {

        editor.addCommand('splitQuote', {
            exec: function(editor) {
                var helper, quote, text, range, cursorNode, cursorNodeLength = 0, cursorNodeOffset, cursorNodeType;

                // is the cursor position within a quote (otrs-style)?
                quote = editor.elementPath().contains('div', false, true);
                if (quote !== null && quote.hasAttribute('type') && quote.getAttribute('type') === 'cite') {

                    // Special handling:
                    // If the cursor is at the end of a line, the
 of this line is still behind
                    // the cursor. If the quote is now splitted, it splits before the
.
                    // We try to find out, if the cursor is at the end of a line just before a

                    // and than move the cursor behind the
, which at the end moves the cursor
                    // to the beginning of the next line.
                    // Splitting the quote there will keep the
 at the end of the previous line and
                    // wil not create an unnecessary empty new line after the split position.
                    // This will still not work correctly if a formatting ends at the end of line,
                    // like e.g.
blablatest
blabla

                    // get current cursor position, container node, length and offset
                    range = editor.getSelection().getRanges()[0];
                    cursorNode = range.startContainer;
                    cursorNodeType = cursorNode.nodeType;

                    if (cursorNodeType === 3 && typeof cursorNode.getLength !== undefined) {
                        cursorNodeLength = cursorNode.getLength();
                    }

                    cursorNodeOffset = range.startOffset;

                    // if cursor position is at the end of text node and next element is

                    if (cursorNodeOffset === cursorNodeLength && cursorNode.hasNext() && cursorNode.getNext().getName() === 'br') {
                        range = editor.createRange();
                        // move cursor position behind the next element (= before the next next ;-) )
                        range.setStart(cursorNode.getNext().getNext(), 0);
                        range.setEnd(cursorNode.getNext().getNext(), 0);
                        editor.getSelection().selectRanges([range]);
                    }

                    // create a helper element to insert at cursor position
                    helper = CKEDITOR.dom.element.createFromHtml('
');
                    editor.insertElement(helper);
                    // break quote at the position of the helper element
                    helper.breakParent(quote);
                    // create a second helper element with only a text node
                    text = CKEDITOR.dom.element.createFromHtml(' ');
                    // and insert after the first part of the quote
                    text.insertAfter(quote);
                    // now create a range and move cursor to the position of the text helper element
                    range = new CKEDITOR.dom.range(editor.document);
                    range.moveToElementEditablePosition(text, true);
                    editor.getSelection().selectRanges([range]);
                }
            }
        });

        editor.ui.addButton('SplitQuote', {
            label: Core.Config.Get('RichText.Lang.SplitQuote') || 'Split Quote',
            command: 'splitQuote',
            toolbar: 'insert'
        });

        editor.setKeystroke(CKEDITOR.CTRL + CKEDITOR.SHIFT + 13, 'splitQuote'); // CTRL+SHIFT+Return
    }
});<|endoftext|>


<commit_before>// @flow
import { convert } from 'emerald-js';
import BigNumber from 'bignumber.js';
import { intervalRates } from '../../store/config';
import createLogger from '../../utils/logger';
import ActionTypes from './actionTypes';
import history from '../wallet/history';

const log = createLogger('networkActions');

export function switchChain({ chain, chainId }) {
  return (dispatch, getState) => {
    dispatch({
      type: ActionTypes.SWITCH_CHAIN,
      chain,
      chainId,
    });
  };
}

export function loadHeight(watch) {
  return (dispatch, getState, api) => {
    return api.geth.eth.getBlockNumber().then((result) => {
      dispatch({
        type: ActionTypes.BLOCK,
        height: result,
      });
    });
  };
}

export function loadPeerCount() {
  return (dispatch, getState, api) => {
    return api.geth.net.peerCount().then((result) => {
      dispatch({
        type: ActionTypes.PEER_COUNT,
        peerCount: result,
      });
    });
  };
}

export function loadAddressTransactions(...props) {
  return (dispatch, getState, api) => {
    return api.geth.eth.getAddressTransactions(...props).then((results) => {
      if (results.length === 0) { return; }

      const trackedTxs = getState().wallet.history.get('trackedTransactions');
      const isAlreadyTracked = trackedTxs.find((tx) => results.indexOf(tx.get('hash')) !== -1);
      if (isAlreadyTracked) { return null; }

      return api.geth.ext.getTransactions(results).then((txes) => {
        return dispatch(history.actions.trackTxs(txes.map((tx) => tx.result)));
      });
    }).catch((e) => {
      log.error(e);
    });
  };
}

export function loadSyncing() {
  return (dispatch, getState, api) => {
    return api.geth.eth.getSyncing().then((result) => {
      if (typeof result === 'object') {
        return dispatch({
          type: ActionTypes.SYNCING,
          syncing: true,
          status: result,
        });
      }

      dispatch({
        type: ActionTypes.SYNCING,
        syncing: false,
      });
    });
  };
}

export function getGasPrice() {
  return (dispatch, getState, api) => {
    return api.geth.eth.gasPrice().then((result) => {
      dispatch({
        type: ActionTypes.GAS_PRICE,
        value: result,
      });
    });
  };
}

export function estimateGas(from: string, to: string, gas: string, gasPrice: string, value: string, data: string) {
  return (dispatch, getState, api) => {
    return api.geth.eth.estimateGas({
      from,
      to,
      gas,
      gasPrice,
      value,
      data,
    });
  };
}
<commit_msg>fix failed to fetch errors<commit_after>// @flow
import { convert } from 'emerald-js';
import BigNumber from 'bignumber.js';
import { intervalRates } from '../../store/config';
import createLogger from '../../utils/logger';
import ActionTypes from './actionTypes';
import history from '../wallet/history';

const log = createLogger('networkActions');

const handleFailedToFetch = (err) => {
  if (err.message.includes('Failed to fetch')) { return; }
  throw err;
};

export function switchChain({ chain, chainId }) {
  return (dispatch, getState) => {
    dispatch({
      type: ActionTypes.SWITCH_CHAIN,
      chain,
      chainId,
    });
  };
}

export function loadHeight(watch) {
  return (dispatch, getState, api) => {
    return api.geth.eth.getBlockNumber().then((result) => {
      dispatch({
        type: ActionTypes.BLOCK,
        height: result,
      });
    }).catch(handleFailedToFetch);
  };
}

export function loadPeerCount() {
  return (dispatch, getState, api) => {
    return api.geth.net.peerCount().then((result) => {
      dispatch({
        type: ActionTypes.PEER_COUNT,
        peerCount: result,
      });
    }).catch(handleFailedToFetch);
  };
}

export function loadAddressTransactions(...props) {
  return (dispatch, getState, api) => {
    return api.geth.eth.getAddressTransactions(...props).then((results) => {
      if (results.length === 0) { return; }

      const trackedTxs = getState().wallet.history.get('trackedTransactions');
      const isAlreadyTracked = trackedTxs.find((tx) => results.indexOf(tx.get('hash')) !== -1);
      if (isAlreadyTracked) { return null; }

      return api.geth.ext.getTransactions(results).then((txes) => {
        return dispatch(history.actions.trackTxs(txes.map((tx) => tx.result)));
      });
    }).catch((e) => {
      log.error(e);
    });
  };
}

export function loadSyncing() {
  return (dispatch, getState, api) => {
    return api.geth.eth.getSyncing().then((result) => {
      if (typeof result === 'object') {
        return dispatch({
          type: ActionTypes.SYNCING,
          syncing: true,
          status: result,
        });
      }

      dispatch({
        type: ActionTypes.SYNCING,
        syncing: false,
      });
    }).catch(handleFailedToFetch);
  };
}

export function getGasPrice() {
  return (dispatch, getState, api) => {
    return api.geth.eth.gasPrice().then((result) => {
      dispatch({
        type: ActionTypes.GAS_PRICE,
        value: result,
      });
    }).catch(handleFailedToFetch);
  };
}

export function estimateGas(from: string, to: string, gas: string, gasPrice: string, value: string, data: string) {
  return (dispatch, getState, api) => {
    return api.geth.eth.estimateGas({
      from,
      to,
      gas,
      gasPrice,
      value,
      data,
    });
  };
}
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 19.02.2016.
 */

import won from "../won-es6.js";
import Immutable from "immutable";
import { actionTypes, actionCreators } from "./actions.js";
import { fetchOwnedData } from "../won-message-utils.js";
import {
  registerAccount,
  transferPrivateAccount,
  acceptTermsOfService,
  login,
  logout,
  parseCredentials,
  generatePrivateId,
  checkLoginStatus,
} from "../won-utils.js";
import {
  clearPrivateId,
  savePrivateId,
  setDisclaimerAccepted,
} from "../won-localstorage.js";
import { stateGoCurrent } from "./cstm-router-actions.js";
import { checkAccessToCurrentRoute } from "../configRouting.js";

import { getIn } from "../utils.js";
import { getOwnedConnectionUris } from "../selectors/connection-selectors.js";
import { loadLatestMessagesOfConnection } from "./connections-actions.js";

/**
 * Makes sure user is either logged in
 * or creates a private-ID account as fallback.
 */
export async function ensureLoggedIn(dispatch, getState) {
  const state = getState();
  if (state.getIn(["account", "loggedIn"])) {
    return;
  }

  const privateId = generatePrivateId();
  try {
    await accountRegister({ privateId })(dispatch, getState);
  } catch (err) {
    console.error(
      `Creating temporary account (${privateId}) has failed due to `,
      err
    );
    dispatch(actionCreators.account__registerFailed({ privateId }));
  }
}

let _loginInProcessFor;
/**
 *
 * @param username
 * @param password
 * @param options
 *    * fetchData(true): whether or not to fetch a users owned needs. If the account
 *    signing in is new, there's no need to fetch this and `false` can be passed here
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *    * redirectToFeed(false): whether or not to redirect to the feed after signing in (needs `redirects` to be true)
 *    * relogIfNecessary(true):  if there's a valid session or privateId, log out from that first.
 *
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountLogin(credentials, options) {
  const options_ = Object.assign(
    {
      // defaults
      fetchData: true,
      doRedirects: true,
      redirectToFeed: false,
      relogIfNecessary: true, // if there's a valid session or privateId, log out from that first.
    },
    options
  );
  return (dispatch, getState) => {
    const state = getState();

    const { email } = parseCredentials(credentials);

    const prevPrivateId = getIn(state, [
      "router",
      "currentParams",
      "privateId",
    ]);
    const prevEmail = state.getIn(["account", "email"]);

    const wasLoggedIn =
      !state.getIn(["process", "processingInitialLoad"]) &&
      (prevPrivateId || prevEmail);

    if (
      state.getIn(["process", "processingLoginForEmail"]) === email ||
      _loginInProcessFor === email
    ) {
      console.debug(
        "Already logging in as ",
        email,
        ". Canceling redundant attempt."
      );
      return;
    }

    if (
      !state.getIn(["process", "processingInitialLoad"]) &&
      ((credentials.privateId && credentials.privateId === prevPrivateId) ||
        (credentials.email && credentials.email === prevEmail))
    ) {
      console.debug(
        "Already logged into this account (" +
          (credentials.privateId || credentials.email) +
          "). Aborting second login attempt."
      );
      return;
    }

    const curriedDispatch = data =>
      dispatch({
        type: actionTypes.account.login,
        payload: Immutable.fromJS(data).merge({ email: email, loggedIn: true }),
      });

    return Promise.resolve()
      .then(() => {
        _loginInProcessFor = email;
        return dispatch({
          type: actionTypes.account.loginStarted,
          payload: { email },
        });
      })
      .then(() => {
        if (wasLoggedIn) {
          return logout().then(() => {
            if (
              options_.doRedirects &&
              getIn(state, ["router", "currentParams", "privateId"])
            ) {
              return stateGoCurrent({ privateId: "" })(dispatch, getState);
            }
          });
        }
      })
      .then(() => {
        if (options_.doRedirects && credentials.privateId) {
          return stateGoCurrent({ privateId: credentials.privateId })(
            dispatch,
            getState
          );
        }
      })
      .then(() => login(credentials))
      .then(data =>
        dispatch({
          type: actionTypes.account.login,
          payload: Immutable.fromJS(data).merge({
            email: email,
            loggedIn: true,
          }),
        })
      )
      .then(() => curriedDispatch({ httpSessionUpgraded: true }))
      .then(() => {
        if (!options_.doRedirects) {
          return;
        } else if (options_.redirectToFeed) {
          return dispatch(
            actionCreators.router__stateGoResetParams("connections")
          );
        } else {
          return checkAccessToCurrentRoute(dispatch, getState);
        }
      })
      .then((/*response*/) => {
        if (options_.fetchData) {
          return fetchOwnedData(email, curriedDispatch);
        } else {
          return Immutable.Map(); // only need to fetch data for non-new accounts
        }
      })
      .then(() => curriedDispatch({ loginFinished: true }))
      .catch(error =>
        error.response.json().then(loginError => {
          return Promise.resolve()
            .then(() => {
              if (wasLoggedIn) {
                return dispatch({
                  type: actionTypes.logout,
                  payload: Immutable.fromJS({ loggedIn: false }),
                });
              }
            })
            .then(() => {
              if (credentials.privateId) {
                loginError = won.PRIVATEID_NOT_FOUND_ERROR;
              }

              dispatch(
                actionCreators.account.loginFailed({
                  loginError: Immutable.fromJS(loginError),
                  error,
                  credentials,
                })
              );
            })
            .then(
              () =>
                options_.doRedirects &&
                checkAccessToCurrentRoute(dispatch, getState)
            );
        })
      )
      .then(() => {
        _loginInProcessFor = undefined;
      })
      .then(() => {
        if (credentials.privateId) {
          savePrivateId(credentials.privateId);
        }
      });
  };
}

let _logoutInProcess;
/**
 *
 * @param options
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *
 * @returns {Function}
 */
export function accountLogout(options) {
  const options_ = {
    doRedirects: true,
    ...options,
  };

  clearPrivateId();

  return (dispatch, getState) => {
    const state = getState();

    if (state.getIn(["process", "processingLogout"]) || _logoutInProcess) {
      console.debug(
        "There's already a logout in process. Aborting redundant attempt."
      );
      return;
    }
    _logoutInProcess = true;

    return Promise.resolve()
      .then(() =>
        dispatch({
          type: actionTypes.account.logoutStarted,
          payload: {},
        })
      )
      .then(() => logout())
      .catch(error => {
        //TODO: PRINT ERROR MESSAGE AND CHANGE STATE ACCORDINGLY
        console.error("Error while trying to log out: ", error);
      })
      .then(() => {
        // for the case that we've been logged in to an anonymous account, we need to remove the privateId here.
        if (
          options_.doRedirects &&
          getIn(state, ["router", "currentParams", "privateId"])
        ) {
          return stateGoCurrent({ privateId: null })(dispatch, getState);
        }
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({
            loggedIn: false,
            httpSessionDowngraded: true,
          }),
        })
      )
      .then(() => {
        won.clearStore();
      })
      .then(
        () =>
          options_.doRedirects && checkAccessToCurrentRoute(dispatch, getState)
      )
      .then(() => {
        _logoutInProcess = false;
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({ loggedIn: false, logoutFinished: true }),
        })
      );
  };
}

/**
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountRegister(credentials) {
  return (dispatch, getState) =>
    registerAccount(credentials)
      .then(() =>
        /*response*/ accountLogin(credentials, {
          fetchData: false,
          redirectToFeed: true,
        })(dispatch, getState)
      )
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError =
          "Registration failed (E-Mail might already be used)";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

/**
 * @param credentials {email, password, privateId}
 * @returns {Function}
 */
export function accountTransfer(credentials) {
  return (dispatch, getState) =>
    transferPrivateAccount(credentials)
      .then(() => {
        credentials.privateId = undefined;
        /*response*/
        accountLogin(credentials, {
          fetchData: true,
          redirectToFeed: true,
        })(dispatch, getState);
      })
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError = "Account Transfer failed";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

export function accountAcceptDisclaimer() {
  return dispatch => {
    setDisclaimerAccepted();
    dispatch({ type: actionTypes.account.acceptDisclaimerSuccess });
  };
}

export function accountAcceptTermsOfService() {
  return dispatch => {
    dispatch({ type: actionTypes.account.acceptTermsOfServiceStarted });
    acceptTermsOfService()
      .then(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceSuccess });
      })
      .catch(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceFailed });
      });
  };
}

export function reconnect() {
  return async (dispatch, getState) => {
    dispatch({ type: actionTypes.reconnect.start });
    try {
      await checkLoginStatus();
      dispatch({ type: actionTypes.reconnect.success });

      const state = getState();

      /*
      * -- check for new connections (i.e. matches and incoming requests) --
      */
      // await pageLoadAction()(dispatch, getState);
      // const email = getIn(state, ["account", "email"]);
      // await fetchOwnedData(email, payload => {
      //   dispatch({
      //     type: actionTypes.initialPageLoad, // TODO make this it's own type
      //     payload: wellFormedPayload(payload),
      //   });
      // });
      //TODO hard reload

      /*
       * -- loading latest messages for all connections (we might have missed some during the dc) --
       */
      const connectionUris = getOwnedConnectionUris(state);
      await Promise.all(
        connectionUris.map(async connectionUri => {
          await loadLatestMessagesOfConnection({
            connectionUri,
            numberOfEvents: 10, //TODO magic number :|
            state,
            dispatch,
            actionTypesToDispatch: {
              start: actionTypes.reconnect.startingToLoadConnectionData,
              success: actionTypes.reconnect.receivedConnectionData,
              failure: actionTypes.reconnect.connectionFailedToLoad,
            },
          });
        })
      );
    } catch (e) {
      if (e.message == "Unauthorized") {
        //FIXME: this seems weird and unintentional to me, the actionTypes.account.logout closes the main menu (see view-reducer.js) and the dispatch after opens it again, is this wanted that way?
        dispatch({ type: actionTypes.account.logout });
        dispatch({ type: actionTypes.view.showMainMenu });
      } else {
        dispatch(actionCreators.lostConnection());
      }
      console.warn(e);
    }
  };
}
<commit_msg>Adds accountVerifyEmailAddress action (calls rest-endpoint)<commit_after>/**
 * Created by ksinger on 19.02.2016.
 */

import won from "../won-es6.js";
import Immutable from "immutable";
import { actionTypes, actionCreators } from "./actions.js";
import { fetchOwnedData } from "../won-message-utils.js";
import {
  registerAccount,
  transferPrivateAccount,
  acceptTermsOfService,
  confirmRegistration,
  login,
  logout,
  parseCredentials,
  generatePrivateId,
  checkLoginStatus,
} from "../won-utils.js";
import {
  clearPrivateId,
  savePrivateId,
  setDisclaimerAccepted,
} from "../won-localstorage.js";
import { stateGoCurrent } from "./cstm-router-actions.js";
import { checkAccessToCurrentRoute } from "../configRouting.js";

import { getIn } from "../utils.js";
import { getOwnedConnectionUris } from "../selectors/connection-selectors.js";
import { loadLatestMessagesOfConnection } from "./connections-actions.js";

/**
 * Makes sure user is either logged in
 * or creates a private-ID account as fallback.
 */
export async function ensureLoggedIn(dispatch, getState) {
  const state = getState();
  if (state.getIn(["account", "loggedIn"])) {
    return;
  }

  const privateId = generatePrivateId();
  try {
    await accountRegister({ privateId })(dispatch, getState);
  } catch (err) {
    console.error(
      `Creating temporary account (${privateId}) has failed due to `,
      err
    );
    dispatch(actionCreators.account__registerFailed({ privateId }));
  }
}

let _loginInProcessFor;
/**
 *
 * @param username
 * @param password
 * @param options
 *    * fetchData(true): whether or not to fetch a users owned needs. If the account
 *    signing in is new, there's no need to fetch this and `false` can be passed here
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *    * redirectToFeed(false): whether or not to redirect to the feed after signing in (needs `redirects` to be true)
 *    * relogIfNecessary(true):  if there's a valid session or privateId, log out from that first.
 *
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountLogin(credentials, options) {
  const options_ = Object.assign(
    {
      // defaults
      fetchData: true,
      doRedirects: true,
      redirectToFeed: false,
      relogIfNecessary: true, // if there's a valid session or privateId, log out from that first.
    },
    options
  );
  return (dispatch, getState) => {
    const state = getState();

    const { email } = parseCredentials(credentials);

    const prevPrivateId = getIn(state, [
      "router",
      "currentParams",
      "privateId",
    ]);
    const prevEmail = state.getIn(["account", "email"]);

    const wasLoggedIn =
      !state.getIn(["process", "processingInitialLoad"]) &&
      (prevPrivateId || prevEmail);

    if (
      state.getIn(["process", "processingLoginForEmail"]) === email ||
      _loginInProcessFor === email
    ) {
      console.debug(
        "Already logging in as ",
        email,
        ". Canceling redundant attempt."
      );
      return;
    }

    if (
      !state.getIn(["process", "processingInitialLoad"]) &&
      ((credentials.privateId && credentials.privateId === prevPrivateId) ||
        (credentials.email && credentials.email === prevEmail))
    ) {
      console.debug(
        "Already logged into this account (" +
          (credentials.privateId || credentials.email) +
          "). Aborting second login attempt."
      );
      return;
    }

    const curriedDispatch = data =>
      dispatch({
        type: actionTypes.account.login,
        payload: Immutable.fromJS(data).merge({ email: email, loggedIn: true }),
      });

    return Promise.resolve()
      .then(() => {
        _loginInProcessFor = email;
        return dispatch({
          type: actionTypes.account.loginStarted,
          payload: { email },
        });
      })
      .then(() => {
        if (wasLoggedIn) {
          return logout().then(() => {
            if (
              options_.doRedirects &&
              getIn(state, ["router", "currentParams", "privateId"])
            ) {
              return stateGoCurrent({ privateId: "" })(dispatch, getState);
            }
          });
        }
      })
      .then(() => {
        if (options_.doRedirects && credentials.privateId) {
          return stateGoCurrent({ privateId: credentials.privateId })(
            dispatch,
            getState
          );
        }
      })
      .then(() => login(credentials))
      .then(data =>
        dispatch({
          type: actionTypes.account.login,
          payload: Immutable.fromJS(data).merge({
            email: email,
            loggedIn: true,
          }),
        })
      )
      .then(() => curriedDispatch({ httpSessionUpgraded: true }))
      .then(() => {
        if (!options_.doRedirects) {
          return;
        } else if (options_.redirectToFeed) {
          return dispatch(
            actionCreators.router__stateGoResetParams("connections")
          );
        } else {
          return checkAccessToCurrentRoute(dispatch, getState);
        }
      })
      .then((/*response*/) => {
        if (options_.fetchData) {
          return fetchOwnedData(email, curriedDispatch);
        } else {
          return Immutable.Map(); // only need to fetch data for non-new accounts
        }
      })
      .then(() => curriedDispatch({ loginFinished: true }))
      .catch(error =>
        error.response.json().then(loginError => {
          return Promise.resolve()
            .then(() => {
              if (wasLoggedIn) {
                return dispatch({
                  type: actionTypes.logout,
                  payload: Immutable.fromJS({ loggedIn: false }),
                });
              }
            })
            .then(() => {
              if (credentials.privateId) {
                loginError = won.PRIVATEID_NOT_FOUND_ERROR;
              }

              dispatch(
                actionCreators.account.loginFailed({
                  loginError: Immutable.fromJS(loginError),
                  error,
                  credentials,
                })
              );
            })
            .then(
              () =>
                options_.doRedirects &&
                checkAccessToCurrentRoute(dispatch, getState)
            );
        })
      )
      .then(() => {
        _loginInProcessFor = undefined;
      })
      .then(() => {
        if (credentials.privateId) {
          savePrivateId(credentials.privateId);
        }
      });
  };
}

let _logoutInProcess;
/**
 *
 * @param options
 *    * doRedirects(true): whether or not to do any redirects at all (e.g. if an invalid route was accessed)
 *
 * @returns {Function}
 */
export function accountLogout(options) {
  const options_ = {
    doRedirects: true,
    ...options,
  };

  clearPrivateId();

  return (dispatch, getState) => {
    const state = getState();

    if (state.getIn(["process", "processingLogout"]) || _logoutInProcess) {
      console.debug(
        "There's already a logout in process. Aborting redundant attempt."
      );
      return;
    }
    _logoutInProcess = true;

    return Promise.resolve()
      .then(() =>
        dispatch({
          type: actionTypes.account.logoutStarted,
          payload: {},
        })
      )
      .then(() => logout())
      .catch(error => {
        //TODO: PRINT ERROR MESSAGE AND CHANGE STATE ACCORDINGLY
        console.error("Error while trying to log out: ", error);
      })
      .then(() => {
        // for the case that we've been logged in to an anonymous account, we need to remove the privateId here.
        if (
          options_.doRedirects &&
          getIn(state, ["router", "currentParams", "privateId"])
        ) {
          return stateGoCurrent({ privateId: null })(dispatch, getState);
        }
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({
            loggedIn: false,
            httpSessionDowngraded: true,
          }),
        })
      )
      .then(() => {
        won.clearStore();
      })
      .then(
        () =>
          options_.doRedirects && checkAccessToCurrentRoute(dispatch, getState)
      )
      .then(() => {
        _logoutInProcess = false;
      })
      .then(() =>
        dispatch({
          type: actionTypes.account.logout,
          payload: Immutable.fromJS({ loggedIn: false, logoutFinished: true }),
        })
      );
  };
}

/**
 * @param credentials either {email, password} or {privateId}
 * @returns {Function}
 */
export function accountRegister(credentials) {
  return (dispatch, getState) =>
    registerAccount(credentials)
      .then(() =>
        /*response*/ accountLogin(credentials, {
          fetchData: false,
          redirectToFeed: true,
        })(dispatch, getState)
      )
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError =
          "Registration failed (E-Mail might already be used)";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

/**
 * @param credentials {email, password, privateId}
 * @returns {Function}
 */
export function accountTransfer(credentials) {
  return (dispatch, getState) =>
    transferPrivateAccount(credentials)
      .then(() => {
        credentials.privateId = undefined;
        /*response*/
        accountLogin(credentials, {
          fetchData: true,
          redirectToFeed: true,
        })(dispatch, getState);
      })
      .catch(error => {
        //TODO: PRINT MORE SPECIFIC ERROR MESSAGE, already registered/password to short etc.
        const registerError = "Account Transfer failed";
        console.error(registerError, error);
        dispatch(
          actionCreators.account__registerFailed({ registerError, error })
        );
      });
}

export function accountAcceptDisclaimer() {
  return dispatch => {
    setDisclaimerAccepted();
    dispatch({ type: actionTypes.account.acceptDisclaimerSuccess });
  };
}

export function accountAcceptTermsOfService() {
  return dispatch => {
    dispatch({ type: actionTypes.account.acceptTermsOfServiceStarted });
    acceptTermsOfService()
      .then(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceSuccess });
      })
      .catch(() => {
        dispatch({ type: actionTypes.account.acceptTermsOfServiceFailed });
      });
  };
}

export function accountVerifyEmailAddress(verificationToken) {
  return dispatch => {
    dispatch({ type: actionTypes.account.verifyEmailAddressStarted });
    confirmRegistration(verificationToken)
      .then(() => {
        dispatch({ type: actionTypes.account.verifyEmailAddressSuccess });
      })
      .catch(error =>
        dispatch({
          type: actionTypes.account.verifyEmailAddressFailed,
          payload: {
            emailVerificationError: Immutable.fromJS(error.jsonResponse),
          },
        })
      );
  };
}

export function reconnect() {
  return async (dispatch, getState) => {
    dispatch({ type: actionTypes.reconnect.start });
    try {
      await checkLoginStatus();
      dispatch({ type: actionTypes.reconnect.success });

      const state = getState();

      /*
      * -- check for new connections (i.e. matches and incoming requests) --
      */
      // await pageLoadAction()(dispatch, getState);
      // const email = getIn(state, ["account", "email"]);
      // await fetchOwnedData(email, payload => {
      //   dispatch({
      //     type: actionTypes.initialPageLoad, // TODO make this it's own type
      //     payload: wellFormedPayload(payload),
      //   });
      // });
      //TODO hard reload

      /*
       * -- loading latest messages for all connections (we might have missed some during the dc) --
       */
      const connectionUris = getOwnedConnectionUris(state);
      await Promise.all(
        connectionUris.map(async connectionUri => {
          await loadLatestMessagesOfConnection({
            connectionUri,
            numberOfEvents: 10, //TODO magic number :|
            state,
            dispatch,
            actionTypesToDispatch: {
              start: actionTypes.reconnect.startingToLoadConnectionData,
              success: actionTypes.reconnect.receivedConnectionData,
              failure: actionTypes.reconnect.connectionFailedToLoad,
            },
          });
        })
      );
    } catch (e) {
      if (e.message == "Unauthorized") {
        //FIXME: this seems weird and unintentional to me, the actionTypes.account.logout closes the main menu (see view-reducer.js) and the dispatch after opens it again, is this wanted that way?
        dispatch({ type: actionTypes.account.logout });
        dispatch({ type: actionTypes.view.showMainMenu });
      } else {
        dispatch(actionCreators.lostConnection());
      }
      console.warn(e);
    }
  };
}
<|endoftext|>