<commit_before>define('app/controllers/backend_add', ['app/models/backend', 'ember'],
    /**
     *  Backend Add Controller
     *
     *  @returns Class
     */
    function (Backend) {
        return Ember.Object.extend({

            /**
             *  Properties
             */

            callback: null,
            formReady: null,

            newBackendKey: null,
            newBackendPort: null,
            newBackendProvider: null,
            newBackendFirstField: null,
            newBackendSecondField: null,
            newBackendProjectName: null,
            newBackendDockerURL: null,
            newBackendOpenStackURL: null,
            newBackendOpenStackRegion: null,
            newBackendOpenStackTenant: null,
            newBackendOpenStackComputeEndpoint: null,

            /**
             *
             *  Methods
             *
             */

            open: function (callback) {
                this._clear();
                this._updateFormReady();
                this.set('callback', callback);

                $('#add-backend-panel').panel('open');
                Ember.run.next(function () {
                    $('.ui-page-active').height($('.ui-panel-open').height());
                    $('body').css('overflow', 'auto');

                    //This is the advanced section of OpenStack, by default
                    //hidden
                    $('#openstack-advanced').val('0').slider('refresh');
                    $('#non-hp-cloud').hide();
                });
            },

            close: function () {
                $('#add-backend-panel').panel('close');
                this._clear();
            },

            add: function () {

                var that = this;
                var projectName = this.newBackendOpenStackTenant || this.newBackendProjectName;

                // Add tenant name to backend title for openstack and hpcloud
                var provider = this.newBackendProvider.provider;
                var title = this.newBackendProvider.title +
                    (provider.provider == 'openstack' || provider.indexOf('hpcloud') > -1 ?
                        ' ' + this.newBackendOpenStackTenant : '');

                Mist.backendsController.addBackend({

                    APIKey: this.newBackendFirstField,
                    APISecret: this.newBackendSecondField,
                    title: title,
                    provider: this.newBackendProvider.provider,
                    APIURL: this.newBackendOpenStackURL,
                    region: this.newBackendOpenStackRegion,
                    tenant: projectName,
                    computeEndpont: this.newBackendOpenStackComputeEndpoint,
                    dockerURL: this.newBackendDockerURL,
                    port: this.newBackendPort,
                    key: this.newBackendKey.id,

                    callback: function (success, backend) {
                        that._giveCallback(success, backend);
                        if (success) that.close();
                    }
                });
            },

            /**
             *
             *  Pseudo-Private Methods
             *
             */

            _clear: function () {

                this.set('callback', null)
                    .set('newBackendPort', null)
                    .set('newBackendFirstField', null)
                    .set('newBackendSecondField', null)
                    .set('newBackendOpenStackURL', null)
                    .set('newBackendOpenStackRegion', null)
                    .set('newBackendOpenStackTenant', null)
                    .set('newBackendOpenStackComputeEndpoint', null)
                    .set('newBackendDockerURL', null)
                    .set('newBackendKey', {id: 'Select SSH Key'})
                    .set('newBackendProvider', {title: 'Select provider'});

                // These should be in a view :(
                $('#new-backend-key').collapsible('collapse').collapsible('option', 'collapsedIcon', 'carat-d');
                $('#new-backend-provider').collapsible('collapse').collapsible('option', 'collapsedIcon', 'carat-d');
            },

            _updateFormReady: function () {

                // Filter out the "Select provider" dummy provider
                if (! ('provider' in this.newBackendProvider)) {
                    this.set('formReady', false);
                    return;
                }

                var ready = false;

                if (this.newBackendProvider.provider == 'docker') {
                    if (this.newBackendDockerURL && this.newBackendPort) {
                        ready = true;
                    }
                } else if (this.newBackendProvider.provider == 'linode') {
                    if (this.newBackendSecondField && this.newBackendSecondField) {
                        ready = true;
                    }
                } else if (this.newBackendProvider.provider == 'digitalocean') {
                    if (this.newBackendSecondField) {
                        ready = true;
                    }
                } else if (this.newBackendFirstField && this.newBackendSecondField) {

                    ready = true;

                    if (this.newBackendProvider.provider == 'openstack') { // Openstack
                        if (!this.newBackendOpenStackURL || !this.newBackendOpenStackTenant) {
                            ready = false;
                        }
                    } else if (this.newBackendProvider.provider.indexOf('hpcloud') > -1) { // HpCloud
                        if (!this.newBackendOpenStackTenant) {
                            ready = false;
                        }
                    } else if (this.newBackendProvider.provider == 'bare_metal') { // Baremetal
                        if (!Mist.keysController.keyExists(this.newBackendKey.id)) {
                            ready = false;
                        }
                    } else if (this.newBackendProvider.provider == 'gce') { // Google Compute Engine
                        if (!this.newBackendProjectName) {
                            ready = false;
                        }
                    }
                }
                this.set('formReady', ready);
            },

            _giveCallback: function (success, backend) {
                if (this.callback) this.callback(success, backend);
            },

            /**
             *
             *  Observers
             *
             */

            formObserver: function () {
                Ember.run.once(this, '_updateFormReady');
            }.observes('newBackendKey',
                       'newBackendProvider',
                       'newBackendFirstField',
                       'newBackendSecondField',
                       'newBackendProjectName',
                       'newBackendDockerURL',
                       'newBackendPort',
                       'newBackendOpenStackURL',
                       'newBackendOpenStackTenant')
        });
    }
);
<commit_msg>Fix openstack title<commit_after>define('app/controllers/backend_add', ['app/models/backend', 'ember'],
    /**
     *  Backend Add Controller
     *
     *  @returns Class
     */
    function (Backend) {
        return Ember.Object.extend({

            /**
             *  Properties
             */

            callback: null,
            formReady: null,

            newBackendKey: null,
            newBackendPort: null,
            newBackendProvider: null,
            newBackendFirstField: null,
            newBackendSecondField: null,
            newBackendProjectName: null,
            newBackendDockerURL: null,
            newBackendOpenStackURL: null,
            newBackendOpenStackRegion: null,
            newBackendOpenStackTenant: null,
            newBackendOpenStackComputeEndpoint: null,

            /**
             *
             *  Methods
             *
             */

            open: function (callback) {
                this._clear();
                this._updateFormReady();
                this.set('callback', callback);

                $('#add-backend-panel').panel('open');
                Ember.run.next(function () {
                    $('.ui-page-active').height($('.ui-panel-open').height());
                    $('body').css('overflow', 'auto');

                    //This is the advanced section of OpenStack, by default
                    //hidden
                    $('#openstack-advanced').val('0').slider('refresh');
                    $('#non-hp-cloud').hide();
                });
            },

            close: function () {
                $('#add-backend-panel').panel('close');
                this._clear();
            },

            add: function () {

                var that = this;
                var projectName = this.newBackendOpenStackTenant || this.newBackendProjectName;

                // Add tenant name to backend title for openstack and hpcloud
                var provider = this.newBackendProvider.provider;
                var title = this.newBackendProvider.title +
                    (provider == 'openstack' || provider.indexOf('hpcloud') > -1 ?
                        ' ' + this.newBackendOpenStackTenant : '');

                Mist.backendsController.addBackend({

                    APIKey: this.newBackendFirstField,
                    APISecret: this.newBackendSecondField,
                    title: title,
                    provider: this.newBackendProvider.provider,
                    APIURL: this.newBackendOpenStackURL,
                    region: this.newBackendOpenStackRegion,
                    tenant: projectName,
                    computeEndpont: this.newBackendOpenStackComputeEndpoint,
                    dockerURL: this.newBackendDockerURL,
                    port: this.newBackendPort,
                    key: this.newBackendKey.id,

                    callback: function (success, backend) {
                        that._giveCallback(success, backend);
                        if (success) that.close();
                    }
                });
            },

            /**
             *
             *  Pseudo-Private Methods
             *
             */

            _clear: function () {

                this.set('callback', null)
                    .set('newBackendPort', null)
                    .set('newBackendFirstField', null)
                    .set('newBackendSecondField', null)
                    .set('newBackendOpenStackURL', null)
                    .set('newBackendOpenStackRegion', null)
                    .set('newBackendOpenStackTenant', null)
                    .set('newBackendOpenStackComputeEndpoint', null)
                    .set('newBackendDockerURL', null)
                    .set('newBackendKey', {id: 'Select SSH Key'})
                    .set('newBackendProvider', {title: 'Select provider'});

                // These should be in a view :(
                $('#new-backend-key').collapsible('collapse').collapsible('option', 'collapsedIcon', 'carat-d');
                $('#new-backend-provider').collapsible('collapse').collapsible('option', 'collapsedIcon', 'carat-d');
            },

            _updateFormReady: function () {

                // Filter out the "Select provider" dummy provider
                if (! ('provider' in this.newBackendProvider)) {
                    this.set('formReady', false);
                    return;
                }

                var ready = false;

                if (this.newBackendProvider.provider == 'docker') {
                    if (this.newBackendDockerURL && this.newBackendPort) {
                        ready = true;
                    }
                } else if (this.newBackendProvider.provider == 'linode') {
                    if (this.newBackendSecondField && this.newBackendSecondField) {
                        ready = true;
                    }
                } else if (this.newBackendProvider.provider == 'digitalocean') {
                    if (this.newBackendSecondField) {
                        ready = true;
                    }
                } else if (this.newBackendFirstField && this.newBackendSecondField) {

                    ready = true;

                    if (this.newBackendProvider.provider == 'openstack') { // Openstack
                        if (!this.newBackendOpenStackURL || !this.newBackendOpenStackTenant) {
                            ready = false;
                        }
                    } else if (this.newBackendProvider.provider.indexOf('hpcloud') > -1) { // HpCloud
                        if (!this.newBackendOpenStackTenant) {
                            ready = false;
                        }
                    } else if (this.newBackendProvider.provider == 'bare_metal') { // Baremetal
                        if (!Mist.keysController.keyExists(this.newBackendKey.id)) {
                            ready = false;
                        }
                    } else if (this.newBackendProvider.provider == 'gce') { // Google Compute Engine
                        if (!this.newBackendProjectName) {
                            ready = false;
                        }
                    }
                }
                this.set('formReady', ready);
            },

            _giveCallback: function (success, backend) {
                if (this.callback) this.callback(success, backend);
            },

            /**
             *
             *  Observers
             *
             */

            formObserver: function () {
                Ember.run.once(this, '_updateFormReady');
            }.observes('newBackendKey',
                       'newBackendProvider',
                       'newBackendFirstField',
                       'newBackendSecondField',
                       'newBackendProjectName',
                       'newBackendDockerURL',
                       'newBackendPort',
                       'newBackendOpenStackURL',
                       'newBackendOpenStackTenant')
        });
    }
);
<|endoftext|>


