<commit_before>
var express = require('../')
  , request = require('./support/http');

describe('res', function(){
  describe('.render(name)', function(){
    it('should expose app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };

      app.use(function(req, res){
        res.render('user.jade');
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should support index.<engine>', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.set('view engine', 'jade');

      app.use(function(req, res){
        res.render('blog/post');
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
blog post
');
        done();
      });
    })

    describe('when an error occurs', function(){
      it('should next(err)', function(done){
        var app = express();

        app.set('views', __dirname + '/fixtures');

        app.use(function(req, res){
          res.render('user.jade');
        });

        app.use(function(err, req, res, next){
          res.end(err.message);
        });

        request(app)
        .get('/')
        .end(function(res){
          res.body.should.match(/user is not defined/);
          done();
        });
      })
    })

    describe('when "view engine" is given', function(){
      it('should render the template', function(done){
        var app = express();

        app.set('view engine', 'jade');
        app.set('views', __dirname + '/fixtures');

        app.use(function(req, res){
          res.render('email');
        });

        request(app)
        .get('/')
        .end(function(res){
          res.body.should.equal('
This is an email
');
          done();
        });
      })
    })
  })

  describe('.render(name, option)', function(){
    it('should render the template', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      var user = { name: 'tobi' };

      app.use(function(req, res){
        res.render('user.jade', { user: user });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should expose app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };

      app.use(function(req, res){
        res.render('user.jade', {});
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should expose res.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      app.use(function(req, res){
        res.locals.user = { name: 'tobi' };
        res.render('user.jade', {});
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should give precedence to res.locals over app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };

      app.use(function(req, res){
        res.locals.user = { name: 'jane' };
        res.render('user.jade', {});
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
jane
');
        done();
      });
    })

    it('should give precedence to res.render() locals over res.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      var jane = { name: 'jane' };

      app.use(function(req, res){
        res.locals.user = { name: 'tobi' };
        res.render('user.jade', { user: jane });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
jane
');
        done();
      });
    })

    it('should give precedence to res.render() locals over app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };
      var jane = { name: 'jane' };

      app.use(function(req, res){
        res.render('user.jade', { user: jane });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
jane
');
        done();
      });
    })
  })

  describe('.render(name, fn)', function(){
    it('should not respond', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      app.use(function(req, res){
        res.locals.user = { name: 'tobi' };
        res.render('user.jade', function(err, html){
          html = html.replace('tobi', 'loki');
          res.end(html);
        });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
loki
');
        done();
      });
    })

    describe('when an error occurs', function(){
      it('should pass it to the callback', function(done){
        var app = express();

        app.set('views', __dirname + '/fixtures');

        app.use(function(req, res){
          res.render('user.jade', function(err){
            res.end(err.message);
          });
        });

        request(app)
        .get('/')
        .end(function(res){
          res.body.should.match(/is not defined/);
          done();
        });
      })
    })
  })
})
<commit_msg>Added res.render() callback with locals test<commit_after>
var express = require('../')
  , request = require('./support/http');

describe('res', function(){
  describe('.render(name)', function(){
    it('should expose app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };

      app.use(function(req, res){
        res.render('user.jade');
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should support index.<engine>', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.set('view engine', 'jade');

      app.use(function(req, res){
        res.render('blog/post');
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
blog post
');
        done();
      });
    })

    describe('when an error occurs', function(){
      it('should next(err)', function(done){
        var app = express();

        app.set('views', __dirname + '/fixtures');

        app.use(function(req, res){
          res.render('user.jade');
        });

        app.use(function(err, req, res, next){
          res.end(err.message);
        });

        request(app)
        .get('/')
        .end(function(res){
          res.body.should.match(/user is not defined/);
          done();
        });
      })
    })

    describe('when "view engine" is given', function(){
      it('should render the template', function(done){
        var app = express();

        app.set('view engine', 'jade');
        app.set('views', __dirname + '/fixtures');

        app.use(function(req, res){
          res.render('email');
        });

        request(app)
        .get('/')
        .end(function(res){
          res.body.should.equal('
This is an email
');
          done();
        });
      })
    })
  })

  describe('.render(name, option)', function(){
    it('should render the template', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      var user = { name: 'tobi' };

      app.use(function(req, res){
        res.render('user.jade', { user: user });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should expose app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };

      app.use(function(req, res){
        res.render('user.jade', {});
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should expose res.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      app.use(function(req, res){
        res.locals.user = { name: 'tobi' };
        res.render('user.jade', {});
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
tobi
');
        done();
      });
    })

    it('should give precedence to res.locals over app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };

      app.use(function(req, res){
        res.locals.user = { name: 'jane' };
        res.render('user.jade', {});
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
jane
');
        done();
      });
    })

    it('should give precedence to res.render() locals over res.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      var jane = { name: 'jane' };

      app.use(function(req, res){
        res.locals.user = { name: 'tobi' };
        res.render('user.jade', { user: jane });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
jane
');
        done();
      });
    })

    it('should give precedence to res.render() locals over app.locals', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');
      app.locals.user = { name: 'tobi' };
      var jane = { name: 'jane' };

      app.use(function(req, res){
        res.render('user.jade', { user: jane });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
jane
');
        done();
      });
    })
  })

  describe('.render(name, options, fn)', function(){
    it('should pass the resulting string', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      app.use(function(req, res){
        var tobi = { name: 'tobi' };
        res.render('user.jade', { user: tobi }, function(err, html){
          html = html.replace('tobi', 'loki');
          res.end(html);
        });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
loki
');
        done();
      });
    })
  })

  describe('.render(name, fn)', function(){
    it('should pass the resulting string', function(done){
      var app = express();

      app.set('views', __dirname + '/fixtures');

      app.use(function(req, res){
        res.locals.user = { name: 'tobi' };
        res.render('user.jade', function(err, html){
          html = html.replace('tobi', 'loki');
          res.end(html);
        });
      });

      request(app)
      .get('/')
      .end(function(res){
        res.body.should.equal('
loki
');
        done();
      });
    })

    describe('when an error occurs', function(){
      it('should pass it to the callback', function(done){
        var app = express();

        app.set('views', __dirname + '/fixtures');

        app.use(function(req, res){
          res.render('user.jade', function(err){
            res.end(err.message);
          });
        });

        request(app)
        .get('/')
        .end(function(res){
          res.body.should.match(/is not defined/);
          done();
        });
      })
    })
  })
})
<|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Button for returning to the level selection screen.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // modules
  var Color = require( 'SCENERY/util/Color' );
  var inherit = require( 'PHET_CORE/inherit' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var ArrowShape = require( 'SCENERY_PHET/ArrowShape' );
  var Path = require( 'SCENERY/nodes/Path' );

  /**
   * @param {Object} options
   * @constructor
   */
  function ReturnToLevelSelectButton( options ) {

    options = _.extend( {
      xMargin: 8, //Match the size of the star button to the refresh buttons, since they often appear together.  see https://github.com/phetsims/scenery-phet/issues/44
      yMargin: 10.9, //Match the size of the star button to the refresh buttons, since they often appear together.  see https://github.com/phetsims/scenery-phet/issues/44
      baseColor: new Color( 242, 233, 22 )//Color match with the yellow in the PhET logo
    }, options );

    RectangularPushButton.call( this, _.extend( { content: new Path( new ArrowShape( 0, 0, -28.5, 0, {
      tailWidth: 8,
      headWidth: 18,
      headHeight: 15
    } ), { fill: 'black' } ) }, options ) );
  }

  return inherit( RectangularPushButton, ReturnToLevelSelectButton );
} );<commit_msg>phetsims/phet-core#11 @param {Options} [options]<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Button for returning to the level selection screen.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // modules
  var Color = require( 'SCENERY/util/Color' );
  var inherit = require( 'PHET_CORE/inherit' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var ArrowShape = require( 'SCENERY_PHET/ArrowShape' );
  var Path = require( 'SCENERY/nodes/Path' );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function ReturnToLevelSelectButton( options ) {

    options = _.extend( {
      xMargin: 8, //Match the size of the star button to the refresh buttons, since they often appear together.  see https://github.com/phetsims/scenery-phet/issues/44
      yMargin: 10.9, //Match the size of the star button to the refresh buttons, since they often appear together.  see https://github.com/phetsims/scenery-phet/issues/44
      baseColor: new Color( 242, 233, 22 )//Color match with the yellow in the PhET logo
    }, options );

    RectangularPushButton.call( this, _.extend( { content: new Path( new ArrowShape( 0, 0, -28.5, 0, {
      tailWidth: 8,
      headWidth: 18,
      headHeight: 15
    } ), { fill: 'black' } ) }, options ) );
  }

  return inherit( RectangularPushButton, ReturnToLevelSelectButton );
} );<|endoftext|>


<commit_before>/* global Marionette */

/**
 *
 * @copyright Copyright (c) 2019, Daniel Calviño Sánchez (danxuliu@gmail.com)
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

(function(OCA, Marionette) {

	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};
	OCA.Talk = OCA.Talk || {};
	OCA.SpreedMe.Views = OCA.SpreedMe.Views || {};
	OCA.Talk.Views = OCA.Talk.Views || {};

	var ConnectionStatus = {
		NEW: 'new',
		CHECKING: 'checking',
		CONNECTED: 'connected',
		COMPLETED: 'completed',
		DISCONNECTED: 'disconnected',
		DISCONNECTED_LONG: 'disconnected-long',
		FAILED: 'failed',
		FAILED_NO_RESTART: 'failed-no-restart',
		CLOSED: 'closed',
	};

	var VideoView = Marionette.View.extend({

		tagName: 'div',
		className: 'videoContainer',

		id: function() {
			return 'container_' + this.options.peerId + '_video_incoming';
		},

		template: OCA.Talk.Views.Templates['videoview'],

		ui: {
			'video': 'video',
			'avatarContainer': '.avatar-container',
			'avatar': '.avatar',
			'nameIndicator': '.nameIndicator',
			'mediaIndicator': '.mediaIndicator',
			'muteIndicator': '.muteIndicator',
			'hideRemoteVideoButton': '.hideRemoteVideo',
			'screenSharingIndicator': '.screensharingIndicator',
			'iceFailedIndicator': '.iceFailedIndicator',
		},

		events: {
			'click @ui.hideRemoteVideoButton': 'toggleVideo',
			'click @ui.screenSharingIndicator': 'switchToScreen',
		},

		initialize: function() {
			this._connectionStatus = ConnectionStatus.NEW;

			// Video is enabled by default, even if it is not initially
			// available.
			this._videoEnabled = true;
			this._screenVisible = false;

			this.render();

			this.getUI('avatar').addClass('icon-loading');
			this.getUI('avatar').css('opacity', '0.5');

			this.getUI('hideRemoteVideoButton').attr('data-original-title', t('spreed', 'Disable video'));
			this.getUI('hideRemoteVideoButton').hide();

			this.getUI('screenSharingIndicator').attr('data-original-title', t('spreed', 'Show screen'));
		},

		onRender: function() {
			this.getUI('hideRemoteVideoButton').tooltip({
				placement: 'top',
				trigger: 'hover'
			});

			this.getUI('screenSharingIndicator').tooltip({
				placement: 'top',
				trigger: 'hover'
			});
		},

		setParticipant: function(userId, participantName) {
			if (userId && userId.length) {
				this.getUI('avatar').avatar(userId, 128);
			} else {
				this.getUI('avatar').imageplaceholder('?', participantName, 128);
				this.getUI('avatar').css('background-color', '#b9b9b9');

				// "Guest" placeholder is not shown until the initial connection
				// for consistency with regular users.
				if (this._connectionStatus !== ConnectionStatus.NEW) {
					participantName = participantName || t('spreed', 'Guest');
				}
			}

			this.getUI('nameIndicator').text(participantName);
		},

		/**
		 * Sets the current status of the connection.
		 *
		 * @param OCA.Talk.Views.VideoView.ConnectionStatus the connection
		 *        status.
		 */
		setConnectionStatus: function(connectionStatus) {
			this._connectionStatus = connectionStatus;

			this.getUI('avatar').removeClass('icon-loading');
			this.getUI('iceFailedIndicator').addClass('not-failed');

			if (connectionStatus === ConnectionStatus.CHECKING ||
					connectionStatus === ConnectionStatus.DISCONNECTED_LONG ||
					connectionStatus === ConnectionStatus.FAILED) {
				this.getUI('avatar').addClass('icon-loading');

				return;
			}

			if (connectionStatus === ConnectionStatus.CONNECTED ||
					connectionStatus === ConnectionStatus.COMPLETED) {
				this.getUI('avatar').css('opacity', '1');

				return;
			}

			if (connectionStatus === ConnectionStatus.FAILED_NO_RESTART) {
				this.getUI('muteIndicator').hide();
				this.getUI('hideRemoteVideoButton').hide();
				this.getUI('screenSharingIndicator').hide();
				this.getUI('iceFailedIndicator').removeClass('not-failed');

				return;
			}
		},

		setAudioAvailable: function(audioAvailable) {
			if (!audioAvailable) {
				this.getUI('muteIndicator')
						.removeClass('audio-on')
						.addClass('audio-off');
				this.setSpeaking(false);

				return;
			}

			this.getUI('muteIndicator')
					.removeClass('audio-off')
					.addClass('audio-on');
		},

		setSpeaking: function(speaking) {
			this.$el.toggleClass('speaking', speaking);
		},

		/**
		 * Sets the element with the video stream.
		 *
		 * @param HTMLVideoElement|null videoElement the element to set, or null
		 *        to remove the current one.
		 */
		setVideoElement: function(videoElement) {
			this.getUI('video').remove();

			if (videoElement) {
				this.$el.prepend(videoElement);

				videoElement.oncontextmenu = function() {
					return false;
				};
			}

			this.bindUIElements();

			// Hide the video until it is explicitly marked as available and
			// enabled.
			this.getUI('video').hide();
		},

		setVideoAvailable: function(videoAvailable) {
			if (!videoAvailable) {
				this.getUI('avatarContainer').show();
				this.getUI('video').hide();
				this.getUI('hideRemoteVideoButton').hide();

				return;
			}

			this.getUI('hideRemoteVideoButton').show();

			if (this._videoEnabled) {
				this.getUI('avatarContainer').hide();
				this.getUI('video').show();
			}
		},

		setVideoEnabled: function(videoEnabled) {
			this._videoEnabled = videoEnabled;

			if (!videoEnabled) {
				this.getUI('avatarContainer').show();
				this.getUI('video').hide();
				this.getUI('hideRemoteVideoButton')
						.attr('data-original-title', t('spreed', 'Enable video'))
						.removeClass('icon-video')
						.addClass('icon-video-off');

				return;
			}

			this.getUI('avatarContainer').hide();
			this.getUI('video').show();
			this.getUI('hideRemoteVideoButton')
					.attr('data-original-title', t('spreed', 'Disable video'))
					.removeClass('icon-video-off')
					.addClass('icon-video');
		},

		toggleVideo: function() {
			if (this._videoEnabled) {
				this.setVideoEnabled(false);
			} else {
				this.setVideoEnabled(true);
			}

			OCA.SpreedMe.speakers.updateVideoContainerDummyIfLatestSpeaker(this.options.peerId);
		},

		setPromoted: function(promoted) {
			this.$el.toggleClass('promoted', promoted);
		},

		setScreenAvailable: function(screenAvailable) {
			if (!screenAvailable) {
				this.getUI('screenSharingIndicator')
						.removeClass('screen-on')
						.addClass('screen-off');

				return;
			}

			this.getUI('screenSharingIndicator')
					.removeClass('screen-off')
					.addClass('screen-on');
		},

		setScreenVisible: function(screenVisible) {
			this._screenVisible = screenVisible;

			this.getUI('screenSharingIndicator').toggleClass('screen-visible', screenVisible);
		},

		switchToScreen: function() {
			if (!this._screenVisible) {
				OCA.SpreedMe.sharedScreens.switchScreenToId(this.options.peerId);
			}

			this.getUI('screenSharingIndicator').tooltip('hide');
		},

		/**
		 * Creates a dummy video container element to show the indicators when
		 * this video view is promoted.
		 *
		 * @return jQuery The jQuery wrapper for the dummy element.
		 */
		newDummyVideoContainer: function() {
			var $dummy = $('<div>')
					.addClass('videoContainer videoContainer-dummy')
					.append(this.getUI('nameIndicator').clone())
					.append(this.getUI('mediaIndicator').clone());

			// Cloning does not copy event handlers by default; it could be
			// forced with a parameter, but the tooltip would have to be
			// explicitly set on the new element anyway. Due to this the click
			// handler is explicitly copied too.
			$dummy.find('.hideRemoteVideo').click(this.toggleVideo.bind(this));
			$dummy.find('.hideRemoteVideo').tooltip({
				placement: 'top',
				trigger: 'hover'
			});

			return $dummy;
		},

	});

	OCA.Talk.Views.VideoView = VideoView;
	OCA.Talk.Views.VideoView.ConnectionStatus = ConnectionStatus;

})(OCA, Marionette);
<commit_msg>Fix hidding video causing audio to not be played<commit_after>/* global Marionette */

