<commit_before>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   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.
 */

/**
 * Controls the appearance and behavior of the dimension list
 *
 * This is where drag and drop lives
 */
var DimensionList = Backbone.View.extend({
    events: {
        'click span': 'select',
        'click a': 'select',
        'click .parent_dimension ul li a.level' : 'select_dimension',
        'click .measure' : 'select_measure',
        'click .addMeasure' : 'measure_dialog'
    },

    initialize: function(args) {
        // Don't lose this
        _.bindAll(this, "render", "load_dimension","select_dimension");

        // Bind parent element
        this.workspace = args.workspace;
        this.cube = args.cube;
    },

    load_dimension: function() {
        this.template = this.cube.get('template_attributes');
        this.render_attributes();
        this.workspace.sync_query();
    },

    render: function() {
         // Fetch from the server if we haven't already
        if (this.cube && this.cube.has('template_attributes')) {
            this.load_dimension();
        } else if (! this.cube){
            $(this.el).html('Could not load attributes. Please log out and log in again.');
        } else {
            var template = _.template($("#template-attributes").html());
            $(this.el).html(template);
            $(this.el).find(".loading").removeClass("hide");
            this.workspace.bind('cube:loaded',  this.load_dimension);
        }

        return this;
    },

    render_attributes: function() {
        // Pull the HTML from cache and hide all dimensions
        var self = this;
        $(this.el).html(this.template);
        if (isIE && isIE <= 8) {
            $(this.el).show();
        } else {
            $(this.el).fadeTo(500,1);
        }

        // Add draggable behavior
        $(this.el).find('.addMeasure, .calculated_measures').show();
        $(this.el).find('.measure').parent('li').draggable({
            cancel: '.not-draggable',
            connectToSortable: $(this.workspace.el).find('.fields_list_body.details ul.connectable'),
            helper: 'clone',
            placeholder: 'placeholder',
            opacity: 0.60,
            tolerance: 'touch',
            containment:    $(self.workspace.el),
            cursorAt: { top: 10, left: 35 }
        });

        $(this.el).find('.level').parent('li').draggable({
            cancel: '.not-draggable, .hierarchy',
            connectToSortable: $(this.workspace.el).find('.fields_list_body.columns > ul.connectable, .fields_list_body.rows > ul.connectable, .fields_list_body.filter > ul.connectable'),
            containment:    $(self.workspace.el),
            //helper: "clone",
            helper: function(event, ui){
                var target = $(event.target).hasClass('d_level') ? $(event.target) : $(event.target).parent();
                var hierarchy = target.find('a').attr('hierarchy');
                var level = target.find('a').attr('level');
                var h = target.parent().clone().removeClass('d_hierarchy').addClass('hierarchy');
                h.find('li a[hierarchy="' + hierarchy + '"]').parent().hide();
                h.find('li a[level="' + level + '"]').parent().show();
                var selection = $('

');
                selection.append(h);
                return selection;

            },

            placeholder: 'placeholder',
            opacity: 0.60,
            tolerance: 'touch',
            cursorAt: {
                top: 10,
                left: 85
            }
        });
    },

    select: function(event) {
        var $target = $(event.target).hasClass('root')
            ? $(event.target) : $(event.target).parent().find('span');
        if ($target.hasClass('root')) {
            $target.find('a').toggleClass('folder_collapsed').toggleClass('folder_expand');
            $target.toggleClass('collapsed').toggleClass('expand');
            $target.siblings('ul.dimension_tree_folder:first').toggle();
            $tree = $target.parents('.sidebar');
            $tree.hide();
            $tree.get(0).offsetHeight; // no need to store this anywhere, the reference is enough
            $tree.show();
        }

        return false;
    },

     select_dimension: function(event, ui) {
        if (this.workspace.query.model.type != "QUERYMODEL") {
            return;
        }
        if ($(event.target).parent().hasClass('ui-state-disabled')) {
            event.preventDefault();
            event.stopPropagation();
            return;
        }

        var hierarchy = $(event.target).attr('hierarchy');
        var hierarchyCaption = $(event.target).parent().parent().attr('hierarchycaption');
        var level = $(event.target).attr('level');
        var axisName = "ROWS";

        if ($(this.workspace.el).find(".workspace_fields ul.hierarchy[hierarchy='" + hierarchy + "']").length > 0) {
             var $level = $(this.workspace.el).find(".workspace_fields ul[hierarchy='" + hierarchy + "'] a[level='" + level + "']").parent().show();
            axisName = $level.parents('.fields_list_body').hasClass('rows') ? "ROWS" : "COLUMNS";
        } else {
            var $axis = $(this.workspace.el).find(".workspace_fields .fields_list[title='ROWS'] ul.hierarchy").length > 0 ?
                $(this.workspace.el).find(".workspace_fields .fields_list[title='COLUMNS'] ul.connectable") :
                $(this.workspace.el).find(".workspace_fields .fields_list[title='ROWS'] ul.connectable") ;

            axisName = $axis.parents('.fields_list').attr('title');
        }
        this.workspace.query.helper.includeLevel(axisName, hierarchy, level);
        this.workspace.sync_query();
        this.workspace.query.run();
        event.preventDefault();
        return false;
    },

    select_measure: function(event, ui) {
        if ($(event.target).parent().hasClass('ui-state-disabled')) {
            return;
        }

        var $target = $(event.target).parent().clone();
        var measure = {
            "name": $target.find('a').attr('measure'),
            "type": $target.find('a').attr('type')
        };
        this.workspace.query.helper.includeMeasure(measure);
        this.workspace.sync_query();
        this.workspace.query.run();
        event.preventDefault();
        return false;
    },

    measure_dialog: function(event, ui) {
        (new MeasuresModal({
                                    workspace: this.workspace,
                                    measure: null
        })).render().open();
    }

});
<commit_msg>always click tree > rows for dimension<commit_after>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   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.
 */

/**
 * Controls the appearance and behavior of the dimension list
 *
 * This is where drag and drop lives
 */
var DimensionList = Backbone.View.extend({
    events: {
        'click span': 'select',
        'click a': 'select',
        'click .parent_dimension ul li a.level' : 'select_dimension',
        'click .measure' : 'select_measure',
        'click .addMeasure' : 'measure_dialog'
    },

    initialize: function(args) {
        // Don't lose this
        _.bindAll(this, "render", "load_dimension","select_dimension");

        // Bind parent element
        this.workspace = args.workspace;
        this.cube = args.cube;
    },

    load_dimension: function() {
        this.template = this.cube.get('template_attributes');
        this.render_attributes();
        this.workspace.sync_query();
    },

    render: function() {
         // Fetch from the server if we haven't already
        if (this.cube && this.cube.has('template_attributes')) {
            this.load_dimension();
        } else if (! this.cube){
            $(this.el).html('Could not load attributes. Please log out and log in again.');
        } else {
            var template = _.template($("#template-attributes").html());
            $(this.el).html(template);
            $(this.el).find(".loading").removeClass("hide");
            this.workspace.bind('cube:loaded',  this.load_dimension);
        }

        return this;
    },

    render_attributes: function() {
        // Pull the HTML from cache and hide all dimensions
        var self = this;
        $(this.el).html(this.template);
        if (isIE && isIE <= 8) {
            $(this.el).show();
        } else {
            $(this.el).fadeTo(500,1);
        }

        // Add draggable behavior
        $(this.el).find('.addMeasure, .calculated_measures').show();
        $(this.el).find('.measure').parent('li').draggable({
            cancel: '.not-draggable',
            connectToSortable: $(this.workspace.el).find('.fields_list_body.details ul.connectable'),
            helper: 'clone',
            placeholder: 'placeholder',
            opacity: 0.60,
            tolerance: 'touch',
            containment:    $(self.workspace.el),
            cursorAt: { top: 10, left: 35 }
        });

        $(this.el).find('.level').parent('li').draggable({
            cancel: '.not-draggable, .hierarchy',
            connectToSortable: $(this.workspace.el).find('.fields_list_body.columns > ul.connectable, .fields_list_body.rows > ul.connectable, .fields_list_body.filter > ul.connectable'),
            containment:    $(self.workspace.el),
            //helper: "clone",
            helper: function(event, ui){
                var target = $(event.target).hasClass('d_level') ? $(event.target) : $(event.target).parent();
                var hierarchy = target.find('a').attr('hierarchy');
                var level = target.find('a').attr('level');
                var h = target.parent().clone().removeClass('d_hierarchy').addClass('hierarchy');
                h.find('li a[hierarchy="' + hierarchy + '"]').parent().hide();
                h.find('li a[level="' + level + '"]').parent().show();
                var selection = $('

');
                selection.append(h);
                return selection;

            },

            placeholder: 'placeholder',
            opacity: 0.60,
            tolerance: 'touch',
            cursorAt: {
                top: 10,
                left: 85
            }
        });
    },

    select: function(event) {
        var $target = $(event.target).hasClass('root')
            ? $(event.target) : $(event.target).parent().find('span');
        if ($target.hasClass('root')) {
            $target.find('a').toggleClass('folder_collapsed').toggleClass('folder_expand');
            $target.toggleClass('collapsed').toggleClass('expand');
            $target.siblings('ul.dimension_tree_folder:first').toggle();
            $tree = $target.parents('.sidebar');
            $tree.hide();
            $tree.get(0).offsetHeight; // no need to store this anywhere, the reference is enough
            $tree.show();
        }

        return false;
    },

     select_dimension: function(event, ui) {
        if (this.workspace.query.model.type != "QUERYMODEL") {
            return;
        }
        if ($(event.target).parent().hasClass('ui-state-disabled')) {
            event.preventDefault();
            event.stopPropagation();
            return;
        }

        var hierarchy = $(event.target).attr('hierarchy');
        var hierarchyCaption = $(event.target).parent().parent().attr('hierarchycaption');
        var level = $(event.target).attr('level');
        var axisName = "ROWS";

        this.workspace.query.helper.includeLevel(axisName, hierarchy, level);
        this.workspace.sync_query();
        this.workspace.query.run();
        event.preventDefault();
        return false;
    },

    select_measure: function(event, ui) {
        if ($(event.target).parent().hasClass('ui-state-disabled')) {
            return;
        }

        var $target = $(event.target).parent().clone();
        var measure = {
            "name": $target.find('a').attr('measure'),
            "type": $target.find('a').attr('type')
        };
        this.workspace.query.helper.includeMeasure(measure);
        this.workspace.sync_query();
        this.workspace.query.run();
        event.preventDefault();
        return false;
    },

    measure_dialog: function(event, ui) {
        (new MeasuresModal({
                                    workspace: this.workspace,
                                    measure: null
        })).render().open();
    }

});
<|endoftext|>


<commit_before>/**
 * @brief タグ関連処理
 */
var gspecv = gspecv || {};

/**
 * @brief タグ関連処理のセットアップ
 *
 * @param selecterIdInfos セレクタ用IDの情報
 */
gspecv.setupTag = function(selecterIdInfos) {
  var editFileName;
  var fileTagNameArray = [];
  var stockTagNameArray = [];

  var $tagCreateButton = $(selecterIdInfos.tagCreateButton);
  var $stockTagList = $(selecterIdInfos.stockTagList);

  // ダイアログのセットアップメソッドを設定
  selecterIdInfos.$tagEditDialog.setup = function(fileName, data) {
    editFileName = fileName;

    return selecterIdInfos.$tagEditDialog;
  };

  selecterIdInfos.$tagEditDialog.on('shown', function() {
    // タグ情報を構築
    var $fileTagList = $(selecterIdInfos.fileTagList);
    $fileTagList.droppable({
      drop: function(event, ui) {
        var $dropedSelecter = $(ui.draggable[0]);
        $dropedSelecter.remove();
        $fileTagList.append($dropedSelecter);
        $dropedSelecter.draggable({ revert: true });
      }
    });
    fileTagNameArray.forEach(function(tagName) {
      $fileTagList.append(createTagLabel(tagName));
    });

    stockTagNameArray.forEach(function(tagName) {
      $stockTagList.append(createTagLabel(tagName));
    });
  });

  $tagCreateButton.on('click', function() {
    // 新しいタグをリストに追加する
    var newTagName = $(selecterIdInfos.tagCreateNameInput).val();
    if(newTagName !== '') {
      stockTagNameArray.push(newTagName);

      $stockTagList.append(createTagLabel(newTagName));
    }
  });

  // 編集したタグを適用する
  selecterIdInfos.$applyTagButton.on('click', function() {
    var info = {
      file_name: editFileName,
      tagNames: []
    };
    $.post('/apply_tag', info)
      .done(function(data) {
      })
      .fail(function(error, errorMessage) {
        alsert(errorMessage);
      });

    selecterIdInfos.$tagEditDialog.modal('hide');
  });

  /**
   * @brief タグのラベルを生成
   *
   * @param tagName タグ名
   *
   * @return タグのラベル
   */
  function createTagLabel(tagName) {
    return $('')
            .addClass('label label-default')
            .draggable({ revert: true })
            .text(tagName);
  }
};

<commit_msg>タグ編集ダイアログ内のタグ構築処理を、setupメソッドに移行<commit_after>/**
 * @brief タグ関連処理
 */
var gspecv = gspecv || {};

/**
 * @brief タグ関連処理のセットアップ
 *
 * @param selecterIdInfos セレクタ用IDの情報
 */
gspecv.setupTag = function(selecterIdInfos) {
  var editFileName;
  var fileTagNameArray = [];
  var stockTagNameArray = [];

  var $tagCreateButton = $(selecterIdInfos.tagCreateButton);
  var $stockTagList = $(selecterIdInfos.stockTagList);

  // ダイアログのセットアップメソッドを設定
  selecterIdInfos.$tagEditDialog.setup = function(fileName, data) {
    editFileName = fileName;

    // タグ情報を構築
    var $fileTagList = $(selecterIdInfos.fileTagList);
    $fileTagList.droppable({
      drop: function(event, ui) {
        var $dropedSelecter = $(ui.draggable[0]);
        $dropedSelecter.remove();
        $fileTagList.append($dropedSelecter);
        $dropedSelecter.draggable({ revert: true });
      }
    });
    fileTagNameArray.forEach(function(tagName) {
      $fileTagList.append(createTagLabel(tagName));
    });

    stockTagNameArray.forEach(function(tagName) {
      $stockTagList.append(createTagLabel(tagName));
    });

    return selecterIdInfos.$tagEditDialog;
  };

  $tagCreateButton.on('click', function() {
    // 新しいタグをリストに追加する
    var newTagName = $(selecterIdInfos.tagCreateNameInput).val();
    if(newTagName !== '') {
      stockTagNameArray.push(newTagName);

      $stockTagList.append(createTagLabel(newTagName));
    }
  });

  // 編集したタグを適用する
  selecterIdInfos.$applyTagButton.on('click', function() {
    var info = {
      file_name: editFileName,
      tagNames: []
    };
    $.post('/apply_tag', info)
      .done(function(data) {
      })
      .fail(function(error, errorMessage) {
        alsert(errorMessage);
      });

    selecterIdInfos.$tagEditDialog.modal('hide');
  });

  /**
   * @brief タグのラベルを生成
   *
   * @param tagName タグ名
   *
   * @return タグのラベル
   */
  function createTagLabel(tagName) {
    return $('')
            .addClass('label label-default')
            .draggable({ revert: true })
            .text(tagName);
  }
};

<|endoftext|>


<commit_before><commit_msg>brapi.js pedigree fix<commit_after><|endoftext|>


<commit_before>var BreathCanvas = function() {};

BreathCanvas.prototype.init = function(color0, color1) {
	var self = this;
	if (!this.initialized) {
		//this.initialized = true;
		this.callbacks = [];
		this.$canvas = $('#breathCanvas');
		this.$controller = $('#breathController');
		this.$instructions = $('#instructions');
		this.hideThumb = true;

		if (this.$canvas.length) {

			var c0 = 'rgb(' + color0[0] + ',' + color0[1] + ',' + color0[2] + ')',
				c1 = 'rgb(' + color1[0] + ',' + color1[1] + ',' + color1[2] + ')';

			this.$controller.css('background', '-webkit-gradient(linear,left top,left bottom,from(' + c0 + '),to(' + c1 + '))');

			this.reset();
			this.showThumb();
			this.started = false;
			var to = null,
				t = new Date().getTime();

			this.$canvas.scroll(function(e) {
				if (self.started) {
					var y = self.$canvas.scrollTop()/self.$canvas.height();
					var t2 = new Date().getTime();
					self.notify(y, t2-t);
					t = t2;
					if (self.initialized && self.hideThumb) self.$controller.addClass('active');
					self.initialized = true;
					clearTimeout(to);
					to = setTimeout(self.showThumb.bind(self), 3000);
				} else {
					e.preventDefault();
					self.$canvas.scrollTop(0);
				}
			});

			this.$instructions.click(function(e) {
				self.hideInstructions();
			});
		}
	}
};

BreathCanvas.prototype.reset = function(callback) {
	var self = this;
	this.started = false;
	this.hideThumb = false;
	this.$canvas.scrollTop(0);
	this.showThumb();
	// this.showInstructions();
	setTimeout(function(){
		self.hideThumb = true;
		if (callback !== undefined) {
			callback();
		}
	},100);
};

BreathCanvas.prototype.start = function() {
	var self = this;
	this.reset(function() {
		self.started = true;
	});
};

BreathCanvas.prototype.stop = function() {
	this.started = false;
};

BreathCanvas.prototype.bind = function(callback) {
	this.callbacks.push(callback);
};

BreathCanvas.prototype.showThumb = function() {
	this.$controller.removeClass('active');
};

BreathCanvas.prototype.showInstructions = function() {
	this.$instructions.show();
}

BreathCanvas.prototype.hideInstructions = function() {
	this.$instructions.hide();
}

BreathCanvas.prototype.notify = function(y) {
	var callbacks = this.callbacks;
	var l = callbacks.length;
	for (var i = 0; i < l; i++) {
		callbacks[i](y);
	}
};<commit_msg>Changed so that the whole circle is visible from start<commit_after>var BreathCanvas = function() {};

BreathCanvas.prototype.init = function(color0, color1) {
	var self = this;
	if (!this.initialized) {
		//this.initialized = true;
		this.callbacks = [];
		this.$canvas = $('#breathCanvas');
		this.$controller = $('#breathController');
		this.$instructions = $('#instructions');
		this.hideThumb = true;

		if (this.$canvas.length) {

			var c0 = 'rgb(' + color0[0] + ',' + color0[1] + ',' + color0[2] + ')',
				c1 = 'rgb(' + color1[0] + ',' + color1[1] + ',' + color1[2] + ')';

			this.$controller.css('background', '-webkit-gradient(linear,left top,left bottom,from(' + c0 + '),to(' + c1 + '))');

			this.reset();
			this.showThumb();
			this.started = false;
			var to = null,
				t = new Date().getTime();

			this.$canvas.scroll(function(e) {
				if (self.started) {
					var y = self.$canvas.scrollTop()/self.$canvas.height();
					var t2 = new Date().getTime();
					self.notify(y, t2-t);
					t = t2;
					if (self.initialized && self.hideThumb) self.$controller.addClass('active');
					self.initialized = true;
					clearTimeout(to);
					to = setTimeout(self.showThumb.bind(self), 3000);
				} else {
					e.preventDefault();
					self.$canvas.scrollTop(50); //Half the circle height
				}
			});

			this.$instructions.click(function(e) {
				self.hideInstructions();
			});
		}
	}
};

BreathCanvas.prototype.reset = function(callback) {
	var self = this;
	this.started = false;
	this.hideThumb = false;
	this.$canvas.scrollTop(50); //Half the circle height
	this.showThumb();
	setTimeout(function(){
		self.hideThumb = true;
		if (callback !== undefined) {
			callback();
		}
	},100);
};

BreathCanvas.prototype.start = function() {
	var self = this;
	this.reset(function() {
		self.started = true;
	});
};

BreathCanvas.prototype.stop = function() {
	this.started = false;
};

BreathCanvas.prototype.bind = function(callback) {
	this.callbacks.push(callback);
};

BreathCanvas.prototype.showThumb = function() {
	this.$controller.removeClass('active');
};

BreathCanvas.prototype.showInstructions = function() {
	this.$instructions.show();
}

BreathCanvas.prototype.hideInstructions = function() {
	this.$instructions.hide();
}

BreathCanvas.prototype.notify = function(y) {
	var callbacks = this.callbacks;
	var l = callbacks.length;
	for (var i = 0; i < l; i++) {
		callbacks[i](y);
	}
};<|endoftext|>


<commit_before>/* globals $ */
(function () {
  'use strict';
  // If browser doesn't support HTML5 History API
  // We just let the anchor tag do its work
  if (!window.history || !window.history.pushState) {
    return;
  }

  // Cache refrence to the content container
  var $formContainer = $('.form-container');

  function attachTabClickHandlers() {

    $('.tab').click(function (event) {
      event.preventDefault();
      var url = event.target.href;

      // Don't bother loading content we already have
      if (url === window.location.href) {
        return;
      }

      $.ajax({
        url: url,
        success: function(htmlData) {
          // Fill our content containe with new stuff
          var content = $('<div>').html(htmlData).find('.form-container').html();
          $formContainer.html(content);
          // Reattach event handlers as we have partially new DOM
          attachTabClickHandlers();
          // Add to history
          window.history.pushState(null, null, url);
        }
      });
    });
  }
  // Kick it all off with initial event handlers
  attachTabClickHandlers();
}());
<commit_msg>Optimizing ajax loading of html<commit_after>/* globals $ */
(function () {
  'use strict';
  // If browser doesn't support HTML5 History API
  // We just let the anchor tag do its work
  if (!window.history || !window.history.pushState) {
    return;
  }

  // Cache refrence to the content container
  var $formContainer = $('.form-container');
  var cachedHtml = {};

  function renderNewHtml (url, htmlData) {
    cachedHtml[url] = htmlData;
    // Fill our content container with new stuff
    var content = $('<div>').html(htmlData).find('.form-container').html();
    $formContainer.html(content);
    // Reattach event handlers as we have partially new DOM
    attachTabClickHandlers();
    // Add to history
    window.history.pushState(null, null, url);
  }

  function attachTabClickHandlers() {

    $('.tab').click(function (event) {
      event.preventDefault();
      var url = event.target.href;

      // Don't bother loading content we already have
      if (url === window.location.href) {
        return;
      }
      // If we've already downloaded this html, use that
      var cachedHtmlData = cachedHtml[url];
      if (cachedHtmlData) {
        return renderNewHtml(url, cachedHtmlData);
      }

      // Otherwise load it from the server
      $.ajax({
        url: url,
        success: renderNewHtml.bind(null, url)
      });

    });
  }
  // Kick it all off with initial event handlers
  attachTabClickHandlers();
}());
<|endoftext|>


<commit_before>import Component from '../../common/Component';
import SubtreeRetainer from '../../common/utils/SubtreeRetainer';
import Dropdown from '../../common/components/Dropdown';
import PostControls from '../utils/PostControls';
import listItems from '../../common/helpers/listItems';
import ItemList from '../../common/utils/ItemList';

/**
 * The `Post` component displays a single post. The basic post template just
 * includes a controls dropdown; subclasses must implement `content` and `attrs`
 * methods.
 *
 * ### Props
 *
 * - `post`
 *
 * @abstract
 */
export default class Post extends Component {
  init() {
    this.loading = false;

    /**
     * Set up a subtree retainer so that the post will not be redrawn
     * unless new data comes in.
     *
     * @type {SubtreeRetainer}
     */
    this.subtree = new SubtreeRetainer(
      () => this.props.post.freshness,
      () => {
        const user = this.props.post.user();
        return user && user.freshness;
      },
      () => this.controlsOpen
    );
  }

  view() {
    const attrs = this.attrs();

    attrs.className = 'Post ' + (this.loading ? 'Post--loading ' : '') + (this.props.post.user() === app.session.user ? 'Post--self' : '') + (attrs.className || '');

    return (

        {this.subtree.retain() || (() => {
          const controls = PostControls.controls(this.props.post, this).toArray();

          return (

              {this.content()}

                  {listItems(this.actionItems().toArray())}
                  {controls.length ?

                    <Dropdown
                      className="Post-controls"
                      buttonClassName="Button Button--icon Button--flat"
                      menuClassName="Dropdown-menu--right"
                      icon="fas fa-ellipsis-h"
                      onshow={() => this.$('.Post-actions').addClass('open')}
                      onhide={() => this.$('.Post-actions').removeClass('open')}>
                      {controls}
                    </Dropdown>

 : ''}

{listItems(this.footerItems().toArray())}

          );
        })()}

    );
  }

  config(isInitialized) {
    const $actions = this.$('.Post-actions');
    const $controls = this.$('.Post-controls');

    $actions.toggleClass('open', $controls.hasClass('open'));
  }

  /**
   * Get attributes for the post element.
   *
   * @return {Object}
   */
  attrs() {
    return {};
  }

  /**
   * Get the post's content.
   *
   * @return {Array}
   */
  content() {
    return [];
  }

  /**
   * Build an item list for the post's actions.
   *
   * @return {ItemList}
   */
  actionItems() {
    return new ItemList();
  }

  /**
   * Build an item list for the post's footer.
   *
   * @return {ItemList}
   */
  footerItems() {
    return new ItemList();
  }
}
<commit_msg>Improved naming of class for post by actor. Made class list for post extensible by using a separate method.<commit_after>import Component from '../../common/Component';
import SubtreeRetainer from '../../common/utils/SubtreeRetainer';
import Dropdown from '../../common/components/Dropdown';
import PostControls from '../utils/PostControls';
import listItems from '../../common/helpers/listItems';
import ItemList from '../../common/utils/ItemList';

/**
 * The `Post` component displays a single post. The basic post template just
 * includes a controls dropdown; subclasses must implement `content` and `attrs`
 * methods.
 *
 * ### Props
 *
 * - `post`
 *
 * @abstract
 */
export default class Post extends Component {
  init() {
    this.loading = false;

    /**
     * Set up a subtree retainer so that the post will not be redrawn
     * unless new data comes in.
     *
     * @type {SubtreeRetainer}
     */
    this.subtree = new SubtreeRetainer(
      () => this.props.post.freshness,
      () => {
        const user = this.props.post.user();
        return user && user.freshness;
      },
      () => this.controlsOpen
    );
  }

  view() {
    const attrs = this.attrs();

    attrs.className = this.classes().join(' ');

    return (

        {this.subtree.retain() || (() => {
          const controls = PostControls.controls(this.props.post, this).toArray();

          return (

              {this.content()}

                  {listItems(this.actionItems().toArray())}
                  {controls.length ?

                    <Dropdown
                      className="Post-controls"
                      buttonClassName="Button Button--icon Button--flat"
                      menuClassName="Dropdown-menu--right"
                      icon="fas fa-ellipsis-h"
                      onshow={() => this.$('.Post-actions').addClass('open')}
                      onhide={() => this.$('.Post-actions').removeClass('open')}>
                      {controls}
                    </Dropdown>

 : ''}

{listItems(this.footerItems().toArray())}

          );
        })()}

    );
  }

  config(isInitialized) {
    const $actions = this.$('.Post-actions');
    const $controls = this.$('.Post-controls');

    $actions.toggleClass('open', $controls.hasClass('open'));
  }

  /**
   * Get attributes for the post element.
   *
   * @return {Object}
   */
  attrs() {
    return {};
  }

  /**
   * Get the post's content.
   *
   * @return {Array}
   */
  content() {
    return [];
  }

  classes() {
    let classes = ['Post'];

    if (this.loading) {
      classes.push('Post--loading');
    }

    if (this.props.post.user() === app.session.user) {
      classes.push('Post--by-actor');
    }

    return classes;
  }

  /**
   * Build an item list for the post's actions.
   *
   * @return {ItemList}
   */
  actionItems() {
    return new ItemList();
  }

  /**
   * Build an item list for the post's footer.
   *
   * @return {ItemList}
   */
  footerItems() {
    return new ItemList();
  }
}
<|endoftext|>


<commit_before>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
	setInterval("updateConversation(), 10000");
})

