<commit_before>(function(){
	"use strict";

	KC3StrategyTabs.expcalc = new KC3StrategyTab("expcalc");

	KC3StrategyTabs.expcalc.definition = {
		tabSelf: KC3StrategyTabs.expcalc,
		goals: {},
		mapexp: [],
		maplist: {},
		shipexp: {},
        goalTemplates: [], // to be initialized in "init"

		rankNames: ["F", "E", "D", "C", "B", "A", "S", "SS" ],
		rankFactors: [0, 0.5, 0.7, 0.8, 1, 1, 1.2],

		/* INIT
		Prepares all data needed
		---------------------------------*/
		init :function(){
			// Check for saved grind data
			if(typeof localStorage.goals != "undefined"){
				this.goals = JSON.parse(localStorage.goals);
			}

			// Get map exp rewards
			this.mapexp = JSON.parse($.ajax({
				url : '../../../../data/exp_map.json',
				async: false
			}).responseText);

			var self = this;
			$.each(this.mapexp, function(worldNum, mapNums){
				$.each(mapNums, function(mapNum, mapExp){
					if(mapExp > 0){
						self.maplist[worldNum+"-"+(mapNum+1)] = mapExp;
					}
				});
			});

            this.goalTemplates = GoalTemplateManager.load();
			console.log(this.maplist);
		},

		/* EXECUTE
		Places data onto the interface
		---------------------------------*/
		execute :function(){
			var self = this;
			// Add map list into the factory drop-downs
			$.each(this.maplist, function(MapName, MapExp){
				$(".tab_expcalc .factory .ship_map select").append("<option>"+MapName+"</option>");
			});

			var editingBox, mapSplit;

			// Edit Button
			$(".tab_expcalc .box_goals").on("click", ".ship_edit", function(){
				editingBox = $(this).parent();
				var grindData = self.goals[ "s"+editingBox.data("id") ];

				$(".ship_target input", editingBox).val( grindData[0] );
				$(".ship_map select", editingBox).val( grindData[1]+"-"+grindData[2] );
				$(".ship_rank select", editingBox).val(  grindData[4] );
				$(".ship_fs input", editingBox).prop("checked", grindData[5]);
				$(".ship_mvp input", editingBox).prop("checked", grindData[6]);

				$(".ship_value" , editingBox).hide();
				$(".ship_input" , editingBox).show();

				$(".ship_edit" , editingBox).hide();
				$(".ship_save" , editingBox).show();
			});

			// Save Button
			$(".tab_expcalc .box_goals").on("click", ".ship_save", function(){
				editingBox = $(this).parent();

				mapSplit = $(".ship_map select", editingBox).val().split("-");
				console.log("mapSplit", mapSplit);
				self.goals["s"+ editingBox.data("id") ] = [
					/*0*/ parseInt($(".ship_target input", editingBox).val(), 10), // target level
					/*1*/ parseInt(mapSplit[0], 10), // world
					/*2*/ parseInt(mapSplit[1], 10), // map
					/*3*/ 0, // node
					/*4*/ parseInt($(".ship_rank select", editingBox).val(), 10), // battle rank
					/*5*/ $(".ship_fs input", editingBox).prop("checked")?1:0, // flagship
					/*6*/ $(".ship_mvp input", editingBox).prop("checked")?1:0 // mvp
				];

				self.save();

				self.recompute( editingBox.data("id") );

				$(".ship_value" , editingBox).show();
				$(".ship_input" , editingBox).hide();

				$(".ship_edit" , editingBox).show();
				$(".ship_save" , editingBox).hide();
			});

			// Add to Goals Button
			$(".tab_expcalc").on("click", ".ship_add", function(){
				editingBox = $(this).parent();
				self.goals["s"+ editingBox.data("id") ] = [];
				self.save();
				//window.location.reload();

				$(".ship_edit", editingBox).show();
				$(".ship_rem", editingBox).show();
				editingBox.removeClass("inactive");
				editingBox.appendTo(".tab_expcalc .box_goals");
				self.recompute( editingBox.data("id") );
			});

            console.log(this.goalTemplates);

            // TODO
            $.each(this.goalTemplates, function(i,x) {
                // TODO show UI
                console.log(x);
            });
            $(".tab_expcalc a.new_template").on("click", function () {
                var goalBox = $(".tab_expcalc .factory .goal_template").clone();
                var dat = GoalTemplateManager.newTemplate();

                function setupTemplate(t) {
                    function doEdit() {
                        $(".ship_edit",t).hide();
                        $(".ship_save",t).show();

                        $(".ship_col .ship_value",t).hide();
                        $(".ship_col .ship_input",t).show();
                        $(".manage_buttons",t).hide();
                    }

                    function doShow() {
                        $(".ship_edit",t).show();
                        $(".ship_save",t).hide();

                        $(".ship_col .ship_value",t).show();
                        $(".ship_col .ship_input",t).hide();
                        $(".manage_buttons",t).show();
                    }

                    $(".ship_save",t).on("click", doShow);
                    $(".ship_edit",t).on("click", doEdit);

                    doShow();
                }

                setupTemplate(goalBox);

                goalBox.appendTo(".tab_expcalc .box_goal_templates");
                self.goalTemplates.push(dat);
                GoalTemplateManager.save( self.goalTemplates );
            });

			// Remove from Goals Button
			$(".tab_expcalc").on("click", ".ship_rem", function(){
				editingBox = $(this).parent();
				delete self.goals["s"+ editingBox.data("id") ];
				self.save();
				//window.location.reload();

				$(".ship_save", editingBox).hide();
				$(".ship_edit", editingBox).hide();
				$(".ship_rem", editingBox).hide();
				editingBox.addClass("inactive");
				var ThisShip = KC3ShipManager.get(editingBox.data("id"));
				if(ThisShip.master().api_aftershipid > 0 && ThisShip.level<ThisShip.master().api_afterlv){
					$(".tab_expcalc .box_recommend .clear").remove();
					editingBox.appendTo(".tab_expcalc .box_recommend");
					$("<div />").addClass("clear").appendTo(".tab_expcalc .box_recommend");
				}else{
					$(".tab_expcalc .box_other .clear").remove();
					editingBox.appendTo(".tab_expcalc .box_other");
					$("<div />").addClass("clear").appendTo(".tab_expcalc .box_other");
				}
			});

			// Show all ship_save
			var goalBox;
			$.each(KC3ShipManager.list, function(index, ThisShip){
				if(!ThisShip.lock){ return true; }

				// Create the ship box
				goalBox = $(".tab_expcalc .factory .ship_goal").clone();
				goalBox.attr("id", "goalBox"+ThisShip.rosterId);
				goalBox.data("id", ThisShip.rosterId);

				// Icon and level, common for all categories
				$(".ship_icon img", goalBox).attr("src", KC3Meta.shipIcon(ThisShip.masterId) );
				$(".ship_icon img", goalBox).attr("title", ThisShip.name() + ' (' + ThisShip.rosterId + ')' );
				$(".ship_name", goalBox).text( ThisShip.name() );
				$(".ship_type", goalBox).text( ThisShip.stype() );
				$(".ship_lv .ship_value", goalBox).text( ThisShip.level );

				// If ship already on the current goals
				if(typeof self.goals["s"+ThisShip.rosterId] != "undefined"){
					$(".ship_edit", goalBox).show();
					$(".ship_rem", goalBox).show();
					goalBox.appendTo(".tab_expcalc .box_goals");

					self.recompute( ThisShip.rosterId );
					return true;
				}

				goalBox.addClass("inactive");

				// If still has next remodel, add to recommendations
				if(ThisShip.master().api_aftershipid > 0 && ThisShip.level<ThisShip.master().api_afterlv){
					$(".ship_target .ship_value", goalBox).text( ThisShip.master().api_afterlv );
					goalBox.appendTo(".tab_expcalc .box_recommend");
					return true;
				}

				// If this is the last remodel stage, add to others
				if(ThisShip.level<99){
					$(".ship_target .ship_value", goalBox).text( 99 );
				}else{
					$(".ship_target .ship_value", goalBox).text( 155 );
				}
				goalBox.appendTo(".tab_expcalc .box_other");
			});

			//this.save();

			$("<div />").addClass("clear").appendTo(".tab_expcalc .box_recommend");
			$("<div />").addClass("clear").appendTo(".tab_expcalc .box_other");
		},

		save: function(){
			localStorage.goals = JSON.stringify(this.goals);
		},

		recompute: function( rosterId ){
			var goalBox = $("#goalBox"+rosterId);
			var grindData = this.goals["s"+rosterId];
			var ThisShip = KC3ShipManager.get( rosterId );
			var MasterShip = ThisShip.master();

			// This has just been added, no grinding data yet, initialize defaults
			if(grindData.length === 0){
				// As much as possible use arrays nowadays to shrink JSON size, we might run out of the 5MB localStorage allocated for our app
				grindData = [
					/*0*/ (MasterShip.api_aftershipid > 0 && ThisShip.level<MasterShip.api_afterlv)?MasterShip.api_afterlv:(ThisShip.level<99)?99:150, // target level
					/*1*/ 1, // world
					/*2*/ 1, // map
					/*3*/ 1, // node
					/*4*/ 6, // E=1 D=2 C=3 B=4 A=5 S=6 SS=7
					/*5*/ 0, // flagship
					/*6*/ 0 // mvp
				];
				this.goals["s"+ThisShip.rosterId] = grindData;
			}else{

			}

			// Target level
			$(".ship_target .ship_value", goalBox).text( grindData[0] );

			// Experience Left
			var expLeft = KC3Meta.expShip(grindData[0])[1] - ThisShip.exp[0];
			$(".ship_exp .ship_value", goalBox).text( expLeft );

			// Base Experience: MAP
			$(".ship_map .ship_value", goalBox).text( grindData[1]+"-"+grindData[2] );
			var expPerSortie = this.maplist[ grindData[1]+"-"+grindData[2] ];

			// Exp Modifier: MVP
			$(".ship_mvp .ship_value", goalBox).text( grindData[6]?"Yes":"No" );
			$(".ship_mvp .ship_value", goalBox).css('color',(grindData[6]? "#e33" : "#039")); //yes : light red, no : deep blue
			if(grindData[6]===1){ expPerSortie = expPerSortie * 2; }

			// Exp Modifier: FLAGSHIP
			$(".ship_fs .ship_value", goalBox).text( grindData[5]?"Yes":"No" );
			$(".ship_mvp .ship_value", goalBox).css('color',(grindData[6]? "#e33" : "#039"));
			if(grindData[5]===1){ expPerSortie = expPerSortie * 1.5; }

			// Exp Modifier: RANK
			$(".ship_rank .ship_value", goalBox).text( this.rankNames[grindData[4]] );
			expPerSortie = expPerSortie * this.rankFactors[grindData[4]];

			// RESULT: Battles Left
			$(".ship_result .ship_value", goalBox).text( Math.ceil(expLeft / expPerSortie) );
		}

	};

})();
<commit_msg>remove / add shipgoals (sync localStorage)<commit_after>(function(){
	"use strict";

	KC3StrategyTabs.expcalc = new KC3StrategyTab("expcalc");

	KC3StrategyTabs.expcalc.definition = {
		tabSelf: KC3StrategyTabs.expcalc,
		goals: {},
		mapexp: [],
		maplist: {},
		shipexp: {},
        goalTemplates: [], // to be initialized in "init"

		rankNames: ["F", "E", "D", "C", "B", "A", "S", "SS" ],
		rankFactors: [0, 0.5, 0.7, 0.8, 1, 1, 1.2],

		/* INIT
		Prepares all data needed
		---------------------------------*/
		init :function(){
			// Check for saved grind data
			if(typeof localStorage.goals != "undefined"){
				this.goals = JSON.parse(localStorage.goals);
			}

			// Get map exp rewards
			this.mapexp = JSON.parse($.ajax({
				url : '../../../../data/exp_map.json',
				async: false
			}).responseText);

			var self = this;
			$.each(this.mapexp, function(worldNum, mapNums){
				$.each(mapNums, function(mapNum, mapExp){
					if(mapExp > 0){
						self.maplist[worldNum+"-"+(mapNum+1)] = mapExp;
					}
				});
			});

            this.goalTemplates = GoalTemplateManager.load();
			console.log(this.maplist);
		},

		/* EXECUTE
		Places data onto the interface
		---------------------------------*/
		execute :function(){
			var self = this;
			// Add map list into the factory drop-downs
			$.each(this.maplist, function(MapName, MapExp){
				$(".tab_expcalc .factory .ship_map select").append("<option>"+MapName+"</option>");
			});

			var editingBox, mapSplit;

			// Edit Button
			$(".tab_expcalc .box_goals").on("click", ".ship_edit", function(){
				editingBox = $(this).parent();
				var grindData = self.goals[ "s"+editingBox.data("id") ];

				$(".ship_target input", editingBox).val( grindData[0] );
				$(".ship_map select", editingBox).val( grindData[1]+"-"+grindData[2] );
				$(".ship_rank select", editingBox).val(  grindData[4] );
				$(".ship_fs input", editingBox).prop("checked", grindData[5]);
				$(".ship_mvp input", editingBox).prop("checked", grindData[6]);

				$(".ship_value" , editingBox).hide();
				$(".ship_input" , editingBox).show();

				$(".ship_edit" , editingBox).hide();
				$(".ship_save" , editingBox).show();
			});

			// Save Button
			$(".tab_expcalc .box_goals").on("click", ".ship_save", function(){
				editingBox = $(this).parent();

				mapSplit = $(".ship_map select", editingBox).val().split("-");
				console.log("mapSplit", mapSplit);
				self.goals["s"+ editingBox.data("id") ] = [
					/*0*/ parseInt($(".ship_target input", editingBox).val(), 10), // target level
					/*1*/ parseInt(mapSplit[0], 10), // world
					/*2*/ parseInt(mapSplit[1], 10), // map
					/*3*/ 0, // node
					/*4*/ parseInt($(".ship_rank select", editingBox).val(), 10), // battle rank
					/*5*/ $(".ship_fs input", editingBox).prop("checked")?1:0, // flagship
					/*6*/ $(".ship_mvp input", editingBox).prop("checked")?1:0 // mvp
				];

				self.save();

				self.recompute( editingBox.data("id") );

				$(".ship_value" , editingBox).show();
				$(".ship_input" , editingBox).hide();

				$(".ship_edit" , editingBox).show();
				$(".ship_save" , editingBox).hide();
			});

			// Add to Goals Button
			$(".tab_expcalc").on("click", ".ship_add", function(){
				editingBox = $(this).parent();
				self.goals["s"+ editingBox.data("id") ] = [];
				self.save();
				//window.location.reload();

				$(".ship_edit", editingBox).show();
				$(".ship_rem", editingBox).show();
				editingBox.removeClass("inactive");
				editingBox.appendTo(".tab_expcalc .box_goals");
				self.recompute( editingBox.data("id") );
			});

            function goalTemplateEdit(t) {
                $(".goal_edit",t).hide();
                $(".goal_save",t).show();

                $(".goal_col .goal_value",t).hide();
                $(".goal_col .goal_input",t).show();
                $(".manage_buttons",t).hide();
            }

            function goalTemplateShow(t) {
                $(".goal_edit",t).show();
                $(".goal_save",t).hide();

                $(".goal_col .goal_value",t).show();
                $(".goal_col .goal_input",t).hide();
                $(".manage_buttons",t).show();
            }

            function goalTemplateRemove(t) {
                var ind = t.index();
                self.goalTemplates.splice(ind,1);
                GoalTemplateManager.save( self.goalTemplates );
                t.remove();
            }

            // TODO
            // Goal Template Edit & Save button events
            $(".tab_expcalc").on("click", ".goal_template .goal_edit", function() {
                var goalBox = $(this).parent().parent();
                goalTemplateEdit(goalBox);
            });

            $(".tab_expcalc").on("click", ".goal_template .goal_save", function() {
                var goalBox = $(this).parent().parent();
                goalTemplateShow(goalBox);
            });

            $(".tab_expcalc").on("click", ".goal_template .goal_rem", function() {
                var goalBox = $(this).parent().parent();
                goalTemplateRemove(goalBox);
            });

            $.each(this.goalTemplates, function(i,x) {
                var goalBox = $(".tab_expcalc .factory .goal_template").clone();
                goalTemplateShow(goalBox);

                goalBox.appendTo(".tab_expcalc .box_goal_templates");
            });

            $(".tab_expcalc a.new_template").on("click", function () {
                var goalBox = $(".tab_expcalc .factory .goal_template").clone();
                var dat = GoalTemplateManager.newTemplate();
                goalTemplateShow(goalBox);

                goalBox.appendTo(".tab_expcalc .box_goal_templates");
                self.goalTemplates.push(dat);
                GoalTemplateManager.save( self.goalTemplates );
            });

			// Remove from Goals Button
			$(".tab_expcalc").on("click", ".ship_rem", function(){
				editingBox = $(this).parent();
				delete self.goals["s"+ editingBox.data("id") ];
				self.save();
				//window.location.reload();

				$(".ship_save", editingBox).hide();
				$(".ship_edit", editingBox).hide();
				$(".ship_rem", editingBox).hide();
				editingBox.addClass("inactive");
				var ThisShip = KC3ShipManager.get(editingBox.data("id"));
				if(ThisShip.master().api_aftershipid > 0 && ThisShip.level<ThisShip.master().api_afterlv){
					$(".tab_expcalc .box_recommend .clear").remove();
					editingBox.appendTo(".tab_expcalc .box_recommend");
					$("<div />").addClass("clear").appendTo(".tab_expcalc .box_recommend");
				}else{
					$(".tab_expcalc .box_other .clear").remove();
					editingBox.appendTo(".tab_expcalc .box_other");
					$("<div />").addClass("clear").appendTo(".tab_expcalc .box_other");
				}
			});

			// Show all ship_save
			var goalBox;
			$.each(KC3ShipManager.list, function(index, ThisShip){
				if(!ThisShip.lock){ return true; }

				// Create the ship box
				goalBox = $(".tab_expcalc .factory .ship_goal").clone();
				goalBox.attr("id", "goalBox"+ThisShip.rosterId);
				goalBox.data("id", ThisShip.rosterId);

				// Icon and level, common for all categories
				$(".ship_icon img", goalBox).attr("src", KC3Meta.shipIcon(ThisShip.masterId) );
				$(".ship_icon img", goalBox).attr("title", ThisShip.name() + ' (' + ThisShip.rosterId + ')' );
				$(".ship_name", goalBox).text( ThisShip.name() );
				$(".ship_type", goalBox).text( ThisShip.stype() );
				$(".ship_lv .ship_value", goalBox).text( ThisShip.level );

				// If ship already on the current goals
				if(typeof self.goals["s"+ThisShip.rosterId] != "undefined"){
					$(".ship_edit", goalBox).show();
					$(".ship_rem", goalBox).show();
					goalBox.appendTo(".tab_expcalc .box_goals");

					self.recompute( ThisShip.rosterId );
					return true;
				}

				goalBox.addClass("inactive");

				// If still has next remodel, add to recommendations
				if(ThisShip.master().api_aftershipid > 0 && ThisShip.level<ThisShip.master().api_afterlv){
					$(".ship_target .ship_value", goalBox).text( ThisShip.master().api_afterlv );
					goalBox.appendTo(".tab_expcalc .box_recommend");
					return true;
				}

				// If this is the last remodel stage, add to others
				if(ThisShip.level<99){
					$(".ship_target .ship_value", goalBox).text( 99 );
				}else{
					$(".ship_target .ship_value", goalBox).text( 155 );
				}
				goalBox.appendTo(".tab_expcalc .box_other");
			});

			//this.save();

			$("<div />").addClass("clear").appendTo(".tab_expcalc .box_recommend");
			$("<div />").addClass("clear").appendTo(".tab_expcalc .box_other");
		},

		save: function(){
			localStorage.goals = JSON.stringify(this.goals);
		},

		recompute: function( rosterId ){
			var goalBox = $("#goalBox"+rosterId);
			var grindData = this.goals["s"+rosterId];
			var ThisShip = KC3ShipManager.get( rosterId );
			var MasterShip = ThisShip.master();

			// This has just been added, no grinding data yet, initialize defaults
			if(grindData.length === 0){
				// As much as possible use arrays nowadays to shrink JSON size, we might run out of the 5MB localStorage allocated for our app
				grindData = [
					/*0*/ (MasterShip.api_aftershipid > 0 && ThisShip.level<MasterShip.api_afterlv)?MasterShip.api_afterlv:(ThisShip.level<99)?99:150, // target level
					/*1*/ 1, // world
					/*2*/ 1, // map
					/*3*/ 1, // node
					/*4*/ 6, // E=1 D=2 C=3 B=4 A=5 S=6 SS=7
					/*5*/ 0, // flagship
					/*6*/ 0 // mvp
				];
				this.goals["s"+ThisShip.rosterId] = grindData;
			}else{

			}

			// Target level
			$(".ship_target .ship_value", goalBox).text( grindData[0] );

			// Experience Left
			var expLeft = KC3Meta.expShip(grindData[0])[1] - ThisShip.exp[0];
			$(".ship_exp .ship_value", goalBox).text( expLeft );

			// Base Experience: MAP
			$(".ship_map .ship_value", goalBox).text( grindData[1]+"-"+grindData[2] );
			var expPerSortie = this.maplist[ grindData[1]+"-"+grindData[2] ];

			// Exp Modifier: MVP
			$(".ship_mvp .ship_value", goalBox).text( grindData[6]?"Yes":"No" );
			$(".ship_mvp .ship_value", goalBox).css('color',(grindData[6]? "#e33" : "#039")); //yes : light red, no : deep blue
			if(grindData[6]===1){ expPerSortie = expPerSortie * 2; }

			// Exp Modifier: FLAGSHIP
			$(".ship_fs .ship_value", goalBox).text( grindData[5]?"Yes":"No" );
			$(".ship_mvp .ship_value", goalBox).css('color',(grindData[6]? "#e33" : "#039"));
			if(grindData[5]===1){ expPerSortie = expPerSortie * 1.5; }

			// Exp Modifier: RANK
			$(".ship_rank .ship_value", goalBox).text( this.rankNames[grindData[4]] );
			expPerSortie = expPerSortie * this.rankFactors[grindData[4]];

			// RESULT: Battles Left
			$(".ship_result .ship_value", goalBox).text( Math.ceil(expLeft / expPerSortie) );
		}

	};

})();
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React from 'react'