<commit_before>define(['/customize/languageSelector.js',
        '/customize/translations/messages.js',
        '/customize/translations/messages.es.js',
        '/customize/translations/messages.fr.js',

    // 1) additional translation files can be added here...
        '/customize/translations/messages.pl.js',
        '/customize/translations/messages.de.js',
        '/customize/translations/messages.pt-br.js',

        '/bower_components/jquery/dist/jquery.min.js'],

    // 2) name your language module here...
        function(LS, Default, Spanish, French, Polish, German, BrPortuguese) {
    var $ = window.jQuery;

    // 3) add your module to this map so it gets used
    var map = {
        'fr': French,
        'es': Spanish,
        'pl': Polish,
        'de': German,
        'pt-br': BrPortuguese,
    };

    var defaultLanguage = 'en';

    var language = LS.getLanguage();

    var messages;

    if (!language || language === defaultLanguage || language === 'default' || !map[language]) {
        messages = Default;
    }
    else {
        // Add the translated keys to the returned object
        messages = $.extend(true, {}, Default, map[language]);
    }

    // messages_languages return the available translations and their name in an object :
    // { "en": "English", "fr": "French", ... }
    messages._languages = {
        'en': Default._languageName
    };
    for (var l in map) {
        messages._languages[l] = map[l]._languageName || l;
    }

    messages._initSelector = LS.main;
    messages._checkTranslationState = function () {
        var missing = [];
        Object.keys(map).forEach(function (code) {
            var translation = map[code];
            Object.keys(Default).forEach(function (k) {
                if (/^_/.test(k) || /nitialState$/.test(k)) { return; }
                if (!translation[k]) {
                    var warning = "key [" + k + "] is missing from translation [" + code + "]";
                    missing.push(warning);
                }
            });
            if (typeof(translation._languageName) !== 'string') {
                var warning = 'key [_languageName] is missing from translation [' + code + ']';
                missing.push(warning);
            }
        });
        return missing;
    };

    // Get keys with parameters
    messages._getKey = function (key, argArray) {
        if (!messages[key]) { return '?'; }
        var text = messages[key];
        return text.replace(/\{(\d+)\}/g, function (str, p1) {
            return argArray[p1] || null;
        });
    };

    var translateText = function (i, e) {
        var $el = $(e);
        var key = $el.data('localization');
        $el.html(messages[key]);
    };
    var translateTitle = function (i, e) {
        var $el = $(this);
        var key = $el.data('localization-title');
        $el.attr('title', messages[key]);
    };
    var translatePlaceholder = function (i, e) {
        var $el = $(this);
        var key = $el.data('localization-placeholder');
        $el.attr('placeholder', messages[key]);
    };
    messages._applyTranslation = function () {
        $('[data-localization]').each(translateText);
        $('#pad-iframe').contents().find('[data-localization]').each(translateText);
        $('[data-localization-title]').each(translateTitle);
        $('[data-localization-placeholder').each(translatePlaceholder);
        $('#pad-iframe').contents().find('[data-localization-title]').each(translateTitle);
    };

    // Non translatable keys
    messages.initialState = [
        '
',
        'This is CryptPad, the zero knowledge realtime collaborative editor.',
        '
',
        'What you type here is encrypted so only people who have the link can access it.',
        '
',
        'Even the server cannot see what you type.',
        '
',
        '
',
        '',
        'What you see here, what you hear here, when you leave here, let it stay here',
        '',
        '
',
    ].join('');

    messages.codeInitialState = [
        '/*\n',
        '   This is CryptPad, the zero knowledge realtime collaborative editor.\n',
        '   What you type here is encrypted so only people who have the link can access it.\n',
        '   Even the server cannot see what you type.\n',
        '   What you see here, what you hear here, when you leave here, let it stay here.\n',
        '*/'
    ].join('');

    messages.slideInitialState = [
        '# CryptSlide\n',
        '* This is a zero knowledge realtime collaborative editor.\n',
        '* What you type here is encrypted so only people who have the link can access it.\n',
        '* Even the server cannot see what you type.\n',
        '* What you see here, what you hear here, when you leave here, let it stay here.\n',
        '\n',
        '---',
        '\n',
        '# How to use\n',
        '1. Write your slides content using markdown syntax\n',
        '  - Learn more about markdown syntax [here](http://www.markdowntutorial.com/)\n',
        '2. Separate your slides with ---\n',
        '3. Click on the "Play" button to see the result'
    ].join('');

    return messages;
});
<commit_msg>Fix error with placeholder translations in Safari<commit_after>define(['/customize/languageSelector.js',
        '/customize/translations/messages.js',
        '/customize/translations/messages.es.js',
        '/customize/translations/messages.fr.js',

    // 1) additional translation files can be added here...
        '/customize/translations/messages.pl.js',
        '/customize/translations/messages.de.js',
        '/customize/translations/messages.pt-br.js',

        '/bower_components/jquery/dist/jquery.min.js'],

    // 2) name your language module here...
        function(LS, Default, Spanish, French, Polish, German, BrPortuguese) {
    var $ = window.jQuery;

    // 3) add your module to this map so it gets used
    var map = {
        'fr': French,
        'es': Spanish,
        'pl': Polish,
        'de': German,
        'pt-br': BrPortuguese,
    };

    var defaultLanguage = 'en';

    var language = LS.getLanguage();

    var messages;

    if (!language || language === defaultLanguage || language === 'default' || !map[language]) {
        messages = Default;
    }
    else {
        // Add the translated keys to the returned object
        messages = $.extend(true, {}, Default, map[language]);
    }

    // messages_languages return the available translations and their name in an object :
    // { "en": "English", "fr": "French", ... }
    messages._languages = {
        'en': Default._languageName
    };
    for (var l in map) {
        messages._languages[l] = map[l]._languageName || l;
    }

    messages._initSelector = LS.main;
    messages._checkTranslationState = function () {
        var missing = [];
        Object.keys(map).forEach(function (code) {
            var translation = map[code];
            Object.keys(Default).forEach(function (k) {
                if (/^_/.test(k) || /nitialState$/.test(k)) { return; }
                if (!translation[k]) {
                    var warning = "key [" + k + "] is missing from translation [" + code + "]";
                    missing.push(warning);
                }
            });
            if (typeof(translation._languageName) !== 'string') {
                var warning = 'key [_languageName] is missing from translation [' + code + ']';
                missing.push(warning);
            }
        });
        return missing;
    };

    // Get keys with parameters
    messages._getKey = function (key, argArray) {
        if (!messages[key]) { return '?'; }
        var text = messages[key];
        return text.replace(/\{(\d+)\}/g, function (str, p1) {
            return argArray[p1] || null;
        });
    };

    var translateText = function (i, e) {
        var $el = $(e);
        var key = $el.data('localization');
        $el.html(messages[key]);
    };
    var translateTitle = function (i, e) {
        var $el = $(this);
        var key = $el.data('localization-title');
        $el.attr('title', messages[key]);
    };
    var translatePlaceholder = function (i, e) {
        var $el = $(this);
        var key = $el.data('localization-placeholder');
        $el.attr('placeholder', messages[key]);
    };
    messages._applyTranslation = function () {
        $('[data-localization]').each(translateText);
        $('#pad-iframe').contents().find('[data-localization]').each(translateText);
        $('[data-localization-title]').each(translateTitle);
        $('[data-localization-placeholder]').each(translatePlaceholder);
        $('#pad-iframe').contents().find('[data-localization-title]').each(translateTitle);
    };

    // Non translatable keys
    messages.initialState = [
        '
',
        'This is CryptPad, the zero knowledge realtime collaborative editor.',
        '
',
        'What you type here is encrypted so only people who have the link can access it.',
        '
',
        'Even the server cannot see what you type.',
        '
',
        '
',
        '',
        'What you see here, what you hear here, when you leave here, let it stay here',
        '',
        '
',
    ].join('');

    messages.codeInitialState = [
        '/*\n',
        '   This is CryptPad, the zero knowledge realtime collaborative editor.\n',
        '   What you type here is encrypted so only people who have the link can access it.\n',
        '   Even the server cannot see what you type.\n',
        '   What you see here, what you hear here, when you leave here, let it stay here.\n',
        '*/'
    ].join('');

    messages.slideInitialState = [
        '# CryptSlide\n',
        '* This is a zero knowledge realtime collaborative editor.\n',
        '* What you type here is encrypted so only people who have the link can access it.\n',
        '* Even the server cannot see what you type.\n',
        '* What you see here, what you hear here, when you leave here, let it stay here.\n',
        '\n',
        '---',
        '\n',
        '# How to use\n',
        '1. Write your slides content using markdown syntax\n',
        '  - Learn more about markdown syntax [here](http://www.markdowntutorial.com/)\n',
        '2. Separate your slides with ---\n',
        '3. Click on the "Play" button to see the result'
    ].join('');

    return messages;
});
<|endoftext|>


<commit_before>var numDots = 5;   // number of lines or rows
var numPositionsPerDot = 5;      // number of positions per line, or columns