/**
 *
 * @copyright Copyright (c) 2019, Daniel Calviño Sánchez (danxuliu@gmail.com)
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

(function(OCA, Marionette) {

	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};
	OCA.Talk = OCA.Talk || {};
	OCA.SpreedMe.Views = OCA.SpreedMe.Views || {};
	OCA.Talk.Views = OCA.Talk.Views || {};

	var ConnectionStatus = {
		NEW: 'new',
		CHECKING: 'checking',
		CONNECTED: 'connected',
		COMPLETED: 'completed',
		DISCONNECTED: 'disconnected',
		DISCONNECTED_LONG: 'disconnected-long',
		FAILED: 'failed',
		FAILED_NO_RESTART: 'failed-no-restart',
		CLOSED: 'closed',
	};

	var VideoView = Marionette.View.extend({

		tagName: 'div',
		className: 'videoContainer',

		id: function() {
			return 'container_' + this.options.peerId + '_video_incoming';
		},

		template: OCA.Talk.Views.Templates['videoview'],

		ui: {
			'video': 'video',
			'avatarContainer': '.avatar-container',
			'avatar': '.avatar',
			'nameIndicator': '.nameIndicator',
			'mediaIndicator': '.mediaIndicator',
			'muteIndicator': '.muteIndicator',
			'hideRemoteVideoButton': '.hideRemoteVideo',
			'screenSharingIndicator': '.screensharingIndicator',
			'iceFailedIndicator': '.iceFailedIndicator',
		},

		events: {
			'click @ui.hideRemoteVideoButton': 'toggleVideo',
			'click @ui.screenSharingIndicator': 'switchToScreen',
		},

		initialize: function() {
			this._connectionStatus = ConnectionStatus.NEW;

			// Video is enabled by default, even if it is not initially
			// available.
			this._videoEnabled = true;
			this._screenVisible = false;

			this.render();

			this.getUI('avatar').addClass('icon-loading');
			this.getUI('avatar').css('opacity', '0.5');

			this.getUI('hideRemoteVideoButton').attr('data-original-title', t('spreed', 'Disable video'));
			this.getUI('hideRemoteVideoButton').hide();

			this.getUI('screenSharingIndicator').attr('data-original-title', t('spreed', 'Show screen'));
		},

		onRender: function() {
			this.getUI('hideRemoteVideoButton').tooltip({
				placement: 'top',
				trigger: 'hover'
			});

			this.getUI('screenSharingIndicator').tooltip({
				placement: 'top',
				trigger: 'hover'
			});
		},

		setParticipant: function(userId, participantName) {
			if (userId && userId.length) {
				this.getUI('avatar').avatar(userId, 128);
			} else {
				this.getUI('avatar').imageplaceholder('?', participantName, 128);
				this.getUI('avatar').css('background-color', '#b9b9b9');

				// "Guest" placeholder is not shown until the initial connection
				// for consistency with regular users.
				if (this._connectionStatus !== ConnectionStatus.NEW) {
					participantName = participantName || t('spreed', 'Guest');
				}
			}

			this.getUI('nameIndicator').text(participantName);
		},

		/**
		 * Sets the current status of the connection.
		 *
		 * @param OCA.Talk.Views.VideoView.ConnectionStatus the connection
		 *        status.
		 */
		setConnectionStatus: function(connectionStatus) {
			this._connectionStatus = connectionStatus;

			this.getUI('avatar').removeClass('icon-loading');
			this.getUI('iceFailedIndicator').addClass('not-failed');

			if (connectionStatus === ConnectionStatus.CHECKING ||
					connectionStatus === ConnectionStatus.DISCONNECTED_LONG ||
					connectionStatus === ConnectionStatus.FAILED) {
				this.getUI('avatar').addClass('icon-loading');

				return;
			}

			if (connectionStatus === ConnectionStatus.CONNECTED ||
					connectionStatus === ConnectionStatus.COMPLETED) {
				this.getUI('avatar').css('opacity', '1');

				return;
			}

			if (connectionStatus === ConnectionStatus.FAILED_NO_RESTART) {
				this.getUI('muteIndicator').hide();
				this.getUI('hideRemoteVideoButton').hide();
				this.getUI('screenSharingIndicator').hide();
				this.getUI('iceFailedIndicator').removeClass('not-failed');

				return;
			}
		},

		setAudioAvailable: function(audioAvailable) {
			if (!audioAvailable) {
				this.getUI('muteIndicator')
						.removeClass('audio-on')
						.addClass('audio-off');
				this.setSpeaking(false);

				return;
			}

			this.getUI('muteIndicator')
					.removeClass('audio-off')
					.addClass('audio-on');
		},

		setSpeaking: function(speaking) {
			this.$el.toggleClass('speaking', speaking);
		},

		/**
		 * Sets the element with the video stream.
		 *
		 * @param HTMLVideoElement|null videoElement the element to set, or null
		 *        to remove the current one.
		 */
		setVideoElement: function(videoElement) {
			this.getUI('video').remove();

			if (videoElement) {
				this.$el.prepend(videoElement);

				videoElement.oncontextmenu = function() {
					return false;
				};
			}

			this.bindUIElements();

			// Hide the video until it is explicitly marked as available and
			// enabled.
			this.getUI('video').addClass('hidden');
		},

		setVideoAvailable: function(videoAvailable) {
			if (!videoAvailable) {
				this.getUI('avatarContainer').show();
				this.getUI('video').addClass('hidden');
				this.getUI('hideRemoteVideoButton').hide();

				return;
			}

			this.getUI('hideRemoteVideoButton').show();

			if (this._videoEnabled) {
				this.getUI('avatarContainer').hide();
				this.getUI('video').removeClass('hidden');
			}
		},

		setVideoEnabled: function(videoEnabled) {
			this._videoEnabled = videoEnabled;

			if (!videoEnabled) {
				this.getUI('avatarContainer').show();
				this.getUI('video').addClass('hidden');
				this.getUI('hideRemoteVideoButton')
						.attr('data-original-title', t('spreed', 'Enable video'))
						.removeClass('icon-video')
						.addClass('icon-video-off');

				return;
			}

			this.getUI('avatarContainer').hide();
			this.getUI('video').removeClass('hidden');
			this.getUI('hideRemoteVideoButton')
					.attr('data-original-title', t('spreed', 'Disable video'))
					.removeClass('icon-video-off')
					.addClass('icon-video');
		},

		toggleVideo: function() {
			if (this._videoEnabled) {
				this.setVideoEnabled(false);
			} else {
				this.setVideoEnabled(true);
			}

			OCA.SpreedMe.speakers.updateVideoContainerDummyIfLatestSpeaker(this.options.peerId);
		},

		setPromoted: function(promoted) {
			this.$el.toggleClass('promoted', promoted);
		},

		setScreenAvailable: function(screenAvailable) {
			if (!screenAvailable) {
				this.getUI('screenSharingIndicator')
						.removeClass('screen-on')
						.addClass('screen-off');

				return;
			}

			this.getUI('screenSharingIndicator')
					.removeClass('screen-off')
					.addClass('screen-on');
		},

		setScreenVisible: function(screenVisible) {
			this._screenVisible = screenVisible;

			this.getUI('screenSharingIndicator').toggleClass('screen-visible', screenVisible);
		},

		switchToScreen: function() {
			if (!this._screenVisible) {
				OCA.SpreedMe.sharedScreens.switchScreenToId(this.options.peerId);
			}

			this.getUI('screenSharingIndicator').tooltip('hide');
		},

		/**
		 * Creates a dummy video container element to show the indicators when
		 * this video view is promoted.
		 *
		 * @return jQuery The jQuery wrapper for the dummy element.
		 */
		newDummyVideoContainer: function() {
			var $dummy = $('<div>')
					.addClass('videoContainer videoContainer-dummy')
					.append(this.getUI('nameIndicator').clone())
					.append(this.getUI('mediaIndicator').clone());

			// Cloning does not copy event handlers by default; it could be
			// forced with a parameter, but the tooltip would have to be
			// explicitly set on the new element anyway. Due to this the click
			// handler is explicitly copied too.
			$dummy.find('.hideRemoteVideo').click(this.toggleVideo.bind(this));
			$dummy.find('.hideRemoteVideo').tooltip({
				placement: 'top',
				trigger: 'hover'
			});

			return $dummy;
		},

	});

	OCA.Talk.Views.VideoView = VideoView;
	OCA.Talk.Views.VideoView.ConnectionStatus = ConnectionStatus;

})(OCA, Marionette);
<|endoftext|>