import Icon from '../Icon'
import StyledButtonLink from './StyledButtonLink'
import {
  inkPropTypes,
  stylingInk,
  stylingLook,
  stylingPosition
} from '../utilStyles'

/**
 * Use <ButtonLink> for internal and external links if it feels like an action
 * or links compete with actions. Choose look and ink according Material Design.
 */
const ButtonLink = props => {
  const onClickHandler = e => {
    if (props.stopPropagation) {
      e.stopPropagation()
    }
  }

  return (
    <StyledButtonLink
      alt={props.alt}
      as="a"
      dense={props.dense}
      download={props.download}
      href={props.href}
      ink={props.ink}
      look={props.look}
      melt={props.buttonGroupMelt}
      onClick={onClickHandler}
      title={props.title}
    >
      {props.icon && <Icon dense={props.dense} icon={props.icon} position={props.iconPosition}/>}
      {props.label}
    </StyledButtonLink>
  )
}

ButtonLink.defaultProps = {
  href: '#',
  iconPosition: stylingPosition.PREPEND,
  ink: stylingInk.BASE,
  look: stylingLook.FLAT
}

ButtonLink.propTypes = {
  /**
   * Describe link target in detail for screen readers and web crawlers.
   */
  alt: PropTypes.string,
  /**
   * May be passed from <ButtonGroup> to morph links into a split button. Do not set manually.
   */
  buttonGroupMelt: PropTypes.bool,
  /**
   * If true, links occupies less space. It should only used for crowded areas like tables and only if necessary.
   */
  dense: PropTypes.bool,
  /**
   * Specify "download" to force direct download or an arbitrary string to suggest a filename.
   */
  download: PropTypes.string,
  /**
   * Specify any valid URL or a contact including prefix like "mailto:". Default is '#'
   */
  href: PropTypes.string,
  /**
   * Display an icon alongside button label. It is possible to omit label text if a icon is chosen. Utilize free
   * Font Awesome 5.1 icons by setting specific classname (e.g. "check").
   * https://fontawesome.com/icons?d=gallery&s=regular,solid&m=free
   */
  icon: PropTypes.string,
  /**
   * Prepend icon or append icon to label. Use 'sole' if label text is omitted. Default value is 'prepend'.
   */
  iconPosition: PropTypes.oneOf([stylingPosition.PREPEND, stylingPosition.SOLE]),
  /**
   * Specify color palette. Default value is 'base'.
   */
  ink: inkPropTypes,
  /**
   * Describe link target concise. Default is ''.
   */
  label: PropTypes.node,
  /**
   * Look of link according Material Design (button section). Default value is 'flat'.
   */
  look: PropTypes.oneOf([
    stylingLook.BALL,
    stylingLook.FLAT,
    stylingLook.RAISED
  ]),
  /**
   * If true, the click event will not be propagated.
   */
  stopPropagation: PropTypes.bool,
  /**
   * Describe link target in detail to instruct users. It is shown as popover on mouse over.
   */
  title: PropTypes.string
}