var dotPositions = [];
var linesYSpots = [];
var numLines = 4;
var numPositionsToDrawForLine = 4;
var maxXDrawLine;

var interDotDistanceY;
var interDotPositionDistanceX;
var nextLineYSpot;
var someColors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000' ];

function setup() {
	frameRate(30);
  createCanvas(600, 400);
	interDotDistanceY = (height/numDots);
  interDotPositionDistanceX = (width/numPositionsPerDot);

	for (var i = 0; i < numDots; i++) {
    for (var j = 0; j <= numPositionsPerDot; j++) {
      var currentYPos = i * interDotDistanceY + 0.5 * interDotDistanceY;
      var currentXPos = j * interDotPositionDistanceX + 0.5 * interDotPositionDistanceX;
		    dotPositions.push([currentXPos, currentYPos]);
    }
	}

  maxDrawLineX = dotPositions[numPositionsToDrawForLine-2][0] + 0.5 * interDotPositionDistanceX;

  nextLineYSpot = [];
  for (var k = 0; k < numLines; k++) {
    var currentLineYSpots = [];
    for (var l = 0; l < numPositionsToDrawForLine; l++) {
      currentLineYSpots.push(getRandomInt(0, numDots-1));
    }
    linesYSpots.push(currentLineYSpots);
    nextLineYSpot.push(getRandomInt(0, numDots-1));
  }
}

function draw() {
  background(220);
	// move Dots
  var jumpThisFrame = false;
	for (var i = 0; i < dotPositions.length; i++) {
    dotPositions[i][0] -= 2;
		if(dotPositions[i][0] < -interDotPositionDistanceX) {  // reset if it left screen
      var howMuchSmaller = -interDotPositionDistanceX -dotPositions[i][0];
		  dotPositions[i][0] = width - howMuchSmaller;
      jumpThisFrame = true;
		}
	}

  if(jumpThisFrame) {
    // draw new random next points for all lines
    for (var k = 0; k < numLines; k++) {
      linesYSpots[k].shift();
      linesYSpots[k].push(nextLineYSpot[k]);
      nextLineYSpot[k] = (getRandomInt(0, numDots-1));
    }
  }

  // draw maxDrawLineX
  stroke(color(0, 255, 0));
  line(maxDrawLineX, 0, maxDrawLineX, height);
  stroke(color(0));

	// draw dots
	fill(color(255, 0, 0));
	for (var j = 0; j < dotPositions.length; j++) {
		ellipse(dotPositions[j][0], dotPositions[j][1], 5);
	}

  // advance Y spots

  // draw lines
  //  - find the current X positions of all points
  var currentPointXPositions = [];
  stroke(color(0, 0, 255));
  for (var l = 0; l <= numPositionsPerDot; l++) {
      currentPointXPositions[l] = dotPositions[l][0];
      // draw vertical lines
      line(currentPointXPositions[l], 0, currentPointXPositions[l], height);
  }
  stroke(color(0));
  // - sort by X values
  sortWithIndeces(currentPointXPositions);
  // - draw lines between first numPositionsToDrawForLine points

  for (var k = 0; k < numLines; k++) {
    var thisLineYSpots = linesYSpots[k]; // these are only the indices, i.e., row numbers
    print("Line " + k + " last y spots: " + thisLineYSpots.join(",") + ". Next=" + nextLineYSpot[k]);

    var thisLineYPositionsOfSpots = []; // y coordinates
    for (var l = 0; l < numPositionsToDrawForLine; l++) {
      thisLineYPositionsOfSpots[l] = thisLineYSpots[l] * interDotDistanceY + 0.5 * interDotDistanceY;
    }
    print("Line " + k + " y positions: " + thisLineYPositionsOfSpots.join(","));

    stroke(color(someColors[k]));
    strokeWeight(4);
    line(0, thisLineYPositionsOfSpots[0], currentPointXPositions[0], thisLineYPositionsOfSpots[0]);
    for (var l = 0; l <= numPositionsToDrawForLine; l++) {

      line(currentPointXPositions[l], thisLineYPositionsOfSpots[l], currentPointXPositions[l+1], thisLineYPositionsOfSpots[l+1]);
    }
    // Draw line towards next point: compute y position at x position maxDrawLineX
    var nextY = nextLineYSpot[k] * interDotDistanceY + 0.5 * interDotDistanceY;
    var lastY = thisLineYPositionsOfSpots[numPositionsToDrawForLine-1];
    var lastX = currentPointXPositions[numPositionsToDrawForLine-1];
    var nextX = currentPointXPositions[numPositionsToDrawForLine];
    if(nextX != lastX + interDotPositionDistanceX) {
      print("##### Assertion failed: nextX=" + nextX + ", but lastX + interDotPositionDistanceX=" + (lastX + interDotPositionDistanceX) + ". Should be euqal. #####");
    }
    var yDiff = nextY - lastY;
    var ascent = yDiff / interDotPositionDistanceX;
    var xDiff = currentPointXPositions[numPositionsToDrawForLine+1] - maxDrawLineX;
    var yAtmaxDrawLineX = lastY + (ascent * xDiff);
    line(currentPointXPositions[numPositionsToDrawForLine-1], lastY, maxDrawLineX, yAtmaxDrawLineX);
    strokeWeight(1);
    print("Line " + k + ": ascent=" + ascent +", nextY=" + nextY +"lastY=" +lastY);

    fill(color(255,127,80));    // draw current dot in orange (stroke with current line's color)
    ellipse(lastX, lastY, 10);
    fill(color(255, 255, 0));   // draw next target in yellow (stroke with current line's color)
    ellipse(nextX, nextY, 10);
  }
}

function sortWithIndeces(toSort) {
  for (var i = 0; i < toSort.length; i++) {
    toSort[i] = [toSort[i], i];
  }
  toSort.sort(function(left, right) {
    return left[0] < right[0] ? -1 : 1;
  });
  toSort.sortIndices = [];
  for (var j = 0; j < toSort.length; j++) {
    toSort.sortIndices.push(toSort[j][1]);
    toSort[j] = toSort[j][0];
  }
  return toSort;
}

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
<commit_msg>fix line towards next point<commit_after>var numDots = 5;   // number of lines or rows
var numPositionsPerDot = 5;      // number of positions per line, or columns

var dotPositions = [];
var linesYSpots = [];
var numLines = 4;
var numPositionsToDrawForLine = 4;
var maxXDrawLine;

var interDotDistanceY;
var interDotPositionDistanceX;
var nextLineYSpot;
var someColors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000' ];

function setup() {
	frameRate(30);
  createCanvas(600, 400);
	interDotDistanceY = (height/numDots);
  interDotPositionDistanceX = (width/numPositionsPerDot);

	for (var i = 0; i < numDots; i++) {
    for (var j = 0; j <= numPositionsPerDot; j++) {
      var currentYPos = i * interDotDistanceY + 0.5 * interDotDistanceY;
      var currentXPos = j * interDotPositionDistanceX + 0.5 * interDotPositionDistanceX;
		    dotPositions.push([currentXPos, currentYPos]);
    }
	}

  maxDrawLineX = dotPositions[numPositionsToDrawForLine-2][0] + 0.5 * interDotPositionDistanceX;

  nextLineYSpot = [];
  for (var k = 0; k < numLines; k++) {
    var currentLineYSpots = [];
    for (var l = 0; l < numPositionsToDrawForLine; l++) {
      currentLineYSpots.push(getRandomInt(0, numDots-1));
    }
    linesYSpots.push(currentLineYSpots);
    nextLineYSpot.push(getRandomInt(0, numDots-1));
  }
}

function draw() {
  background(220);
	// move Dots
  var jumpThisFrame = false;
	for (var i = 0; i < dotPositions.length; i++) {
    dotPositions[i][0] -= 2;
		if(dotPositions[i][0] < -interDotPositionDistanceX) {  // reset if it left screen
      var howMuchSmaller = -interDotPositionDistanceX -dotPositions[i][0];
		  dotPositions[i][0] = width - howMuchSmaller;
      jumpThisFrame = true;
		}
	}

  if(jumpThisFrame) {
    // draw new random next points for all lines
    for (var k = 0; k < numLines; k++) {
      linesYSpots[k].shift();
      linesYSpots[k].push(nextLineYSpot[k]);
      nextLineYSpot[k] = (getRandomInt(0, numDots-1));
    }
  }

  // draw maxDrawLineX
  stroke(color(0, 255, 0));
  line(maxDrawLineX, 0, maxDrawLineX, height);
  stroke(color(0));

	// draw dots
	fill(color(255, 0, 0));
	for (var j = 0; j < dotPositions.length; j++) {
		ellipse(dotPositions[j][0], dotPositions[j][1], 5);
	}

  // advance Y spots

  // draw lines
  //  - find the current X positions of all points
  var currentPointXPositions = [];
  stroke(color(0, 0, 255));
  for (var l = 0; l <= numPositionsPerDot; l++) {
      currentPointXPositions[l] = dotPositions[l][0];
      // draw vertical lines
      line(currentPointXPositions[l], 0, currentPointXPositions[l], height);
  }
  stroke(color(0));
  // - sort by X values
  sortWithIndeces(currentPointXPositions);
  // - draw lines between first numPositionsToDrawForLine points

  for (var k = 0; k < numLines; k++) {
    var thisLineYSpots = linesYSpots[k]; // these are only the indices, i.e., row numbers
    print("Line " + k + " last y spots: " + thisLineYSpots.join(",") + ". Next=" + nextLineYSpot[k]);

    var thisLineYPositionsOfSpots = []; // y coordinates
    for (var l = 0; l < numPositionsToDrawForLine; l++) {
      thisLineYPositionsOfSpots[l] = thisLineYSpots[l] * interDotDistanceY + 0.5 * interDotDistanceY;
    }
    print("Line " + k + " y positions: " + thisLineYPositionsOfSpots.join(","));

    stroke(color(someColors[k]));
    strokeWeight(4);
    line(0, thisLineYPositionsOfSpots[0], currentPointXPositions[0], thisLineYPositionsOfSpots[0]);
    for (var l = 0; l <= numPositionsToDrawForLine; l++) {

      line(currentPointXPositions[l], thisLineYPositionsOfSpots[l], currentPointXPositions[l+1], thisLineYPositionsOfSpots[l+1]);
    }
    // Draw line towards next point: compute y position at x position maxDrawLineX
    var nextY = nextLineYSpot[k] * interDotDistanceY + 0.5 * interDotDistanceY;
    var lastY = thisLineYPositionsOfSpots[numPositionsToDrawForLine-1];
    var lastX = currentPointXPositions[numPositionsToDrawForLine-1];
    var nextX = currentPointXPositions[numPositionsToDrawForLine];
    if(nextX != lastX + interDotPositionDistanceX) {
      print("##### Assertion failed: nextX=" + nextX + ", but lastX + interDotPositionDistanceX=" + (lastX + interDotPositionDistanceX) + ". Should be euqal. #####");
    }
    var yDiff = nextY - lastY;
    var xDiff = nextX - lastX;
    var ascent = yDiff / xDiff;
    var xDiffToMaxDrawLineX = maxDrawLineX - lastX;
    var yAtmaxDrawLineX = lastY + (ascent * xDiffToMaxDrawLineX);
    line(currentPointXPositions[numPositionsToDrawForLine-1], lastY, maxDrawLineX, yAtmaxDrawLineX);
    strokeWeight(1);
    print("Line " + k + ": ascent=" + ascent +", nextY=" + nextY +", lastY=" +lastY + ". lastX=" + lastX + " ,nextX=" + nextX + ". xDiff=" + xDiff + " (interDotPositionDistanceX=" + interDotPositionDistanceX + "), yDiff=" + yDiff);

    fill(color(255,127,80));    // draw current dot in orange (stroke with current line's color)
    ellipse(lastX, lastY, 10);
    fill(color(255, 255, 0));   // draw next target in yellow (stroke with current line's color)
    ellipse(nextX, nextY, 10);
  }
}