<commit_before>var eb = require("../eventBus");
var Handlebones = require("handlebones");
var template = require("../tmpl/vote-view");
var CommentModel = require("../models/comment");
var serverClient = require("../lib/polis");
var Utils = require("../util/utils");
var Strings = require("../strings");

var iOS = Utils.isIos();

module.exports = Handlebones.ModelView.extend({
    name: "vote-view",
    template: template,
    events: {

      "click #agreeButton": "participantAgreed",
      "click #disagreeButton": "participantDisagreed",
      "click #passButton": "participantPassed",
      "click #subscribeBtn": "subscribeBtn",
      "click #starBtn": "starBtn",
      "hover .starbutton": function(){
        this.$(".starbutton").html("");
      }
    },
  context: function() {
    var ctx = Handlebones.ModelView.prototype.context.apply(this, arguments);
    ctx.iOS = iOS;
    ctx.customStyles = "";
    // if (ctx.txt && ctx.txt.length < 30) {
    //   ctx.customStyles += "text-align: center; ";
      // ctx.customStyles += "padding-top: 39px; ";
    //   ctx.customStyles += "font-size: 22px; ";
    // }
    ctx.email = userObject.email;
    ctx.subscribed = this.isSubscribed();
    if (ctx.created) {
      ctx.createdString = (new Date(ctx.created * 1)).toString().match(/(.*?) [0-9]+:/)[1];
    }
    ctx.s = Strings;

    var social = ctx.social;
    var socialCtx = {
      name: Strings.anonPerson,
      img: Utils.getAnonPicUrl(),
      link: "",
      anon: true,
    };
    if (social) {
      var hasTwitter = social.screen_name;
      var hasFacebook = social.fb_name;
      if (hasFacebook) {
        socialCtx = {
          name: social.fb_name,
          img: social.fb_picture,
          link: social.fb_link,
        };
      }
      if (hasTwitter) {
        socialCtx = {
          name: social.name,
          img: social.profile_image_url_https,
          link: "https://twitter.com/" + social.screen_name,
          screen_name: social.screen_name,
        };
      }
    }
    ctx.social = socialCtx;

    return ctx;
  },
  animateOut: function() {
    // Animating opacity directly instead of jQuery's fadeOut because we don't want display:none at the end.
    this.$el.children().children().animate({
      opacity: 0
    }, 200);
  },
  animateIn: function() {
    this.$el.children().children().animate({
      opacity: 1
    }, 200);
  },
  initialize: function(options) {
    Handlebones.ModelView.prototype.initialize.apply(this, arguments);
      eb.on(eb.exitConv, cleanup);
    function cleanup() {
      //alert('cleanup');
      eb.off(eb.exitConv, cleanup);
    }
    var serverClient = options.serverClient;
    var votesByMe = options.votesByMe;
    var votesByMeFetched = $.Deferred();
    votesByMeFetched.then(function() {
      if (votesByMe.size() > 0) {
        eb.trigger(eb.interacted);
      }
    });
    this.conversationModel = options.conversationModel;

    var is_public = options.is_public;
    var conversation_id = this.conversation_id = options.conversation_id;
    var pid = this.pid = options.pid;
    this.isSubscribed = options.isSubscribed;
    console.dir(serverClient);

    if (this.pid < 0) {
      // DEMO_MODE
      votesByMeFetched.resolve();
    }
    votesByMe.on("sync", votesByMeFetched.resolve);

    var that = this;
    var waitingForComments = true;
    var commentPollInterval = 5 * 1000;
    function pollForComments(optionalPromiseForPreExisingNextCommentCall) {
      if (waitingForComments && !Utils.isHidden()) {
          getNextAndShow(optionalPromiseForPreExisingNextCommentCall);
      } else {
        // try to try again later
        setTimeout(pollForComments, commentPollInterval);
      }
    }
    function showComment(model) {
      that.model.clear({silent: true});
      that.model.set(_.extend({
        empty: false
      }, model));
      that.render();
      that.trigger("showComment");
      waitingForComments = false;
    }
    function getNextAndShow(optionalPromiseForPreExisingNextCommentCall) {
      var params = {};
      if (that.model && that.model.get("tid")) {
        params.notTid = that.model.get("tid");
      }
      var promise = optionalPromiseForPreExisingNextCommentCall || serverClient.getNextComment(params);
      promise.then(function(c) {
        var email = that.$(".email").val();
        if (!email) { // Don't clobber view if user is writing an email
          if (!that.conversationModel.get("is_active")) {
            showClosedConversationNotice();
          } else if (c && c.txt) {
            showComment(c);
          } else {
            showEmpty();
          }
        }
        setTimeout(pollForComments, commentPollInterval);
      }, function(err) {
        setTimeout(pollForComments, commentPollInterval);
      });
    }
    function onFail(err) {
      this.animateIn();
      console.error(err);

      if (!Utils.cookiesEnabled()) {
        // TODO send GA event
        alert("Sorry, voting requires cookies to be enabled. If you do enable cookies, be sure to reload the page after.");
      } else if (err && err.responseText === "polis_err_conversation_is_closed"){
        alert("This conversation is closed. No further voting is allowed.");
      } else {
        alert("Apologies, your vote failed to send. Please check your connection and try again.");
      }
    }
    function onVote(result) {
      var that = this;
      eb.trigger(eb.vote);
      eb.trigger(eb.interacted);
      if (result.nextComment) {
        showComment(result.nextComment);
      } else {
        showEmpty();
      }
      this.animateIn();

      // Fix for stuck hover effects for touch events.
      // Remove when this is fix is accepted
      // https://github.com/twbs/bootstrap/issues/12832
      this.$(".btn-vote").blur();
    }
    function showClosedConversationNotice() {
      that.model.clear({silent: true});
      that.model.set({
        empty: true,
        txt1: "This conversation is closed.",
        txt2: "No further voting is allowed."
      });
      that.render();
    }
    function showEmpty() {
      var userHasVoted = !!votesByMe.size();

      waitingForComments = true;
      // pollForComments();

      var message1;
      var message2;
      if (userHasVoted) {
        message1 = "You've voted on all the comments.";
        message2 = "You can write your own by clicking the Write tab.";
      } else {
        message1 = "There aren't any comments yet.";
        if (is_public) {
          message2 =  "Get this conversation started by inviting more participants, or add your own comment in the 'write' tab.";
        } else {
          message2 =  "Get this conversation started by adding your own comment in the 'write' tab.";
        }
      }

      // TODO show some indication of whether they should wait around or not (how many active users there are, etc)
      that.model.clear({silent: true});
      that.model.set({
        empty: true,
        txt1: message1,
        txt2: message2
      });
      that.render();
    }

    this.onButtonClicked = function() {
      this.animateOut();
    };
    this.starBtn = function(e) {
      var starred = !that.model.get("starred");

      that.model.set("starred", starred);
      if (starred) {
        $("#starredLabel").fadeIn(200);
        setTimeout(function() {
          $("#starredLabel").fadeOut(600);
        }, 1500);
      }
    };
    this.subscribeBtn = function(e) {
      var that = this;
      var email = this.$(".email").val();
      serverClient.convSub({
        type: 1, // 1 for email
        email: email,
        conversation_id: conversation_id
      }).then(function() {
        userObject.email = that.$(".email").val(); // TODO this is kinda crappy
        that.isSubscribed(1); // TODO this is totally crappy
        that.$(".email").val("");
        that.model.set("foo", Math.random()); // trigger render
        // alert("you have subscribed");
      }, function(err) {
        alert("Error subscribing");
        console.error(err);
      });
    },
    this.participantAgreed = function(e) {
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      if (!starred) {
        starred = void 0; // don't bother sending up false, no need to put a vote value of 0 in the db.
      }
      votesByMe.add({
        vote: -1,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      serverClient.agree(tid, starred)
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantDisagreed = function() {
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      votesByMe.add({
        vote: 1,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      serverClient.disagree(tid, starred)
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantPassed = function() {
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      votesByMe.add({
        vote: 0,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      serverClient.pass(tid, starred)
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantStarred = function() {
      var tid = this.model.get("tid");
      votesByMe.add({
        participantStarred: true,
        vote: -1,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      $.when(serverClient.star(tid), serverClient.agree(tid))
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantTrashed = function() {
      var tid = this.model.get("tid");
      this.onButtonClicked();
      serverClient.trash(tid)
          .then(onVote.bind(this), onFail.bind(this));
    };

    pollForComments(options.firstCommentPromise); // call immediately using a promise for the first comment (latency reduction hack)
    this.listenTo(this, "rendered", function(){
      // this.$("#agreeButton").tooltip({
      //   title: "This comment represents my opinion",
      //   placement: "right",
      //   delay: { show: 500, hide: 0 },
      //   container: "body"
      // });
      // this.$("#disagreeButton").tooltip({
      //   title: "This comment does not represent my opinion",
      //   placement: "top",
      //   delay: { show: 500, hide: 0 }
      // });
      this.$("#passButton").tooltip({
        title: "'No reaction', or 'I am unsure'",
        placement: "top",
        delay: { show: 500, hide: 0 }
      });
      this.$("#starButton").tooltip({
        title: "'Critical point', or 'central to my worldview'",
        placement: "top",
        delay: { show: 500, hide: 0 },
        container: "body"
      });
      this.$("#trashButton").tooltip({
        title: "This comment is irrelevant and/or abusive",
        placement: "top",
        delay: { show: 500, hide: 0 }
      });

    });
  }
  });
<commit_msg>remove references to 'write tab'<commit_after>var eb = require("../eventBus");
var Handlebones = require("handlebones");
var template = require("../tmpl/vote-view");
var CommentModel = require("../models/comment");
var serverClient = require("../lib/polis");
var Utils = require("../util/utils");
var Strings = require("../strings");

var iOS = Utils.isIos();

module.exports = Handlebones.ModelView.extend({
    name: "vote-view",
    template: template,
    events: {

      "click #agreeButton": "participantAgreed",
      "click #disagreeButton": "participantDisagreed",
      "click #passButton": "participantPassed",
      "click #subscribeBtn": "subscribeBtn",
      "click #starBtn": "starBtn",
      "hover .starbutton": function(){
        this.$(".starbutton").html("");
      }
    },
  context: function() {
    var ctx = Handlebones.ModelView.prototype.context.apply(this, arguments);
    ctx.iOS = iOS;
    ctx.customStyles = "";
    // if (ctx.txt && ctx.txt.length < 30) {
    //   ctx.customStyles += "text-align: center; ";
      // ctx.customStyles += "padding-top: 39px; ";
    //   ctx.customStyles += "font-size: 22px; ";
    // }
    ctx.email = userObject.email;
    ctx.subscribed = this.isSubscribed();
    if (ctx.created) {
      ctx.createdString = (new Date(ctx.created * 1)).toString().match(/(.*?) [0-9]+:/)[1];
    }
    ctx.s = Strings;

    var social = ctx.social;
    var socialCtx = {
      name: Strings.anonPerson,
      img: Utils.getAnonPicUrl(),
      link: "",
      anon: true,
    };
    if (social) {
      var hasTwitter = social.screen_name;
      var hasFacebook = social.fb_name;
      if (hasFacebook) {
        socialCtx = {
          name: social.fb_name,
          img: social.fb_picture,
          link: social.fb_link,
        };
      }
      if (hasTwitter) {
        socialCtx = {
          name: social.name,
          img: social.profile_image_url_https,
          link: "https://twitter.com/" + social.screen_name,
          screen_name: social.screen_name,
        };
      }
    }
    ctx.social = socialCtx;

    return ctx;
  },
  animateOut: function() {
    // Animating opacity directly instead of jQuery's fadeOut because we don't want display:none at the end.
    this.$el.children().children().animate({
      opacity: 0
    }, 200);
  },
  animateIn: function() {
    this.$el.children().children().animate({
      opacity: 1
    }, 200);
  },
  initialize: function(options) {
    Handlebones.ModelView.prototype.initialize.apply(this, arguments);
      eb.on(eb.exitConv, cleanup);
    function cleanup() {
      //alert('cleanup');
      eb.off(eb.exitConv, cleanup);
    }
    var serverClient = options.serverClient;
    var votesByMe = options.votesByMe;
    var votesByMeFetched = $.Deferred();
    votesByMeFetched.then(function() {
      if (votesByMe.size() > 0) {
        eb.trigger(eb.interacted);
      }
    });
    this.conversationModel = options.conversationModel;

    var is_public = options.is_public;
    var conversation_id = this.conversation_id = options.conversation_id;
    var pid = this.pid = options.pid;
    this.isSubscribed = options.isSubscribed;
    console.dir(serverClient);

    if (this.pid < 0) {
      // DEMO_MODE
      votesByMeFetched.resolve();
    }
    votesByMe.on("sync", votesByMeFetched.resolve);

    var that = this;
    var waitingForComments = true;
    var commentPollInterval = 5 * 1000;
    function pollForComments(optionalPromiseForPreExisingNextCommentCall) {
      if (waitingForComments && !Utils.isHidden()) {
          getNextAndShow(optionalPromiseForPreExisingNextCommentCall);
      } else {
        // try to try again later
        setTimeout(pollForComments, commentPollInterval);
      }
    }
    function showComment(model) {
      that.model.clear({silent: true});
      that.model.set(_.extend({
        empty: false
      }, model));
      that.render();
      that.trigger("showComment");
      waitingForComments = false;
    }
    function getNextAndShow(optionalPromiseForPreExisingNextCommentCall) {
      var params = {};
      if (that.model && that.model.get("tid")) {
        params.notTid = that.model.get("tid");
      }
      var promise = optionalPromiseForPreExisingNextCommentCall || serverClient.getNextComment(params);
      promise.then(function(c) {
        var email = that.$(".email").val();
        if (!email) { // Don't clobber view if user is writing an email
          if (!that.conversationModel.get("is_active")) {
            showClosedConversationNotice();
          } else if (c && c.txt) {
            showComment(c);
          } else {
            showEmpty();
          }
        }
        setTimeout(pollForComments, commentPollInterval);
      }, function(err) {
        setTimeout(pollForComments, commentPollInterval);
      });
    }
    function onFail(err) {
      this.animateIn();
      console.error(err);

      if (!Utils.cookiesEnabled()) {
        // TODO send GA event
        alert("Sorry, voting requires cookies to be enabled. If you do enable cookies, be sure to reload the page after.");
      } else if (err && err.responseText === "polis_err_conversation_is_closed"){
        alert("This conversation is closed. No further voting is allowed.");
      } else {
        alert("Apologies, your vote failed to send. Please check your connection and try again.");
      }
    }
    function onVote(result) {
      var that = this;
      eb.trigger(eb.vote);
      eb.trigger(eb.interacted);
      if (result.nextComment) {
        showComment(result.nextComment);
      } else {
        showEmpty();
      }
      this.animateIn();

      // Fix for stuck hover effects for touch events.
      // Remove when this is fix is accepted
      // https://github.com/twbs/bootstrap/issues/12832
      this.$(".btn-vote").blur();
    }
    function showClosedConversationNotice() {
      that.model.clear({silent: true});
      that.model.set({
        empty: true,
        txt1: "This conversation is closed.",
        txt2: "No further voting is allowed."
      });
      that.render();
    }
    function showEmpty() {
      var userHasVoted = !!votesByMe.size();

      waitingForComments = true;
      // pollForComments();

      var message1;
      var message2;
      if (userHasVoted) {
        message1 = "You've voted on all the comments.";
        message2 = "If you have something to add, try writing your own comment.";
      } else {
        message1 = "There aren't any comments yet.";
        if (is_public) {
          message2 =  "Get this conversation started by inviting more participants, or add a comment.";
        } else {
          message2 =  "Get this conversation started by adding a comment.";
        }
      }

      // TODO show some indication of whether they should wait around or not (how many active users there are, etc)
      that.model.clear({silent: true});
      that.model.set({
        empty: true,
        txt1: message1,
        txt2: message2
      });
      that.render();
    }

    this.onButtonClicked = function() {
      this.animateOut();
    };
    this.starBtn = function(e) {
      var starred = !that.model.get("starred");

      that.model.set("starred", starred);
      if (starred) {
        $("#starredLabel").fadeIn(200);
        setTimeout(function() {
          $("#starredLabel").fadeOut(600);
        }, 1500);
      }
    };
    this.subscribeBtn = function(e) {
      var that = this;
      var email = this.$(".email").val();
      serverClient.convSub({
        type: 1, // 1 for email
        email: email,
        conversation_id: conversation_id
      }).then(function() {
        userObject.email = that.$(".email").val(); // TODO this is kinda crappy
        that.isSubscribed(1); // TODO this is totally crappy
        that.$(".email").val("");
        that.model.set("foo", Math.random()); // trigger render
        // alert("you have subscribed");
      }, function(err) {
        alert("Error subscribing");
        console.error(err);
      });
    },
    this.participantAgreed = function(e) {
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      if (!starred) {
        starred = void 0; // don't bother sending up false, no need to put a vote value of 0 in the db.
      }
      votesByMe.add({
        vote: -1,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      serverClient.agree(tid, starred)
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantDisagreed = function() {
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      votesByMe.add({
        vote: 1,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      serverClient.disagree(tid, starred)
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantPassed = function() {
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      votesByMe.add({
        vote: 0,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      serverClient.pass(tid, starred)
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantStarred = function() {
      var tid = this.model.get("tid");
      votesByMe.add({
        participantStarred: true,
        vote: -1,
        conversation_id: conversation_id,
        pid: pid,
        tid: tid
      });
      this.onButtonClicked();
      $.when(serverClient.star(tid), serverClient.agree(tid))
          .then(onVote.bind(this), onFail.bind(this));
    };
    this.participantTrashed = function() {
      var tid = this.model.get("tid");
      this.onButtonClicked();
      serverClient.trash(tid)
          .then(onVote.bind(this), onFail.bind(this));
    };

    pollForComments(options.firstCommentPromise); // call immediately using a promise for the first comment (latency reduction hack)
    this.listenTo(this, "rendered", function(){
      // this.$("#agreeButton").tooltip({
      //   title: "This comment represents my opinion",
      //   placement: "right",
      //   delay: { show: 500, hide: 0 },
      //   container: "body"
      // });
      // this.$("#disagreeButton").tooltip({
      //   title: "This comment does not represent my opinion",
      //   placement: "top",
      //   delay: { show: 500, hide: 0 }
      // });
      this.$("#passButton").tooltip({
        title: "'No reaction', or 'I am unsure'",
        placement: "top",
        delay: { show: 500, hide: 0 }
      });
      this.$("#starButton").tooltip({
        title: "'Critical point', or 'central to my worldview'",
        placement: "top",
        delay: { show: 500, hide: 0 },
        container: "body"
      });
      this.$("#trashButton").tooltip({
        title: "This comment is irrelevant and/or abusive",
        placement: "top",
        delay: { show: 500, hide: 0 }
      });

    });
  }
  });
<|endoftext|>


<commit_before>/*
    Stores configuration settings for the playlist builder.
    A container object (ex a div) must be passed in, the playlist will be built on this element.
*/

var Config = function(params) {

        var that = this,
            defaultParams;

        defaultParams = {

            ac: new (window.AudioContext || window.webkitAudioContext),

            resolution: 4096, //resolution - samples per pixel to draw.
            timeFormat: 'hh:mm:ss.uuu',
            mono: true, //whether to draw multiple channels or combine them.
            fadeType: 'logarithmic',

            timescale: false, //whether or not to include the time measure.

            UITheme: "default", // bootstrap || jQueryUI || default

            waveColor: '#c7c7c7',
            progressColor: 'orange',
            loadingColor: 'purple',
            cursorColor: 'green',
            markerColor: 'green',
            selectBorderColor: 'red',
            selectBackgroundColor: 'rgba(0,0,0,0.1)',

            timeColor: 'grey',
            fontColor: 'black',
            fadeColor: 'black',

            waveHeight: 128, //height of each canvas element a waveform is on.

            trackscroll: {
                left: 0,
                top: 0
            },

            state: 'select',

            cursorPos: 0 //value is kept in seconds.
        };

        params = Object.create(params);
        Object.keys(defaultParams).forEach(function(key) {
            if (!(key in params)) {
                params[key] = defaultParams[key];
            }
        });

        /*
            Start of all getter methods for config.
        */

        that.getContainer = function getContainer() {
            return params.container;
        };

        that.isTimeScaleEnabled = function isTimeScaleEnabled() {
            return params.timescale;
        };

        that.getFadeType = function getFadeType() {
            return params.fadeType;
        };

        that.isDisplayMono = function isDisplayMono() {
            return params.mono;
        };

        that.getUITheme = function getUITheme() {
            return params.UITheme;
        };

        that.getCursorPos = function getCursorPos() {
            return params.cursorPos;
        };

        that.getState = function getState() {
            return params.state;
        };

        that.getAudioContext = function getAudioContext() {
            return params.ac;
        };

        that.getSampleRate = function getSampleRate() {
            return params.ac.sampleRate;
        };

        that.getCurrentTime = function getCurrentTime() {
            return params.ac.currentTime;
        };

        that.getTimeFormat = function getTimeFormat() {
            return params.timeFormat;
        };

        that.getResolution = function getResolution() {
            return params.resolution;
        };

        that.getWaveHeight = function getWaveHeight() {
            return params.waveHeight;
        };

        that.getColorScheme = function getColorScheme() {
            return {
                waveColor: params.waveColor,
                progressColor: params.progressColor,
                loadingColor: params.loadingColor,
                cursorColor: params.cursorColor,
                markerColor: params.markerColor,
                timeColor: params.timeColor,
                fontColor: params.fontColor,
                fadeColor: params.fadeColor,
                selectBorderColor: params.selectBorderColor,
                selectBackgroundColor: params.selectBackgroundColor,
            };
        };

        that.getTrackScroll = function getTrackScroll() {
            var scroll = params.trackscroll;

            return {
                left: scroll.left,
                top: scroll.top
            };
        };

        /*
            Start of all setter methods for config.
        */

        that.setResolution = function setResolution(resolution) {
            params.resolution = resolution;
        };

        that.setTimeFormat = function setTimeFormat(format) {
            params.timeFormat = format;
        };

        that.setFadeType = function setFadeType(type) {
            params.fadeType = type;
        };

        that.setDisplayMono = function setDisplayMono(bool) {
            params.mono = bool;
        };

        that.setCursorPos = function setCursorPos(pos) {
            params.cursorPos = pos;
        };

        that.setState = function setState(state) {
            params.state = state;
        };

        that.setTrackScroll = function setTrackScroll(left, top) {
            var scroll = params.trackscroll;

            scroll.left = left;
            scroll.top = top;
        };
};
<commit_msg>putting back default wavecolor<commit_after>/*
    Stores configuration settings for the playlist builder.
    A container object (ex a div) must be passed in, the playlist will be built on this element.
*/

var Config = function(params) {

        var that = this,
            defaultParams;

        defaultParams = {

            ac: new (window.AudioContext || window.webkitAudioContext),

            resolution: 4096, //resolution - samples per pixel to draw.
            timeFormat: 'hh:mm:ss.uuu',
            mono: true, //whether to draw multiple channels or combine them.
            fadeType: 'logarithmic',

            timescale: false, //whether or not to include the time measure.

            UITheme: "default", // bootstrap || jQueryUI || default

            waveColor: 'grey',
            progressColor: 'orange',
            loadingColor: 'purple',
            cursorColor: 'green',
            markerColor: 'green',
            selectBorderColor: 'red',
            selectBackgroundColor: 'rgba(0,0,0,0.1)',

            timeColor: 'grey',
            fontColor: 'black',
            fadeColor: 'black',

            waveHeight: 128, //height of each canvas element a waveform is on.

            trackscroll: {
                left: 0,
                top: 0
            },

            state: 'select',

            cursorPos: 0 //value is kept in seconds.
        };

        params = Object.create(params);
        Object.keys(defaultParams).forEach(function(key) {
            if (!(key in params)) {
                params[key] = defaultParams[key];
            }
        });

        /*
            Start of all getter methods for config.
        */

        that.getContainer = function getContainer() {
            return params.container;
        };

        that.isTimeScaleEnabled = function isTimeScaleEnabled() {
            return params.timescale;
        };

        that.getFadeType = function getFadeType() {
            return params.fadeType;
        };

        that.isDisplayMono = function isDisplayMono() {
            return params.mono;
        };

        that.getUITheme = function getUITheme() {
            return params.UITheme;
        };

        that.getCursorPos = function getCursorPos() {
            return params.cursorPos;
        };

        that.getState = function getState() {
            return params.state;
        };

        that.getAudioContext = function getAudioContext() {
            return params.ac;
        };

        that.getSampleRate = function getSampleRate() {
            return params.ac.sampleRate;
        };

        that.getCurrentTime = function getCurrentTime() {
            return params.ac.currentTime;
        };

        that.getTimeFormat = function getTimeFormat() {
            return params.timeFormat;
        };

        that.getResolution = function getResolution() {
            return params.resolution;
        };

        that.getWaveHeight = function getWaveHeight() {
            return params.waveHeight;
        };

        that.getColorScheme = function getColorScheme() {
            return {
                waveColor: params.waveColor,
                progressColor: params.progressColor,
                loadingColor: params.loadingColor,
                cursorColor: params.cursorColor,
                markerColor: params.markerColor,
                timeColor: params.timeColor,
                fontColor: params.fontColor,
                fadeColor: params.fadeColor,
                selectBorderColor: params.selectBorderColor,
                selectBackgroundColor: params.selectBackgroundColor,
            };
        };

        that.getTrackScroll = function getTrackScroll() {
            var scroll = params.trackscroll;

            return {
                left: scroll.left,
                top: scroll.top
            };
        };

        /*
            Start of all setter methods for config.
        */

        that.setResolution = function setResolution(resolution) {
            params.resolution = resolution;
        };

        that.setTimeFormat = function setTimeFormat(format) {
            params.timeFormat = format;
        };

        that.setFadeType = function setFadeType(type) {
            params.fadeType = type;
        };

        that.setDisplayMono = function setDisplayMono(bool) {
            params.mono = bool;
        };

        that.setCursorPos = function setCursorPos(pos) {
            params.cursorPos = pos;
        };

        that.setState = function setState(state) {
            params.state = state;
        };

        that.setTrackScroll = function setTrackScroll(left, top) {
            var scroll = params.trackscroll;

            scroll.left = left;
            scroll.top = top;
        };
};
<|endoftext|>


<commit_before>/* WP Mailto Links */
/*global window, jQuery*/
(function (window) {
    'use strict';

    var document = window.document;

    // add event handler
    function addEvt(el, evt, fn) {
        if (el.attachEvent) {
            // IE method
            el.attachEvent('on' + evt, fn);
        } else if (el.addEventListener) {
            // Standard JS method
            el.addEventListener(evt, fn, false);
        }
    }

    // encoding method
    function rot13(s) {
        // source: http://jsfromhell.com/string/rot13
        return s.replace(/[a-zA-Z]/g, function (c) {
            return String.fromCharCode((c <= 'Z' ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
        });
    }

    // fetch email from data attribute
    function fetchEmail(el) {
        var email = el.getAttribute('data-enc-email');

        if (!email) {
            return null;
        }

        email = email.replace('[at]', '@');
        email = rot13(email.replace(/\[a\]/g, '@'));
        return email;
    }

    // replace email in title attribute
    function parseTitle(el) {
        var title = el.getAttribute('title');
        var email = fetchEmail(el);

        if (title && email) {
            title = title.replace('{{email}}', email);
            el.setAttribute('title', title);
        }
    }

    // open mailto link
    function mailto(el) {
        var email = fetchEmail(el);

        if (email) {
            window.location.href = 'mailto:' + email;
        }
    }

    // on DOM ready...
    if (window.jQuery) {
    // jQuery DOMready method
        jQuery(function ($) {
            $('body').delegate('a[data-enc-email]', 'click', function () {
                mailto(this);
            });

            $('a[data-enc-email]').each(function () {
                parseTitle(this);
            });
        });
    } else {
    // use onload when jQuery not available
        addEvt(window, 'load', function () {
            var links = document.getElementsByTagName('a');
            var addClick = function (a) {
                addEvt(a, 'click', function () {
                    mailto(a);
                });
            };
            var a;
            var i;

            // check each <a> element
            for (i = 0; i < links.length; i += 1) {
                a = links[i];

                // click event for opening in a new window
                if (a.getAttribute('data-enc-email')) {
                    parseTitle(a);
                    addClick(a);
                }
            }
        });
    }

}(window));
<commit_msg>Solved bug [at] sign in CC and BCC (ID 29)<commit_after>/* WP Mailto Links */
/*global window, jQuery*/
(function (window) {
    'use strict';

    var document = window.document;

    // add event handler
    function addEvt(el, evt, fn) {
        if (el.attachEvent) {
            // IE method
            el.attachEvent('on' + evt, fn);
        } else if (el.addEventListener) {
            // Standard JS method
            el.addEventListener(evt, fn, false);
        }
    }

    // encoding method
    function rot13(s) {
        // source: http://jsfromhell.com/string/rot13
        return s.replace(/[a-zA-Z]/g, function (c) {
            return String.fromCharCode((c <= 'Z' ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
        });
    }

    // fetch email from data attribute
    function fetchEmail(el) {
        var email = el.getAttribute('data-enc-email');

        if (!email) {
            return null;
        }

        // replace [at] sign
        email = email.replace(/\[at\]/g, '@');

        // encode
        email = rot13(email);

        return email;
    }

    // replace email in title attribute
    function parseTitle(el) {
        var title = el.getAttribute('title');
        var email = fetchEmail(el);

        if (title && email) {
            title = title.replace('{{email}}', email);
            el.setAttribute('title', title);
        }
    }

    // open mailto link
    function mailto(el) {
        var email = fetchEmail(el);

        if (email) {
            window.location.href = 'mailto:' + email;
        }
    }

    // on DOM ready...
    if (window.jQuery) {
    // jQuery DOMready method
        jQuery(function ($) {
            $('body').delegate('a[data-enc-email]', 'click', function () {
                mailto(this);
            });

            $('a[data-enc-email]').each(function () {
                parseTitle(this);
            });
        });
    } else {
    // use onload when jQuery not available
        addEvt(window, 'load', function () {
            var links = document.getElementsByTagName('a');
            var addClick = function (a) {
                addEvt(a, 'click', function () {
                    mailto(a);
                });
            };
            var a;
            var i;

            // check each <a> element
            for (i = 0; i < links.length; i += 1) {
                a = links[i];

                // click event for opening in a new window
                if (a.getAttribute('data-enc-email')) {
                    parseTitle(a);
                    addClick(a);
                }
            }
        });
    }

}(window));
<|endoftext|>


<commit_before>define(function (require) {

  var moment = require('moment'),
      AppDispatcher = require('dispatchers/AppDispatcher'),
      ImageConstants = require('constants/ImageConstants');

  return {

    updateImageAttributes: function (image, newAttributes) {
      if(newAttributes.end_date != null) {
        var end_date;
        if (typeof newAttributes.end_date === "object") {
            end_date = newAttributes.end_date
        } else {
            end_date = moment(newAttributes.end_date);
        }
        newAttributes.end_date = end_date;
      }
      image.set({
        name: newAttributes.name,
        description: newAttributes.description,
        end_date: newAttributes.end_date,
        tags: newAttributes.tags
      });
      AppDispatcher.handleRouteAction({
        actionType: ImageConstants.IMAGE_UPDATE,
        payload: {image: image}
      });
    }

  };

});
<commit_msg>Trickle-down Merge branch 'larping-loon'<commit_after>define(function (require) {

  var moment = require('moment'),
      AppDispatcher = require('dispatchers/AppDispatcher'),
      ImageConstants = require('constants/ImageConstants');

  return {

    updateImageAttributes: function (image, newAttributes) {
      if(newAttributes.end_date != null) {
        var end_date;
        if (typeof newAttributes.end_date === "object") {
            end_date = newAttributes.end_date
        } else {
            end_date = moment(newAttributes.end_date, "M/DD/YYYY hh:mm a z");
        }
        newAttributes.end_date = end_date;
      }
      image.set({
        name: newAttributes.name,
        description: newAttributes.description,
        end_date: newAttributes.end_date,
        tags: newAttributes.tags
      });
      AppDispatcher.handleRouteAction({
        actionType: ImageConstants.IMAGE_UPDATE,
        payload: {image: image}
      });
    }

  };

});
<|endoftext|>


<commit_before>/*
 * Inspired by RouterLink from Vue Router
 *  --> API should match!
 */

import { computed, getCurrentInstance } from 'vue'

import { prevent } from '../../utils/event.js'
import { vmHasRouter } from '../../utils/private/vm.js'

// Get the original path value of a record by following its aliasOf
function getOriginalPath (record) {
  return record
    ? (
        record.aliasOf
          ? record.aliasOf.path
          : record.path
      ) : ''
}

function isSameRouteRecord (a, b) {
  // since the original record has an undefined value for aliasOf
  // but all aliases point to the original record, this will always compare
  // the original record
  return (a.aliasOf || a) === (b.aliasOf || b)
}

function includesParams (outer, inner) {
  for (const key in inner) {
    const
      innerValue = inner[ key ],
      outerValue = outer[ key ]

    if (typeof innerValue === 'string') {
      if (innerValue !== outerValue) {
        return false
      }
    }
    else if (
      Array.isArray(outerValue) === false
      || outerValue.length !== innerValue.length
      || innerValue.some((value, i) => value !== outerValue[ i ])
    ) {
      return false
    }
  }

  return true
}

function isEquivalentArray (a, b) {
  return Array.isArray(b) === true
    ? a.length === b.length && a.every((value, i) => value === b[ i ])
    : a.length === 1 && a[ 0 ] === b
}

function isSameRouteLocationParamsValue (a, b) {
  return Array.isArray(a) === true
    ? isEquivalentArray(a, b)
    : (
        Array.isArray(b) === true
          ? isEquivalentArray(b, a)
          : a === b
      )
}

function isSameRouteLocationParams (a, b) {
  if (Object.keys(a).length !== Object.keys(b).length) {
    return false
  }

  for (const key in a) {
    if (isSameRouteLocationParamsValue(a[ key ], b[ key ]) === false) {
      return false
    }
  }

  return true
}

export const useRouterLinkProps = {
  to: [ String, Object ],
  replace: Boolean,
  exact: Boolean,
  activeClass: {
    type: String,
    default: 'q-router-link--active'
  },
  exactActiveClass: {
    type: String,
    default: 'q-router-link--exact-active'
  },
  disable: Boolean
}

export default function () {
  const vm = getCurrentInstance()
  const { props, attrs, proxy } = vm

  const hasRouter = vmHasRouter(vm)

  const hasLink = computed(() =>
    hasRouter === true
    && props.disable !== true
    && props.to !== void 0 && props.to !== null && props.to !== ''
  )

  const linkTag = computed(() => (
    hasLink.value === true
      ? 'a'
      : (props.tag || 'div')
  ))

  const linkRoute = computed(() => (
    hasLink.value === true
      ? proxy.$router.resolve(props.to)
      : null
  ))

  const linkActiveIndex = computed(() => {
    if (hasLink.value === false) {
      return null
    }

    const
      { matched } = linkRoute.value,
      { length } = matched,
      routeMatched = matched[ length - 1 ]

    if (routeMatched === void 0) {
      return -1
    }

    const currentMatched = proxy.$route.matched

    if (currentMatched.length === 0) {
      return -1
    }

    const index = currentMatched.findIndex(
      isSameRouteRecord.bind(null, routeMatched)
    )

    if (index > -1) {
      return index
    }

    // possible parent record
    const parentRecordPath = getOriginalPath(matched[ length - 2 ])

    return (
      // we are dealing with nested routes
      length > 1
      // if the parent and matched route have the same path, this link is
      // referring to the empty child. Or we currently are on a different
      // child of the same parent
      && getOriginalPath(routeMatched) === parentRecordPath
      // avoid comparing the child with its parent
      && currentMatched[ currentMatched.length - 1 ].path !== parentRecordPath
        ? currentMatched.findIndex(
            isSameRouteRecord.bind(null, matched[ length - 2 ])
          )
        : index
    )
  })

  const linkIsActive = computed(() =>
    hasLink.value === true
    && linkActiveIndex.value > -1
    && includesParams(proxy.$route.params, linkRoute.value.params)
  )

  const linkIsExactActive = computed(() =>
    linkIsActive.value === true
      && linkActiveIndex.value === proxy.$route.matched.length - 1
      && isSameRouteLocationParams(proxy.$route.params, linkRoute.value.params)
  )

  const linkClass = computed(() => (
    hasLink.value === true
      ? (
          linkIsExactActive.value === true
            ? ` ${ props.exactActiveClass } ${ props.activeClass }`
            : (
                props.exact === true
                  ? ''
                  : (linkIsActive.value === true ? ` ${ props.activeClass }` : '')
              )
        )
      : ''
  ))

  const linkProps = computed(() => (
    hasLink.value === true
      ? {
          href: linkRoute.value.href,
          target: attrs.target,
          role: 'link'
        }
      : {}
  ))

  // should match RouterLink from Vue Router
  function navigateToLink (e) {
    if (
      // component is not disabled
      props.disable === true

      // don't redirect with control keys
      || e.metaKey || e.altKey || e.ctrlKey || e.shiftKey

      // don't redirect when preventDefault called
      // ...unless calling go() from @click(e, go)
      || (e.__qNavigate !== true && e.defaultPrevented === true)

      // don't redirect on right click
      || (e.button !== undefined && e.button !== 0)

      // don't redirect if it should open in a new window
      || attrs.target === '_blank'
    ) {
      return false
    }

    prevent(e)

    return proxy.$router[ props.replace === true ? 'replace' : 'push' ](props.to)
      .catch(() => {})
  }

  return {
    hasLink,
    linkTag,
    linkRoute,
    linkIsActive,
    linkIsExactActive,
    linkClass,
    linkProps,

    navigateToLink
  }
}
<commit_msg>fix(QRouteTab/QBtn/..): protect against Vue Router failing to resolve a route #10610<commit_after>/*
 * Inspired by RouterLink from Vue Router
 *  --> API should match!
 */

import { computed, getCurrentInstance } from 'vue'

import { prevent } from '../../utils/event.js'
import { vmHasRouter } from '../../utils/private/vm.js'

// Get the original path value of a record by following its aliasOf
function getOriginalPath (record) {
  return record
    ? (
        record.aliasOf
          ? record.aliasOf.path
          : record.path
      ) : ''
}

function isSameRouteRecord (a, b) {
  // since the original record has an undefined value for aliasOf
  // but all aliases point to the original record, this will always compare
  // the original record
  return (a.aliasOf || a) === (b.aliasOf || b)
}

function includesParams (outer, inner) {
  for (const key in inner) {
    const
      innerValue = inner[ key ],
      outerValue = outer[ key ]

    if (typeof innerValue === 'string') {
      if (innerValue !== outerValue) {
        return false
      }
    }
    else if (
      Array.isArray(outerValue) === false
      || outerValue.length !== innerValue.length
      || innerValue.some((value, i) => value !== outerValue[ i ])
    ) {
      return false
    }
  }

  return true
}

function isEquivalentArray (a, b) {
  return Array.isArray(b) === true
    ? a.length === b.length && a.every((value, i) => value === b[ i ])
    : a.length === 1 && a[ 0 ] === b
}

function isSameRouteLocationParamsValue (a, b) {
  return Array.isArray(a) === true
    ? isEquivalentArray(a, b)
    : (
        Array.isArray(b) === true
          ? isEquivalentArray(b, a)
          : a === b
      )
}

function isSameRouteLocationParams (a, b) {
  if (Object.keys(a).length !== Object.keys(b).length) {
    return false
  }

  for (const key in a) {
    if (isSameRouteLocationParamsValue(a[ key ], b[ key ]) === false) {
      return false
    }
  }

  return true
}

export const useRouterLinkProps = {
  to: [ String, Object ],
  replace: Boolean,
  exact: Boolean,
  activeClass: {
    type: String,
    default: 'q-router-link--active'
  },
  exactActiveClass: {
    type: String,
    default: 'q-router-link--exact-active'
  },
  disable: Boolean
}

export default function () {
  const vm = getCurrentInstance()
  const { props, attrs, proxy } = vm

  const hasRouter = vmHasRouter(vm)

  const hasLinkConfigured = computed(() =>
    hasRouter === true
    && props.disable !== true
    && props.to !== void 0 && props.to !== null && props.to !== ''
  )

  const linkRoute = computed(() => {
    if (hasLinkConfigured.value === true) {
      try { return proxy.$router.resolve(props.to) }
      catch (err) {}
    }

    return null
  })

  const hasLink = computed(() => linkRoute.value !== null)

  const linkTag = computed(() => (
    hasLink.value === true
      ? 'a'
      : (props.tag || 'div')
  ))

  const linkActiveIndex = computed(() => {
    if (hasLink.value === false) {
      return null
    }

    const
      { matched } = linkRoute.value,
      { length } = matched,
      routeMatched = matched[ length - 1 ]

    if (routeMatched === void 0) {
      return -1
    }

    const currentMatched = proxy.$route.matched

    if (currentMatched.length === 0) {
      return -1
    }

    const index = currentMatched.findIndex(
      isSameRouteRecord.bind(null, routeMatched)
    )

    if (index > -1) {
      return index
    }

    // possible parent record
    const parentRecordPath = getOriginalPath(matched[ length - 2 ])

    return (
      // we are dealing with nested routes
      length > 1
      // if the parent and matched route have the same path, this link is
      // referring to the empty child. Or we currently are on a different
      // child of the same parent
      && getOriginalPath(routeMatched) === parentRecordPath
      // avoid comparing the child with its parent
      && currentMatched[ currentMatched.length - 1 ].path !== parentRecordPath
        ? currentMatched.findIndex(
            isSameRouteRecord.bind(null, matched[ length - 2 ])
          )
        : index
    )
  })

  const linkIsActive = computed(() =>
    hasLink.value === true
    && linkActiveIndex.value > -1
    && includesParams(proxy.$route.params, linkRoute.value.params)
  )

  const linkIsExactActive = computed(() =>
    linkIsActive.value === true
      && linkActiveIndex.value === proxy.$route.matched.length - 1
      && isSameRouteLocationParams(proxy.$route.params, linkRoute.value.params)
  )

  const linkClass = computed(() => (
    hasLink.value === true
      ? (
          linkIsExactActive.value === true
            ? ` ${ props.exactActiveClass } ${ props.activeClass }`
            : (
                props.exact === true
                  ? ''
                  : (linkIsActive.value === true ? ` ${ props.activeClass }` : '')
              )
        )
      : ''
  ))

  const linkProps = computed(() => (
    hasLink.value === true
      ? {
          href: linkRoute.value.href,
          target: attrs.target,
          role: 'link'
        }
      : {}
  ))

  // should match RouterLink from Vue Router
  function navigateToLink (e) {
    if (
      // component is not disabled
      props.disable === true

      // don't redirect with control keys
      || e.metaKey || e.altKey || e.ctrlKey || e.shiftKey

      // don't redirect when preventDefault called
      // ...unless calling go() from @click(e, go)
      || (e.__qNavigate !== true && e.defaultPrevented === true)

      // don't redirect on right click
      || (e.button !== undefined && e.button !== 0)

      // don't redirect if it should open in a new window
      || attrs.target === '_blank'
    ) {
      return false
    }

    prevent(e)

    return proxy.$router[ props.replace === true ? 'replace' : 'push' ](props.to)
      .catch(() => {})
  }

  return {
    hasLink,
    linkTag,
    linkRoute,
    linkIsActive,
    linkIsExactActive,
    linkClass,
    linkProps,

    navigateToLink
  }
}
<|endoftext|>


<commit_before>'use strict';

const config = require('../config')

module.exports = function(controller) {

  const apiai = require('../apiai')({token: config.apiAiToken})

  controller.middleware.receive.use(apiai.receive);
  controller.hears(['ask-for-timesheet'], 'direct_message', apiai.hears, function(bot, message){
    //recupero timesheet e rispondo
  })
}
<commit_msg>Send users his total hours for a specific day<commit_after>'use strict';

const moment = require('moment')
const config = require('../config')
const redmine = require('../redmine')(config.redmineUrl, config.redmineApiKey)
const ideatos = require('../ideatos')

const hummanReadableDate = function (day) {
  return moment(day).locale('it').calendar(null, {
    lastDay: '[Ieri]',
    sameDay: '[Oggi]',
    nextDay: '[Domani]',
    lastWeek: '[Lo scorso] dddd',
    nextWeek: 'dddd',
    sameElse: 'L'
  })
}

module.exports = function (controller) {

  const apiai = require('../apiai')({ token: config.apiAiToken })

  controller.middleware.receive.use(apiai.receive);
  controller.hears([ 'ask-for-timesheet' ], 'direct_message', apiai.hears, function (bot, message) {
    const day = moment(message.entities.date).format('YYYY-MM-DD')
    bot.api.users.info({ user: message.user }, (error, response) => {

        bot.startConversation(message, (err, convo) => {

        const hours = redmine.timeSheet.retrieveLog((err, hours) => {
            convo.say(`${hummanReadableDate(day)} hai segnato ${hours} ore`)
          },
          ideatos.bySlackName(`@${response.user.name}`).redmineId,
          day,
          day
        )
      })
    })
  })
}<|endoftext|>