export default ButtonLink
<commit_msg>feat(tocco-ui): add props tabIndex and target to ButtonLink<commit_after>import PropTypes from 'prop-types'
import React from 'react'

import Icon from '../Icon'
import StyledButtonLink from './StyledButtonLink'
import {
  inkPropTypes,
  stylingInk,
  stylingLook,
  stylingPosition
} from '../utilStyles'

/**
 * Use <ButtonLink> for internal and external links if it feels like an action
 * or links compete with actions. Choose look and ink according Material Design.
 */
const ButtonLink = props => {
  const onClickHandler = e => {
    if (props.stopPropagation) {
      e.stopPropagation()
    }
  }

  return (
    <StyledButtonLink
      alt={props.alt}
      as="a"
      dense={props.dense}
      download={props.download}
      href={props.href}
      ink={props.ink}
      look={props.look}
      melt={props.buttonGroupMelt}
      onClick={onClickHandler}
      tabIndex={props.tabIndex}
      target={props.target}
      title={props.title}
    >
      {props.icon && <Icon dense={props.dense} icon={props.icon} position={props.iconPosition}/>}
      {props.label}
    </StyledButtonLink>
  )
}

ButtonLink.defaultProps = {
  href: '#',
  iconPosition: stylingPosition.PREPEND,
  ink: stylingInk.BASE,
  look: stylingLook.FLAT,
  target: '_self'
}

ButtonLink.propTypes = {
  /**
   * Describe link target in detail for screen readers and web crawlers.
   */
  alt: PropTypes.string,
  /**
   * May be passed from <ButtonGroup> to morph links into a split button. Do not set manually.
   */
  buttonGroupMelt: PropTypes.bool,
  /**
   * If true, links occupies less space. It should only used for crowded areas like tables and only if necessary.
   */
  dense: PropTypes.bool,
  /**
   * Specify "download" to force direct download or an arbitrary string to suggest a filename.
   */
  download: PropTypes.string,
  /**
   * Specify any valid URL or a contact including prefix like "mailto:". Default is '#'
   */
  href: PropTypes.string,
  /**
   * Display an icon alongside button label. It is possible to omit label text if a icon is chosen. Utilize free
   * Font Awesome 5.1 icons by setting specific classname (e.g. "check").
   * https://fontawesome.com/icons?d=gallery&s=regular,solid&m=free
   */
  icon: PropTypes.string,
  /**
   * Prepend icon or append icon to label. Use 'sole' if label text is omitted. Default value is 'prepend'.
   */
  iconPosition: PropTypes.oneOf([stylingPosition.PREPEND, stylingPosition.SOLE]),
  /**
   * Specify color palette. Default value is 'base'.
   */
  ink: inkPropTypes,
  /**
   * Describe link target concise. Default is ''.
   */
  label: PropTypes.node,
  /**
   * Look of link according Material Design (button section). Default value is 'flat'.
   */
  look: PropTypes.oneOf([
    stylingLook.BALL,
    stylingLook.FLAT,
    stylingLook.RAISED
  ]),
  /**
   * If true, the click event will not be propagated.
   */
  stopPropagation: PropTypes.bool,
  /**
   * Tabindex indicates if the button can be focused and if/where it participates
   * in sequential keyboard navigation.
   */
  tabIndex: PropTypes.number,
  /**
   * Define browsing context. Default value is '_self'. Possible values: _self|_blank
   */
  target: PropTypes.oneOf(['_self', '_blank']),
  /**
   * Describe link target in detail to instruct users. It is shown as popover on mouse over.
   */
  title: PropTypes.string
}

export default ButtonLink
<|endoftext|>


<commit_before>var fs = require('fs');
var http = require('http');
var BrowserStackTunnel = require('browserstacktunnel-wrapper');

var HOSTNAME = 'localhost';
var PORTS = require('../grunt/utils').availablePorts;
var ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY;
var READY_FILE = process.env.SAUCE_CONNECT_READY_FILE;

// We need to start fake servers, otherwise the tunnel does not start.
var fakeServers = [];
var hosts = [];

PORTS.forEach(function(port) {
  fakeServers.push(http.createServer(function() {}).listen(port));
  hosts.push({
    name: HOSTNAME,
    port: port,
    sslFlag: 0
  });
});

var tunnel = new BrowserStackTunnel({
  key: ACCESS_KEY,
  hosts: hosts
});

tunnel.start(function(error) {
  console.log('** callback **')
  if (error) {
    console.error('Can not establish the tunnel', error);
  } else {
    console.log('Tunnel established.');
    fakeServers.forEach(function(server) {
      server.close();
    });

    if (READY_FILE) {
      fs.writeFile(READY_FILE, '');
    }
  }
});

tunnel.on('error', function(error) {
  console.error(error);
});
<commit_msg>chore(travis): log used ports<commit_after>var fs = require('fs');
var http = require('http');
var BrowserStackTunnel = require('browserstacktunnel-wrapper');

var HOSTNAME = 'localhost';
var PORTS = require('../grunt/utils').availablePorts;
var ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY;
var READY_FILE = process.env.SAUCE_CONNECT_READY_FILE;

// We need to start fake servers, otherwise the tunnel does not start.
var fakeServers = [];
var hosts = [];

PORTS.forEach(function(port) {
  fakeServers.push(http.createServer(function() {}).listen(port));
  hosts.push({
    name: HOSTNAME,
    port: port,
    sslFlag: 0
  });
});

var tunnel = new BrowserStackTunnel({
  key: ACCESS_KEY,
  hosts: hosts
});

console.log('Starting tunnel on ports', PORTS.join(', '));
tunnel.start(function(error) {
  if (error) {
    console.error('Can not establish the tunnel', error);
  } else {
    console.log('Tunnel established.');
    fakeServers.forEach(function(server) {
      server.close();
    });

    if (READY_FILE) {
      fs.writeFile(READY_FILE, '');
    }
  }
});

tunnel.on('error', function(error) {
  console.error(error);
});
<|endoftext|>


<commit_before>import demoTest from '../../../tests/shared/demoTest';

demoTest('mention');
<commit_msg>jest mock generateRandomKey for ssr test<commit_after>import demoTest from '../../../tests/shared/demoTest';

jest.mock('draft-js/lib/generateRandomKey', () => () => '123');

demoTest('mention');
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var utils = require('./utils'),
    fieldset = require('./fieldset'),
    _ = require('./underscore')._;

/**
 * Form object, presents fields and parses responses.
 *
 * @param {Object} fields
 * @constructor
 * @api public
 */

var Form = exports.Form = function Form(fields, doc) {
    this.fields = (fields && fields.fields) ? fields.fields: fields;
    /*
    if (utils.constructorName(fields) === 'Type') {
        this.type = fields;
        this.fields = fields.field;
    }
    else {
        this.fields = fields;
    }
    */
    if (doc) {
        this.values = doc;
    }
};

/**
 * Parses a request and validates the result, binding values and errors to
 * the form instance.
 *
 * @param {Object} req
 * @return {Form}
 * @api public
 */