$( window ).load(function() {
      window.scrollTo(0, $(document).height());
 });
/*
 * Function that is called when the document is ready.
 */
function initializePage() {
	$("#sendmessage").click(sendMessage);
	window.scrollTo(0, $(document).height());
}

function updateConversation() {
	$.get("/queryMessages", displayMessages);

	function displayMessages(result){
		$("#conversationbody").html();
		for(var i=0; i < result.messages.length; i++) {
			var message = result.messages[i].message;
			$("#conversationbody").append(
				'
'+
	  				'' +
	  				'
'
	  					+'
' + message.sender.name+ '
'+
	  					'
' + message.send_time+ '
' +
	  				'
'+
	  				'
' + message.translated+ '
'+
	  			'
'
			);
		}
		window.scrollTo(0, $(document).height());
	//	console.log("HELLO");
	}
}

function sendMessage(e) {
	e.preventDefault();

	ga('send', 'event', 'Send Message in Convo Button', 'click');

	var textbox = $("#messagetext");
	console.log("SEND MESSAGE");
	var params = "recipient=" + "" + "&message="+textbox.val();
	console.log(params);
	$.post("/chat/sendMessage", params, displayResults);
}

function displayResults(result) {
	location.href = "/conversation/"+result["_id"];
}<commit_msg>disabled auto update<commit_after>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
	//setInterval("updateConversation(), 15000");
})