function sortWithIndeces(toSort) {
  for (var i = 0; i < toSort.length; i++) {
    toSort[i] = [toSort[i], i];
  }
  toSort.sort(function(left, right) {
    return left[0] < right[0] ? -1 : 1;
  });
  toSort.sortIndices = [];
  for (var j = 0; j < toSort.length; j++) {
    toSort.sortIndices.push(toSort[j][1]);
    toSort[j] = toSort[j][0];
  }
  return toSort;
}

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
<|endoftext|>


<commit_before>var Plotly = require('../src/plotly');

describe('plot schema', function() {
    'use strict';

    var plotSchema = Plotly.PlotSchema.get(),
        valObjects = plotSchema.defs.valObjects;

    var isValObject = Plotly.PlotSchema.isValObject,
        isPlainObject = Plotly.Lib.isPlainObject;

    var VALTYPES = Object.keys(valObjects),
        ROLES = ['info', 'style', 'data'];

    function assertPlotSchema(callback) {
        var traces = plotSchema.traces;

        Object.keys(traces).forEach(function(traceName) {
            Plotly.PlotSchema.crawl(traces[traceName].attributes, callback);
        });

        Plotly.PlotSchema.crawl(plotSchema.layout.layoutAttributes, callback);
    }

    it('all attributes should have a valid `valType`', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    expect(VALTYPES.indexOf(attr.valType) !== -1).toBe(true);
                }
            }
        );

    });

    it('all attributes should only have valid `role`', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    expect(ROLES.indexOf(attr.role) !== -1).toBe(true);
                }
            }
        );
    });

    it('all nested objects should have the *object* `role`', function() {
        assertPlotSchema(
            function(attr) {
                if(!isValObject(attr) && isPlainObject(attr)) {
                    expect(attr.role === 'object').toBe(true);
                }
            }
        );
    });

    it('all attributes should have the required options', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    var keys = Object.keys(attr);

                    valObjects[attr.valType].requiredOpts.forEach(function(opt) {
                        expect(keys.indexOf(opt) !== -1).toBe(true);
                    });
                }
            }
        );
    });

    it('all attributes should only have compatible options', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    var valObject = valObjects[attr.valType],
                        opts = valObject.requiredOpts
                            .concat(valObject.otherOpts)
                            .concat(['valType', 'description', 'role']);

                    Object.keys(attr).forEach(function(key) {
                        // handle the histogram marker.color case
                        if(opts.indexOf(key)===-1 && opts[key]===undefined) return;

                        expect(opts.indexOf(key) !== -1).toBe(true);
                    });
                }
            }
        );
    });

    it('all subplot objects should contain _isSubplotObj', function() {
        var IS_SUBPLOT_OBJ = '_isSubplotObj',
            astrs = ['xaxis', 'yaxis', 'scene', 'geo'],
            list = [];

        // check if the subplot objects have '_isSubplotObj'
        astrs.forEach(function(astr) {
            expect(
                Plotly.Lib.nestedProperty(
                    plotSchema.layout.layoutAttributes,
                    astr + '.' + IS_SUBPLOT_OBJ
                ).get()
            ).toBe(true);
        });

        // check that no other object has '_isSubplotObj'
        assertPlotSchema(
            function(attr, attrName) {
                if(attr[IS_SUBPLOT_OBJ] === true) list.push(attrName);
            }
        );
        expect(list).toEqual(astrs);
    });

    it('layout.annotations and layout.shapes should contain `items`', function() {
        var astrs = ['annotations', 'shapes'];

        astrs.forEach(function(astr) {
            expect(
                isPlainObject(
                    Plotly.Lib.nestedProperty(
                        plotSchema.layout.layoutAttributes, astr
                    ).get().items
                )
            ).toBe(true);
        });
    });

    it('valObjects descriptions should be strings', function() {
        assertPlotSchema(
            function(attr) {
                var isValid;

                if(isValObject(attr)) {
                    // attribute don't have to have a description (for now)
                    isValid = (typeof attr.description === 'string') ||
                        (attr.description === undefined);

                    expect(isValid).toBe(true);
                }
            }
        );
    });

    it('deprecated attributes should have a `valType` and `role`', function() {
        var DEPRECATED = '_deprecated';

        assertPlotSchema(
            function(attr) {
                if(isPlainObject(attr[DEPRECATED])) {
                    Object.keys(attr[DEPRECATED]).forEach(function(dAttrName) {
                        var dAttr = attr[DEPRECATED][dAttrName];

                        expect(VALTYPES.indexOf(dAttr.valType) !== -1).toBe(true);
                        expect(ROLES.indexOf(dAttr.role) !== -1).toBe(true);
                    });
                }
            }
        );
    });
});
<commit_msg>handle annotations/shape plot-schema 'items' case in test<commit_after>var Plotly = require('../src/plotly');

describe('plot schema', function() {
    'use strict';

    var plotSchema = Plotly.PlotSchema.get(),
        valObjects = plotSchema.defs.valObjects;

    var isValObject = Plotly.PlotSchema.isValObject,
        isPlainObject = Plotly.Lib.isPlainObject;

    var VALTYPES = Object.keys(valObjects),
        ROLES = ['info', 'style', 'data'];

    function assertPlotSchema(callback) {
        var traces = plotSchema.traces;

        Object.keys(traces).forEach(function(traceName) {
            Plotly.PlotSchema.crawl(traces[traceName].attributes, callback);
        });

        Plotly.PlotSchema.crawl(plotSchema.layout.layoutAttributes, callback);
    }

    it('all attributes should have a valid `valType`', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    expect(VALTYPES.indexOf(attr.valType) !== -1).toBe(true);
                }
            }
        );

    });

    it('all attributes should only have valid `role`', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    expect(ROLES.indexOf(attr.role) !== -1).toBe(true);
                }
            }
        );
    });

    it('all nested objects should have the *object* `role`', function() {
        assertPlotSchema(
            function(attr, attrName) {
                if(!isValObject(attr) && isPlainObject(attr) && attrName!=='items') {
                    expect(attr.role === 'object').toBe(true);
                }
            }
        );
    });

    it('all attributes should have the required options', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    var keys = Object.keys(attr);

                    valObjects[attr.valType].requiredOpts.forEach(function(opt) {
                        expect(keys.indexOf(opt) !== -1).toBe(true);
                    });
                }
            }
        );
    });

    it('all attributes should only have compatible options', function() {
        assertPlotSchema(
            function(attr) {
                if(isValObject(attr)) {
                    var valObject = valObjects[attr.valType],
                        opts = valObject.requiredOpts
                            .concat(valObject.otherOpts)
                            .concat(['valType', 'description', 'role']);

                    Object.keys(attr).forEach(function(key) {
                        // handle the histogram marker.color case
                        if(opts.indexOf(key)===-1 && opts[key]===undefined) return;

                        expect(opts.indexOf(key) !== -1).toBe(true);
                    });
                }
            }
        );
    });

    it('all subplot objects should contain _isSubplotObj', function() {
        var IS_SUBPLOT_OBJ = '_isSubplotObj',
            astrs = ['xaxis', 'yaxis', 'scene', 'geo'],
            list = [];

        // check if the subplot objects have '_isSubplotObj'
        astrs.forEach(function(astr) {
            expect(
                Plotly.Lib.nestedProperty(
                    plotSchema.layout.layoutAttributes,
                    astr + '.' + IS_SUBPLOT_OBJ
                ).get()
            ).toBe(true);
        });

        // check that no other object has '_isSubplotObj'
        assertPlotSchema(
            function(attr, attrName) {
                if(attr[IS_SUBPLOT_OBJ] === true) list.push(attrName);
            }
        );
        expect(list).toEqual(astrs);
    });

    it('layout.annotations and layout.shapes should contain `items`', function() {
        var astrs = ['annotations', 'shapes'];

        astrs.forEach(function(astr) {
            expect(
                isPlainObject(
                    Plotly.Lib.nestedProperty(
                        plotSchema.layout.layoutAttributes, astr
                    ).get().items
                )
            ).toBe(true);
        });
    });

    it('valObjects descriptions should be strings', function() {
        assertPlotSchema(
            function(attr) {
                var isValid;

                if(isValObject(attr)) {
                    // attribute don't have to have a description (for now)
                    isValid = (typeof attr.description === 'string') ||
                        (attr.description === undefined);

                    expect(isValid).toBe(true);
                }
            }
        );
    });

    it('deprecated attributes should have a `valType` and `role`', function() {
        var DEPRECATED = '_deprecated';

        assertPlotSchema(
            function(attr) {
                if(isPlainObject(attr[DEPRECATED])) {
                    Object.keys(attr[DEPRECATED]).forEach(function(dAttrName) {
                        var dAttr = attr[DEPRECATED][dAttrName];

                        expect(VALTYPES.indexOf(dAttr.valType) !== -1).toBe(true);
                        expect(ROLES.indexOf(dAttr.role) !== -1).toBe(true);
                    });
                }
            }
        );
    });
});
<|endoftext|>