Form.prototype.validate = function (req) {
    this.raw = req.form || {};
    this.values = exports.parseRaw(this.fields, this.raw);
    this.errors = fieldset.validate(
        this.fields, this.values, this.values, this.raw, [], false
    );
    return this;
};

/**
 * After a form has called validate, this function will return true if the form
 * is valid, false otherwise.
 *
 * @return {Boolean}
 * @api public
 */

Form.prototype.isValid = function () {
    return !(this.errors && this.errors.length);
};

/**
 * Converts current form to a HTML string, using an optional renderer class.
 *
 * @param {Object} req
 * @param {Renderer} rendererClass
 * @return {String}
 * @api public
 */

Form.prototype.toHTML = function (req, rendererClass) {
    var values = this.values || fieldset.createDefaults(
        this.fields,
        req.userCtx
    );
    var renderer = new rendererClass();
    return renderer.start() +
        this.renderFields(
            renderer, this.fields, values, this.raw, this.errors, []
        ) + renderer.end();
};

/**
 * Filters an array of errors, returning only those below a specific field path
 *
 * @param {Array} errs
 * @param {Array} path
 * @return {Array}
 * @api public
 */

var errsBelowPath = function (errs, path) {
    return _.filter(errs, function (e) {
        for (var i = 0, len = path.length; i < len; i++) {
            if (path[i] !== e.field[i]) {
                return false;
            }
        }
        return true;
    });
};

/**
 * Iterates over fields and sub-objects calling the correct renderer function on
 * each. Returns a HTML representation of the fields. Used internally by the
 * toHTML method, you should not need to call this function directly.
 *
 * TODO: this method is fugly, refactor!
 *
 * @param {Object} renderer
 * @param {Object} fields
 * @param {Object} values
 * @param {Array} errs
 * @param {Array} path
 * @return {String}
 * @api public
 */

Form.prototype.renderFields = function (renderer, fields, values, raw, errs, path) {
    fields = fields || {};
    values = values || {};
    raw = raw || {};
    errs = errs || [];
    path = path || [];
    var that = this;

    var keys = _.keys(fields);
    return _.reduce(keys, function (html, k) {
        var f_path = path.concat([k]);
        var f_errs = errsBelowPath(errs, f_path);
        var cname = utils.constructorName(fields[k]);
        if (cname === 'Field') {
            return html + renderer.field(
                fields[k],
                f_path.join('.'),
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs
            );
        }
        else if (cname === 'Embedded') {
            var new_renderer = renderer.embed(
                fields[k].type,
                f_path.join('.'),
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs
            );
            html += new_renderer.start();
            html += that.renderFields(
                new_renderer,
                fields[k].type.fields,
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs,
                f_path
            );
            html += new_renderer.end();
            return html;
        }
        else if (cname === 'EmbeddedList') {
            var type = fields[k].type;
            var new_renderer = renderer.embedList(
                type,
                f_path.join('.'),
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs
            );
            html += new_renderer.start();
            html += _.reduce(values[k], function (html, v, i) {
                var f_path2 = f_path.concat([i]);
                var f_errs2 = errsBelowPath(f_errs, f_path2);
                var v_renderer = new_renderer.each(
                    type,
                    f_path2.join('.'),
                    v,
                    (raw[k][i] === undefined) ? v[i]: raw[k][i],
                    f_errs2
                );
                html += v_renderer.start();
                html += that.renderFields(
                    v_renderer,
                    type.fields,
                    v,
                    (raw[k][i] === undefined) ? v[i]: raw[k][i],
                    f_errs2,
                    f_path2
                );
                html += v_renderer.end();
                return html;
            }, '');
            html += new_renderer.end();
            return html;
        }
        else {
            return html + that.renderFields(
                renderer,
                fields[k],
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                errs,
                f_path
            );
        }
    }, '');
};

/**
 * Transforms a flat object from a request query to a proper
 * hierarchy of properties.
 *
 * {'one.two': 'val'} --> {one: {two: 'val'}}
 *
 * @param {Object} query
 * @api public
 */

exports.formValuesToTree = function (form) {
    var tree = {};
    for (var k in form) {
        utils.setPropertyPath(tree, k.split('.'), form[k]);
    }
    return tree;
};

/**
 * Transforms a raw query object from formValuesToTree to a
 * document which follows the schema for the given type.
 *
 * @param {Type} type
 * @param {Object} raw
 * @return {Object}
 * @api public
 */

exports.parseRaw = function (fields, raw) {
    var doc = {};
    raw = raw || {};

    for (var k in fields) {
        var f = fields[k];
        var cname = utils.constructorName(f);
        if (cname === 'Field') {
            if (!f.isEmpty(raw[k])) {
                doc[k] = f.parse(raw[k]);
            }
        }
        else if (cname === 'Embedded') {
            if (!f.isEmpty(raw[k])) {
                doc[k] = exports.parseRaw(f.type.fields, raw[k]);
            }
        }
        else if (cname === 'EmbeddedList') {
            doc[k] = [];
            for (var i = 0, len = raw[k].length; i < len; i++) {
                if (!f.isEmpty(raw[k][i])) {
                    doc[k][i] = exports.parseRaw(f.type.fields, raw[k][i]);
                }
            }
        }
        else {
            doc[k] = exports.parseRaw(f, raw[k]);
        }
    }
    log('parseRaw');
    log(doc);
    return doc;
};

exports.render = require('./render');
<commit_msg>a few fixes for parsing form data in the admin app<commit_after>/**
 * Module dependencies
 */

var utils = require('./utils'),
    fieldset = require('./fieldset'),
    _ = require('./underscore')._;

/**
 * Form object, presents fields and parses responses.
 *
 * @param {Object} fields
 * @constructor
 * @api public
 */

var Form = exports.Form = function Form(fields, doc) {
    this.fields = (fields && fields.fields) ? fields.fields: fields;
    /*
    if (utils.constructorName(fields) === 'Type') {
        this.type = fields;
        this.fields = fields.field;
    }
    else {
        this.fields = fields;
    }
    */
    if (doc) {
        this.values = doc;
    }
};

/**
 * Parses a request and validates the result, binding values and errors to
 * the form instance.
 *
 * @param {Object} req
 * @return {Form}
 * @api public
 */

Form.prototype.validate = function (req) {
    this.raw = req.form || {};
    var tree = exports.formValuesToTree(this.raw);
    this.values = exports.parseRaw(this.fields, tree);
    this.errors = fieldset.validate(
        this.fields, this.values, this.values, this.raw, [], false
    );
    return this;
};

/**
 * After a form has called validate, this function will return true if the form
 * is valid, false otherwise.
 *
 * @return {Boolean}
 * @api public
 */

Form.prototype.isValid = function () {
    return !(this.errors && this.errors.length);
};

/**
 * Converts current form to a HTML string, using an optional renderer class.
 *
 * @param {Object} req
 * @param {Renderer} rendererClass
 * @return {String}
 * @api public
 */

Form.prototype.toHTML = function (req, rendererClass) {
    var values = this.values || fieldset.createDefaults(
        this.fields,
        req.userCtx
    );
    var renderer = new rendererClass();
    return renderer.start() +
        this.renderFields(
            renderer, this.fields, values, this.raw, this.errors, []
        ) + renderer.end();
};

/**
 * Filters an array of errors, returning only those below a specific field path
 *
 * @param {Array} errs
 * @param {Array} path
 * @return {Array}
 * @api public
 */

var errsBelowPath = function (errs, path) {
    return _.filter(errs, function (e) {
        for (var i = 0, len = path.length; i < len; i++) {
            if (path[i] !== e.field[i]) {
                return false;
            }
        }
        return true;
    });
};

/**
 * Iterates over fields and sub-objects calling the correct renderer function on
 * each. Returns a HTML representation of the fields. Used internally by the
 * toHTML method, you should not need to call this function directly.
 *
 * TODO: this method is fugly, refactor!
 *
 * @param {Object} renderer
 * @param {Object} fields
 * @param {Object} values
 * @param {Array} errs
 * @param {Array} path
 * @return {String}
 * @api public
 */

Form.prototype.renderFields = function (renderer, fields, values, raw, errs, path) {
    fields = fields || {};
    values = values || {};
    raw = raw || {};
    errs = errs || [];
    path = path || [];
    var that = this;

    var keys = _.keys(fields);
    return _.reduce(keys, function (html, k) {
        var f_path = path.concat([k]);
        var f_errs = errsBelowPath(errs, f_path);
        var cname = utils.constructorName(fields[k]);
        if (cname === 'Field') {
            return html + renderer.field(
                fields[k],
                f_path.join('.'),
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs
            );
        }
        else if (cname === 'Embedded') {
            var new_renderer = renderer.embed(
                fields[k].type,
                f_path.join('.'),
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs
            );
            html += new_renderer.start();
            html += that.renderFields(
                new_renderer,
                fields[k].type.fields,
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs,
                f_path
            );
            html += new_renderer.end();
            return html;
        }
        else if (cname === 'EmbeddedList') {
            var type = fields[k].type;
            var new_renderer = renderer.embedList(
                type,
                f_path.join('.'),
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                f_errs
            );
            html += new_renderer.start();
            html += _.reduce(values[k], function (html, v, i) {
                var f_path2 = f_path.concat([i]);
                var f_errs2 = errsBelowPath(f_errs, f_path2);
                var v_renderer = new_renderer.each(
                    type,
                    f_path2.join('.'),
                    v,
                    (raw[k][i] === undefined) ? v[i]: raw[k][i],
                    f_errs2
                );
                html += v_renderer.start();
                html += that.renderFields(
                    v_renderer,
                    type.fields,
                    v,
                    (raw[k][i] === undefined) ? v[i]: raw[k][i],
                    f_errs2,
                    f_path2
                );
                html += v_renderer.end();
                return html;
            }, '');
            html += new_renderer.end();
            return html;
        }
        else {
            return html + that.renderFields(
                renderer,
                fields[k],
                values[k],
                (raw[k] === undefined) ? values[k]: raw[k],
                errs,
                f_path
            );
        }
    }, '');
};

/**
 * Transforms a flat object from a request query to a proper
 * hierarchy of properties.
 *
 * {'one.two': 'val'} --> {one: {two: 'val'}}
 *
 * @param {Object} query
 * @api public
 */

exports.formValuesToTree = function (form) {
    var tree = {};
    for (var k in form) {
        utils.setPropertyPath(tree, k.split('.'), form[k]);
    }
    return tree;
};

/**
 * Transforms a raw query object from formValuesToTree to a
 * document which follows the schema for the given type.
 *
 * @param {Type} type
 * @param {Object} raw
 * @return {Object}
 * @api public
 */