$( window ).load(function() {
      window.scrollTo(0, $(document).height());
 });
/*
 * Function that is called when the document is ready.
 */
function initializePage() {
	$("#sendmessage").click(sendMessage);
	window.scrollTo(0, $(document).height());
}

function updateConversation() {
	$.get("/queryMessages", displayMessages);

	function displayMessages(result){
		$("#conversationbody").html();
		for(var i=0; i < result.messages.length; i++) {
			var message = result.messages[i].message;
			$("#conversationbody").append(
				'
'+
	  				'' +
	  				'
'
	  					+'
' + message.sender.name+ '
'+
	  					'
' + message.send_time+ '
' +
	  				'
'+
	  				'
' + message.translated+ '
'+
	  			'
'
			);
		}
		window.scrollTo(0, $(document).height());
	//	console.log("HELLO");
	}
}

function sendMessage(e) {
	e.preventDefault();

	ga('send', 'event', 'Send Message in Convo Button', 'click');

	var textbox = $("#messagetext");
	console.log("SEND MESSAGE");
	var params = "recipient=" + "" + "&message="+textbox.val();
	console.log(params);
	$.post("/chat/sendMessage", params, displayResults);
}

function displayResults(result) {
	location.href = "/conversation/"+result["_id"];
}<|endoftext|>


<commit_before>/** @jsx React.DOM */

var Leaderboards = React.createClass({
  getInitialState: function() {
    return {leaders: []};
  },

  componentDidMount: function() {
    var xhr = new XMLHttpRequest();
    xhr.open('get', '//' + location.host + "/api/get/leaders", true);
    xhr.onload = function() {
      var data = JSON.parse(xhr.responseText);
      this.setState({ leaders: data });
    }.bind(this);
    xhr.send();
  },

  render: function() {
    if (Array.isArray(this.state.leaders.highest_avg)) {
      this.state.leaders.highest_avg.sort(function(a, b) {
        return parseInt(b.avg) - parseInt(a.avg);
      });

      this.state.leaders.highest_kpm.forEach(function(kpm) {
        kpm.kpm_date = kpm.kpm_date.split("T")[0];
      })
    }

    return (

          <KappaBoard kappas={this.state.leaders.most_kappa}/>
          <KPMBoard kpm={this.state.leaders.highest_kpm}/>
          <AvgBoard avg={this.state.leaders.highest_avg}/>

    );
  }
});