<commit_before>/* global $, APP, interfaceConfig */
/* jshint -W101 */

import UIUtil from "../util/UIUtil";
import UIEvents from "../../../service/UI/UIEvents";
import LargeContainer from './LargeContainer';
import BottomToolbar from '../toolbars/BottomToolbar';
import Avatar from "../avatar/Avatar";
import {createDeferred} from '../../util/helpers';

const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE;
const FADE_DURATION_MS = 300;

/**
 * Get stream id.
 * @param {JitsiTrack?} stream
 */
function getStreamOwnerId(stream) {
    if (!stream) {
        return;
    }
    if (stream.isLocal()) { // local stream doesn't have method "getParticipantId"
        return APP.conference.localId;
    } else {
        return stream.getParticipantId();
    }
}

/**
 * Returns an array of the video dimensions, so that it keeps it's aspect
 * ratio and fits available area with it's larger dimension. This method
 * ensures that whole video will be visible and can leave empty areas.
 *
 * @return an array with 2 elements, the video width and the video height
 */
function getDesktopVideoSize(videoWidth,
                             videoHeight,
                             videoSpaceWidth,
                             videoSpaceHeight) {

    let aspectRatio = videoWidth / videoHeight;

    let availableWidth = Math.max(videoWidth, videoSpaceWidth);
    let availableHeight = Math.max(videoHeight, videoSpaceHeight);

    videoSpaceHeight -= BottomToolbar.getFilmStripHeight();

    if (availableWidth / aspectRatio >= videoSpaceHeight) {
        availableHeight = videoSpaceHeight;
        availableWidth = availableHeight * aspectRatio;
    }

    if (availableHeight * aspectRatio >= videoSpaceWidth) {
        availableWidth = videoSpaceWidth;
        availableHeight = availableWidth / aspectRatio;
    }

    return [ availableWidth, availableHeight ];
}

/**
 * Returns an array of the video dimensions. It respects the
 * VIDEO_LAYOUT_FIT config, to fit the video to the screen, by hiding some parts
 * of it, or to fit it to the height or width.
 *
 * @param videoWidth the original video width
 * @param videoHeight the original video height
 * @param videoSpaceWidth the width of the video space
 * @param videoSpaceHeight the height of the video space
 * @return an array with 2 elements, the video width and the video height
 */
function getCameraVideoSize(videoWidth,
                            videoHeight,
                            videoSpaceWidth,
                            videoSpaceHeight) {

    let aspectRatio = videoWidth / videoHeight;

    let availableWidth = videoWidth;
    let availableHeight = videoHeight;

    if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
        availableHeight = videoSpaceHeight;
        availableWidth = availableHeight*aspectRatio;
    }
    else if (interfaceConfig.VIDEO_LAYOUT_FIT == 'width') {
        availableWidth = videoSpaceWidth;
        availableHeight = availableWidth/aspectRatio;
    }
    else if (interfaceConfig.VIDEO_LAYOUT_FIT == 'both') {
        availableWidth = Math.max(videoWidth, videoSpaceWidth);
        availableHeight = Math.max(videoHeight, videoSpaceHeight);

        if (availableWidth / aspectRatio < videoSpaceHeight) {
            availableHeight = videoSpaceHeight;
            availableWidth = availableHeight * aspectRatio;
        }

        if (availableHeight * aspectRatio < videoSpaceWidth) {
            availableWidth = videoSpaceWidth;
            availableHeight = availableWidth / aspectRatio;
        }
    }

    return [ availableWidth, availableHeight ];
}

/**
 * Returns an array of the video horizontal and vertical indents,
 * so that if fits its parent.
 *
 * @return an array with 2 elements, the horizontal indent and the vertical
 * indent
 */
function getCameraVideoPosition(videoWidth,
                                videoHeight,
                                videoSpaceWidth,
                                videoSpaceHeight) {
    // Parent height isn't completely calculated when we position the video in
    // full screen mode and this is why we use the screen height in this case.
    // Need to think it further at some point and implement it properly.
    if (UIUtil.isFullScreen()) {
        videoSpaceHeight = window.innerHeight;
    }

    let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
    let verticalIndent = (videoSpaceHeight - videoHeight) / 2;

    return { horizontalIndent, verticalIndent };
}

/**
 * Returns an array of the video horizontal and vertical indents.
 * Centers horizontally and top aligns vertically.
 *
 * @return an array with 2 elements, the horizontal indent and the vertical
 * indent
 */
function getDesktopVideoPosition(videoWidth,
                                 videoHeight,
                                 videoSpaceWidth,
                                 videoSpaceHeight) {

    let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;

    let verticalIndent = 0;// Top aligned

    return { horizontalIndent, verticalIndent };
}

export const VideoContainerType = "video";

/**
 * Container for user video.
 */
class VideoContainer extends LargeContainer {
    // FIXME: With Temasys we have to re-select everytime
    get $video () {
        return $('#largeVideo');
    }

    get id () {
        return getStreamOwnerId(this.stream);
    }

    constructor (onPlay) {
        super();
        this.stream = null;
        this.videoType = null;

        this.isVisible = false;

        this.$avatar = $('#dominantSpeaker');
        this.$wrapper = $('#largeVideoWrapper');

        // This does not work with Temasys plugin - has to be a property to be
        // copied between new <object> elements
        //this.$video.on('play', onPlay);
        this.$video[0].onplay = onPlay;
    }

    /**
     * Get size of video element.
     * @returns {{width, height}}
     */
    getStreamSize () {
        let video = this.$video[0];
        return {
            width: video.videoWidth,
            height: video.videoHeight
        };
    }

    /**
     * Calculate optimal video size for specified container size.
     * @param {number} containerWidth container width
     * @param {number} containerHeight container height
     * @returns {{availableWidth, availableHeight}}
     */
    getVideoSize (containerWidth, containerHeight) {
        let { width, height } = this.getStreamSize();
        if (this.stream && this.isScreenSharing()) {
            return getDesktopVideoSize( width,
                                        height,
                                        containerWidth,
                                        containerHeight);
        } else {
            return getCameraVideoSize(  width,
                                        height,
                                        containerWidth,
                                        containerHeight);
        }
    }

    /**
     * Calculate optimal video position (offset for top left corner)
     * for specified video size and container size.
     * @param {number} width video width
     * @param {number} height video height
     * @param {number} containerWidth container width
     * @param {number} containerHeight container height
     * @returns {{horizontalIndent, verticalIndent}}
     */
    getVideoPosition (width, height, containerWidth, containerHeight) {
        if (this.stream && this.isScreenSharing()) {
            return getDesktopVideoPosition( width,
                                            height,
                                            containerWidth,
                                            containerHeight);
        } else {
            return getCameraVideoPosition(  width,
                                            height,
                                            containerWidth,
                                            containerHeight);
        }
    }

    resize (containerWidth, containerHeight, animate = false) {
        let [width, height]
            = this.getVideoSize(containerWidth, containerHeight);
        let { horizontalIndent, verticalIndent }
            = this.getVideoPosition(width, height,
                                    containerWidth, containerHeight);

        // update avatar position
        let top = containerHeight / 2 - avatarSize / 4 * 3;

        this.$avatar.css('top', top);

        this.$wrapper.animate({
            width: width,
            height: height,

            top: verticalIndent,
            bottom: verticalIndent,

            left: horizontalIndent,
            right: horizontalIndent
        }, {
            queue: false,
            duration: animate ? 500 : 0
        });
    }

    /**
     * Update video stream.
     * @param {JitsiTrack?} stream new stream
     * @param {string} videoType video type
     */
    setStream (stream, videoType) {
        this.stream = stream;
        this.videoType = videoType;

        if(!stream)
            return;

        stream.attach(this.$video[0]);

        let flipX = stream.isLocal() && !this.isScreenSharing();
        this.$video.css({
            transform: flipX ? 'scaleX(-1)' : 'none'
        });
    }

    /**
     * Check if current video stream is screen sharing.
     * @returns {boolean}
     */
    isScreenSharing () {
        return this.videoType === 'desktop';
    }

    /**
     * Show or hide user avatar.
     * @param {boolean} show
     */
    showAvatar (show) {
        this.$avatar.css("visibility", show ? "visible" : "hidden");
    }

    /**
     * Show or hide watermark.
     * @param {boolean} show
     */
    showWatermark (show) {
        $('.watermark').css('visibility', show ? 'visible' : 'hidden');
    }

    // We are doing fadeOut/fadeIn animations on parent div which wraps
    // largeVideo, because when Temasys plugin is in use it replaces
    // <video> elements with plugin <object> tag. In Safari jQuery is
    // unable to store values on this plugin object which breaks all
    // animation effects performed on it directly.