exports.parseRaw = function (fields, raw) {
    log('raw:');
    log(raw);
    var doc = {};
    raw = raw || {};

    for (var k in fields) {
        var f = fields[k];
        var cname = utils.constructorName(f);
        if (cname === 'Field') {
            if (!f.isEmpty(raw[k]) || !f.omit_empty) {
                doc[k] = f.parse(raw[k]);
            }
        }
        else if (cname === 'Embedded') {
            if (!f.isEmpty(raw[k]) || !f.omit_empty) {
                doc[k] = exports.parseRaw(f.type.fields, raw[k]);
            }
        }
        else if (cname === 'EmbeddedList') {
            doc[k] = [];
            for (var i = 0, len = raw[k].length; i < len; i++) {
                if (!f.isEmpty(raw[k][i]) || !f.omit_empty) {
                    doc[k][i] = exports.parseRaw(f.type.fields, raw[k][i]);
                }
            }
        }
        else {
            doc[k] = exports.parseRaw(f, raw[k]);
        }
    }
    log('parseRaw');
    log(doc);
    return doc;
};

exports.render = require('./render');
<|endoftext|>


<commit_before>
define([
    'flight/lib/component',
    '../withPopover',
    'util/vertex/formatters',
    'util/withDataRequest',
    'd3'
], function(
    defineComponent,
    withPopover,
    F,
    withDataRequest,
    d3) {
    'use strict';

    return defineComponent(PropertyInfo, withPopover, withDataRequest);

    function PropertyInfo() {

        this.defaultAttrs({
            deleteButtonSelector: '.btn-danger',
            editButtonSelector: '.btn-edit',
            addButtonSelector: '.btn-add',
            replyButtonSelector: '.reply',
            justificationValueSelector: 'a'
        });

        this.before('initialize', function(node, config) {
            config.template = 'propertyInfo/template';
        });

        this.after('initialize', function() {
            var self = this;

            this.after('setupWithTemplate', function() {
                this.dataRequest('config', 'properties')
                    .done(function(config) {
                        var splitRegex = /\s*,\s*/,
                            metadataDisplay =
                                config['properties.metadata.propertyNamesDisplay'].split(splitRegex).map(i18n),
                            metadataType =
                                config['properties.metadata.propertyNamesType'].split(splitRegex);

                        self.metadataProperties =
                            config['properties.metadata.propertyNames'].split(splitRegex);

                        if (self.metadataProperties.length !== metadataDisplay.length ||
                            self.metadataProperties.length !== metadataType.length) {
                            throw new Error('Metadata properties must have display names and types');
                        }
                        self.metadataPropertiesDisplayMap = _.object(self.metadataProperties, metadataDisplay);
                        self.metadataPropertiesTypeMap = _.object(self.metadataProperties, metadataType);

                        self.on(self.popover, 'click', {
                            deleteButtonSelector: self.onDelete,
                            editButtonSelector: self.onEdit,
                            addButtonSelector: self.onAdd,
                            replyButtonSelector: self.onReply,
                            justificationValueSelector: self.teardown
                        });

                        self.contentRoot = d3.select(self.popover.get(0))
                            .select('.popover-content');
                        self.update(self.attr.property);

                        self.on(document, 'verticesUpdated', self.onVerticesUpdated);
                    });
            });
        });

        this.update = function(property) {
            var vertexId = this.attr.vertexId,
                positionDialog = this.positionDialog.bind(this),
                displayNames = this.metadataPropertiesDisplayMap,
                displayTypes = this.metadataPropertiesTypeMap,
                isComment = property.name === 'http://lumify.io/comment#entry',
                isCommentCreator = isComment &&
                    property.metadata['http://lumify.io#modifiedBy'] === lumifyData.currentUser.id,
                canEdit = isComment ?
                    isCommentCreator :
                    (
                        F.vertex.sandboxStatus(property) ||
                        property.name === 'http://lumify.io#visibilityJson'
                    ),
                canDelete = canEdit && property.name !== 'http://lumify.io#visibilityJson',
                metadata = _.chain(this.metadataProperties || [])
                    .map(function(name) {
                        if ('metadata' in property) {
                            if (name in property.metadata) {
                                return [name, property.metadata[name]];
                            }
                        }
                        if (name in property) {
                            return [name, property[name]];
                        }
                    })
                    .compact()
                    .value()
                row = this.contentRoot.select('table')
                    .selectAll('tr')
                    .data(metadata)
                    .call(function() {
                        this.enter()
                            .append('tr')
                            .call(function() {
                                this.append('td').attr('class', 'property-name');
                                this.append('td').attr('class', 'property-value');
                            });
                    });

            this.contentRoot.select('.btn-danger')
                .style('display', canDelete ? 'inline' : 'none')
                .classed('requires-EDIT', !isComment)
                .classed('requires-COMMENT', isComment)
            this.contentRoot.select('.editadd')
                .style('display', isComment && !isCommentCreator ? 'none' : 'inline')
                .classed('btn-edit', canEdit)
                .classed('btn-add', !canEdit)
                .classed('requires-EDIT', !isComment)
                .classed('requires-COMMENT', isComment)
                .classed('nodelete', !canDelete)
                .text(canEdit ?
                  i18n('popovers.property_info.button.edit') :
                  i18n('popovers.property_info.button.add')
                );
            this.contentRoot.select('.reply').each(function() {
                var $this = $(this);
                if (isComment) {
                    $this.show();
                } else {
                    $this.hide();
                }
            })
            this.contentRoot.selectAll('tr')
                .call(function() {
                    var self = this;

                    this.select('td.property-name').text(function(d) {
                        return displayNames[d[0]];
                    });

                    var valueElement = self.select('td.property-value')
                        .each(function(d) {
                            var self = this,
                                $self = $(this),
                                typeName = displayTypes[d[0]],
                                formatter = F.vertex.metadata[typeName],
                                formatterAsync = F.vertex.metadata[typeName + 'Async'],
                                value = d[1];

                            if (formatter) {
                                formatter(this, value);
                            } else if (formatterAsync) {
                                formatterAsync(self, value, property, vertexId)
                                    .catch(function() {
                                        d3.select(self).text(i18n('popovers.property_info.error', value));
                                    })
                                    .finally(positionDialog);
                                d3.select(this).text(i18n('popovers.property_info.loading'));
                            } else {
                                console.warn('No metadata type formatter: ' + typeName);
                                d3.select(this).text(value);
                            }
                        });
                })

                // Hide blank metadata
                .each(function(d) {
                    $(this).toggle($(this).find('.property-value').text() !== '');
                });

            // Justification
            var justification = [];
            if (property.metadata &&
                (property.metadata._justificationMetadata || property.metadata._sourceMetadata)) {
                justification.push(true);
            }

            var table = this.contentRoot.select('table'),
                justificationRow = this.contentRoot.selectAll('.justification')

            justificationRow
                .data(justification)
                .call(function() {
                    this.enter()
                        .call(function() {
                            this.insert('div', 'button').attr('class', 'justification')
                                .call(function() {
                                    this.append('div')
                                        .attr('class', 'property-name property-justification')
                                        .text(i18n('popovers.property_info.justification'));
                                    this.append('div')
                                        .attr('class', 'justificationValue');
                                });
                        });
                    this.exit().remove();

                    var node = this.select('.justificationValue').node();
                    if (node) {
                        require(['util/vertex/justification/viewer'], function(JustificationViewer) {
                            $(node).teardownAllComponents();
                            JustificationViewer.attachTo(node, {
                                justificationMetadata: property.metadata._justificationMetadata,
                                sourceMetadata: property.metadata._sourceMetadata
                            });
                            positionDialog();
                        });
                    }
                })

            row.exit().remove();

            positionDialog();
        };

        this.onVerticesUpdated = function(event, data) {
            var vertex = _.findWhere(data.vertices, {
                    id: this.attr.vertexId
                }),
                property = vertex && _.findWhere(vertex.properties, {
                    name: this.attr.property.name,
                    key: this.attr.property.key
                });
            if (vertex && !property) {
                this.teardown();
            } else if (property) {
                this.attr.property = property;
                this.update(property);
            }
        };

        this.onReply = function() {
            var metadata = this.attr.property.metadata['http://lumify.io/comment#path'],
                path = (metadata ? (metadata + '/') : '') + this.attr.property.key;

            this.trigger('editProperty', {
                path: path
            });
            this.teardown();
        };

        this.onAdd = function() {
            this.trigger('editProperty', {
                property: _.omit(this.attr.property, 'key')
            });
            this.teardown();
        };

        this.onEdit = function() {
            this.trigger('editProperty', {
                property: this.attr.property
            });
            this.teardown();
        };

        this.onDelete = function(e) {
            e.stopPropagation();
            var button = this.popover.find('.btn-danger').addClass('loading').attr('disabled', true);
            this.trigger('deleteProperty', {
                property: _.pick(this.attr.property, 'name', 'key')
            });
        };
    }
});
<commit_msg>remove confidence mentadata from comments<commit_after>
define([
    'flight/lib/component',
    '../withPopover',
    'util/vertex/formatters',
    'util/withDataRequest',
    'd3'
], function(
    defineComponent,
    withPopover,
    F,
    withDataRequest,
    d3) {
    'use strict';

    return defineComponent(PropertyInfo, withPopover, withDataRequest);

    function PropertyInfo() {

        this.defaultAttrs({
            deleteButtonSelector: '.btn-danger',
            editButtonSelector: '.btn-edit',
            addButtonSelector: '.btn-add',
            replyButtonSelector: '.reply',
            justificationValueSelector: 'a'
        });

        this.before('initialize', function(node, config) {
            config.template = 'propertyInfo/template';
        });

        this.after('initialize', function() {
            var self = this;

            this.after('setupWithTemplate', function() {
                this.dataRequest('config', 'properties')
                    .done(function(config) {
                        var splitRegex = /\s*,\s*/,
                            metadataDisplay =
                                config['properties.metadata.propertyNamesDisplay'].split(splitRegex).map(i18n),
                            metadataType =
                                config['properties.metadata.propertyNamesType'].split(splitRegex);

                        self.metadataProperties =
                            config['properties.metadata.propertyNames'].split(splitRegex);

                        if (self.metadataProperties.length !== metadataDisplay.length ||
                            self.metadataProperties.length !== metadataType.length) {
                            throw new Error('Metadata properties must have display names and types');
                        }
                        self.metadataPropertiesDisplayMap = _.object(self.metadataProperties, metadataDisplay);
                        self.metadataPropertiesTypeMap = _.object(self.metadataProperties, metadataType);

                        self.on(self.popover, 'click', {
                            deleteButtonSelector: self.onDelete,
                            editButtonSelector: self.onEdit,
                            addButtonSelector: self.onAdd,
                            replyButtonSelector: self.onReply,
                            justificationValueSelector: self.teardown
                        });

                        self.contentRoot = d3.select(self.popover.get(0))
                            .select('.popover-content');
                        self.update(self.attr.property);

                        self.on(document, 'verticesUpdated', self.onVerticesUpdated);
                    });
            });
        });

        this.update = function(property) {
            var vertexId = this.attr.vertexId,
                positionDialog = this.positionDialog.bind(this),
                displayNames = this.metadataPropertiesDisplayMap,
                displayTypes = this.metadataPropertiesTypeMap,
                isComment = property.name === 'http://lumify.io/comment#entry',
                isCommentCreator = isComment &&
                    property.metadata['http://lumify.io#modifiedBy'] === lumifyData.currentUser.id,
                canEdit = isComment ?
                    isCommentCreator :
                    (
                        F.vertex.sandboxStatus(property) ||
                        property.name === 'http://lumify.io#visibilityJson'
                    ),
                canDelete = canEdit && property.name !== 'http://lumify.io#visibilityJson',
                metadata = _.chain(this.metadataProperties || [])
                    .map(function(name) {
                        if ('metadata' in property) {
                            if (name in property.metadata) {
                                return [name, property.metadata[name]];
                            }
                        }
                        if (name in property) {
                            return [name, property[name]];
                        }
                    })
                    .compact()
                    .filter(function(m) {
                        if (m[0] === 'http://lumify.io#confidence' && isComment) {
                            return false;
                        }
                        return true;
                    })
                    .value()
                row = this.contentRoot.select('table')
                    .selectAll('tr')
                    .data(metadata)
                    .call(function() {
                        this.enter()
                            .append('tr')
                            .call(function() {
                                this.append('td').attr('class', 'property-name');
                                this.append('td').attr('class', 'property-value');
                            });
                    });

            this.contentRoot.select('.btn-danger')
                .style('display', canDelete ? 'inline' : 'none')
                .classed('requires-EDIT', !isComment)
                .classed('requires-COMMENT', isComment)
            this.contentRoot.select('.editadd')
                .style('display', isComment && !isCommentCreator ? 'none' : 'inline')
                .classed('btn-edit', canEdit)
                .classed('btn-add', !canEdit)
                .classed('requires-EDIT', !isComment)
                .classed('requires-COMMENT', isComment)
                .classed('nodelete', !canDelete)
                .text(canEdit ?
                  i18n('popovers.property_info.button.edit') :
                  i18n('popovers.property_info.button.add')
                );
            this.contentRoot.select('.reply').each(function() {
                var $this = $(this);
                if (isComment) {
                    $this.show();
                } else {
                    $this.hide();
                }
            })
            this.contentRoot.selectAll('tr')
                .call(function() {
                    var self = this;

                    this.select('td.property-name').text(function(d) {
                        return displayNames[d[0]];
                    });

                    var valueElement = self.select('td.property-value')
                        .each(function(d) {
                            var self = this,
                                $self = $(this),
                                typeName = displayTypes[d[0]],
                                formatter = F.vertex.metadata[typeName],
                                formatterAsync = F.vertex.metadata[typeName + 'Async'],
                                value = d[1];

                            if (formatter) {
                                formatter(this, value);
                            } else if (formatterAsync) {
                                formatterAsync(self, value, property, vertexId)
                                    .catch(function() {
                                        d3.select(self).text(i18n('popovers.property_info.error', value));
                                    })
                                    .finally(positionDialog);
                                d3.select(this).text(i18n('popovers.property_info.loading'));
                            } else {
                                console.warn('No metadata type formatter: ' + typeName);
                                d3.select(this).text(value);
                            }
                        });
                })

                // Hide blank metadata
                .each(function(d) {
                    $(this).toggle($(this).find('.property-value').text() !== '');
                });

            // Justification
            var justification = [];
            if (property.metadata &&
                (property.metadata._justificationMetadata || property.metadata._sourceMetadata)) {
                justification.push(true);
            }

            var table = this.contentRoot.select('table'),
                justificationRow = this.contentRoot.selectAll('.justification')

            justificationRow
                .data(justification)
                .call(function() {
                    this.enter()
                        .call(function() {
                            this.insert('div', 'button').attr('class', 'justification')
                                .call(function() {
                                    this.append('div')
                                        .attr('class', 'property-name property-justification')
                                        .text(i18n('popovers.property_info.justification'));
                                    this.append('div')
                                        .attr('class', 'justificationValue');
                                });
                        });
                    this.exit().remove();

                    var node = this.select('.justificationValue').node();
                    if (node) {
                        require(['util/vertex/justification/viewer'], function(JustificationViewer) {
                            $(node).teardownAllComponents();
                            JustificationViewer.attachTo(node, {
                                justificationMetadata: property.metadata._justificationMetadata,
                                sourceMetadata: property.metadata._sourceMetadata
                            });
                            positionDialog();
                        });
                    }
                })

            row.exit().remove();

            positionDialog();
        };

        this.onVerticesUpdated = function(event, data) {
            var vertex = _.findWhere(data.vertices, {
                    id: this.attr.vertexId
                }),
                property = vertex && _.findWhere(vertex.properties, {
                    name: this.attr.property.name,
                    key: this.attr.property.key
                });
            if (vertex && !property) {
                this.teardown();
            } else if (property) {
                this.attr.property = property;
                this.update(property);
            }
        };

        this.onReply = function() {
            var metadata = this.attr.property.metadata['http://lumify.io/comment#path'],
                path = (metadata ? (metadata + '/') : '') + this.attr.property.key;

            this.trigger('editProperty', {
                path: path
            });
            this.teardown();
        };

        this.onAdd = function() {
            this.trigger('editProperty', {
                property: _.omit(this.attr.property, 'key')
            });
            this.teardown();
        };

        this.onEdit = function() {
            this.trigger('editProperty', {
                property: this.attr.property
            });
            this.teardown();
        };

        this.onDelete = function(e) {
            e.stopPropagation();
            var button = this.popover.find('.btn-danger').addClass('loading').attr('disabled', true);
            this.trigger('deleteProperty', {
                property: _.pick(this.attr.property, 'name', 'key')
            });
        };
    }
});
<|endoftext|>