var KappaBoard = React.createClass({
  render: function() {
    var rows = [];
    if (Array.isArray(this.props.kappas)) {
      this.props.kappas.forEach(function(kappa) {
        rows.push(

          {kappa.name}
          {kappa.kappas}
          {kappa.minutes}
);
      });
    }
    return (

Most Kappas

            Streamer
            Kappas
            Minutes

          {rows}

    );
  }
});

var KPMBoard = React.createClass({
  render: function() {
    var rows = [];
    if (Array.isArray(this.props.kpm)) {
      this.props.kpm.forEach(function(kpm) {
        rows.push(

          {kpm.name}
          {kpm.kpm}
          {kpm.kpm_date}
);
      });
    }
    return (

Highest KPM

            Streamer
            KPM
            Date

          {rows}

    );
  }
});

var AvgBoard = React.createClass({
  render: function() {
    var rows = [];
    if (Array.isArray(this.props.avg)) {
      this.props.avg.forEach(function(avg) {
        rows.push(

          {avg.name}
          {avg.avg.toFixed(2)}
);
      });
    }
    return (

Highest Average KPM (>100 Minutes)

            Streamer
            Average

          {rows}

    );
  }
});

React.render(
  <Leaderboards />,
  document.getElementById('content')
);
<commit_msg>fix sort<commit_after>/** @jsx React.DOM */

var Leaderboards = React.createClass({
  getInitialState: function() {
    return {leaders: []};
  },

  componentDidMount: function() {
    var xhr = new XMLHttpRequest();
    xhr.open('get', '//' + location.host + "/api/get/leaders", true);
    xhr.onload = function() {
      var data = JSON.parse(xhr.responseText);
      this.setState({ leaders: data });
    }.bind(this);
    xhr.send();
  },

  render: function() {
    if (Array.isArray(this.state.leaders.highest_avg)) {
      this.state.leaders.highest_avg.sort(function(a, b) {
        return parseFloat(b.avg) - parseFloat(a.avg);
      });

      this.state.leaders.highest_kpm.forEach(function(kpm) {
        kpm.kpm_date = kpm.kpm_date.split("T")[0];
      })
    }

    return (

          <KappaBoard kappas={this.state.leaders.most_kappa}/>
          <KPMBoard kpm={this.state.leaders.highest_kpm}/>
          <AvgBoard avg={this.state.leaders.highest_avg}/>

    );
  }
});

var KappaBoard = React.createClass({
  render: function() {
    var rows = [];
    if (Array.isArray(this.props.kappas)) {
      this.props.kappas.forEach(function(kappa) {
        rows.push(

          {kappa.name}
          {kappa.kappas}
          {kappa.minutes}
);
      });
    }
    return (

Most Kappas

            Streamer
            Kappas
            Minutes

          {rows}

    );
  }
});

var KPMBoard = React.createClass({
  render: function() {
    var rows = [];
    if (Array.isArray(this.props.kpm)) {
      this.props.kpm.forEach(function(kpm) {
        rows.push(

          {kpm.name}
          {kpm.kpm}
          {kpm.kpm_date}
);
      });
    }
    return (

Highest KPM

            Streamer
            KPM
            Date

          {rows}

    );
  }
});

var AvgBoard = React.createClass({
  render: function() {
    var rows = [];
    if (Array.isArray(this.props.avg)) {
      this.props.avg.forEach(function(avg) {
        rows.push(

          {avg.name}
          {avg.avg.toFixed(2)}
);
      });
    }
    return (

Highest Average KPM (>100 Minutes)

            Streamer
            Average

          {rows}

    );
  }
});

React.render(
  <Leaderboards />,
  document.getElementById('content')
);
<|endoftext|>


<commit_before>define(function (require){

	var BaseObject = require('BaseObject'),
		THREE = require('three'),
		utils = require('utils'),
		input = require('input');

	return BaseObject.extend({

		// variables

		renderer: null,
		model: null,

		// functions
		init: function () {
			this.__init();
			input.on("input", this.handleInput);
        },
        handleInput: function (event) {
            if (event.type == "key") {
                var key = utils.getKeyCode(event.code);
                console.log("key " + key + " pressed");
            }
        },
		addToScreen: function () {
			document.body.appendChild( this.renderer.domElement );
		},
		removeFromScreen: function () {
			document.body.removeChild( this.renderer.domElement );
		},
		startRendering: function (levelModel) {
			var scope = this;

			// setup renderer
			if (window.WebGLRenderingContext)
				this.renderer = new THREE.WebGLRenderer();
			else
				this.renderer = new THREE.CanvasRenderer();

			this.renderer.shadowMapEnabled = true;
			this.renderer.shadowMapType = THREE.PCFShadowMap;

			this.renderer.setClearColor(0x00688B, 1);
			this.renderer.setSize( window.innerWidth, window.innerHeight );
			window.onresize = function() {
				scope.renderer.setSize( window.innerWidth, window.innerHeight );
			};

			this.model = levelModel; // set the level to render
		},
		update: function (dt) {
			this.__update(dt);
			if(this.model !== null) {
				this.renderer.render(this.model.scene, this.model.camera);
			}
		},
		dispose: function() {
			this.__dispose();
			this.removeFromScreen();
		}
	});
});<commit_msg>recoded LevelViewport<commit_after>define(function (require){

    var BaseObject = require('BaseObject'),
        THREE = require('three'),
        utils = require('utils'),
        input = require('input');

    return BaseObject.extend({

        // variables

        renderer: null,
        model: null,

        // functions
        init: function () {
            this.__init();
            input.on("input", this.handleInput);
        },
        handleInput: function (event) {
            if (!this.model) return;

            if (event.type == "key") {
                var key = utils.getKeyCode(event.code);
            }
        },
        addToScreen: function () {
            document.body.appendChild( this.renderer.domElement );
        },
        removeFromScreen: function () {
            document.body.removeChild( this.renderer.domElement );
        },
        startRendering: function (levelModel) {
            var scope = this;

            // setup renderer
            if (window.WebGLRenderingContext)
                this.renderer = new THREE.WebGLRenderer();
            else
                this.renderer = new THREE.CanvasRenderer();

            this.renderer.shadowMapEnabled = true;
            this.renderer.shadowMapType = THREE.PCFShadowMap;

            this.renderer.setClearColor(0x00688B, 1);
            this.renderer.setSize( window.innerWidth, window.innerHeight );
            window.onresize = function() {
                scope.renderer.setSize( window.innerWidth, window.innerHeight );
            };

            this.model = levelModel; // set the level to render
        },
        update: function (dt) {
            this.__update(dt);
            this.model && this.renderer.render(this.model.scene, this.model.camera);
        },
        dispose: function() {
            this.__dispose();
            this.removeFromScreen();
        }
    });
});<|endoftext|>


<commit_before>var RCSS = require('rcss');

var listStyle = {
    paddingRight: "20px",
    paddingBottom: "15px",
}

var ghWidgetStyle = {
    color: "#dddddd",
}

var numberStyle = {
    fontSize: "10em",
    textAlign: "center",
    marginTop: "20px",
}

var captionStyle = {
    fontSize: "1.8em",
    textAlign: "center",
    marginTop: "10px",
}

var titleStyle = {
    fontSize: "1.8em",
    marginTop: "10px",
    marginBottom: "10px",
    marginLeft: "10px",
    marginRight: "10px",
    fontWeight: "bold",
}

var changelistStyle = {
    fontSize: "0.8em",
    paddingTop: "10px",
    paddingLeft: "20px"
}

module.exports = {
    listStyleClass: RCSS.registerClass(listStyle),
    ghStyleClass: RCSS.registerClass(ghWidgetStyle),
    numberClass: RCSS.registerClass(numberStyle),
    captionClass: RCSS.registerClass(captionStyle),
    titleClass: RCSS.registerClass(titleStyle),
    changelistClass: RCSS.registerClass(changelistStyle),
}<commit_msg>more color tweaks<commit_after>var RCSS = require('rcss');
var colors = require('./ka-colors.js');

var listStyle = {
    paddingRight: "20px",
    paddingBottom: "15px",
}

var ghWidgetStyle = {
    color: colors.grayExtraLight,
}

var numberStyle = {
    fontSize: "10em",
    textAlign: "center",
    marginTop: "20px",
}

var captionStyle = {
    fontSize: "1.8em",
    textAlign: "center",
    marginTop: "10px",
}

var titleStyle = {
    fontSize: "1.8em",
    marginTop: "10px",
    marginBottom: "10px",
    marginLeft: "10px",
    marginRight: "10px",
    fontWeight: "bold",
}

var changelistStyle = {
    fontSize: "0.8em",
    paddingTop: "10px",
    paddingLeft: "20px"
}

module.exports = {
    listStyleClass: RCSS.registerClass(listStyle),
    ghStyleClass: RCSS.registerClass(ghWidgetStyle),
    numberClass: RCSS.registerClass(numberStyle),
    captionClass: RCSS.registerClass(captionStyle),
    titleClass: RCSS.registerClass(titleStyle),
    changelistClass: RCSS.registerClass(changelistStyle),
}<|endoftext|>


<commit_before>/*global define,dojo */
/*jslint browser:true,sloppy:true,nomen:true,unparam:true,plusplus:true */
/*
 | Copyright 2014 Esri
 |
 | 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.
 */