    show () {
        // its already visible
        if (this.isVisible) {
            return Promise.resolve();
        }

        let $wrapper = this.$wrapper;
        return new Promise((resolve) => {
            this.$wrapper.css('visibility', 'visible').fadeTo(
                FADE_DURATION_MS,
                1,
                () => {
                    this.showWatermark(true);
                    this.isVisible = true;
                    resolve();
                }
            );
        });
    }

    hide () {
        // its already hidden
        if (!this.isVisible) {
            return Promise.resolve();
        }

        return new Promise((resolve) => {
            this.$wrapper.fadeTo(FADE_DURATION_MS, 0, () => {
                this.$wrapper.css('visibility', 'hidden');
                this.showWatermark(false);
                this.isVisible = false;
                resolve();
            });
        });
    }
}

/**
 * Manager for all Large containers.
 */
export default class LargeVideoManager {
    constructor () {
        this.containers = {};

        this.state = VideoContainerType;
        this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType));
        this.addContainer(VideoContainerType, this.videoContainer);

        this.width = 0;
        this.height = 0;

        this.$container = $('#largeVideoContainer');

        this.$container.css({
            display: 'inline-block'
        });

        if (interfaceConfig.SHOW_JITSI_WATERMARK) {
            let leftWatermarkDiv = this.$container.find("div.watermark.leftwatermark");

            leftWatermarkDiv.css({display: 'block'});

            leftWatermarkDiv.parent().attr('href', interfaceConfig.JITSI_WATERMARK_LINK);
        }

        if (interfaceConfig.SHOW_BRAND_WATERMARK) {
            let rightWatermarkDiv = this.$container.find("div.watermark.rightwatermark");

            rightWatermarkDiv.css({
                display: 'block',
                backgroundImage: 'url(images/rightwatermark.png)'
            });

            rightWatermarkDiv.parent().attr('href', interfaceConfig.BRAND_WATERMARK_LINK);
        }

        if (interfaceConfig.SHOW_POWERED_BY) {
            this.$container.children("a.poweredby").css({display: 'block'});
        }

        this.$container.hover(
            e => this.onHoverIn(e),
            e => this.onHoverOut(e)
        );
    }

    onHoverIn (e) {
        if (!this.state) {
            return;
        }
        let container = this.getContainer(this.state);
        container.onHoverIn(e);
    }

    onHoverOut (e) {
        if (!this.state) {
            return;
        }
        let container = this.getContainer(this.state);
        container.onHoverOut(e);
    }

    get id () {
        return this.videoContainer.id;
    }

    scheduleLargeVideoUpdate () {
        if (this.updateInProcess || !this.newStreamData) {
            return;
        }

        this.updateInProcess = true;

        let container = this.getContainer(this.state);

        container.hide().then(() => {
            let {id, stream, videoType, resolve} = this.newStreamData;
            this.newStreamData = null;

            console.info("hover in %s", id);
            this.state = VideoContainerType;
            this.videoContainer.setStream(stream, videoType);

            // change the avatar url on large
            this.updateAvatar(Avatar.getAvatarUrl(id));

            let isVideoMuted = stream ? stream.isMuted() : true;

            // show the avatar on large if needed
            this.videoContainer.showAvatar(isVideoMuted);

            let promise;

            // do not show stream if video is muted
            // but we still should show watermark
            if (isVideoMuted) {
                this.videoContainer.showWatermark(true);
                promise = Promise.resolve();
            } else {
                promise = this.videoContainer.show();
            }

            // resolve updateLargeVideo promise after everything is done
            promise.then(resolve);

            return promise;
        }).then(() => {
            // after everything is done check again if there are any pending new streams.
            this.updateInProcess = false;
            this.scheduleLargeVideoUpdate();
        });
    }

    /**
     * Update large video.
     * Switches to large video even if previously other container was visible.
     * @param userID the userID of the participant associated with the stream
     * @param {JitsiTrack?} stream new stream
     * @param {string?} videoType new video type
     * @returns {Promise}
     */
    updateLargeVideo (userID, stream, videoType) {
        if (this.newStreamData) {
            this.newStreamData.reject();
        }

        this.newStreamData = createDeferred();
        this.newStreamData.id = userID;
        this.newStreamData.stream = stream;
        this.newStreamData.videoType = videoType;

        this.scheduleLargeVideoUpdate();

        return this.newStreamData.promise;
    }

    /**
     * Update container size optionally taking side bar size into account.
     * @param {boolean} isSideBarVisible if side bar is visible.
     */
    updateContainerSize (isSideBarVisible) {
        this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
        this.height = window.innerHeight;
    }

    /**
     * Resize Large container of specified type.
     * @param {string} type type of container which should be resized.
     * @param {boolean} [animate=false] if resize process should be animated.
     */
    resizeContainer (type, animate = false) {
        let container = this.getContainer(type);
        container.resize(this.width, this.height, animate);
    }

    /**
     * Resize all Large containers.
     * @param {boolean} animate if resize process should be animated.
     */
    resize (animate) {
        // resize all containers
        Object.keys(this.containers)
            .forEach(type => this.resizeContainer(type, animate));

        this.$container.animate({
            width: this.width,
            height: this.height
        }, {
            queue: false,
            duration: animate ? 500 : 0
        });
    }

    /**
     * Enables/disables the filter indicating a video problem to the user.
     *
     * @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
     */
    enableVideoProblemFilter (enable) {
        this.videoContainer.$video.toggleClass("videoProblemFilter", enable);
    }

    /**
     * Updates the src of the dominant speaker avatar
     */
    updateAvatar (avatarUrl) {
        $("#dominantSpeakerAvatar").attr('src', avatarUrl);
    }

    /**
     * Add container of specified type.
     * @param {string} type container type
     * @param {LargeContainer} container container to add.
     */
    addContainer (type, container) {
        if (this.containers[type]) {
            throw new Error(`container of type ${type} already exist`);
        }

        this.containers[type] = container;
        this.resizeContainer(type);
    }

    /**
     * Get Large container of specified type.
     * @param {string} type container type.
     * @returns {LargeContainer}
     */
    getContainer (type) {
        let container = this.containers[type];

        if (!container) {
            throw new Error(`container of type ${type} doesn't exist`);
        }

        return container;
    }

    /**
     * Remove Large container of specified type.
     * @param {string} type container type.
     */
    removeContainer (type) {
        if (!this.containers[type]) {
            throw new Error(`container of type ${type} doesn't exist`);
        }

        delete this.containers[type];
    }

    /**
     * Show Large container of specified type.
     * Does nothing if such container is already visible.
     * @param {string} type container type.
     * @returns {Promise}
     */
    showContainer (type) {
        if (this.state === type) {
            return Promise.resolve();
        }

        let oldContainer = this.containers[this.state];
        oldContainer.hide();

        this.state = type;
        let container = this.getContainer(type);

        return container.show();
    }
}
<commit_msg>fix watermark logo blinking during video switching<commit_after>/* global $, APP, interfaceConfig */
/* jshint -W101 */

import UIUtil from "../util/UIUtil";
import UIEvents from "../../../service/UI/UIEvents";
import LargeContainer from './LargeContainer';
import BottomToolbar from '../toolbars/BottomToolbar';
import Avatar from "../avatar/Avatar";
import {createDeferred} from '../../util/helpers';

const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE;
const FADE_DURATION_MS = 300;

/**
 * Get stream id.
 * @param {JitsiTrack?} stream
 */
function getStreamOwnerId(stream) {
    if (!stream) {
        return;
    }
    if (stream.isLocal()) { // local stream doesn't have method "getParticipantId"
        return APP.conference.localId;
    } else {
        return stream.getParticipantId();
    }
}

/**
 * Returns an array of the video dimensions, so that it keeps it's aspect
 * ratio and fits available area with it's larger dimension. This method
 * ensures that whole video will be visible and can leave empty areas.
 *
 * @return an array with 2 elements, the video width and the video height
 */
function getDesktopVideoSize(videoWidth,
                             videoHeight,
                             videoSpaceWidth,
                             videoSpaceHeight) {

    let aspectRatio = videoWidth / videoHeight;

    let availableWidth = Math.max(videoWidth, videoSpaceWidth);
    let availableHeight = Math.max(videoHeight, videoSpaceHeight);

    videoSpaceHeight -= BottomToolbar.getFilmStripHeight();

    if (availableWidth / aspectRatio >= videoSpaceHeight) {
        availableHeight = videoSpaceHeight;
        availableWidth = availableHeight * aspectRatio;
    }

    if (availableHeight * aspectRatio >= videoSpaceWidth) {
        availableWidth = videoSpaceWidth;
        availableHeight = availableWidth / aspectRatio;
    }

    return [ availableWidth, availableHeight ];
}

/**
 * Returns an array of the video dimensions. It respects the
 * VIDEO_LAYOUT_FIT config, to fit the video to the screen, by hiding some parts
 * of it, or to fit it to the height or width.
 *
 * @param videoWidth the original video width
 * @param videoHeight the original video height
 * @param videoSpaceWidth the width of the video space
 * @param videoSpaceHeight the height of the video space
 * @return an array with 2 elements, the video width and the video height
 */
function getCameraVideoSize(videoWidth,
                            videoHeight,
                            videoSpaceWidth,
                            videoSpaceHeight) {

    let aspectRatio = videoWidth / videoHeight;

    let availableWidth = videoWidth;
    let availableHeight = videoHeight;

    if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
        availableHeight = videoSpaceHeight;
        availableWidth = availableHeight*aspectRatio;
    }
    else if (interfaceConfig.VIDEO_LAYOUT_FIT == 'width') {
        availableWidth = videoSpaceWidth;
        availableHeight = availableWidth/aspectRatio;
    }
    else if (interfaceConfig.VIDEO_LAYOUT_FIT == 'both') {
        availableWidth = Math.max(videoWidth, videoSpaceWidth);
        availableHeight = Math.max(videoHeight, videoSpaceHeight);

        if (availableWidth / aspectRatio < videoSpaceHeight) {
            availableHeight = videoSpaceHeight;
            availableWidth = availableHeight * aspectRatio;
        }

        if (availableHeight * aspectRatio < videoSpaceWidth) {
            availableWidth = videoSpaceWidth;
            availableHeight = availableWidth / aspectRatio;
        }
    }

    return [ availableWidth, availableHeight ];
}