<commit_before><commit_msg>feat: Execute function from parent controller since child controller<commit_after><|endoftext|>


<commit_before>var games = [
    "RationsPlease",
    "SuperSeaSerpentSimulator"
];

var portfolioItemTemplatePath = "templates/portfolioItemTemplate.html";
var modalTemplatePath = "templates/modalTemplate.html";

var portfolioItemTemplate;
var modalTemplate;

var versionNum = "v.29";

var GenerateContent = function(game){
	var portfolioItem = portfolioItemTemplate.clone();
	portfolioItem.id = game + "-portfolio-item";
	$("#portfolio-items").append(portfolioItem);

	var modal = modalTemplate.clone();
	modal = game + "-portfolio-modal";
	$("#modals").append(modal);
};

var LoadPortfolioItemData = function(game, data) {
	var portfolioItem = $("#"+game+"-portfolio-item");

	if(data.hasOwnProperty("id")) {
		//console.log("setting id");
		var modalIdHolder = portfolioItem.find(".portfolio-link")[0];
		modalIdHolder.href = "#" + data["id"] + "-modal";
		//console.log(modalIdHolder);
	}
	else {
		console.log("missing id");
	}

	if(data.hasOwnProperty("portfolio-item-title")) {
		//console.log("setting title");
		var title = portfolioItem.find(".portfolio-item-title")[0];
		title.innerHTML = data["portfolio-item-title"];
		//console.log(title);
	}
	else {
		console.log("missing portfolio item title");
	}

	if(data.hasOwnProperty("portfolio-item-image")) {
		//console.log("setting image");
		var image = portfolioItem.find(".portfolio-item-image")[0];
		image.src = data["portfolio-item-image"];
		//console.log(image);
	}
	else {
		console.log("missing portfolio item image");
	}

	//console.log(portfolioItem);
};

var LoadModalData = function(game, data) {
	var modal = $("#"+game+"-portfolio-modal");

	if(data.hasOwnProperty("id")) {
		//console.log("setting id");
		var modalIdHolder = modal.find(".portfolio-modal")[0];
		modalIdHolder.id = data["id"] + "-modal";
		//console.log(modalIdHolder);
	}
	else {
		console.log("missing id");
	}

	if(data.hasOwnProperty("modal-projectHeading")) {
		//console.log("setting projectHeading");
		var projectHeading = modal.find(".modal-projectHeading")[0];
		projectHeading.innerHTML = data["modal-projectHeading"];
		//console.log(projectHeading);
	}
	else {
		console.log("missing modal project heading");
	}

	if(data.hasOwnProperty("modal-intro")) {
		//console.log("setting intro");
		var intro = modal.find(".modal-intro")[0];
		intro.innerHTML = data["modal-intro"];
		//console.log(intro);
	}
	else {
		console.log("missing modal intro");
	}

	if(data.hasOwnProperty("modal-img")) {
		//console.log("setting image");
		var image = modal.find(".modal-img")[0];
		image.src = data["modal-img"];
		//console.log(image);
	}
	else {
		console.log("missing modal image");
	}

	if(data.hasOwnProperty("modal-html")) {
		//console.log("setting html");
		var html = modal.find(".modal-html")[0];
		html.innerHTML = data["modal-html"];
		//console.log(html);
	}
	else {
		console.log("missing modal html");
	}

	//console.log(modal);
};

var LoadFromJson = function(game, data) {
	//console.log(data);
	LoadPortfolioItemData(game, data);
	LoadModalData(game, data);
};

var LoadContent = function(game) {
	//console.log("Generating content for " + game);
	$.getJSON("data/" + game + ".json", null, function(data) {
		LoadFromJson(game, data);
	});
};

$(document).ready(function() {
	console.log(versionNum);

	portfolioItemTemplate = $('<div>').load(portfolioItemTemplatePath, function() { // load the portfolio item template
		modalTemplate = $('<div>').load(modalTemplatePath, function() { // load the modal template
			// fill in the data
			for (var i = games.length - 1; i >= 0; i--) {
				var game = games[i];
				GenerateContent(game); // generate first to ensure ordering
				LoadContent(game);
			}
		});
	});
});<commit_msg>json test 29<commit_after>var games = [
    "RationsPlease",
    "SuperSeaSerpentSimulator"
];

var portfolioItemTemplatePath = "templates/portfolioItemTemplate.html";
var modalTemplatePath = "templates/modalTemplate.html";

var portfolioItemTemplate;
var modalTemplate;

var versionNum = "v.29";

var GenerateContent = function(game){
	var portfolioItem = portfolioItemTemplate.clone();
	portfolioItem.id = game + "-portfolio-item";
	$("#portfolio-items").append(portfolioItem);

	var modal = modalTemplate.clone();
	modal = game + "-portfolio-modal";
	$("#modals").append(modal);
};