define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/_base/array',
    "dojo/dom-attr",
    'dojo/dom-construct',
    'dojo/dom-style',
    'dojo/dom-class',
    'dojo/on',
    'dojo/topic',
    'dojo/NodeList-dom',

    'application/lib/SvgHelper',

    'dijit/_WidgetBase',
    'dijit/_TemplatedMixin',

    'dojo/text!./ItemListView.html'
], function (declare, lang, array, domAttr, domConstruct, domStyle, domClass, on, topic, nld,
    SvgHelper,
    _WidgetBase, _TemplatedMixin,
    template) {

    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: template,
        votesField: null,

        /**
         * Widget post-create, called automatically in widget creation
         * life cycle, after constructor. Sets class variables.
         */
        postCreate: function () {
            var linkActionBox;

            this.inherited(arguments);
            this.i18n = this.appConfig.i18n.item_list;
            this.hide();

            // Create the checkbox for linking the item list to the map extents
            linkActionBox = domConstruct.create("input", { "type": "checkbox", "class": "esriCTChkBox", id: "linkToMap", "value": "linkToMap" }, this.itemListActionBar);
            domConstruct.create("label", { "class": "css-label", "for": "linkToMap", innerHTML: this.i18n.linkToMapViewOptionLabel }, this.itemListActionBar);
            this.own(on(linkActionBox, "change", lang.hitch(this, function () {
                topic.publish("linkToMapViewChanged", linkActionBox.checked);
            })));
            linkActionBox.checked = this.linkToMapView;
        },

        /**
         * Shows the widget with a simple display: ''
         */
        show: function () {
            domStyle.set(this.domNode, 'display', '');
        },

        /**
         * Hides the widget with a simple display: 'none'
         */
        hide: function () {
            domStyle.set(this.domNode, 'display', 'none');
        },

        /**
         * Sets the fields that are needed to display feature information in this list (number of votes).
         * Needs to be called before first setItems to tell the widget which fields to look for.
         * @param {string} votesField Name of votes property
         */
        setFields: function (votesField) {
            this.votesField = votesField;
        },

        /**
         * Sets the items to be displayed in the items list, and then builds the list.
         * @param {array} items feature collection or array
         */
        setItems: function (items) {
            this.items = items;
            this.clearList();
            this.buildList();
        },

        /**
         * Sets the OID of the item to be considered the current selection.
         * @param {OID} itemOID
         */
        setSelection: function (itemOID) {
            this.selectedItemOID = itemOID;
        },

        /**
         * Clears the items list
         */
        clearList: function () {
            domConstruct.empty(this.list);
        },

        /**
         * Builds the items list
         */
        buildList: function () {
            array.forEach(this.items, lang.hitch(this, this.buildItemSummary));
        },

        /**
         * Builds an individual item summary given an item.
         * @param  {feature} item to display in the list
         */
        buildItemSummary: function (item) {

            var itemTitle, itemVotes, itemSummaryDiv, itemTitleDiv, favDiv, iconDiv, likeIconSurf;

            itemTitle = this.getItemTitle(item) || " ";

            itemVotes = this.getItemVotes(item);

            itemSummaryDiv = domConstruct.create('div', {
                'class': 'itemSummary',
                'click': lang.partial(this.summaryClick, this, item)
            }, this.list);

            itemTitleDiv = domConstruct.create('div', {
                'class': 'itemTitle',
                'innerHTML': itemTitle
            }, itemSummaryDiv);

            // If we're displaying votes, create the count and icon displays
            if (itemVotes) {
                if (itemVotes.needSpace) {
                    domClass.add(itemTitleDiv, "itemListTitleOverride");
                }

                favDiv = domConstruct.create('div', {
                    'class': 'itemFav',
                    'title': this.i18n.likesForThisItemTooltip
                }, itemSummaryDiv);

                domConstruct.create('div', {
                    'class': 'itemVotes',
                    'innerHTML': itemVotes.label
                }, favDiv);

                iconDiv = domConstruct.create('div', {
                    'class': 'fav'
                }, favDiv);

                likeIconSurf = SvgHelper.createSVGItem(this.appConfig.likeIcon, iconDiv, 12, 12);
            }

            // If this item's OID matches the current selection, apply the theme to highlight it
            if (this.selectedItemOID === item.attributes[item._layer.objectIdField]) {
                domClass.add(itemSummaryDiv, "appTheme");
                if (favDiv) {
                    domClass.add(favDiv, "appThemeAccentText");
                    SvgHelper.changeColor(likeIconSurf, this.appConfig.theme.accentText);
                }
            }
        },

        /**
         * Gets title of feature for list display
         * @param  {feature} item The feature for which to get the title
         * @return {string} The title of the feature
         */
        getItemTitle: function (item) {
            return item.getTitle ? item.getTitle() : "";
        },

        /**
         * Gets the number of votes for an item
         * @param  {feature} item The feature for which to get the vote count
         * @return {null|object} Object containing "label" with vote count for the item in a shortened form
         * (num if <1000, floor(count/1000)+"k" if <1M, floor(count/1000000)+"M" otherwise) and "needSpace"
         * that's indicates if an extra digit of room is needed to handle numbers between 99K and 1M, exclusive;
         * returns null if the feature layer's votes field is unknown
         */
        getItemVotes: function (item) {
            var needSpace = false, votes;

            if (this.votesField) {
                votes = item.attributes[this.votesField] || 0;
                if (votes > 999) {
                    if (votes > 99999) {
                        needSpace = true;
                    }
                    if (votes > 999999) {
                        votes = Math.floor(votes / 1000000) + "M";
                    } else {
                        votes = Math.floor(votes / 1000) + "k";
                    }
                }
                return {
                    "label": votes,
                    "needSpace": needSpace
                };
            }
            return null;
        },

        /**
         * Called on an item summary click
         * @param  {context} self The widget itself, since 'this' is the row that was clicked
         * @param  {feature} feat The feature that corresponds to the row that was clicked
         * @param  {mouse event} evt  The click event on the row that was clicked
         */
        summaryClick: function (self, feat, evt) {
            // 'this' = row click
            topic.publish('itemSelected', feat);
        }

    });
});
<commit_msg>Added 'title' attribute echoing the item's title in case it is cut off in the item list display<commit_after>/*global define,dojo */
/*jslint browser:true,sloppy:true,nomen:true,unparam:true,plusplus:true */
/*
 | Copyright 2014 Esri
 |
 | 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.
 */
define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/_base/array',
    "dojo/dom-attr",
    'dojo/dom-construct',
    'dojo/dom-style',
    'dojo/dom-class',
    'dojo/on',
    'dojo/topic',
    'dojo/NodeList-dom',

    'application/lib/SvgHelper',

    'dijit/_WidgetBase',
    'dijit/_TemplatedMixin',

    'dojo/text!./ItemListView.html'
], function (declare, lang, array, domAttr, domConstruct, domStyle, domClass, on, topic, nld,
    SvgHelper,
    _WidgetBase, _TemplatedMixin,
    template) {

    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: template,
        votesField: null,

        /**
         * Widget post-create, called automatically in widget creation
         * life cycle, after constructor. Sets class variables.
         */
        postCreate: function () {
            var linkActionBox;

            this.inherited(arguments);
            this.i18n = this.appConfig.i18n.item_list;
            this.hide();

            // Create the checkbox for linking the item list to the map extents
            linkActionBox = domConstruct.create("input", { "type": "checkbox", "class": "esriCTChkBox", id: "linkToMap", "value": "linkToMap" }, this.itemListActionBar);
            domConstruct.create("label", { "class": "css-label", "for": "linkToMap", innerHTML: this.i18n.linkToMapViewOptionLabel }, this.itemListActionBar);
            this.own(on(linkActionBox, "change", lang.hitch(this, function () {
                topic.publish("linkToMapViewChanged", linkActionBox.checked);
            })));
            linkActionBox.checked = this.linkToMapView;
        },

        /**
         * Shows the widget with a simple display: ''
         */
        show: function () {
            domStyle.set(this.domNode, 'display', '');
        },

        /**
         * Hides the widget with a simple display: 'none'
         */
        hide: function () {
            domStyle.set(this.domNode, 'display', 'none');
        },

        /**
         * Sets the fields that are needed to display feature information in this list (number of votes).
         * Needs to be called before first setItems to tell the widget which fields to look for.
         * @param {string} votesField Name of votes property
         */
        setFields: function (votesField) {
            this.votesField = votesField;
        },

        /**
         * Sets the items to be displayed in the items list, and then builds the list.
         * @param {array} items feature collection or array
         */
        setItems: function (items) {
            this.items = items;
            this.clearList();
            this.buildList();
        },

        /**
         * Sets the OID of the item to be considered the current selection.
         * @param {OID} itemOID
         */
        setSelection: function (itemOID) {
            this.selectedItemOID = itemOID;
        },

        /**
         * Clears the items list
         */
        clearList: function () {
            domConstruct.empty(this.list);
        },

        /**
         * Builds the items list
         */
        buildList: function () {
            array.forEach(this.items, lang.hitch(this, this.buildItemSummary));
        },

        /**
         * Builds an individual item summary given an item.
         * @param  {feature} item to display in the list
         */
        buildItemSummary: function (item) {

            var itemTitle, itemVotes, itemSummaryDiv, itemTitleDiv, favDiv, iconDiv, likeIconSurf;

            itemTitle = this.getItemTitle(item) || " ";

            itemVotes = this.getItemVotes(item);

            itemSummaryDiv = domConstruct.create('div', {
                'class': 'itemSummary',
                'click': lang.partial(this.summaryClick, this, item)
            }, this.list);

            itemTitleDiv = domConstruct.create('div', {
                'class': 'itemTitle',
                'title': itemTitle,
                'innerHTML': itemTitle
            }, itemSummaryDiv);

            // If we're displaying votes, create the count and icon displays
            if (itemVotes) {
                if (itemVotes.needSpace) {
                    domClass.add(itemTitleDiv, "itemListTitleOverride");
                }

                favDiv = domConstruct.create('div', {
                    'class': 'itemFav',
                    'title': this.i18n.likesForThisItemTooltip
                }, itemSummaryDiv);

                domConstruct.create('div', {
                    'class': 'itemVotes',
                    'innerHTML': itemVotes.label
                }, favDiv);

                iconDiv = domConstruct.create('div', {
                    'class': 'fav'
                }, favDiv);

                likeIconSurf = SvgHelper.createSVGItem(this.appConfig.likeIcon, iconDiv, 12, 12);
            }

            // If this item's OID matches the current selection, apply the theme to highlight it
            if (this.selectedItemOID === item.attributes[item._layer.objectIdField]) {
                domClass.add(itemSummaryDiv, "appTheme");
                if (favDiv) {
                    domClass.add(favDiv, "appThemeAccentText");
                    SvgHelper.changeColor(likeIconSurf, this.appConfig.theme.accentText);
                }
            }
        },

        /**
         * Gets title of feature for list display
         * @param  {feature} item The feature for which to get the title
         * @return {string} The title of the feature
         */
        getItemTitle: function (item) {
            return item.getTitle ? item.getTitle() : "";
        },

        /**
         * Gets the number of votes for an item
         * @param  {feature} item The feature for which to get the vote count
         * @return {null|object} Object containing "label" with vote count for the item in a shortened form
         * (num if <1000, floor(count/1000)+"k" if <1M, floor(count/1000000)+"M" otherwise) and "needSpace"
         * that's indicates if an extra digit of room is needed to handle numbers between 99K and 1M, exclusive;
         * returns null if the feature layer's votes field is unknown
         */
        getItemVotes: function (item) {
            var needSpace = false, votes;

            if (this.votesField) {
                votes = item.attributes[this.votesField] || 0;
                if (votes > 999) {
                    if (votes > 99999) {
                        needSpace = true;
                    }
                    if (votes > 999999) {
                        votes = Math.floor(votes / 1000000) + "M";
                    } else {
                        votes = Math.floor(votes / 1000) + "k";
                    }
                }
                return {
                    "label": votes,
                    "needSpace": needSpace
                };
            }
            return null;
        },

        /**
         * Called on an item summary click
         * @param  {context} self The widget itself, since 'this' is the row that was clicked
         * @param  {feature} feat The feature that corresponds to the row that was clicked
         * @param  {mouse event} evt  The click event on the row that was clicked
         */
        summaryClick: function (self, feat, evt) {
            // 'this' = row click
            topic.publish('itemSelected', feat);
        }

    });
});
<|endoftext|>