/**
 * Returns an array of the video horizontal and vertical indents,
 * so that if fits its parent.
 *
 * @return an array with 2 elements, the horizontal indent and the vertical
 * indent
 */
function getCameraVideoPosition(videoWidth,
                                videoHeight,
                                videoSpaceWidth,
                                videoSpaceHeight) {
    // Parent height isn't completely calculated when we position the video in
    // full screen mode and this is why we use the screen height in this case.
    // Need to think it further at some point and implement it properly.
    if (UIUtil.isFullScreen()) {
        videoSpaceHeight = window.innerHeight;
    }

    let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
    let verticalIndent = (videoSpaceHeight - videoHeight) / 2;

    return { horizontalIndent, verticalIndent };
}

/**
 * Returns an array of the video horizontal and vertical indents.
 * Centers horizontally and top aligns vertically.
 *
 * @return an array with 2 elements, the horizontal indent and the vertical
 * indent
 */
function getDesktopVideoPosition(videoWidth,
                                 videoHeight,
                                 videoSpaceWidth,
                                 videoSpaceHeight) {

    let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;

    let verticalIndent = 0;// Top aligned

    return { horizontalIndent, verticalIndent };
}

export const VideoContainerType = "video";

/**
 * Container for user video.
 */
class VideoContainer extends LargeContainer {
    // FIXME: With Temasys we have to re-select everytime
    get $video () {
        return $('#largeVideo');
    }

    get id () {
        return getStreamOwnerId(this.stream);
    }

    constructor (onPlay) {
        super();
        this.stream = null;
        this.videoType = null;

        this.isVisible = false;

        this.$avatar = $('#dominantSpeaker');
        this.$wrapper = $('#largeVideoWrapper');

        // This does not work with Temasys plugin - has to be a property to be
        // copied between new <object> elements
        //this.$video.on('play', onPlay);
        this.$video[0].onplay = onPlay;
    }

    /**
     * Get size of video element.
     * @returns {{width, height}}
     */
    getStreamSize () {
        let video = this.$video[0];
        return {
            width: video.videoWidth,
            height: video.videoHeight
        };
    }

    /**
     * Calculate optimal video size for specified container size.
     * @param {number} containerWidth container width
     * @param {number} containerHeight container height
     * @returns {{availableWidth, availableHeight}}
     */
    getVideoSize (containerWidth, containerHeight) {
        let { width, height } = this.getStreamSize();
        if (this.stream && this.isScreenSharing()) {
            return getDesktopVideoSize( width,
                                        height,
                                        containerWidth,
                                        containerHeight);
        } else {
            return getCameraVideoSize(  width,
                                        height,
                                        containerWidth,
                                        containerHeight);
        }
    }

    /**
     * Calculate optimal video position (offset for top left corner)
     * for specified video size and container size.
     * @param {number} width video width
     * @param {number} height video height
     * @param {number} containerWidth container width
     * @param {number} containerHeight container height
     * @returns {{horizontalIndent, verticalIndent}}
     */
    getVideoPosition (width, height, containerWidth, containerHeight) {
        if (this.stream && this.isScreenSharing()) {
            return getDesktopVideoPosition( width,
                                            height,
                                            containerWidth,
                                            containerHeight);
        } else {
            return getCameraVideoPosition(  width,
                                            height,
                                            containerWidth,
                                            containerHeight);
        }
    }

    resize (containerWidth, containerHeight, animate = false) {
        let [width, height]
            = this.getVideoSize(containerWidth, containerHeight);
        let { horizontalIndent, verticalIndent }
            = this.getVideoPosition(width, height,
                                    containerWidth, containerHeight);

        // update avatar position
        let top = containerHeight / 2 - avatarSize / 4 * 3;

        this.$avatar.css('top', top);

        this.$wrapper.animate({
            width: width,
            height: height,

            top: verticalIndent,
            bottom: verticalIndent,

            left: horizontalIndent,
            right: horizontalIndent
        }, {
            queue: false,
            duration: animate ? 500 : 0
        });
    }

    /**
     * Update video stream.
     * @param {JitsiTrack?} stream new stream
     * @param {string} videoType video type
     */
    setStream (stream, videoType) {
        this.stream = stream;
        this.videoType = videoType;

        if(!stream)
            return;

        stream.attach(this.$video[0]);

        let flipX = stream.isLocal() && !this.isScreenSharing();
        this.$video.css({
            transform: flipX ? 'scaleX(-1)' : 'none'
        });
    }

    /**
     * Check if current video stream is screen sharing.
     * @returns {boolean}
     */
    isScreenSharing () {
        return this.videoType === 'desktop';
    }

    /**
     * Show or hide user avatar.
     * @param {boolean} show
     */
    showAvatar (show) {
        this.$avatar.css("visibility", show ? "visible" : "hidden");
    }

    // We are doing fadeOut/fadeIn animations on parent div which wraps
    // largeVideo, because when Temasys plugin is in use it replaces
    // <video> elements with plugin <object> tag. In Safari jQuery is
    // unable to store values on this plugin object which breaks all
    // animation effects performed on it directly.

    show () {
        // its already visible
        if (this.isVisible) {
            return Promise.resolve();
        }

        let $wrapper = this.$wrapper;
        return new Promise((resolve) => {
            this.$wrapper.css('visibility', 'visible').fadeTo(
                FADE_DURATION_MS,
                1,
                () => {
                    this.isVisible = true;
                    resolve();
                }
            );
        });
    }

    hide () {
        // its already hidden
        if (!this.isVisible) {
            return Promise.resolve();
        }

        return new Promise((resolve) => {
            this.$wrapper.fadeTo(FADE_DURATION_MS, 0, () => {
                this.$wrapper.css('visibility', 'hidden');
                this.isVisible = false;
                resolve();
            });
        });
    }
}

/**
 * Manager for all Large containers.
 */