var LoadPortfolioItemData = function(game, data) {
	var portfolioItem = $(game+"-portfolio-item");

	if(data.hasOwnProperty("id")) {
		//console.log("setting id");
		var modalIdHolder = portfolioItem.find(".portfolio-link")[0];
		modalIdHolder.href = "#" + data["id"] + "-modal";
		//console.log(modalIdHolder);
	}
	else {
		console.log("missing id");
	}

	if(data.hasOwnProperty("portfolio-item-title")) {
		//console.log("setting title");
		var title = portfolioItem.find(".portfolio-item-title")[0];
		title.innerHTML = data["portfolio-item-title"];
		//console.log(title);
	}
	else {
		console.log("missing portfolio item title");
	}

	if(data.hasOwnProperty("portfolio-item-image")) {
		//console.log("setting image");
		var image = portfolioItem.find(".portfolio-item-image")[0];
		image.src = data["portfolio-item-image"];
		//console.log(image);
	}
	else {
		console.log("missing portfolio item image");
	}

	//console.log(portfolioItem);
};

var LoadModalData = function(game, data) {
	var modal = $(game+"-portfolio-modal");

	if(data.hasOwnProperty("id")) {
		//console.log("setting id");
		var modalIdHolder = modal.find(".portfolio-modal")[0];
		modalIdHolder.id = data["id"] + "-modal";
		//console.log(modalIdHolder);
	}
	else {
		console.log("missing id");
	}

	if(data.hasOwnProperty("modal-projectHeading")) {
		//console.log("setting projectHeading");
		var projectHeading = modal.find(".modal-projectHeading")[0];
		projectHeading.innerHTML = data["modal-projectHeading"];
		//console.log(projectHeading);
	}
	else {
		console.log("missing modal project heading");
	}

	if(data.hasOwnProperty("modal-intro")) {
		//console.log("setting intro");
		var intro = modal.find(".modal-intro")[0];
		intro.innerHTML = data["modal-intro"];
		//console.log(intro);
	}
	else {
		console.log("missing modal intro");
	}

	if(data.hasOwnProperty("modal-img")) {
		//console.log("setting image");
		var image = modal.find(".modal-img")[0];
		image.src = data["modal-img"];
		//console.log(image);
	}
	else {
		console.log("missing modal image");
	}

	if(data.hasOwnProperty("modal-html")) {
		//console.log("setting html");
		var html = modal.find(".modal-html")[0];
		html.innerHTML = data["modal-html"];
		//console.log(html);
	}
	else {
		console.log("missing modal html");
	}

	//console.log(modal);
};

var LoadFromJson = function(game, data) {
	//console.log(data);
	LoadPortfolioItemData(game, data);
	LoadModalData(game, data);
};

var LoadContent = function(game) {
	//console.log("Generating content for " + game);
	$.getJSON("data/" + game + ".json", null, function(data) {
		LoadFromJson(game, data);
	});
};

$(document).ready(function() {
	console.log(versionNum);

	portfolioItemTemplate = $('<div>').load(portfolioItemTemplatePath, function() { // load the portfolio item template
		modalTemplate = $('<div>').load(modalTemplatePath, function() { // load the modal template
			// fill in the data
			for (var i = games.length - 1; i >= 0; i--) {
				var game = games[i];
				GenerateContent(game); // generate first to ensure ordering
				LoadContent(game);
			}
		});
	});
});<|endoftext|>


<commit_before>const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')

let win

function createWindow () {
  win = new BrowserWindow({width: 800, height: 600});

  win.loadURL(url.format({
    pathname: path.join(__dirname, 'app','index.html'),
    protocol: 'file:',
    slashes: true
  }))
  win.on('closed', () => {
    win = null
  })
}

app.on('ready', createWindow)

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (win === null) {
    createWindow()
  }
})
<commit_msg>Fix windows size<commit_after>const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')

let win

function createWindow () {
  win = new BrowserWindow({width: 800, height: 600, resizable: false});

  win.loadURL(url.format({
    pathname: path.join(__dirname, 'app','index.html'),
    protocol: 'file:',
    slashes: true
  }))
  win.on('closed', () => {
    win = null
  })
}

app.on('ready', createWindow)

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (win === null) {
    createWindow()
  }
})
<|endoftext|>


<commit_before>const electron = require('electron')
const {ipcMain, globalShortcut, app, Tray, BrowserWindow, dialog} = require('electron')
const autoUpdater = require('electron-updater').autoUpdater
autoUpdater.requestHeaders = {"Cache-Control": "no-cache"}
require('electron-debug')({
  enabled: true // enable debug shortcuts in prod build
})

const child_process = require('child_process')
const ms = require('ms')
const path = require('path')
const url = require('url')
const log = require('electron-log')
const assetsDirectory = path.join(__dirname, 'assets')
const {version} = require('./package')
const fs = require('fs')
const osascript = require('node-osascript')
const appDataPath = app.getPath('userData')
currentMobstersFilePath = path.join(appDataPath, 'active-mobsters')

log.info(`Running version ${version}`)

const returnFocusOsascript = `tell application "System Events"
	set activeApp to name of application processes whose frontmost is true
	if (activeApp = {"Mobster"} or activeApp = {"Electron"}) then
		tell application "System Events"
      delay 0.25 -- prevent issues when user is still holding down Command for a fraction of a second pressing Cmd+Shift+K shortcut
			key code 48 using {command down}
		end tell
	end if
end tell`

function returnFocusMac() {
  osascript.execute(returnFocusOsascript, function(err, result, raw){
  if (err) { return console.error(err) }
  console.log(result, raw)
});
}

function writeToFile(filePath, fileContents) {
  fs.writeFile(filePath, fileContents, function(err) {
    if (err) {
      console.log(err)
    }
  })
}

function updateMobsterNamesFile(currentMobsterNames) {
    writeToFile(currentMobstersFilePath, currentMobsterNames)
}

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow, timerWindow, tray

const timerHeight = 130
const timerWidth = 150

const onMac = /^darwin/.test(process.platform)
const onWindows = /^win/.test(process.platform)
const isDev = require('electron-is-dev')

function focusMainWindow() {
    // TODO: workaround - remove once
    // https://github.com/electron/electron/issues/2867#issuecomment-264312493 has been resolved
    if (onWindows) {
      mainWindow.minimize()
    }
    mainWindow.show()
    mainWindow.focus()
}

function hideMainWindow() {
  mainWindow.hide()
  returnFocus()
}

function positionWindowLeft(window) {
  let {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
  window.setPosition(0, height - timerHeight);
}

function positionWindowRight(window) {
  const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
  window.setPosition(width - timerWidth, height - timerHeight);
}

function returnFocus() {
    if (onMac) {
      returnFocusMac()
    }
}

function startTimer(flags) {
  timerWindow = new BrowserWindow({transparent: true, frame: false, alwaysOnTop: true,
    width: timerWidth, height: timerHeight, focusable: false})

  positionWindowRight(timerWindow)

  ipcMain.once('timer-flags', (event) => {
    event.returnValue = flags
  })

  timerWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'timer.html'),
    protocol: 'file:',
    slashes: true
  }))

}

ipcMain.on('timer-mouse-hover', (event) => {
  [x, y] = timerWindow.getPosition()
  if (x === 0) {
    positionWindowRight(timerWindow)
  } else {
    positionWindowLeft(timerWindow)
  }
})

function closeTimer() {
  if (timerWindow) {
    timerWindow.close()
    timerWindow = null
  }
}

function createWindow () {
  mainWindow = new BrowserWindow({
    transparent: true,
    frame: false,
    alwaysOnTop: true,
    icon: `${assetsDirectory}/icon.ico`
  })
  setTimeout(() => {
    mainWindow.setAlwaysOnTop(true) // delay to workaround https://github.com/electron/electron/issues/8287
  }, 1000)
  mainWindow.maximize()

  electron.screen.on('display-metrics-changed', function () {
    mainWindow.maximize()
  })

  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'setup.html'),
    protocol: 'file:',
    slashes: true
  }))

  ipcMain.on('start-timer', (event, flags) => {
    startTimer(flags)
    hideMainWindow()
  })

  ipcMain.on('save-mobsters-file', (event, currentMobsterNames) => {
    updateMobsterNamesFile(currentMobsterNames)
  })

  ipcMain.on('timer-done', (event, timeElapsed) => {
    closeTimer()
    mainWindow.webContents.send('timer-done', timeElapsed)
    focusMainWindow()
  })

  ipcMain.on('quit', (event) => {
    app.quit()
  })

  ipcMain.on('showFeedbackForm', (event) => {
    new BrowserWindow({show: true, frame: true, alwaysOnTop: true}).loadURL('https://dillonkearns.typeform.com/to/k9P6iV')
  })

  ipcMain.on('hide', (event) => {
    toggleMainWindow()
  })

  ipcMain.on('quit-and-install', () => {
    autoUpdater.quitAndInstall()
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })

}

function toggleMainWindow() {
  if (mainWindow.isVisible()) {
    hideMainWindow()
  }
  else {
    focusMainWindow()
  }
}

function onClickTrayIcon() {
  if (!timerWindow) {
    toggleMainWindow()
  } else {
    closeTimer()
    focusMainWindow()
  }
}

const createTray = () => {
  tray = new Tray(path.join(assetsDirectory, 'tray-icon.png'))
  tray.on('right-click', onClickTrayIcon)
  tray.on('double-click', onClickTrayIcon)
  tray.on('click', onClickTrayIcon)
}