<commit_before>import Vue from 'vue'
import Hello from '@/components/Hello'

describe('Hello.vue', () => {
  it('should render correct contents', () => {
    const Constructor = Vue.extend(Hello)
    const vm = new Constructor().$mount()
    expect(vm.$el.querySelector('.hello h1').textContent)
      .to.equal('Welcome to Your Vue.js App')
  })
})
<commit_msg>[#2] update unit test<commit_after>import Vue from 'vue'
import Hello from '@/components/Hello'

describe('Hello.vue', () => {
  it('should render correct contents', () => {
    const Constructor = Vue.extend(Hello)
    const vm = new Constructor().$mount()
    expect(vm.$el.querySelector('.welcome h1').textContent)
      .to.equal('Welcome to JusticeAI! To get started, please tell us what you’d like to do.')
  })
})
<|endoftext|>


<commit_before>'use strict'

import './sidelist.less'

const React = require('react')
const Colors = require('material-ui/styles/colors')
const MailboxList = require('./MailboxList')
const SidelistAddMailbox = require('./SidelistAddMailbox')
const SidelistSettings = require('./SidelistSettings')

module.exports = React.createClass({
  displayName: 'Sidelist',

  /* **************************************************************************/
  // Data lifecyle
  /* **************************************************************************/

  shouldComponentUpdate (nextProps, nextState) {
    return false
  },

  /* **************************************************************************/
  // Rendering
  /* **************************************************************************/

  /**
  * Renders the app
  */
  render () {
    const { style, className, ...passProps } = this.props
    return (
      <div
        {...passProps}
        style={Object.assign({
          backgroundColor: Colors.blueGrey900,
          position: 'absolute',
          top: 0,
          bottom: 0,
          left: 0,
          right: 0
        }, style)}>
        <MailboxList />
        <SidelistAddMailbox />
        <SidelistSettings />
      </div>
    )
  }
})
<commit_msg>Removed unused var<commit_after>'use strict'

import './sidelist.less'

const React = require('react')
const Colors = require('material-ui/styles/colors')
const MailboxList = require('./MailboxList')
const SidelistAddMailbox = require('./SidelistAddMailbox')
const SidelistSettings = require('./SidelistSettings')

module.exports = React.createClass({
  displayName: 'Sidelist',

  /* **************************************************************************/
  // Data lifecyle
  /* **************************************************************************/

  shouldComponentUpdate (nextProps, nextState) {
    return false
  },

  /* **************************************************************************/
  // Rendering
  /* **************************************************************************/

  /**
  * Renders the app
  */
  render () {
    const { style, ...passProps } = this.props
    return (
      <div
        {...passProps}
        style={Object.assign({
          backgroundColor: Colors.blueGrey900,
          position: 'absolute',
          top: 0,
          bottom: 0,
          left: 0,
          right: 0
        }, style)}>
        <MailboxList />
        <SidelistAddMailbox />
        <SidelistSettings />
      </div>
    )
  }
})
<|endoftext|>


<commit_before>
var pdbs = [ "1stm", "3nap", "1sid", "2ft1", "4cwu" ];
var colors = [ "red", "yellow", "green", "lightblue", "violet" ];

Promise.all( pdbs.map( function( id ){
    return stage.loadFile( "rcsb://" + id )
} ) ).then( function( ol ){

    ol.map( function( o, i ){
        var s = o.structure;
        var bu1 = s.biomolDict.BU1;
        o.setPosition( bu1.getCenter( s ).negate() );
        o.addRepresentation( "surface", {
            sele: "polymer",
            assembly: "BU1",
            color: colors[ i ],
            scaleFactor: 0.10,
            surfaceType: "sas"
        } );
    } );

    stage.tasks.onZeroOnce( function(){ stage.autoView(); } );
    stage.setParameters( { clipNear: 50 } );

} );
<commit_msg>added near-clip slider to virus example<commit_after>
function addElement( el ){
    Object.assign( el.style, {
        position: "absolute",
        zIndex: 10
    } );
    stage.viewer.container.appendChild( el );
}

function createElement( name, properties, style ){
    var el = document.createElement( name );
    Object.assign( el, properties );
    Object.assign( el.style, style );
    return el;
}

var pdbs = [ "1stm", "3nap", "1sid", "2ft1", "4cwu" ];
var colors = [ "red", "yellow", "green", "lightblue", "violet" ];

Promise.all( pdbs.map( function( id ){
    return stage.loadFile( "rcsb://" + id )
} ) ).then( function( ol ){

    ol.map( function( o, i ){
        var s = o.structure;
        var bu1 = s.biomolDict.BU1;
        o.setPosition( bu1.getCenter( s ).negate() );
        o.addRepresentation( "surface", {
            sele: "polymer",
            assembly: "BU1",
            color: colors[ i ],
            scaleFactor: 0.10,
            surfaceType: "sas"
        } );
    } );

    stage.tasks.onZeroOnce( function(){ stage.autoView(); } );
    stage.setParameters( { clipNear: 50 } );

    var clipRange = createElement( "input", {
      type: "range",
      value: 50,
      min: 0,
      max: 100,
      step: 1
    }, { top: "1em", left: "1em" } );

    clipRange.oninput = function( e ){
        stage.setParameters( { clipNear: e.target.value } );
    };

    addElement( clipRange );

} );
<|endoftext|>


<commit_before>var app = angular.module('purkinje', ['ui.bootstrap', 'tc.chartjs']);
//var app = angular.module('purkinje', []);

app.config(['$interpolateProvider',
    function($interpolateProvider) {
        $interpolateProvider.startSymbol('{[');
        $interpolateProvider.endSymbol(']}');
    }
]);

/**
 * Timing of $digest
 * see http://www.rosher.co.uk/post/Angular-Tips-Measuring-Rendering-Performance
 */
angular.module('purkinje').run(['$rootScope',
    function($rootScope) {
        var $oldDigest = $rootScope.$digest;
        var $newDigest = function() {
            console.time("$digest");
            $oldDigest.apply($rootScope);
            console.timeEnd("$digest");
        }
        $rootScope.$digest = $newDigest;
    }
]);
<commit_msg>Angular run block injection to facilitate testing<commit_after>var app = angular.module('purkinje', ['ui.bootstrap', 'tc.chartjs']);
//var app = angular.module('purkinje', []);

app.config(['$interpolateProvider',
    function($interpolateProvider) {
        $interpolateProvider.startSymbol('{[');
        $interpolateProvider.endSymbol(']}');
    }
]);

/**
 * Timing of $digest
 * see http://www.rosher.co.uk/post/Angular-Tips-Measuring-Rendering-Performance
 */
angular.module('purkinje').run(wrapTimerWithDigest);

wrapTimerWithDigest.$inject = ['$rootScope'];

// Run block injection to facilitate testing
// see https://github.com/johnpapa/angularjs-styleguide
function wrapTimerWithDigest($rootScope) {
    var $oldDigest = $rootScope.$digest;
    var $newDigest = function() {
        console.time("$digest");
        $oldDigest.apply($rootScope);
        console.timeEnd("$digest");
    }
    $rootScope.$digest = $newDigest;
}
<|endoftext|>


<commit_before>import values from 'lodash/values';
import appFirebase from '../services/appFirebase';

class FirebasePersistor {
  constructor(user) {
    this.user = user;
    this.firebase = appFirebase.child(`workspaces/${user.id}`);
  }

  getCurrentProjectKey() {
    return new Promise((resolve) => {
      this.firebase.child('currentProjectKey').once('value', (snapshot) => {
        resolve(snapshot.val());
      });
    });
  }

  setCurrentProjectKey(projectKey) {
    return new Promise((resolve, reject) => {
      this.firebase.child('currentProjectKey').set(projectKey, (error) => {
        if (error) {
          reject(error);
        } else {
          resolve();
        }
      });
    });
  }

  all() {
    return new Promise((resolve) => {
      this.firebase.child('projects').once('value', (projects) => {
        resolve(values(projects.val() || {}));
      });
    });
  }

  load(projectKey) {
    return new Promise((resolve) => {
      this.firebase.child('projects').child(projectKey).
        once('value', (snapshot) => {
          resolve(snapshot.val());
        });
    });
  }

  save(project) {
    return new Promise((resolve, reject) => {
      this.firebase.child('projects').child(project.projectKey).
        setWithPriority(project, -Date.now(), (error) => {
          if (error) {
            reject(error);
          } else {
            resolve();
          }
        });
    });
  }

  saveCurrentProject(project) {
    this.save(project);
    this.setCurrentProjectKey(project.projectKey);
  }
}

export default FirebasePersistor;
<commit_msg>Return a Promise from FirebasePersistor#saveCurrentProject<commit_after>import values from 'lodash/values';
import appFirebase from '../services/appFirebase';

class FirebasePersistor {
  constructor(user) {
    this.user = user;
    this.firebase = appFirebase.child(`workspaces/${user.id}`);
  }

  getCurrentProjectKey() {
    return new Promise((resolve) => {
      this.firebase.child('currentProjectKey').once('value', (snapshot) => {
        resolve(snapshot.val());
      });
    });
  }

  setCurrentProjectKey(projectKey) {
    return new Promise((resolve, reject) => {
      this.firebase.child('currentProjectKey').set(projectKey, (error) => {
        if (error) {
          reject(error);
        } else {
          resolve();
        }
      });
    });
  }

  all() {
    return new Promise((resolve) => {
      this.firebase.child('projects').once('value', (projects) => {
        resolve(values(projects.val() || {}));
      });
    });
  }

  load(projectKey) {
    return new Promise((resolve) => {
      this.firebase.child('projects').child(projectKey).
        once('value', (snapshot) => {
          resolve(snapshot.val());
        });
    });
  }

  save(project) {
    return new Promise((resolve, reject) => {
      this.firebase.child('projects').child(project.projectKey).
        setWithPriority(project, -Date.now(), (error) => {
          if (error) {
            reject(error);
          } else {
            resolve();
          }
        });
    });
  }

  saveCurrentProject(project) {
    return Promise.all([
      this.save(project),
      this.setCurrentProjectKey(project.projectKey),
    ]);
  }
}

export default FirebasePersistor;
<|endoftext|>


<commit_before>const BrowserView = electron.BrowserView

var viewMap = {} // id: view
var viewStateMap = {} // id: view state
var selectedView = null

var temporaryPopupViews = {} // id: view

const defaultViewWebPreferences = {
  nodeIntegration: false,
  nodeIntegrationInSubFrames: true,
  scrollBounce: true,
  safeDialogs: true,
  safeDialogsMessage: 'Prevent this page from creating additional dialogs',
  preload: __dirname + '/dist/preload.js',
  contextIsolation: true,
  sandbox: true,
  enableRemoteModule: false,
  allowPopups: false,
  // partition: partition || 'persist:webcontent',
  enableWebSQL: false,
  autoplayPolicy: (settings.get('enableAutoplay') ? 'no-user-gesture-required' : 'user-gesture-required'),
  // match Chrome's default for anti-fingerprinting purposes (Electron defaults to 0)
  minimumFontSize: 6
}

function createView (existingViewId, id, webPreferencesString, boundsString, events) {
  viewStateMap[id] = { loadedInitialURL: false }

  let view
  if (existingViewId) {
    view = temporaryPopupViews[existingViewId]
    delete temporaryPopupViews[existingViewId]

    // the initial URL has already been loaded, so set the background color
    view.setBackgroundColor('#fff')
    viewStateMap[id].loadedInitialURL = true
  } else {
    view = new BrowserView({ webPreferences: Object.assign({}, defaultViewWebPreferences, JSON.parse(webPreferencesString)) })
  }

  events.forEach(function (event) {
    view.webContents.on(event, function (e) {
      var args = Array.prototype.slice.call(arguments).slice(1)

      mainWindow.webContents.send('view-event', {
        viewId: id,
        event: event,
        args: args
      })
    })
  })

  view.webContents.on('select-bluetooth-device', function (event, deviceList, callback) {
    event.preventDefault()
    callback('')
  })

  view.webContents.setWindowOpenHandler(function (details) {
    /*
      Opening a popup with window.open() generally requires features to be set
      So if there are no features, the event is most likely from clicking on a link, which should open a new tab.
      Clicking a link can still have a "new-window" or "foreground-tab" disposition depending on which keys are pressed
      when it is clicked.
      (https://github.com/minbrowser/min/issues/1835)
    */
    if (!details.features) {
      mainWindow.webContents.send('view-event', {
        viewId: id,
        event: 'new-tab',
        args: [details.url, !(details.disposition === 'background-tab')]
      })
      return {
        action: 'deny'
      }
    }

    return {
      action: 'allow'
    }
  })

  view.webContents.removeAllListeners('-add-new-contents')

  view.webContents.on('-add-new-contents', function (e, webContents, disposition, _userGesture, _left, _top, _width, _height, url, frameName, referrer, rawFeatures, postData) {
    if (!filterPopups(url)) {
      return
    }

    var view = new BrowserView({ webPreferences: defaultViewWebPreferences, webContents: webContents })

    var popupId = Math.random().toString()
    temporaryPopupViews[popupId] = view

    mainWindow.webContents.send('view-event', {
      viewId: id,
      event: 'did-create-popup',
      args: [popupId, url]
    })
  })

  view.webContents.on('ipc-message', function (e, channel, data) {
    mainWindow.webContents.send('view-ipc', {
      id: id,
      name: channel,
      data: data,
      frameId: e.frameId
    })
  })

  // Open a login prompt when site asks for http authentication
  view.webContents.on('login', (event, authenticationResponseDetails, authInfo, callback) => {
    if (authInfo.scheme !== 'basic') { // Only for basic auth
      return
    }
    event.preventDefault()
    var title = l('loginPromptTitle').replace('%h', authInfo.host).replace('%r', authInfo.realm)
    createPrompt({
      text: title,
      values: [{ placeholder: l('username'), id: 'username', type: 'text' },
        { placeholder: l('password'), id: 'password', type: 'password' }],
      ok: l('dialogConfirmButton'),
      cancel: l('dialogSkipButton'),
      width: 400,
      height: 200
    }, function (result) {
      // resend request with auth credentials
      callback(result.username, result.password)
    })
  })

  // show an "open in app" prompt for external protocols

  function handleExternalProtocol (e, url, isInPlace, isMainFrame, frameProcessId, frameRoutingId) {
    var knownProtocols = ['http', 'https', 'file', 'min', 'about', 'data', 'javascript', 'chrome'] // TODO anything else?
    if (!knownProtocols.includes(url.split(':')[0])) {
      var externalApp = app.getApplicationNameForProtocol(url)
      if (externalApp) {
        // TODO find a better way to do this
        // (the reason to use executeJS instead of the Electron dialog API is so we get the "prevent this page from creating additional dialogs" checkbox)
        var sanitizedName = externalApp.replace(/[^a-zA-Z0-9.]/g, '')
        if (view.webContents.getURL()) {
          view.webContents.executeJavaScript('confirm("' + l('openExternalApp').replace('%s', sanitizedName) + '")').then(function (result) {
            if (result === true) {
              electron.shell.openExternal(url)
            }
          })
        } else {
          // the code above tries to show the dialog in a browserview, but if the view has no URL, this won't work.
          // so show the dialog globally as a fallback
          var result = electron.dialog.showMessageBoxSync({
            type: 'question',
            buttons: ['OK', 'Cancel'],
            message: l('openExternalApp').replace('%s', sanitizedName).replace(/\\/g, '')
          })

          if (result === 0) {
            electron.shell.openExternal(url)
          }
        }
      }
    }
  }

  view.webContents.on('did-start-navigation', handleExternalProtocol)
  /*
  It's possible for an HTTP request to redirect to an external app link
  (primary use case for this is OAuth from desktop app > browser > back to app)
  and did-start-navigation isn't (always?) emitted for redirects, so we need this handler as well
  */
  view.webContents.on('will-redirect', handleExternalProtocol)

  view.setBounds(JSON.parse(boundsString))

  viewMap[id] = view

  return view
}

function destroyView (id) {
  if (!viewMap[id]) {
    return
  }

  if (viewMap[id] === mainWindow.getBrowserView()) {
    mainWindow.setBrowserView(null)
    selectedView = null
  }
  viewMap[id].webContents.destroy()

  delete viewMap[id]
  delete viewStateMap[id]
}

function destroyAllViews () {
  for (const id in viewMap) {
    destroyView(id)
  }
}

function setView (id) {
  if (viewStateMap[id].loadedInitialURL) {
    mainWindow.setBrowserView(viewMap[id])
  } else {
    mainWindow.setBrowserView(null)
  }
  selectedView = id
}

function setBounds (id, bounds) {
  if (viewMap[id]) {
    viewMap[id].setBounds(bounds)
  }
}

function focusView (id) {
  // empty views can't be focused because they won't propogate keyboard events correctly, see https://github.com/minbrowser/min/issues/616
  // also, make sure the view exists, since it might not if the app is shutting down
  if (viewMap[id] && (viewMap[id].webContents.getURL() !== '' || viewMap[id].webContents.isLoading())) {
    viewMap[id].webContents.focus()
  } else if (mainWindow) {
    mainWindow.webContents.focus()
  }
}

function hideCurrentView () {
  mainWindow.setBrowserView(null)
  selectedView = null
  mainWindow.webContents.focus()
}

function getView (id) {
  return viewMap[id]
}

function getViewIDFromWebContents (contents) {
  for (var id in viewMap) {
    if (viewMap[id].webContents === contents) {
      return id
    }
  }
}

ipc.on('createView', function (e, args) {
  createView(args.existingViewId, args.id, args.webPreferencesString, args.boundsString, args.events)
})

ipc.on('destroyView', function (e, id) {
  destroyView(id)
})

ipc.on('destroyAllViews', function () {
  destroyAllViews()
})

ipc.on('setView', function (e, args) {
  setView(args.id)
  setBounds(args.id, args.bounds)
  if (args.focus) {
    focusView(args.id)
  }
})

ipc.on('setBounds', function (e, args) {
  setBounds(args.id, args.bounds)
})

ipc.on('focusView', function (e, id) {
  focusView(id)
})

ipc.on('hideCurrentView', function (e) {
  hideCurrentView()
})

ipc.on('loadURLInView', function (e, args) {
  // wait until the first URL is loaded to set the background color so that new tabs can use a custom background
  if (!viewStateMap[args.id].loadedInitialURL) {
    viewMap[args.id].setBackgroundColor('#fff')
    // If the view has no URL, it won't be attached yet
    if (args.id === selectedView) {
      mainWindow.setBrowserView(viewMap[args.id])
    }
  }
  viewMap[args.id].webContents.loadURL(args.url)
  viewStateMap[args.id].loadedInitialURL = true
})

ipc.on('callViewMethod', function (e, data) {
  var error, result
  try {
    var webContents = viewMap[data.id].webContents
    var methodOrProp = webContents[data.method]
    if (methodOrProp instanceof Function) {
      // call function
      result = methodOrProp.apply(webContents, data.args)
    } else {
      // set property
      if (data.args && data.args.length > 0) {
        webContents[data.method] = data.args[0]
      }
      // read property
      result = methodOrProp
    }
  } catch (e) {
    error = e
  }
  if (result instanceof Promise) {
    result.then(function (result) {
      if (data.callId) {
        mainWindow.webContents.send('async-call-result', { callId: data.callId, error: null, result })
      }
    })
    result.catch(function (error) {
      if (data.callId) {
        mainWindow.webContents.send('async-call-result', { callId: data.callId, error, result: null })
      }
    })
  } else if (data.callId) {
    mainWindow.webContents.send('async-call-result', { callId: data.callId, error, result })
  }
})

ipc.on('getCapture', function (e, data) {
  var view = viewMap[data.id]
  if (!view) {
    // view could have been destroyed
    return
  }

  view.webContents.capturePage().then(function (img) {
    var size = img.getSize()
    if (size.width === 0 && size.height === 0) {
      return
    }
    img = img.resize({ width: data.width, height: data.height })
    mainWindow.webContents.send('captureData', { id: data.id, url: img.toDataURL() })
  })
})

ipc.on('saveViewCapture', function (e, data) {
  var view = viewMap[data.id]
  if (!view) {
    // view could have been destroyed
  }

  view.webContents.capturePage().then(function (image) {
    view.webContents.downloadURL(image.toDataURL())
  })
})

global.getView = getView
<commit_msg>fix flickering when pressing esc<commit_after>const BrowserView = electron.BrowserView

var viewMap = {} // id: view
var viewStateMap = {} // id: view state
var selectedView = null

var temporaryPopupViews = {} // id: view

const defaultViewWebPreferences = {
  nodeIntegration: false,
  nodeIntegrationInSubFrames: true,
  scrollBounce: true,
  safeDialogs: true,
  safeDialogsMessage: 'Prevent this page from creating additional dialogs',
  preload: __dirname + '/dist/preload.js',
  contextIsolation: true,
  sandbox: true,
  enableRemoteModule: false,
  allowPopups: false,
  // partition: partition || 'persist:webcontent',
  enableWebSQL: false,
  autoplayPolicy: (settings.get('enableAutoplay') ? 'no-user-gesture-required' : 'user-gesture-required'),
  // match Chrome's default for anti-fingerprinting purposes (Electron defaults to 0)
  minimumFontSize: 6
}

function createView (existingViewId, id, webPreferencesString, boundsString, events) {
  viewStateMap[id] = { loadedInitialURL: false }

  let view
  if (existingViewId) {
    view = temporaryPopupViews[existingViewId]
    delete temporaryPopupViews[existingViewId]

    // the initial URL has already been loaded, so set the background color
    view.setBackgroundColor('#fff')
    viewStateMap[id].loadedInitialURL = true
  } else {
    view = new BrowserView({ webPreferences: Object.assign({}, defaultViewWebPreferences, JSON.parse(webPreferencesString)) })
  }

  events.forEach(function (event) {
    view.webContents.on(event, function (e) {
      var args = Array.prototype.slice.call(arguments).slice(1)

      mainWindow.webContents.send('view-event', {
        viewId: id,
        event: event,
        args: args
      })
    })
  })

  view.webContents.on('select-bluetooth-device', function (event, deviceList, callback) {
    event.preventDefault()
    callback('')
  })

  view.webContents.setWindowOpenHandler(function (details) {
    /*
      Opening a popup with window.open() generally requires features to be set
      So if there are no features, the event is most likely from clicking on a link, which should open a new tab.
      Clicking a link can still have a "new-window" or "foreground-tab" disposition depending on which keys are pressed
      when it is clicked.
      (https://github.com/minbrowser/min/issues/1835)
    */
    if (!details.features) {
      mainWindow.webContents.send('view-event', {
        viewId: id,
        event: 'new-tab',
        args: [details.url, !(details.disposition === 'background-tab')]
      })
      return {
        action: 'deny'
      }
    }

    return {
      action: 'allow'
    }
  })

  view.webContents.removeAllListeners('-add-new-contents')

  view.webContents.on('-add-new-contents', function (e, webContents, disposition, _userGesture, _left, _top, _width, _height, url, frameName, referrer, rawFeatures, postData) {
    if (!filterPopups(url)) {
      return
    }

    var view = new BrowserView({ webPreferences: defaultViewWebPreferences, webContents: webContents })

    var popupId = Math.random().toString()
    temporaryPopupViews[popupId] = view

    mainWindow.webContents.send('view-event', {
      viewId: id,
      event: 'did-create-popup',
      args: [popupId, url]
    })
  })

  view.webContents.on('ipc-message', function (e, channel, data) {
    mainWindow.webContents.send('view-ipc', {
      id: id,
      name: channel,
      data: data,
      frameId: e.frameId
    })
  })

  // Open a login prompt when site asks for http authentication
  view.webContents.on('login', (event, authenticationResponseDetails, authInfo, callback) => {
    if (authInfo.scheme !== 'basic') { // Only for basic auth
      return
    }
    event.preventDefault()
    var title = l('loginPromptTitle').replace('%h', authInfo.host).replace('%r', authInfo.realm)
    createPrompt({
      text: title,
      values: [{ placeholder: l('username'), id: 'username', type: 'text' },
        { placeholder: l('password'), id: 'password', type: 'password' }],
      ok: l('dialogConfirmButton'),
      cancel: l('dialogSkipButton'),
      width: 400,
      height: 200
    }, function (result) {
      // resend request with auth credentials
      callback(result.username, result.password)
    })
  })

  // show an "open in app" prompt for external protocols

  function handleExternalProtocol (e, url, isInPlace, isMainFrame, frameProcessId, frameRoutingId) {
    var knownProtocols = ['http', 'https', 'file', 'min', 'about', 'data', 'javascript', 'chrome'] // TODO anything else?
    if (!knownProtocols.includes(url.split(':')[0])) {
      var externalApp = app.getApplicationNameForProtocol(url)
      if (externalApp) {
        // TODO find a better way to do this
        // (the reason to use executeJS instead of the Electron dialog API is so we get the "prevent this page from creating additional dialogs" checkbox)
        var sanitizedName = externalApp.replace(/[^a-zA-Z0-9.]/g, '')
        if (view.webContents.getURL()) {
          view.webContents.executeJavaScript('confirm("' + l('openExternalApp').replace('%s', sanitizedName) + '")').then(function (result) {
            if (result === true) {
              electron.shell.openExternal(url)
            }
          })
        } else {
          // the code above tries to show the dialog in a browserview, but if the view has no URL, this won't work.
          // so show the dialog globally as a fallback
          var result = electron.dialog.showMessageBoxSync({
            type: 'question',
            buttons: ['OK', 'Cancel'],
            message: l('openExternalApp').replace('%s', sanitizedName).replace(/\\/g, '')
          })

          if (result === 0) {
            electron.shell.openExternal(url)
          }
        }
      }
    }
  }

  view.webContents.on('did-start-navigation', handleExternalProtocol)
  /*
  It's possible for an HTTP request to redirect to an external app link
  (primary use case for this is OAuth from desktop app > browser > back to app)
  and did-start-navigation isn't (always?) emitted for redirects, so we need this handler as well
  */
  view.webContents.on('will-redirect', handleExternalProtocol)

  view.setBounds(JSON.parse(boundsString))

  viewMap[id] = view

  return view
}

function destroyView (id) {
  if (!viewMap[id]) {
    return
  }

  if (viewMap[id] === mainWindow.getBrowserView()) {
    mainWindow.setBrowserView(null)
    selectedView = null
  }
  viewMap[id].webContents.destroy()

  delete viewMap[id]
  delete viewStateMap[id]
}

function destroyAllViews () {
  for (const id in viewMap) {
    destroyView(id)
  }
}

function setView (id) {
  // setBrowserView causes flickering, so we only want to call it if the view is actually changing
  // see https://github.com/minbrowser/min/issues/1966
  if (mainWindow.getBrowserView() !== viewMap[id]) {
    if (viewStateMap[id].loadedInitialURL) {
      mainWindow.setBrowserView(viewMap[id])
    } else {
      mainWindow.setBrowserView(null)
    }
    selectedView = id
  }
}

function setBounds (id, bounds) {
  if (viewMap[id]) {
    viewMap[id].setBounds(bounds)
  }
}

function focusView (id) {
  // empty views can't be focused because they won't propogate keyboard events correctly, see https://github.com/minbrowser/min/issues/616
  // also, make sure the view exists, since it might not if the app is shutting down
  if (viewMap[id] && (viewMap[id].webContents.getURL() !== '' || viewMap[id].webContents.isLoading())) {
    viewMap[id].webContents.focus()
  } else if (mainWindow) {
    mainWindow.webContents.focus()
  }
}

function hideCurrentView () {
  mainWindow.setBrowserView(null)
  selectedView = null
  mainWindow.webContents.focus()
}

function getView (id) {
  return viewMap[id]
}

function getViewIDFromWebContents (contents) {
  for (var id in viewMap) {
    if (viewMap[id].webContents === contents) {
      return id
    }
  }
}

ipc.on('createView', function (e, args) {
  createView(args.existingViewId, args.id, args.webPreferencesString, args.boundsString, args.events)
})

ipc.on('destroyView', function (e, id) {
  destroyView(id)
})

ipc.on('destroyAllViews', function () {
  destroyAllViews()
})

ipc.on('setView', function (e, args) {
  setView(args.id)
  setBounds(args.id, args.bounds)
  if (args.focus) {
    focusView(args.id)
  }
})

ipc.on('setBounds', function (e, args) {
  setBounds(args.id, args.bounds)
})

ipc.on('focusView', function (e, id) {
  focusView(id)
})

ipc.on('hideCurrentView', function (e) {
  hideCurrentView()
})

ipc.on('loadURLInView', function (e, args) {
  // wait until the first URL is loaded to set the background color so that new tabs can use a custom background
  if (!viewStateMap[args.id].loadedInitialURL) {
    viewMap[args.id].setBackgroundColor('#fff')
    // If the view has no URL, it won't be attached yet
    if (args.id === selectedView) {
      mainWindow.setBrowserView(viewMap[args.id])
    }
  }
  viewMap[args.id].webContents.loadURL(args.url)
  viewStateMap[args.id].loadedInitialURL = true
})

ipc.on('callViewMethod', function (e, data) {
  var error, result
  try {
    var webContents = viewMap[data.id].webContents
    var methodOrProp = webContents[data.method]
    if (methodOrProp instanceof Function) {
      // call function
      result = methodOrProp.apply(webContents, data.args)
    } else {
      // set property
      if (data.args && data.args.length > 0) {
        webContents[data.method] = data.args[0]
      }
      // read property
      result = methodOrProp
    }
  } catch (e) {
    error = e
  }
  if (result instanceof Promise) {
    result.then(function (result) {
      if (data.callId) {
        mainWindow.webContents.send('async-call-result', { callId: data.callId, error: null, result })
      }
    })
    result.catch(function (error) {
      if (data.callId) {
        mainWindow.webContents.send('async-call-result', { callId: data.callId, error, result: null })
      }
    })
  } else if (data.callId) {
    mainWindow.webContents.send('async-call-result', { callId: data.callId, error, result })
  }
})

ipc.on('getCapture', function (e, data) {
  var view = viewMap[data.id]
  if (!view) {
    // view could have been destroyed
    return
  }

  view.webContents.capturePage().then(function (img) {
    var size = img.getSize()
    if (size.width === 0 && size.height === 0) {
      return
    }
    img = img.resize({ width: data.width, height: data.height })
    mainWindow.webContents.send('captureData', { id: data.id, url: img.toDataURL() })
  })
})

ipc.on('saveViewCapture', function (e, data) {
  var view = viewMap[data.id]
  if (!view) {
    // view could have been destroyed
  }

  view.webContents.capturePage().then(function (image) {
    view.webContents.downloadURL(image.toDataURL())
  })
})

global.getView = getView
<|endoftext|>