export default class LargeVideoManager {
    constructor () {
        this.containers = {};

        this.state = VideoContainerType;
        this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType));
        this.addContainer(VideoContainerType, this.videoContainer);

        this.width = 0;
        this.height = 0;

        this.$container = $('#largeVideoContainer');

        this.$container.css({
            display: 'inline-block'
        });

        if (interfaceConfig.SHOW_JITSI_WATERMARK) {
            let leftWatermarkDiv = this.$container.find("div.watermark.leftwatermark");

            leftWatermarkDiv.css({display: 'block'});

            leftWatermarkDiv.parent().attr('href', interfaceConfig.JITSI_WATERMARK_LINK);
        }

        if (interfaceConfig.SHOW_BRAND_WATERMARK) {
            let rightWatermarkDiv = this.$container.find("div.watermark.rightwatermark");

            rightWatermarkDiv.css({
                display: 'block',
                backgroundImage: 'url(images/rightwatermark.png)'
            });

            rightWatermarkDiv.parent().attr('href', interfaceConfig.BRAND_WATERMARK_LINK);
        }

        if (interfaceConfig.SHOW_POWERED_BY) {
            this.$container.children("a.poweredby").css({display: 'block'});
        }

        this.$container.hover(
            e => this.onHoverIn(e),
            e => this.onHoverOut(e)
        );
    }

    onHoverIn (e) {
        if (!this.state) {
            return;
        }
        let container = this.getContainer(this.state);
        container.onHoverIn(e);
    }

    onHoverOut (e) {
        if (!this.state) {
            return;
        }
        let container = this.getContainer(this.state);
        container.onHoverOut(e);
    }

    get id () {
        return this.videoContainer.id;
    }

    scheduleLargeVideoUpdate () {
        if (this.updateInProcess || !this.newStreamData) {
            return;
        }

        this.updateInProcess = true;

        let container = this.getContainer(this.state);

        container.hide().then(() => {
            let {id, stream, videoType, resolve} = this.newStreamData;
            this.newStreamData = null;

            console.info("hover in %s", id);
            this.state = VideoContainerType;
            this.videoContainer.setStream(stream, videoType);

            // change the avatar url on large
            this.updateAvatar(Avatar.getAvatarUrl(id));

            let isVideoMuted = stream ? stream.isMuted() : true;

            // show the avatar on large if needed
            this.videoContainer.showAvatar(isVideoMuted);

            let promise;

            // do not show stream if video is muted
            // but we still should show watermark
            if (isVideoMuted) {
                this.showWatermark(true);
                promise = Promise.resolve();
            } else {
                promise = this.videoContainer.show();
            }

            // resolve updateLargeVideo promise after everything is done
            promise.then(resolve);

            return promise;
        }).then(() => {
            // after everything is done check again if there are any pending new streams.
            this.updateInProcess = false;
            this.scheduleLargeVideoUpdate();
        });
    }

    /**
     * Update large video.
     * Switches to large video even if previously other container was visible.
     * @param userID the userID of the participant associated with the stream
     * @param {JitsiTrack?} stream new stream
     * @param {string?} videoType new video type
     * @returns {Promise}
     */
    updateLargeVideo (userID, stream, videoType) {
        if (this.newStreamData) {
            this.newStreamData.reject();
        }

        this.newStreamData = createDeferred();
        this.newStreamData.id = userID;
        this.newStreamData.stream = stream;
        this.newStreamData.videoType = videoType;

        this.scheduleLargeVideoUpdate();

        return this.newStreamData.promise;
    }

    /**
     * Update container size optionally taking side bar size into account.
     * @param {boolean} isSideBarVisible if side bar is visible.
     */
    updateContainerSize (isSideBarVisible) {
        this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
        this.height = window.innerHeight;
    }

    /**
     * Resize Large container of specified type.
     * @param {string} type type of container which should be resized.
     * @param {boolean} [animate=false] if resize process should be animated.
     */
    resizeContainer (type, animate = false) {
        let container = this.getContainer(type);
        container.resize(this.width, this.height, animate);
    }

    /**
     * Resize all Large containers.
     * @param {boolean} animate if resize process should be animated.
     */
    resize (animate) {
        // resize all containers
        Object.keys(this.containers)
            .forEach(type => this.resizeContainer(type, animate));

        this.$container.animate({
            width: this.width,
            height: this.height
        }, {
            queue: false,
            duration: animate ? 500 : 0
        });
    }

    /**
     * Enables/disables the filter indicating a video problem to the user.
     *
     * @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
     */
    enableVideoProblemFilter (enable) {
        this.videoContainer.$video.toggleClass("videoProblemFilter", enable);
    }

    /**
     * Updates the src of the dominant speaker avatar
     */
    updateAvatar (avatarUrl) {
        $("#dominantSpeakerAvatar").attr('src', avatarUrl);
    }

    /**
     * Show or hide watermark.
     * @param {boolean} show
     */
    showWatermark (show) {
        $('.watermark').css('visibility', show ? 'visible' : 'hidden');
    }

    /**
     * Add container of specified type.
     * @param {string} type container type
     * @param {LargeContainer} container container to add.
     */
    addContainer (type, container) {
        if (this.containers[type]) {
            throw new Error(`container of type ${type} already exist`);
        }

        this.containers[type] = container;
        this.resizeContainer(type);
    }

    /**
     * Get Large container of specified type.
     * @param {string} type container type.
     * @returns {LargeContainer}
     */
    getContainer (type) {
        let container = this.containers[type];

        if (!container) {
            throw new Error(`container of type ${type} doesn't exist`);
        }

        return container;
    }

    /**
     * Remove Large container of specified type.
     * @param {string} type container type.
     */
    removeContainer (type) {
        if (!this.containers[type]) {
            throw new Error(`container of type ${type} doesn't exist`);
        }

        delete this.containers[type];
    }

    /**
     * Show Large container of specified type.
     * Does nothing if such container is already visible.
     * @param {string} type container type.
     * @returns {Promise}
     */
    showContainer (type) {
        if (this.state === type) {
            return Promise.resolve();
        }

        let oldContainer = this.containers[this.state];
        if (this.state === VideoContainerType) {
            this.showWatermark(false);
        }
        oldContainer.hide();

        this.state = type;
        let container = this.getContainer(type);

        return container.show().then(() => {
            if (type === VideoContainerType) {
                this.showWatermark(true);
            }
        });
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 The Closure Compiler Authors.
 *
 * 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.
 */

/**
 * @fileoverview AngularJS' HTTP promises. This version of the externs file
 * provides templated promises.
 * @see https://docs.angularjs.org/api/ng/service/$http
 * @externs
 */

/**
 * Left here for backwards compatibility, but no longer used.
 *
 * @typedef {function((string|Object), number,
 *     function(string=): (string|Object|null), angular.$http.Config)}
 */
angular.HttpCallback;

/**
 * @constructor
 * @template T
 */
angular.$http.Response = function() {};

/** @type {T} */
angular.$http.Response.prototype.data;

/** @type {number} */
angular.$http.Response.prototype.status;

/**
 * @param {string=} name
 * @return {string|Object}
 */
angular.$http.Response.prototype.headers = function(name) {};

/** @type {!angular.$http.Config} */
angular.$http.Response.prototype.config;

/**
 * @constructor
 * @extends {angular.$q.Promise.<!angular.$http.Response.<T>>}
 * @template T
 */
angular.$http.HttpPromise = function() {};

/**
 * @param {function(T, number, function(string=):
 *     (string|Object|null), angular.$http.Config)} callback
 * @return {!angular.$http.HttpPromise.<T>} Promise for chaining.
 */
angular.$http.HttpPromise.prototype.success = function(callback) {};

/**
 * @param {function(*, number, function(string=):
 *     (string|Object|null), angular.$http.Config)} callback
 * @return {!angular.$http.HttpPromise.<T>} Promise for chaining.
 */
angular.$http.HttpPromise.prototype.error = function(callback) {};
<commit_msg>Automated g4 rollback of changelist 101130913.<commit_after>/*
 * Copyright 2014 The Closure Compiler Authors.
 *
 * 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.
 */

/**
 * @fileoverview AngularJS' HTTP promises. This version of the externs file
 * provides templated promises.
 * @see https://docs.angularjs.org/api/ng/service/$http
 * @externs
 */

/**
 * Left here for backwards compatibility, but no longer used.
 *
 * @typedef {function((string|Object), number,
 *     function(string=): (string|Object|null), angular.$http.Config)}
 */
angular.HttpCallback;

/**
 * @constructor
 * @template T
 */
angular.$http.Response = function() {};

/** @type {T} */
angular.$http.Response.prototype.data;

/** @type {number} */
angular.$http.Response.prototype.status;

/**
 * @param {string=} name
 * @return {string|Object}
 */
angular.$http.Response.prototype.headers = function(name) {};

/** @type {!angular.$http.Config} */
angular.$http.Response.prototype.config;

/**
 * @constructor
 * @extends {angular.$q.Promise.<!angular.$http.Response.<T>>}
 * @template T
 */
angular.$http.HttpPromise = function() {};

/**
 * @param {?(function(T):
 *             (RESULT|IThenable.<RESULT>|Thenable))=} opt_onFulfilled
 * @param {?(function(*): *)=} opt_onRejected
 * @param {?(function(*): *)=} opt_notifyCallback
 * @return {!angular.$http.HttpPromise.<RESULT>}
 * @template RESULT
 * @override
 */
angular.$http.HttpPromise.prototype.then =
    function(opt_onFulfilled, opt_onRejected, opt_notifyCallback) {};

/**
 * @param {?function(?)} callback
 * @return {!angular.$http.HttpPromise.<T>}
 * @override
 */
angular.$http.HttpPromise.prototype.catch = function(callback) {};

/**
 * @param {?function(?)} callback
 * @return {!angular.$http.HttpPromise.<T>}
 * @override
 */
angular.$http.HttpPromise.prototype.finally = function(callback) {};

/**
 * @param {function(T, number, function(string=):
 *     (string|Object|null), angular.$http.Config)} callback
 * @return {!angular.$http.HttpPromise.<T>} Promise for chaining.
 */
angular.$http.HttpPromise.prototype.success = function(callback) {};

/**
 * @param {function(*, number, function(string=):
 *     (string|Object|null), angular.$http.Config)} callback
 * @return {!angular.$http.HttpPromise.<T>} Promise for chaining.
 */
angular.$http.HttpPromise.prototype.error = function(callback) {};
<|endoftext|>


<commit_before>/**
 * Copyright 2016-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * 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.
 *
 *------------------------------------------------------------------------
 *
 * @description - Default screen theme.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 *
 * @flow
 *
 */
'use strict'; // eslint-disable-line

import FontTheme from '../fonts/san-francisco-font';

import ColorTheme from './color-theme';

export default {
    body: {
        shade: `light`
    },
    header: {
        shade: `light`,
        overlay: `opaque`,
        size: `normal`,
        dropShadowed: true,
        initialAnimation: `none`
    },
    size: {
        header: {
            status: 30,
            navigation: {
                small: 48,
                normal: 54,
                large: 72
            },
            media: 72
        }
    },
    font: {
        header: {
            label: {
                small: FontTheme.boldSmall,
                normal: FontTheme.bold,
                large: FontTheme.boldLarge
            }
        }
    },
    color: {
        body: {
            opacity: ColorTheme.opacity,
            dark: ColorTheme.light.default,
            light: ColorTheme.dark.default
        },
        header: {
            opacity: ColorTheme.opacity,
            status: {
                dark: ColorTheme.dark.primary,
                light: ColorTheme.light.primary
            },
            navigation: {
                dark: ColorTheme.dark.primary,
                light: ColorTheme.light.primary
            },
            media: {
                dark: ColorTheme.dark.primary,
                light: ColorTheme.light.primary
            },
            label: {
                dark: ColorTheme.light.default,
                light: ColorTheme.dark.default
            }
        }
    },
    animation: {
        header: {
            none: {
                refName: ``,
                transitions: []
            }
        }
    }
};
<commit_msg>Added missing default  animation to BodyScreen theme<commit_after>/**
 * Copyright 2016-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * 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.
 *
 *------------------------------------------------------------------------
 *
 * @description - Default screen theme.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 *
 * @flow
 *
 */
'use strict'; // eslint-disable-line

import FontTheme from '../fonts/san-francisco-font';

import ColorTheme from './color-theme';

export default {
    body: {
        shade: `light`
    },
    header: {
        shade: `light`,
        overlay: `opaque`,
        size: `normal`,
        dropShadowed: true,
        initialAnimation: `none`
    },
    size: {
        header: {
            status: 30,
            navigation: {
                small: 48,
                normal: 54,
                large: 72
            },
            media: 72
        }
    },
    font: {
        header: {
            label: {
                small: FontTheme.boldSmall,
                normal: FontTheme.bold,
                large: FontTheme.boldLarge
            }
        }
    },
    color: {
        body: {
            opacity: ColorTheme.opacity,
            dark: ColorTheme.light.default,
            light: ColorTheme.dark.default
        },
        header: {
            opacity: ColorTheme.opacity,
            status: {
                dark: ColorTheme.dark.primary,
                light: ColorTheme.light.primary
            },
            navigation: {
                dark: ColorTheme.dark.primary,
                light: ColorTheme.light.primary
            },
            media: {
                dark: ColorTheme.dark.primary,
                light: ColorTheme.light.primary
            },
            label: {
                dark: ColorTheme.light.default,
                light: ColorTheme.dark.default
            }
        }
    },
    animation: {
        header: {
            none: {
                refName: ``,
                transitions: []
            }
        },
        body: {
            none: {
                refName: ``,
                transitions: []
            }
        }
    }
};
<|endoftext|>