function onReady() {
  createWindow()
  createTray()
  registerShortcuts()
  setupAutoUpdater()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', onReady)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

function setupAutoUpdater() {
  autoUpdater.logger = log;
  autoUpdater.on('checking-for-update', () => {
      log.info('checking-for-update')
  });

  autoUpdater.on('update-available', () => {
      log.info('update-available')
  });

  autoUpdater.on('update-downloaded', (versionInfo) => {
    log.info('update-downloaded... Imma let you finish... but first, Imma install it once you push okay ;-)')
    log.info('update-available: ', versionInfo)
    mainWindow.webContents.send('update-downloaded', versionInfo)
  });

  autoUpdater.on('update-not-available', () => {
    log.info('update-not-available')
  });
  if (!isDev) {
    autoUpdater.checkForUpdates()
  }
}

function registerShortcuts() {
  globalShortcut.register('CommandOrControl+Shift+K', () => {
    if (timerWindow) {
      let dialogActionIndex = dialog.showMessageBox({
        type: 'warning',
        buttons: ['Stop timer', 'Keep it running'],
        message: 'Stop the timer?',
        cancelId: 1,
      })
      if (dialogActionIndex !== 1) {
        closeTimer()
        mainWindow.show()
        mainWindow.focus()
      }
    } else {
      toggleMainWindow()
    }
  })
  globalShortcut.register('CommandOrControl+Shift+;', copyActiveMobsters)
}

function copyActiveMobsters() {
  mainWindow.webContents.send('copy-active-mobsters')
}
<commit_msg>Enusre that dialog box pops up in foreground<commit_after>const electron = require('electron')
const {ipcMain, globalShortcut, app, Tray, BrowserWindow, dialog} = require('electron')
const autoUpdater = require('electron-updater').autoUpdater
autoUpdater.requestHeaders = {"Cache-Control": "no-cache"}
require('electron-debug')({
  enabled: true // enable debug shortcuts in prod build
})

const child_process = require('child_process')
const ms = require('ms')
const path = require('path')
const url = require('url')
const log = require('electron-log')
const assetsDirectory = path.join(__dirname, 'assets')
const {version} = require('./package')
const fs = require('fs')
const osascript = require('node-osascript')
const appDataPath = app.getPath('userData')
currentMobstersFilePath = path.join(appDataPath, 'active-mobsters')

log.info(`Running version ${version}`)

const returnFocusOsascript = `tell application "System Events"
	set activeApp to name of application processes whose frontmost is true
	if (activeApp = {"Mobster"} or activeApp = {"Electron"}) then
		tell application "System Events"
      delay 0.25 -- prevent issues when user is still holding down Command for a fraction of a second pressing Cmd+Shift+K shortcut
			key code 48 using {command down}
		end tell
	end if
end tell`

function returnFocusMac() {
  osascript.execute(returnFocusOsascript, function(err, result, raw){
  if (err) { return console.error(err) }
  console.log(result, raw)
});
}

function writeToFile(filePath, fileContents) {
  fs.writeFile(filePath, fileContents, function(err) {
    if (err) {
      console.log(err)
    }
  })
}

function updateMobsterNamesFile(currentMobsterNames) {
    writeToFile(currentMobstersFilePath, currentMobsterNames)
}

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow, timerWindow, tray

const timerHeight = 130
const timerWidth = 150

const onMac = /^darwin/.test(process.platform)
const onWindows = /^win/.test(process.platform)
const isDev = require('electron-is-dev')

function focusMainWindow() {
    // TODO: workaround - remove once
    // https://github.com/electron/electron/issues/2867#issuecomment-264312493 has been resolved
    if (onWindows) {
      mainWindow.minimize()
    }
    mainWindow.show()
    mainWindow.focus()
}

function hideMainWindow() {
  mainWindow.hide()
  returnFocus()
}

function positionWindowLeft(window) {
  let {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
  window.setPosition(0, height - timerHeight);
}

function positionWindowRight(window) {
  const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize
  window.setPosition(width - timerWidth, height - timerHeight);
}

function returnFocus() {
    if (onMac) {
      returnFocusMac()
    }
}

function startTimer(flags) {
  timerWindow = new BrowserWindow({transparent: true, frame: false, alwaysOnTop: true,
    width: timerWidth, height: timerHeight, focusable: false})

  positionWindowRight(timerWindow)

  ipcMain.once('timer-flags', (event) => {
    event.returnValue = flags
  })

  timerWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'timer.html'),
    protocol: 'file:',
    slashes: true
  }))

}

ipcMain.on('timer-mouse-hover', (event) => {
  [x, y] = timerWindow.getPosition()
  if (x === 0) {
    positionWindowRight(timerWindow)
  } else {
    positionWindowLeft(timerWindow)
  }
})

function closeTimer() {
  if (timerWindow) {
    timerWindow.close()
    timerWindow = null
  }
}

function createWindow () {
  mainWindow = new BrowserWindow({
    transparent: true,
    frame: false,
    alwaysOnTop: true,
    icon: `${assetsDirectory}/icon.ico`
  })
  setTimeout(() => {
    mainWindow.setAlwaysOnTop(true) // delay to workaround https://github.com/electron/electron/issues/8287
  }, 1000)
  mainWindow.maximize()

  electron.screen.on('display-metrics-changed', function () {
    mainWindow.maximize()
  })

  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'setup.html'),
    protocol: 'file:',
    slashes: true
  }))

  ipcMain.on('start-timer', (event, flags) => {
    startTimer(flags)
    hideMainWindow()
  })

  ipcMain.on('save-mobsters-file', (event, currentMobsterNames) => {
    updateMobsterNamesFile(currentMobsterNames)
  })

  ipcMain.on('timer-done', (event, timeElapsed) => {
    closeTimer()
    mainWindow.webContents.send('timer-done', timeElapsed)
    focusMainWindow()
  })

  ipcMain.on('quit', (event) => {
    app.quit()
  })

  ipcMain.on('showFeedbackForm', (event) => {
    new BrowserWindow({show: true, frame: true, alwaysOnTop: true}).loadURL('https://dillonkearns.typeform.com/to/k9P6iV')
  })

  ipcMain.on('hide', (event) => {
    toggleMainWindow()
  })

  ipcMain.on('quit-and-install', () => {
    autoUpdater.quitAndInstall()
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })

}

function toggleMainWindow() {
  if (mainWindow.isVisible()) {
    hideMainWindow()
  }
  else {
    focusMainWindow()
  }
}

function onClickTrayIcon() {
  if (!timerWindow) {
    toggleMainWindow()
  } else {
    closeTimer()
    focusMainWindow()
  }
}

const createTray = () => {
  tray = new Tray(path.join(assetsDirectory, 'tray-icon.png'))
  tray.on('right-click', onClickTrayIcon)
  tray.on('double-click', onClickTrayIcon)
  tray.on('click', onClickTrayIcon)
}

function onReady() {
  createWindow()
  createTray()
  registerShortcuts()
  setupAutoUpdater()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', onReady)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

function setupAutoUpdater() {
  autoUpdater.logger = log;
  autoUpdater.on('checking-for-update', () => {
      log.info('checking-for-update')
  });

  autoUpdater.on('update-available', () => {
      log.info('update-available')
  });

  autoUpdater.on('update-downloaded', (versionInfo) => {
    log.info('update-downloaded... Imma let you finish... but first, Imma install it once you push okay ;-)')
    log.info('update-available: ', versionInfo)
    mainWindow.webContents.send('update-downloaded', versionInfo)
  });

  autoUpdater.on('update-not-available', () => {
    log.info('update-not-available')
  });
  if (!isDev) {
    autoUpdater.checkForUpdates()
  }
}

function registerShortcuts() {
  globalShortcut.register('CommandOrControl+Shift+K', () => {
    if (timerWindow) {
      app.focus() // ensure that app is focused so dialog appears in foreground
      let dialogActionIndex = dialog.showMessageBox({
        type: 'warning',
        buttons: ['Stop timer', 'Keep it running'],
        message: 'Stop the timer?',
        cancelId: 1,
      })
      if (dialogActionIndex !== 1) {
        closeTimer()
        mainWindow.show()
        mainWindow.focus()
      }
    } else {
      toggleMainWindow()
    }
  })
  globalShortcut.register('CommandOrControl+Shift+;', copyActiveMobsters)
}

function copyActiveMobsters() {
  mainWindow.webContents.send('copy-active-mobsters')
}
<|endoftext|>


<commit_before>import React from 'react'
import { connect } from 'react-redux'

import { Response } from '../api'
import { setActive } from './actions/Overlay'
import { BodyInput, CaptureCompletion } from './BodyInput'
import { BoostRules } from './Boosts'
import { Results } from './Results'
import Tabs from './Tabs'
import { triggerSearch } from './actions/Search'
import { FieldInstanceBoost } from '../api/IndexBoost'
import { PageLimitOffset } from './Paginator'

const close = ({ closeOverlay }) => (

×

ESC

)

const Close = connect(
  null,
  dispatch => ({ closeOverlay: () => dispatch(setActive(false)) })
)(close)

const logo = ({ closeOverlay, src, alt, className }) => (

)

const Logo = connect(
  null,
  dispatch => ({ closeOverlay: () => dispatch(setActive(false)) })
)(logo)

const overlay = ({ active, children }) => {
  if (!active) {
    return null
  }

  return (

        {children}

  )
}

const Overlay = connect(
  ({ overlay }) => ({ active: overlay.active })
)(overlay)

class wrappedBodyInput extends React.Component {
  componentDidUpdate() {
    this.props.triggerSearch()
  }

  render() {
    return (
      <BodyInput
        prefixBoosts={this.props.prefixBoosts}
        containsBoosts={this.props.containsBoosts}
      />
    )
  }
}

const WrappedBodyInput = connect(
  ({ search }) => ({ body: search.body }),
  (dispatch) => ({ triggerSearch: () => dispatch(triggerSearch('default')) })
)(wrappedBodyInput)

const InstanceFieldBoosts = ({ instanceBoosts = {} }) => (

    {Object.keys(instanceBoosts).map(i => (
      <FieldInstanceBoost key={i} field={i} value={instanceBoosts[i]} namespace='default'/>
    ))}

)

const DefaultOverlay = ({ tabs, tabsOnChange, defaultTab, logoUrl, prefixBoosts, containsBoosts, instanceBoosts, showImage }) => (
  <Overlay>
    <Close />

      <Logo src={logoUrl} alt='Logo' />
      <WrappedBodyInput prefixBoosts={prefixBoosts} containsBoosts={containsBoosts} />
      <InstanceFieldBoosts instanceBoosts={instanceBoosts}/>
      <BoostRules />
      <PageLimitOffset />

    <Tabs defaultTab={defaultTab} tabs={tabs} onChange={tabsOnChange} />
    <Response>
      <CaptureCompletion />
      <Results showImage={showImage} />
    </Response>
  </Overlay>
)

export { DefaultOverlay, Overlay, Close, Logo }
<commit_msg>Rework default overlay to use pipelines<commit_after>import React from 'react'
import { connect } from 'react-redux'

import Response from '../pipeline/Response'
import { setActive } from './actions/Overlay'
import { Results, PipelineSummary } from './Results'
import Tabs from './Tabs'
import PipelineInput from '../pipeline/PipelineInput'

const close = ({ closeOverlay }) => (

×

ESC

)

const Close = connect(
  null,
  dispatch => ({ closeOverlay: () => dispatch(setActive(false)) })
)(close)

const logo = ({ closeOverlay, src, alt, className }) => (

)

const Logo = connect(
  null,
  dispatch => ({ closeOverlay: () => dispatch(setActive(false)) })
)(logo)

const overlay = ({ active, children }) => {
  if (!active) {
    return null
  }

  return (

        {children}

  )
}

const Overlay = connect(
  ({ overlay }) => ({ active: overlay.active })
)(overlay)

const DefaultOverlay = ({ tabs, tabsOnChange, defaultTab, logoUrl, pipeline }) => (
  <Overlay>
    <Close />

      <Logo src={logoUrl} alt='Logo' />
      <PipelineInput pipeline={pipeline} />

    <Tabs defaultTab={defaultTab} tabs={tabs} onChange={tabsOnChange} />
    <Response pipeline={pipeline}>
      <PipelineSummary pipeline={pipeline} />
      <Results />
    </Response>
  </Overlay>
)

export { DefaultOverlay, Overlay, Close, Logo }
<|endoftext|>