<commit_before>var TFS = Titanium.Filesystem;
var src = Titanium.Process.getEnv('KR_APP_INSTALL_FROM');
var appname, appid, appurl, apppub, appimg;
var launch;
var win32 = false;

function launchApp()
{
	// cause the app to exit with special restart
	// code which will re-launch the original app
	if (launch)
	{
		try
		{
			Titanium.Desktop.openApplication(String(launch));
		}
		catch(E)
		{
			alert("Error launching: "+launch+". Message: "+E);
		}
	}
	Titanium.App.exit(0);
}

var install_started;

function finishInstall()
{
	var ts = new Date().getTime()-install_started;
	if (ts < 1000)
	{
		setTimeout(function()
		{
			finishInstall();
		},1000);

		return;
	}
	$('#install_app').slideUp(200);
	$('#install_app_finished').fadeIn();
}

function installApp(dir)
{
	if (!win32)
	{
		var dest = TFS.getFile(src,'.installed');
		dest.write("#"+new Date());
		try
		{
			Titanium.Process.restart();
		}
		catch(E)
		{
			alert(E);
		}
		return;
	}
	install_started = new Date().getTime();

	var runtime = TFS.getFile(Titanium.Process.getEnv('KR_RUNTIME'));
	var dest = TFS.getFile(dir);

	// create app structure
	var result = Titanium.createApp(runtime,dest,appname,appid,false);

	// remember in case we launch
	launch = result.executable;

	var resource_files = TFS.getFile(src, 'Resources');
	var module_files = TFS.getFile(src, 'modules');
	var runtime_files = TFS.getFile(src, 'runtime');

	// manifest + tiapp.xml (below) + app resources dir
	var count = 3;
	if (module_files.isDirectory())
	{
		count++;
	}
	if (runtime_files.isDirectory())
	{
		count++;
	}

	var increment = 100 / count;
	var value = 0;
	var total = 0;

	function fileCopied()
	{
		value += increment;
		total++;
		$('#progressbar_install').progressbar('value',value)

		if (count == total)
		{
			// complete
			finishInstall();
		}
	}

	TFS.getFile(src,'manifest').copy(result.base);
	fileCopied();
	TFS.getFile(src,'tiapp.xml').copy(result.base);
	fileCopied();

	TFS.asyncCopy(resource_files,result.resources,function(fn,c,t)
	{
		fileCopied();
		if (c == t)
		{
			if (module_files.isDirectory())
			{
				var module_dest = TFS.getFile(result.base, 'modules');
				TFS.asyncCopy(module_files,module_dest,function(fn,c,t) { fileCopied(); });
			}
			if (runtime_files.isDirectory())
			{
				var runtime_dest = TFS.getFile(result.base, 'runtime');
				TFS.asyncCopy(runtime_files,runtime_dest,function(fn,count,total) { fileCopied(); });
			}
		}
	});
}

$(function(){

	if (Titanium.platform != 'win32')
	{
		$('#install_screen').hide();
		$('#install_btn').html('Continue');
		Titanium.UI.currentWindow.setHeight(300);
	}
	else
	{
		win32 = true;
		Titanium.UI.currentWindow.setHeight(510);
	}
	Titanium.UI.currentWindow.setWidth(675);
	Titanium.UI.currentWindow.setX(Titanium.UI.currentWindow.CENTERED);
	Titanium.UI.currentWindow.setY(Titanium.UI.currentWindow.CENTERED);

	// show install info screen
	$('#installation_info').fadeIn();
	$('#install').removeClass('disabled');

	// create progress bars
	$('#progressbar').progressbar({value:0})
	$('#progressbar_install').progressbar({value:0})

	// cancel install click
	$('#cancel').click(function()
	{
		if (confirm('Are you sure you want to cancel?')==true)
		{
			Titanium.UI.currentWindow.close();
		}
	});

	// finish install click
	$('#finish').click(function()
	{
		if ($('#open_after_install').is(':checked'))
		{
			launchApp();
		}
		else
		{
			Titanium.App.exit(0);
		}
	})

	// install app click
	$('#install').click(function()
	{
		if (win32)
		{
			$('#installation_info').slideUp(100);
			Titanium.UI.currentWindow.setHeight(220)
			Titanium.UI.currentWindow.setX(Titanium.UI.currentWindow.CENTERED);

			// show installation screen
			$('#install_app').fadeIn()
		}
		else
		{
			Titanium.UI.currentWindow.setVisible(false);
		}
		// run the installer
		installApp($('#install_location').val());
	});

	$('#file_browse').click(function()
	{
		var props = {multiple:false};
		props.directories = true;
		props.files = false;

		Titanium.UI.openFiles(function(f)
		{
			if (f.length)
			{
				$('#install_location').val(f[0]);
			}
		},
		props);
	});

	var manifest = TFS.getFile(src,'manifest');
	if (!manifest.isFile())
	{
		alert("Invalid application bundle. Couldn't find manifest!");
		return;
	}
	var results = Titanium.Project.getManifest(manifest);
	if (!results.success)
	{
		alert("Invalid application bundle. " + results.message);
		return;
	}

	appid = results.properties.appid;
	appname = results.properties.appname;
	appurl = results.properties.url || 'Not specified';
	apppub = results.properties.publisher || 'Unknown';
	appimg = results.properties.image;

	if (win32)
	{
		Titanium.UI.currentWindow.setTitle(appname + ' Installation');
		$('#intro').html('Information about the application you are about to install.');
	}
	else
	{
		Titanium.UI.currentWindow.setTitle(appname + ' Verification');
		$('#intro').html('Information about the application you are run.');
	}

	$('.appname').html(appname);
	$('#apppub').html(apppub).attr('href',appurl).click(function()
	{
		Titanium.Desktop.openURL(appurl);
	});
	$('#appurl').html(appurl).attr('href',appurl).click(function()
	{
		Titanium.Desktop.openURL(appurl);
	});
	if (appimg)
	{
		appimg = 'file://' + TFS.getFile(src,'Resources',appimg).nativePath();
		$('.appimg').attr('src',appimg);
	}
	else
	{
		$('.appimg').css('visibility','hidden');
	}

	$('#install_location').val(TFS.getProgramsDirectory());
});
<commit_msg>TI-91: add Start menu shortcut if requested<commit_after>var TFS = Titanium.Filesystem;
var src = Titanium.Process.getEnv('KR_APP_INSTALL_FROM');
var appname, appid, appurl, apppub, appimg;
var launch;
var win32 = false;

function launchApp()
{
	// cause the app to exit with special restart
	// code which will re-launch the original app
	if (launch)
	{
		try
		{
			Titanium.Desktop.openApplication(String(launch));
		}
		catch(E)
		{
			alert("Error launching: "+launch+". Message: "+E);
		}
	}
	Titanium.App.exit(0);
}

var install_started;

function createShortcutIfNeeded()
{
	if ($('#create_shortcut').is(':checked'))
	{
		if (Titanium.platform == 'win32')
		{
			var userDir = TFS.getUserDirectory();
			var startMenu = TFS.getFile(userDir, "Start Menu");
			var to = TFS.getFile(startMenu, appname);

			var exeFile = TFS.getFile(launch);
			exeFile.createShortcut(to);
		}
	}
}

function finishInstall()
{
	var ts = new Date().getTime()-install_started;
	if (ts < 1000)
	{
		setTimeout(function()
		{
			finishInstall();
		},1000);

		return;
	}

	// create shortcut if needed
	createShortcutIfNeeded();

	$('#install_app').slideUp(200);
	$('#install_app_finished').fadeIn();
}

function installApp(dir)
{
	if (!win32)
	{
		var dest = TFS.getFile(src,'.installed');
		dest.write("#"+new Date());
		try
		{
			Titanium.Process.restart();
		}
		catch(E)
		{
			alert(E);
		}
		return;
	}
	install_started = new Date().getTime();

	var runtime = TFS.getFile(Titanium.Process.getEnv('KR_RUNTIME'));
	var dest = TFS.getFile(dir);

	// create app structure
	var result = Titanium.createApp(runtime,dest,appname,appid,false);

	// remember in case we launch
	launch = result.executable;

	var resource_files = TFS.getFile(src, 'Resources');
	var module_files = TFS.getFile(src, 'modules');
	var runtime_files = TFS.getFile(src, 'runtime');

	// manifest + tiapp.xml (below) + app resources dir
	var count = 3;
	if (module_files.isDirectory())
	{
		count++;
	}
	if (runtime_files.isDirectory())
	{
		count++;
	}

	var increment = 100 / count;
	var value = 0;
	var total = 0;

	function fileCopied()
	{
		value += increment;
		total++;
		$('#progressbar_install').progressbar('value',value)

		if (count == total)
		{
			// complete
			finishInstall();
		}
	}

	TFS.getFile(src,'manifest').copy(result.base);
	fileCopied();
	TFS.getFile(src,'tiapp.xml').copy(result.base);
	fileCopied();

	TFS.asyncCopy(resource_files,result.resources,function(fn,c,t)
	{
		fileCopied();
		if (c == t)
		{
			if (module_files.isDirectory())
			{
				var module_dest = TFS.getFile(result.base, 'modules');
				TFS.asyncCopy(module_files,module_dest,function(fn,c,t) { fileCopied(); });
			}
			if (runtime_files.isDirectory())
			{
				var runtime_dest = TFS.getFile(result.base, 'runtime');
				TFS.asyncCopy(runtime_files,runtime_dest,function(fn,count,total) { fileCopied(); });
			}
		}
	});
}

$(function(){

	if (Titanium.platform != 'win32')
	{
		$('#install_screen').hide();
		$('#install_btn').html('Continue');
		Titanium.UI.currentWindow.setHeight(300);
	}
	else
	{
		win32 = true;
		Titanium.UI.currentWindow.setHeight(510);
	}
	Titanium.UI.currentWindow.setWidth(675);
	Titanium.UI.currentWindow.setX(Titanium.UI.currentWindow.CENTERED);
	Titanium.UI.currentWindow.setY(Titanium.UI.currentWindow.CENTERED);

	// show install info screen
	$('#installation_info').fadeIn();
	$('#install').removeClass('disabled');

	// create progress bars
	$('#progressbar').progressbar({value:0})
	$('#progressbar_install').progressbar({value:0})

	// cancel install click
	$('#cancel').click(function()
	{
		if (confirm('Are you sure you want to cancel?')==true)
		{
			Titanium.UI.currentWindow.close();
		}
	});

	// finish install click
	$('#finish').click(function()
	{
		if ($('#open_after_install').is(':checked'))
		{
			launchApp();
		}
		else
		{
			Titanium.App.exit(0);
		}
	})

	// install app click
	$('#install').click(function()
	{
		if (win32)
		{
			$('#installation_info').slideUp(100);
			Titanium.UI.currentWindow.setHeight(220)
			Titanium.UI.currentWindow.setX(Titanium.UI.currentWindow.CENTERED);

			// show installation screen
			$('#install_app').fadeIn()
		}
		else
		{
			Titanium.UI.currentWindow.setVisible(false);
		}
		// run the installer
		installApp($('#install_location').val());
	});

	$('#file_browse').click(function()
	{
		var props = {multiple:false};
		props.directories = true;
		props.files = false;

		Titanium.UI.openFiles(function(f)
		{
			if (f.length)
			{
				$('#install_location').val(f[0]);
			}
		},
		props);
	});

	var manifest = TFS.getFile(src,'manifest');
	if (!manifest.isFile())
	{
		alert("Invalid application bundle. Couldn't find manifest!");
		return;
	}
	var results = Titanium.Project.getManifest(manifest);
	if (!results.success)
	{
		alert("Invalid application bundle. " + results.message);
		return;
	}

	appid = results.properties.appid;
	appname = results.properties.appname;
	appurl = results.properties.url || 'Not specified';
	apppub = results.properties.publisher || 'Unknown';
	appimg = results.properties.image;

	if (win32)
	{
		Titanium.UI.currentWindow.setTitle(appname + ' Installation');
		$('#intro').html('Information about the application you are about to install.');
	}
	else
	{
		Titanium.UI.currentWindow.setTitle(appname + ' Verification');
		$('#intro').html('Information about the application you are run.');
	}

	$('.appname').html(appname);
	$('#apppub').html(apppub).attr('href',appurl).click(function()
	{
		Titanium.Desktop.openURL(appurl);
	});
	$('#appurl').html(appurl).attr('href',appurl).click(function()
	{
		Titanium.Desktop.openURL(appurl);
	});
	if (appimg)
	{
		appimg = 'file://' + TFS.getFile(src,'Resources',appimg).nativePath();
		$('.appimg').attr('src',appimg);
	}
	else
	{
		$('.appimg').css('visibility','hidden');
	}

	$('#install_location').val(TFS.getProgramsDirectory());
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage } from 'react-intl';
import { union, get, uniqBy } from 'lodash';
import { prettyUrl, formatCurrency, imagePreview } from '../lib/utils';
import { Router } from '../server/pages';
import Currency from './Currency';
import Avatar from './Avatar';
import Logo from './Logo';
import { defaultBackgroundImage } from '../constants/collectives';
import Link from './Link';
import Button from './Button';
import GoalsCover from './GoalsCover';
import MenuBar from './MenuBar';
import TopBackersCoverWithData from './TopBackersCoverWithData';

class CollectiveCover extends React.Component {

  static propTypes = {
    collective: PropTypes.object.isRequired,
    href: PropTypes.string,
    title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    style: PropTypes.object,
    cta: PropTypes.object // { href, label }
  }

  constructor(props) {
    super(props);
    this.messages = defineMessages({
      'contribute': { id: 'collective.contribute', defaultMessage: 'contribute' },
      'apply': { id: 'host.apply.btn', defaultMessage: "Apply to create a collective" },
      'ADMIN': { id: 'roles.admin.label', defaultMessage: 'Core Contributor' },
      'MEMBER': { id: 'roles.member.label', defaultMessage: 'Contributor' }
    });

    if (props.cta) {
      const label = props.cta.label;
      this.cta = { href: props.cta.href, label: this.messages[label] ? props.intl.formatMessage(this.messages[label]) : label };
    }
  }

  getMemberTooltip(member) {
    const { intl } = this.props;
    let tooltip = member.member.name;
    if (this.messages[member.role]) {
      tooltip += `
${intl.formatMessage(this.messages[member.role])}`;
    }
    const description = member.description || member.member.description;
    if (description) {
      tooltip += `
${description}`
    }
    return tooltip;
  }

  render() {
    const {
      collective,
      className,
      LoggedInUser
    } = this.props;

    const {
      company,
      type,
      website,
      twitterHandle,
      stats
    } = collective;

    const href = this.props.href || collective.path || `/${collective.slug}`;
    const title = this.props.title || collective.name;
    const description = this.props.description || collective.description;
    const formattedYearlyIncome = stats && stats.yearlyBudget > 0 && formatCurrency(stats.yearlyBudget, collective.currency, { precision: 0 });
    const backgroundImage = imagePreview(collective.backgroundImage || get(collective,'parentCollective.backgroundImage'), defaultBackgroundImage[collective.type], { height: 500 });
    const customStyles = get(collective, 'settings.style.hero.cover') || get(collective.parentCollective, 'settings.style.hero.cover');
    const style = {
      backgroundImage: `url('${backgroundImage}')`,
      backgroundPosition: 'center center',
      backgroundSize: 'cover',
      backgroundRepeat: 'no-repeat',
      ...customStyles
    };

    const logo = collective.image || get(collective.parentCollective, 'image');

    let membersPreview = [];
    if (collective.members) {
      const admins = collective.members.filter(m => m.role === 'ADMIN');
      const members = collective.members.filter(m => m.role === 'MEMBER');
      const backers = collective.members.filter(m => m.role === 'BACKER');
      backers.sort((a, b) => b.stats && b.stats.totalDonations - a.stats && a.stats.totalDonations);
      membersPreview = uniqBy(union(admins, members, backers).filter(m => m.member), m => m.member.id).slice(0, 5);
    }
    const additionalBackers = (get(stats, 'backers.all') || (get(collective, 'members') || []).length) - membersPreview.length;

    return (
      <div className={`CollectiveCover ${className} ${type}`}>

            <Link route={href} className="goBack">
              { collective.type === 'USER' && <Avatar src={logo} className="logo" radius="10rem" /> }
              { collective.type !== 'USER' && <Logo src={logo} className="logo" type={collective.type} website={collective.website} height="10rem" /> }
            </Link>

{title}

            { company && company.substr(0,1) === '@' &&
<Link route={`/${company.substr(1)}`}>{company.substr(1)}</Link>
 }
            { company && company.substr(0,1) !== '@' &&
{company}
 }
            { description &&
{description}
 }
            { collective.type !== 'EVENT' &&

                { collective.host && collective.isActive &&
<FormattedMessage id="collective.cover.hostedBy" defaultMessage="Hosted by" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { collective.host && !collective.isActive &&
<FormattedMessage id="collective.cover.pendingApprovalFrom" defaultMessage="Pending approval from" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { twitterHandle &&
<a href={`https://twitter.com/${twitterHandle}`} target="_blank">@{twitterHandle}
 }
                { website &&
{prettyUrl(website) }
 }

            }
            { collective.type === 'EVENT' &&

                  <Link route={`/${collective.parentCollective.slug}`}>{collective.parentCollective.name}</Link>

            }

            { className !== "small" && collective.type === "COLLECTIVE" &&

                <TopBackersCoverWithData
                  collective={this.props.collective}
                  LoggedInUser={LoggedInUser}
                  limit={10}
                  />

            }

            { ['USER','ORGANIZATION'].indexOf(collective.type) !== -1 && stats && stats.totalAmountSent > 0 && !collective.isHost &&

                    <Currency value={stats.totalAmountSent} currency={collective.currency} />

                  <FormattedMessage id="collective.stats.totalAmountSent.label" defaultMessage="Total amount donated" />

                { stats.totalAmountRaised > 0 &&

                      <Currency value={stats.totalAmountRaised} currency={collective.currency} />

                    <FormattedMessage id="collective.stats.totalAmountRaised.label" defaultMessage="Total amount raised" />

                }

            }

            { className !== "small" && collective.type === "COLLECTIVE" &&
              <GoalsCover
                collective={collective}
                LoggedInUser={LoggedInUser}
                />
            }

            { this.props.cta &&
              <Button className="blue" href={this.cta.href}>{this.cta.label}</Button>
            }

        { className !== "small" &&
          <MenuBar
            collective={collective}
            LoggedInUser={LoggedInUser}
            cta={this.cta}
            />
        }

    );
  }
}

export default withIntl(CollectiveCover);
<commit_msg>don't show goals for events<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import { defineMessages, FormattedMessage } from 'react-intl';
import { union, get, uniqBy } from 'lodash';
import { prettyUrl, formatCurrency, imagePreview } from '../lib/utils';
import { Router } from '../server/pages';
import Currency from './Currency';
import Avatar from './Avatar';
import Logo from './Logo';
import { defaultBackgroundImage } from '../constants/collectives';
import Link from './Link';
import Button from './Button';
import GoalsCover from './GoalsCover';
import MenuBar from './MenuBar';
import TopBackersCoverWithData from './TopBackersCoverWithData';

class CollectiveCover extends React.Component {

  static propTypes = {
    collective: PropTypes.object.isRequired,
    href: PropTypes.string,
    title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    style: PropTypes.object,
    cta: PropTypes.object // { href, label }
  }

  constructor(props) {
    super(props);
    this.messages = defineMessages({
      'contribute': { id: 'collective.contribute', defaultMessage: 'contribute' },
      'apply': { id: 'host.apply.btn', defaultMessage: "Apply to create a collective" },
      'ADMIN': { id: 'roles.admin.label', defaultMessage: 'Core Contributor' },
      'MEMBER': { id: 'roles.member.label', defaultMessage: 'Contributor' }
    });

    if (props.cta) {
      const label = props.cta.label;
      this.cta = { href: props.cta.href, label: this.messages[label] ? props.intl.formatMessage(this.messages[label]) : label };
    }
  }

  getMemberTooltip(member) {
    const { intl } = this.props;
    let tooltip = member.member.name;
    if (this.messages[member.role]) {
      tooltip += `
${intl.formatMessage(this.messages[member.role])}`;
    }
    const description = member.description || member.member.description;
    if (description) {
      tooltip += `
${description}`
    }
    return tooltip;
  }

  render() {
    const {
      collective,
      className,
      LoggedInUser
    } = this.props;

    const {
      company,
      type,
      website,
      twitterHandle,
      stats
    } = collective;

    const href = this.props.href || collective.path || `/${collective.slug}`;
    const title = this.props.title || collective.name;
    const description = this.props.description || collective.description;
    const formattedYearlyIncome = stats && stats.yearlyBudget > 0 && formatCurrency(stats.yearlyBudget, collective.currency, { precision: 0 });
    const backgroundImage = imagePreview(collective.backgroundImage || get(collective,'parentCollective.backgroundImage'), defaultBackgroundImage[collective.type], { height: 500 });
    const customStyles = get(collective, 'settings.style.hero.cover') || get(collective.parentCollective, 'settings.style.hero.cover');
    const style = {
      backgroundImage: `url('${backgroundImage}')`,
      backgroundPosition: 'center center',
      backgroundSize: 'cover',
      backgroundRepeat: 'no-repeat',
      ...customStyles
    };

    const logo = collective.image || get(collective.parentCollective, 'image');

    let membersPreview = [];
    if (collective.members) {
      const admins = collective.members.filter(m => m.role === 'ADMIN');
      const members = collective.members.filter(m => m.role === 'MEMBER');
      const backers = collective.members.filter(m => m.role === 'BACKER');
      backers.sort((a, b) => b.stats && b.stats.totalDonations - a.stats && a.stats.totalDonations);
      membersPreview = uniqBy(union(admins, members, backers).filter(m => m.member), m => m.member.id).slice(0, 5);
    }
    const additionalBackers = (get(stats, 'backers.all') || (get(collective, 'members') || []).length) - membersPreview.length;

    return (
      <div className={`CollectiveCover ${className} ${type}`}>

            <Link route={href} className="goBack">
              { collective.type === 'USER' && <Avatar src={logo} className="logo" radius="10rem" /> }
              { collective.type !== 'USER' && <Logo src={logo} className="logo" type={collective.type} website={collective.website} height="10rem" /> }
            </Link>

{title}

            { company && company.substr(0,1) === '@' &&
<Link route={`/${company.substr(1)}`}>{company.substr(1)}</Link>
 }
            { company && company.substr(0,1) !== '@' &&
{company}
 }
            { description &&
{description}
 }
            { collective.type !== 'EVENT' &&

                { collective.host && collective.isActive &&
<FormattedMessage id="collective.cover.hostedBy" defaultMessage="Hosted by" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { collective.host && !collective.isActive &&
<FormattedMessage id="collective.cover.pendingApprovalFrom" defaultMessage="Pending approval from" /><Link route={`/${collective.host.slug}`}>{collective.host.name} </Link>
 }
                { twitterHandle &&
<a href={`https://twitter.com/${twitterHandle}`} target="_blank">@{twitterHandle}
 }
                { website &&
{prettyUrl(website) }
 }

            }
            { collective.type === 'EVENT' &&

                  <Link route={`/${collective.parentCollective.slug}`}>{collective.parentCollective.name}</Link>

            }

          { collective.type !== 'EVENT' &&

              { className !== "small" && collective.type === "COLLECTIVE" &&

                  <TopBackersCoverWithData
                    collective={this.props.collective}
                    LoggedInUser={LoggedInUser}
                    limit={10}
                    />

              }

              { ['USER','ORGANIZATION'].indexOf(collective.type) !== -1 && stats && stats.totalAmountSent > 0 && !collective.isHost &&

                      <Currency value={stats.totalAmountSent} currency={collective.currency} />

                    <FormattedMessage id="collective.stats.totalAmountSent.label" defaultMessage="Total amount donated" />

                  { stats.totalAmountRaised > 0 &&

                        <Currency value={stats.totalAmountRaised} currency={collective.currency} />

                      <FormattedMessage id="collective.stats.totalAmountRaised.label" defaultMessage="Total amount raised" />

                  }

              }

              { className !== "small" && collective.type === "COLLECTIVE" &&
                <GoalsCover
                  collective={collective}
                  LoggedInUser={LoggedInUser}
                  />
              }

              { this.props.cta &&
                <Button className="blue" href={this.cta.href}>{this.cta.label}</Button>
              }

          }

        { className !== "small" &&
          <MenuBar
            collective={collective}
            LoggedInUser={LoggedInUser}
            cta={this.cta}
            />
        }

    );
  }
}

export default withIntl(CollectiveCover);
<|endoftext|>


<commit_before>/*******************************************************************************
 *  Code contributed to the webinos project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright 2013 Torsec -Computer and network security group-
 * Politecnico di Torino
 *
 ******************************************************************************/

(function () {
    "use strict";

    var fs = require('fs');
    var xml2js = require('xml2js');
    var convert2xml = require('data2xml')({attrProp : '$', valProp : '_'});
    //var util = require('util');

    /**
    * Translate from manifest to policy
    * @function
    * @param manifestFile Application manifest
    * @param appId Application ID
    * @param features Optional features allowed by the user
    */
    var manifest2policy = function(manifestFile, appId, features) {
        var manifest = null;

        try {
            var xmlManifest = fs.readFileSync(manifestFile);
            // Load xml parser
            var xmlParser = new xml2js.Parser(xml2js.defaults["0.2"]);
            // Parse manifest
            xmlParser.parseString(xmlManifest, function(err, data) {
                if (err === undefined || err === null) {
                    manifest = data['widget'];
                } else {
                    console.log(err);
                    return '';
                }
            });
        } catch (error) {
            console.log(error);
            return '';
        }

        // target
        var target = [];
        target[0] = {};
        target[0].subject = [];
        var subjectMatch = [];
        // subject-match on application ID
        if (appId !== null && appId !== undefined) {
            subjectMatch.push({'$' : {'attr' : 'id', 'match' : appId}});
        } else {
            console.log('appId is missing');
            return '';
        }
        // subject-match on user ID, assuming to receive it as a parameter
        /*if (userId !== null && userId !== undefined) {
            subjectMatch.push({'$' : {'attr' : 'user-id', 'match' : userId}});
        } else {
            console.log('userId is missing');
            return '';
        }*/
        target[0].subject[0] = {'subject-match' : subjectMatch};

        // rule
        var rule = [];
        // 'permit' rule
        rule[0] = {};
        rule[0].$ = {'effect' : 'permit'};
        rule[0].condition = [];
        rule[0].condition[0] = {};
        rule[0].condition[0].$ = {'combine' : 'or'};
        rule[0].condition[0]['resource-match'] = [];

        if (manifest.feature !== null && manifest.feature !== undefined) {
            for (var i = 0; i < manifest.feature.length; i++) {
                if (manifest.feature[i].$.required === 'true' ||
                    (manifest.feature[i].$.required === 'false' &&
                    features !== null && features !== undefined &&
                    features.indexOf(manifest.feature[i].$.name) >= 0)) {

                    rule[0].condition[0]['resource-match'].push({
                        '$' : {'attr' : 'api-feature',
                        'match' : manifest.feature[i].$.name}});
                }
            }
        } else {
            console.log('features are missing');
            return '';
        }
        // default 'deny' rule
        rule[1] = {};
        rule[1].$ = {'effect' : 'deny'};

        // DataHandlingPolicy
        if (manifest.DataHandlingPolicy !== null &&
            manifest.DataHandlingPolicy !== undefined) {

            var dhp = manifest.DataHandlingPolicy;
            var dhpId = [];
            for (var i = 0; i < dhp.length; i++) {
                dhpId[i] = {};
                dhpId[i].oldId = dhp[i].$.PolicyId;
                dhp[i].$.PolicyId = dhp[i].$.PolicyId + '-' + appId;
                dhpId[i].newId = dhp[i].$.PolicyId;
            }
        } else {
            console.log('DataHandlingPolicy is missing');
            return '';
        }

        // ProvisionalActions
        if (manifest.ProvisionalActions !== null &&
            manifest.ProvisionalActions !== undefined) {
            var pa = manifest.ProvisionalActions;
            for (var i = 0; i < pa[0].ProvisionalAction.length; i++) {
                for (var j = 0;
                     j < pa[0].ProvisionalAction[i].AttributeValue.length;
                     j++) {
                    for (var k = 0; k < dhpId.length; k++) {
                        if (pa[0].ProvisionalAction[i].AttributeValue[j] ===
                            dhpId[k].oldId) {

                            pa[0].ProvisionalAction[i].AttributeValue[j] =
                                dhpId[k].newId;
                        }
                    }
                }
            }
        } else {
            console.log('ProvisionalActions are missing');
            return '';
        }

        var policy = {};
        policy.target = target;
        policy.rule = rule;
        policy.DataHandlingPreferences = dhp;
        policy.ProvisionalActions = pa;

        try {
            var data = convert2xml('policy', policy);
            data = data.replace('<?xml version=\"1.0\" encoding=\"utf-8\"?>\n',
                                '');
            //fs.writeFileSync('./outputPolicyFile.xml', data);
        } catch (error) {
            console.log(error);
            return '';
        }
        //console.log(util.inspect(data, false, null));
        return data;
    };

    exports.manifest2policy = manifest2policy;

}());
<commit_msg>Modified appId definition<commit_after>/*******************************************************************************
 *  Code contributed to the webinos project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright 2013 Torsec -Computer and network security group-
 * Politecnico di Torino
 *
 ******************************************************************************/

(function () {
    "use strict";

    var fs = require('fs');
    var xml2js = require('xml2js');
    var convert2xml = require('data2xml')({attrProp : '$', valProp : '_'});
    //var util = require('util');

    /**
    * Translate from manifest to policy
    * @function
    * @param manifestFile Application manifest
    * @param appId Application ID
    * @param features Optional features allowed by the user
    */
    var manifest2policy = function(manifestFile, features) {
        var manifest = null;

        try {
            var xmlManifest = fs.readFileSync(manifestFile);
            // Load xml parser
            var xmlParser = new xml2js.Parser(xml2js.defaults["0.2"]);
            // Parse manifest
            xmlParser.parseString(xmlManifest, function(err, data) {
                if (err === undefined || err === null) {
                    manifest = data['widget'];
                } else {
                    console.log(err);
                    return '';
                }
            });
        } catch (error) {
            console.log(error);
            return '';
        }

        // target
        var target = [];
        target[0] = {};
        target[0].subject = [];
        var subjectMatch = [];
        // appId defined as author-name
        if (manifest.author !== null && manifest.author !== undefined &&
            manifest.name !== null && manifest.name !== undefined &&
            manifest.author[0].length > 0 && manifest.name[0].length > 0) {

            var appId = manifest.author[0]+'-'+manifest.name[0];
        } else {
            console.log('It is not possible to define appId');
            return '';
        }
        // subject-match on application ID
        subjectMatch.push({'$' : {'attr' : 'id', 'match' : appId}});
        // subject-match on user ID, assuming to receive it as a parameter
        /*if (userId !== null && userId !== undefined) {
            subjectMatch.push({'$' : {'attr' : 'user-id', 'match' : userId}});
        } else {
            console.log('userId is missing');
            return '';
        }*/
        target[0].subject[0] = {'subject-match' : subjectMatch};

        // rule
        var rule = [];
        // 'permit' rule
        rule[0] = {};
        rule[0].$ = {'effect' : 'permit'};
        rule[0].condition = [];
        rule[0].condition[0] = {};
        rule[0].condition[0].$ = {'combine' : 'or'};
        rule[0].condition[0]['resource-match'] = [];

        if (manifest.feature !== null && manifest.feature !== undefined) {
            for (var i = 0; i < manifest.feature.length; i++) {
                if (manifest.feature[i].$.required === 'true' ||
                    (manifest.feature[i].$.required === 'false' &&
                    features !== null && features !== undefined &&
                    features.indexOf(manifest.feature[i].$.name) >= 0)) {

                    rule[0].condition[0]['resource-match'].push({
                        '$' : {'attr' : 'api-feature',
                        'match' : manifest.feature[i].$.name}});
                }
            }
        } else {
            console.log('features are missing');
            return '';
        }
        // default 'deny' rule
        rule[1] = {};
        rule[1].$ = {'effect' : 'deny'};

        // DataHandlingPolicy
        if (manifest.DataHandlingPolicy !== null &&
            manifest.DataHandlingPolicy !== undefined) {

            var dhp = manifest.DataHandlingPolicy;
            var dhpId = [];
            for (var i = 0; i < dhp.length; i++) {
                dhpId[i] = {};
                dhpId[i].oldId = dhp[i].$.PolicyId;
                dhp[i].$.PolicyId = dhp[i].$.PolicyId + '-' + appId;
                dhpId[i].newId = dhp[i].$.PolicyId;
            }
        } else {
            console.log('DataHandlingPolicy is missing');
            return '';
        }

        // ProvisionalActions
        if (manifest.ProvisionalActions !== null &&
            manifest.ProvisionalActions !== undefined) {
            var pa = manifest.ProvisionalActions;
            for (var i = 0; i < pa[0].ProvisionalAction.length; i++) {
                for (var j = 0;
                     j < pa[0].ProvisionalAction[i].AttributeValue.length;
                     j++) {
                    for (var k = 0; k < dhpId.length; k++) {
                        if (pa[0].ProvisionalAction[i].AttributeValue[j] ===
                            dhpId[k].oldId) {

                            pa[0].ProvisionalAction[i].AttributeValue[j] =
                                dhpId[k].newId;
                        }
                    }
                }
            }
        } else {
            console.log('ProvisionalActions are missing');
            return '';
        }

        var policy = {};
        policy.target = target;
        policy.rule = rule;
        policy.DataHandlingPreferences = dhp;
        policy.ProvisionalActions = pa;

        try {
            var data = convert2xml('policy', policy);
            data = data.replace('<?xml version=\"1.0\" encoding=\"utf-8\"?>\n',
                                '');
            //fs.writeFileSync('./outputPolicyFile.xml', data);
        } catch (error) {
            console.log(error);
            return '';
        }
        //console.log(util.inspect(data, false, null));
        return data;
    };

    exports.manifest2policy = manifest2policy;

}());
<|endoftext|>


<commit_before>import Ember from 'ember';
import { test } from 'qunit';
import sinon from 'sinon';
import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';
import Initializer from 'ember-blog/initializers/ember-blog-initializer';
import InstanceInitializer from 'ember-blog/instance-initializers/ember-blog-instance-initializer';

let originalExceptionHandler;
moduleForAcceptance('Acceptance | routable engine demo',{
  beforeEach() {
    originalExceptionHandler = Ember.Test.adapter.exception;
  },
  afterEach() {
    Ember.Test.adapter.exception = originalExceptionHandler;
  }
});

test('can invoke components', async function(assert) {
  await visit('/routable-engine-demo/blog/new');

  assert.equal(currentURL(), '/routable-engine-demo/blog/new');

  assert.equal(
    this.application
    .$('.routable-hello-world')
    .text()
    .trim(),
    'Hello, world!'
  );
  assert.equal(
    this.application
    .$('.hello-name')
    .text()
    .trim(),
    'Hello, Jerry!',
    'Re-rendered hello-name component correctly'
  );
});

test("can deserialize a route's params", async function(assert) {
  assert.expect(3);

  await visit('/routable-engine-demo/blog/post/1');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1');

  assert.equal(
    this.application
    .$('h3.post-title')
    .text()
    .trim(),
    'Post 1'
  );
  assert.equal(
    this.application
    .$('p.author')
    .text()
    .trim(),
    'Derek Zoolander'
  );
});

test('can link-to application route of Engine', async function(assert) {
  await visit('/routable-engine-demo/blog/post/new');
  await click('.routable-post-blog-home-link-app');

  assert.equal(currentURL(), '/routable-engine-demo/blog');
  assert.equal(currentPath(), 'routable-engine-demo.blog.index');
});

test('correctly handles navigation with query param in initial url', async function(
  assert
) {
  assert.expect(9);

  await visit('/routable-engine-demo/blog/post/1?lang=English');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'url is visited properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 1'
  );

  await click('.routable-post-comments-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1/comments?lang=English',
    'query param is carried to sub-route'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 2'
  );

  await click('.back-to-post-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'query param is carried back to original route properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 3'
  );

  await click('.routable-post-blog-home-link');
  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog',
    'query param is removed when changing route hierarchy'
  );

  await click('.routable-post-1-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'query param is rehydrated in url'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 4'
  );
});

test('can link-to a route with query params from outside an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo');
  await click('.blog-post-1-link-jp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Japanese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Japanese'
  );
});

test('can programmatically transition to a route with query params from outside an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo');
  await click('.blog-post-1-link-ch');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Chinese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Chinese'
  );
});

test('can link-to a route with query params within an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog');
  await click('.routable-post-1-link-jp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Japanese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Japanese'
  );
});

test('can programmatically transition to a route with query params within an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog');
  await click('.routable-post-1-link-ch');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Chinese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Chinese'
  );
});

test('can perform a QP-only link-to transition', async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1?lang=English');
  await click('.routable-post-jp-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Japanese',
    'URL is updated properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Japanese',
    'DOM is updated properly'
  );
});

test('can perform a QP-only transitionTo', async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1?lang=English');
  await click('.routable-post-ch-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Chinese',
    'URL is updated properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Chinese',
    'DOM is updated properly'
  );
});

test('can perform a redirect transition with QP', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/redirect');
  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English'
  );
});

test('can perform an afterModel transition with QP', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/after-model/3');
  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/3?lang=English'
  );
});

test('query param bucket cache does not collide when starting outside Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/post/1?lang=English');
  await click('.blog-post-no-qp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1',
    'URL does not have any query params'
  );

  await click('.non-blog-post');
  assert.equal(
    currentURL(),
    '/post/1?lang=English',
    'URL has original query params'
  );
});

test('query param bucket cache does not collide when starting inside Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1?lang=English');
  await click('.non-blog-post');

  assert.equal(currentURL(), '/post/1', 'URL does not have any query params');

  await click('.blog-post-no-qp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'URL has original query params'
  );
});

test("a route can lookup another route's model", async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1/comments');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/comments');

  assert.equal(
    this.application
    .$('h4.comments')
    .text()
    .trim(),
    'Comments for Post 1'
  );
});

test('can render a link', async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1');

  assert.equal(
    this.application.$('a.routable-post-comments-link').attr('href'),
    '/routable-engine-demo/blog/post/1/comments'
  );
});

test('link-to current-when attribute prepends engine mount point', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/eager-blog/post/1');

  assert.equal(currentURL(), '/routable-engine-demo/eager-blog/post/1');
  assert.ok(
    this.application.$('a.current-when-test-link').hasClass('active')
  );
});

test('internal links can be clicked', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/blog/post/1');
  await click('.routable-post-home-link');

  assert.equal(currentURL(), '/');
});

test('external links can be clicked', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/blog/post/1');
  await click('.routable-post-comments-link');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/comments');
});

test('a route can use transitionTo to transition to internal route', async function(
  assert
) {
  assert.expect(1);

  await visit('/routable-engine-demo/blog/new');
  await click('.trigger-transition-to');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1');
});

test('internal links can be clicked', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/special-admin-blog-here/post/1');
  await click('.routable-post-comments-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/special-admin-blog-here/post/1/comments'
  );
});

test('transitionTo works properly within parent application', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/normal-route');

  assert.equal(currentURL(), '/routeless-engine-demo');
});

test('transitionToExternal transitions to the parent application from within an engine and returns a thenable Transition object', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/ember-blog/post/1');
  await click('.routable-post-transition-to-home-button');

  assert.equal(currentURL(), '/');

  await click('.routeable-engine');
  await click('.ember-blog-new');
  await click('.trigger-transition-to');

  assert.ok(
    this.application
    .$('.routable-post-transition-to-home-button')
    .hasClass('transitioned-to-external')
  );
});

test('replaceWithExternal transitions to the parent application from within an engine and returns a thenable Transition object', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/ember-blog/post/1');
  await click('.routable-post-replace-with-home-button');

  assert.equal(currentURL(), '/');

  await click('.routeable-engine');
  await click('.ember-blog-new');
  await click('.trigger-transition-to');

  assert.ok(
    this.application
    .$('.routable-post-replace-with-home-button')
    .hasClass('replaced-with-external')
  );
});

test('loading routes and intermediateTransitionTo work within an engine', async function(
  assert
) {
  assert.expect(2);

  this.application.__container__.lookup('router:main').reopen({
    intermediateTransitionTo(routeName) {
      assert.equal(routeName, 'blog.post.loading');
      this._super(...arguments);
    },
  });

  await visit('/routable-engine-demo/blog/post/1/comments');
  await click('.routable-post-likes-link');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/likes');
});

test('error routes and intermediateTransitionTo work within an engine', async function(
  assert
) {
  assert.expect(3);

  Ember.Test.adapter.exception = () => {};

  this.application.__container__.lookup('router:main').reopen({
    intermediateTransitionTo(routeName) {
      assert.equal(routeName, 'blog.post.error');
      this._super(...arguments);
    },
  });

  await visit('/routable-engine-demo/blog/post/1/comments');
  await click('.routable-post-diggs-link');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/comments');
  assert.equal(find('.error-message').text(), 'Error: Nope!');
});

test('initializers run within engine', async function(assert) {
  assert.expect(1);

  let stub = sinon.stub(Initializer, 'initialize');

  await visit('/routable-engine-demo/blog/new');

  assert.ok(stub.calledOnce, 'Initializer ran once');
  stub.restore();
});

test('instance initializers run within engine', async function(assert) {
  assert.expect(1);

  let stub = sinon.stub(InstanceInitializer, 'initialize');

  await visit('/routable-engine-demo/blog/new');

  assert.ok(stub.calledOnce, 'Instance initializer ran once');
  stub.restore();
});

test('instance-initializers run after initializers', async function(assert) {
  assert.expect(2);

  let appInitialized = false;
  let instanceInitialized = false;

  let appInit = sinon.stub(Initializer, 'initialize').callsFake(() => {
    appInitialized = true;
    assert.ok(!instanceInitialized, 'instance initialized has not run yet');
  });

  let instanceInit = sinon.stub(InstanceInitializer, 'initialize').callsFake(() => {
    instanceInitialized = true;
    assert.ok(appInitialized, 'initializer already ran');
  });

  await visit('/routable-engine-demo/blog/new');

  appInit.restore();
  instanceInit.restore();
});
<commit_msg>Use sinon sandbox, to ensure cleanup always occurs<commit_after>import Ember from 'ember';
import { test } from 'qunit';
import { default as _sinon } from 'sinon';
import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';
import Initializer from 'ember-blog/initializers/ember-blog-initializer';
import InstanceInitializer from 'ember-blog/instance-initializers/ember-blog-instance-initializer';

let originalExceptionHandler;
let sinon;

moduleForAcceptance('Acceptance | routable engine demo',{
  beforeEach() {
    originalExceptionHandler = Ember.Test.adapter.exception;
    sinon = _sinon.createSandbox();
  },
  afterEach() {
    sinon.restore();
    Ember.Test.adapter.exception = originalExceptionHandler;
  }
});

test('can invoke components', async function(assert) {
  await visit('/routable-engine-demo/blog/new');

  assert.equal(currentURL(), '/routable-engine-demo/blog/new');

  assert.equal(
    this.application
    .$('.routable-hello-world')
    .text()
    .trim(),
    'Hello, world!'
  );
  assert.equal(
    this.application
    .$('.hello-name')
    .text()
    .trim(),
    'Hello, Jerry!',
    'Re-rendered hello-name component correctly'
  );
});

test("can deserialize a route's params", async function(assert) {
  assert.expect(3);

  await visit('/routable-engine-demo/blog/post/1');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1');

  assert.equal(
    this.application
    .$('h3.post-title')
    .text()
    .trim(),
    'Post 1'
  );
  assert.equal(
    this.application
    .$('p.author')
    .text()
    .trim(),
    'Derek Zoolander'
  );
});

test('can link-to application route of Engine', async function(assert) {
  await visit('/routable-engine-demo/blog/post/new');
  await click('.routable-post-blog-home-link-app');

  assert.equal(currentURL(), '/routable-engine-demo/blog');
  assert.equal(currentPath(), 'routable-engine-demo.blog.index');
});

test('correctly handles navigation with query param in initial url', async function(
  assert
) {
  assert.expect(9);

  await visit('/routable-engine-demo/blog/post/1?lang=English');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'url is visited properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 1'
  );

  await click('.routable-post-comments-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1/comments?lang=English',
    'query param is carried to sub-route'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 2'
  );

  await click('.back-to-post-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'query param is carried back to original route properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 3'
  );

  await click('.routable-post-blog-home-link');
  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog',
    'query param is removed when changing route hierarchy'
  );

  await click('.routable-post-1-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'query param is rehydrated in url'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'English',
    'query param value is passed through correctly - 4'
  );
});

test('can link-to a route with query params from outside an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo');
  await click('.blog-post-1-link-jp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Japanese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Japanese'
  );
});

test('can programmatically transition to a route with query params from outside an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo');
  await click('.blog-post-1-link-ch');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Chinese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Chinese'
  );
});

test('can link-to a route with query params within an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog');
  await click('.routable-post-1-link-jp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Japanese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Japanese'
  );
});

test('can programmatically transition to a route with query params within an Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog');
  await click('.routable-post-1-link-ch');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Chinese'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Chinese'
  );
});

test('can perform a QP-only link-to transition', async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1?lang=English');
  await click('.routable-post-jp-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Japanese',
    'URL is updated properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Japanese',
    'DOM is updated properly'
  );
});

test('can perform a QP-only transitionTo', async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1?lang=English');
  await click('.routable-post-ch-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=Chinese',
    'URL is updated properly'
  );
  assert.equal(
    this.application
    .$('p.language')
    .text()
    .trim(),
    'Chinese',
    'DOM is updated properly'
  );
});

test('can perform a redirect transition with QP', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/redirect');
  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English'
  );
});

test('can perform an afterModel transition with QP', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/after-model/3');
  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/3?lang=English'
  );
});

test('query param bucket cache does not collide when starting outside Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/post/1?lang=English');
  await click('.blog-post-no-qp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1',
    'URL does not have any query params'
  );

  await click('.non-blog-post');
  assert.equal(
    currentURL(),
    '/post/1?lang=English',
    'URL has original query params'
  );
});

test('query param bucket cache does not collide when starting inside Engine', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1?lang=English');
  await click('.non-blog-post');

  assert.equal(currentURL(), '/post/1', 'URL does not have any query params');

  await click('.blog-post-no-qp');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/blog/post/1?lang=English',
    'URL has original query params'
  );
});

test("a route can lookup another route's model", async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1/comments');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/comments');

  assert.equal(
    this.application
    .$('h4.comments')
    .text()
    .trim(),
    'Comments for Post 1'
  );
});

test('can render a link', async function(assert) {
  assert.expect(2);

  await visit('/routable-engine-demo/blog/post/1');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1');

  assert.equal(
    this.application.$('a.routable-post-comments-link').attr('href'),
    '/routable-engine-demo/blog/post/1/comments'
  );
});

test('link-to current-when attribute prepends engine mount point', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/eager-blog/post/1');

  assert.equal(currentURL(), '/routable-engine-demo/eager-blog/post/1');
  assert.ok(
    this.application.$('a.current-when-test-link').hasClass('active')
  );
});

test('internal links can be clicked', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/blog/post/1');
  await click('.routable-post-home-link');

  assert.equal(currentURL(), '/');
});

test('external links can be clicked', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/blog/post/1');
  await click('.routable-post-comments-link');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/comments');
});

test('a route can use transitionTo to transition to internal route', async function(
  assert
) {
  assert.expect(1);

  await visit('/routable-engine-demo/blog/new');
  await click('.trigger-transition-to');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1');
});

test('internal links can be clicked', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/special-admin-blog-here/post/1');
  await click('.routable-post-comments-link');

  assert.equal(
    currentURL(),
    '/routable-engine-demo/special-admin-blog-here/post/1/comments'
  );
});

test('transitionTo works properly within parent application', async function(assert) {
  assert.expect(1);

  await visit('/routable-engine-demo/normal-route');

  assert.equal(currentURL(), '/routeless-engine-demo');
});

test('transitionToExternal transitions to the parent application from within an engine and returns a thenable Transition object', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/ember-blog/post/1');
  await click('.routable-post-transition-to-home-button');

  assert.equal(currentURL(), '/');

  await click('.routeable-engine');
  await click('.ember-blog-new');
  await click('.trigger-transition-to');

  assert.ok(
    this.application
    .$('.routable-post-transition-to-home-button')
    .hasClass('transitioned-to-external')
  );
});

test('replaceWithExternal transitions to the parent application from within an engine and returns a thenable Transition object', async function(
  assert
) {
  assert.expect(2);

  await visit('/routable-engine-demo/ember-blog/post/1');
  await click('.routable-post-replace-with-home-button');

  assert.equal(currentURL(), '/');

  await click('.routeable-engine');
  await click('.ember-blog-new');
  await click('.trigger-transition-to');

  assert.ok(
    this.application
    .$('.routable-post-replace-with-home-button')
    .hasClass('replaced-with-external')
  );
});

test('loading routes and intermediateTransitionTo work within an engine', async function(
  assert
) {
  assert.expect(2);

  this.application.__container__.lookup('router:main').reopen({
    intermediateTransitionTo(routeName) {
      assert.equal(routeName, 'blog.post.loading');
      this._super(...arguments);
    },
  });

  await visit('/routable-engine-demo/blog/post/1/comments');
  await click('.routable-post-likes-link');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/likes');
});

test('error routes and intermediateTransitionTo work within an engine', async function(
  assert
) {
  assert.expect(3);

  Ember.Test.adapter.exception = () => {};

  this.application.__container__.lookup('router:main').reopen({
    intermediateTransitionTo(routeName) {
      assert.equal(routeName, 'blog.post.error');
      this._super(...arguments);
    },
  });

  await visit('/routable-engine-demo/blog/post/1/comments');
  await click('.routable-post-diggs-link');

  assert.equal(currentURL(), '/routable-engine-demo/blog/post/1/comments');
  assert.equal(find('.error-message').text(), 'Error: Nope!');
});

test('initializers run within engine', async function(assert) {
  assert.expect(1);

  let stub = sinon.stub(Initializer, 'initialize');

  await visit('/routable-engine-demo/blog/new');

  assert.ok(stub.calledOnce, 'Initializer ran once');
});

test('instance initializers run within engine', async function(assert) {
  assert.expect(1);

  let stub = sinon.stub(InstanceInitializer, 'initialize');

  await visit('/routable-engine-demo/blog/new');

  assert.ok(stub.calledOnce, 'Instance initializer ran once');
});

test('instance-initializers run after initializers', async function(assert) {
  assert.expect(2);

  let appInitialized = false;
  let instanceInitialized = false;

  let appInit = sinon.stub(Initializer, 'initialize').callsFake(() => {
    appInitialized = true;
    assert.ok(!instanceInitialized, 'instance initialized has not run yet');
  });

  let instanceInit = sinon.stub(InstanceInitializer, 'initialize').callsFake(() => {
    instanceInitialized = true;
    assert.ok(appInitialized, 'initializer already ran');
  });

  await visit('/routable-engine-demo/blog/new');

  appInit.restore();
  instanceInit.restore();
});
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////////
//		Comments
//////////////////////////////////////////////////////////////////////////////////

/**
 * Three View
 * @param {String} label -
 * @param {[type]} id    [description]
 */
function TreeViewItem( label, id ) {

	this.children = [];
	this.collapsed = false;

	this.label = label || '';
	this.id = id || null;

	// create the domElement
	this.liElement = document.createElement( 'li' );

	var containerDiv = document.createElement( 'div' )
	containerDiv.classList.add('container')
	this.liElement.appendChild(containerDiv)

	//////////////////////////////////////////////////////////////////////////////////
	//		Comments
	//////////////////////////////////////////////////////////////////////////////////
	var collapseIcon = document.createElement('i')
	collapseIcon.setAttribute('title', 'Toggle to collapse/expand tree')
	containerDiv.appendChild( collapseIcon );
	this._updateCollapseIcon()
	collapseIcon.addEventListener('click', function(event){
		var hasChildren = this.liElement.querySelector('ul li') ? true : false
		if( hasChildren === false )	return

		this._toggleCollapse()

		event.preventDefault()
	}.bind(this))

	// add label
	var labelElement = document.createElement( 'p' );
	labelElement.textContent = this.label;
	containerDiv.appendChild( labelElement );

;(function(){
	var visibilityIcon = document.createElement('i')
	visibilityIcon.setAttribute('title', 'Toggle visibility of this object')
	visibilityIcon.classList.add('fa')
	visibilityIcon.classList.add('fa-eye')
	visibilityIcon.style.float = 'right'
	visibilityIcon.style.paddingTop = '0.3em'
	containerDiv.appendChild(visibilityIcon)

	visibilityIcon.addEventListener( 'click', function( event ) {
		// notify the selection
		this.treeView.onToggleVisibility( this.id );

		// event.preventDefault();
		event.stopPropagation()
	}.bind(this) );
}.bind(this))()

;(function(){
	var exportIcon = document.createElement('i')
	exportIcon.setAttribute('title', 'Export this object in console')
	exportIcon.classList.add('fa')
	exportIcon.classList.add('fa-share')
	exportIcon.style.float = 'right'
	exportIcon.style.paddingTop = '0.3em'
	containerDiv.appendChild(exportIcon)

	exportIcon.addEventListener( 'click', function( event ) {
		// notify the selection
		this.treeView.onExport( this.id );

		// event.preventDefault();
		event.stopPropagation()
	}.bind(this) );
}.bind(this))()

	// create the list for the children
	this.ulDomElement = null;

	this.treeView = null;	// TODO to be renamed .root ?
	this.parentItem = null;	// TODO to be renamed .parent ?

	containerDiv.addEventListener( 'click', function( event ) {
		// clear all active
		this.treeView.clearActive();
		// mark current as active
		containerDiv.classList.add( 'active' );
		// notify the selection
		this.treeView.onSelect( this.id );

		// event.preventDefault();
		event.stopPropagation()
	}.bind( this ) );

}

TreeViewItem.prototype.createRootNode = function() {
	this.ulDomElement = document.createElement( 'ul' );
}

TreeViewItem.prototype._toggleCollapse = function () {

	this.liElement.classList.toggle( 'collapsed' );

	this._updateCollapseIcon()
}

TreeViewItem.prototype.collapseAll = function () {
 	var isCollapsed	= this.liElement.classList.contains('collapsed')
	var hasChildren = this.liElement.querySelector('ul li') ? true : false

	if( isCollapsed === false && hasChildren === true ){
		this.liElement.classList.add( 'collapsed' );
	}

	this.children.forEach(function(child){
		child.collapseAll()
	})
}

TreeViewItem.prototype.expandAll = function () {
 	var isCollapsed	= this.liElement.classList.contains('collapsed')
	var hasChildren = this.liElement.querySelector('ul li') ? true : false

	if( isCollapsed === true && hasChildren === true ){
		this.liElement.classList.remove( 'collapsed' );
	}

	this.children.forEach(function(child){
		child.expandAll()
	})
}

TreeViewItem.prototype._updateCollapseIcon = function () {
	var isCollapsed	= this.liElement.classList.contains('collapsed')
	var hasChildren = this.liElement.querySelector('ul li') ? true : false

	// update collapse-icon
	var collapseIcon = this.liElement.querySelector('i')

	if( hasChildren === false ){
		collapseIcon.className = 'fa fa-minus-square-o'
	}else if( isCollapsed ){
		collapseIcon.className = 'fa fa-plus-square'
	}else{
		collapseIcon.className = 'fa fa-minus-square'
	}
}
//////////////////////////////////////////////////////////////////////////////////
//		Comments
//////////////////////////////////////////////////////////////////////////////////

/**
 * append a child TreeViewItem
 *
 * @param {TreeViewItem} child - the child to add
 */
TreeViewItem.prototype.appendChild = function( childItem ) {
	childItem.detach()

	childItem.treeView = this.treeView;
	childItem.parentItem = this;

	if( this.ulDomElement === null ) {
		this.ulDomElement = document.createElement( 'ul' );
		this.liElement.appendChild( this.ulDomElement );
	}

	this.children.push( childItem );
	this.ulDomElement.appendChild( childItem.liElement );

	this._updateCollapseIcon()
}

/**
 * remove a child TreeViewItem
 *
 * @param {TreeViewItem} child - the child to remove
 */
TreeViewItem.prototype.removeChild = function( childItem ) {

	childItem.treeView = null;
	childItem.parentItem = null;

	// FIXME this seems like crap
	if( this.ulDomElement === null ) {
		this.liElement.removeChild( this.ulDomElement );
	}

	for( var j = 0; j < this.children.length; j++ ) {
		if( this.children[ j ] === childItem ) {
			this.children.splice( j, 1 );
			break;
		}
	}
	this.ulDomElement.removeChild( childItem.liElement );

	this._updateCollapseIcon()
}

TreeViewItem.prototype.detach = function () {
	if( this.parentItem === null )	return

	this.parentItem.removeChild( this );

	this._updateCollapseIcon()
};
<commit_msg>dblclick on treeviewitem to toggle collapse<commit_after>//////////////////////////////////////////////////////////////////////////////////
//		Comments
//////////////////////////////////////////////////////////////////////////////////

/**
 * Three View
 * @param {String} label -
 * @param {[type]} id    [description]
 */
function TreeViewItem( label, id ) {

	this.children = [];
	this.collapsed = false;

	this.label = label || '';
	this.id = id || null;

	// create the domElement
	this.liElement = document.createElement( 'li' );

	var containerDiv = document.createElement( 'div' )
	containerDiv.classList.add('container')
	this.liElement.appendChild(containerDiv)

	//////////////////////////////////////////////////////////////////////////////////
	//		Comments
	//////////////////////////////////////////////////////////////////////////////////
	var collapseIcon = document.createElement('i')
	collapseIcon.setAttribute('title', 'Toggle to collapse/expand tree')
	containerDiv.appendChild( collapseIcon );
	this._updateCollapseIcon()
	collapseIcon.addEventListener('click', function(event){
		var hasChildren = this.liElement.querySelector('ul li') ? true : false
		if( hasChildren === false )	return

		this._toggleCollapse()

		event.preventDefault()
	}.bind(this))

	// add label
	var labelElement = document.createElement( 'p' );
	labelElement.textContent = this.label;
	containerDiv.appendChild( labelElement );

;(function(){
	var visibilityIcon = document.createElement('i')
	visibilityIcon.setAttribute('title', 'Toggle visibility of this object')
	visibilityIcon.classList.add('fa')
	visibilityIcon.classList.add('fa-eye')
	visibilityIcon.style.float = 'right'
	visibilityIcon.style.paddingTop = '0.3em'
	containerDiv.appendChild(visibilityIcon)

	visibilityIcon.addEventListener( 'click', function( event ) {
		// notify the selection
		this.treeView.onToggleVisibility( this.id );

		// event.preventDefault();
		event.stopPropagation()
	}.bind(this) );
}.bind(this))()

;(function(){
	var exportIcon = document.createElement('i')
	exportIcon.setAttribute('title', 'Export this object in console')
	exportIcon.classList.add('fa')
	exportIcon.classList.add('fa-share')
	exportIcon.style.float = 'right'
	exportIcon.style.paddingTop = '0.3em'
	containerDiv.appendChild(exportIcon)

	exportIcon.addEventListener( 'click', function( event ) {
		// notify the selection
		this.treeView.onExport( this.id );

		// event.preventDefault();
		event.stopPropagation()
	}.bind(this) );
}.bind(this))()

	// create the list for the children
	this.ulDomElement = null;

	this.treeView = null;	// TODO to be renamed .root ?
	this.parentItem = null;	// TODO to be renamed .parent ?

	containerDiv.addEventListener( 'click', function( event ) {
		// clear all active
		this.treeView.clearActive();
		// mark current as active
		containerDiv.classList.add( 'active' );
		// notify the selection
		this.treeView.onSelect( this.id );

		// event.preventDefault();
		event.stopPropagation()
	}.bind( this ) );

	// if dblclick on container, then toggle collapse
	containerDiv.addEventListener( 'dblclick', function( event ) {
		this._toggleCollapse()

		// event.preventDefault();
		event.stopPropagation()
	}.bind( this ));
}

TreeViewItem.prototype.createRootNode = function() {
	this.ulDomElement = document.createElement( 'ul' );
}

TreeViewItem.prototype._toggleCollapse = function () {

	this.liElement.classList.toggle( 'collapsed' );

	this._updateCollapseIcon()
}

TreeViewItem.prototype.collapseAll = function () {
 	var isCollapsed	= this.liElement.classList.contains('collapsed')
	var hasChildren = this.liElement.querySelector('ul li') ? true : false

	if( isCollapsed === false && hasChildren === true ){
		this.liElement.classList.add( 'collapsed' );
	}

	this.children.forEach(function(child){
		child.collapseAll()
	})
}

TreeViewItem.prototype.expandAll = function () {
 	var isCollapsed	= this.liElement.classList.contains('collapsed')
	var hasChildren = this.liElement.querySelector('ul li') ? true : false

	if( isCollapsed === true && hasChildren === true ){
		this.liElement.classList.remove( 'collapsed' );
	}

	this.children.forEach(function(child){
		child.expandAll()
	})
}

TreeViewItem.prototype._updateCollapseIcon = function () {
	var isCollapsed	= this.liElement.classList.contains('collapsed')
	var hasChildren = this.liElement.querySelector('ul li') ? true : false

	// update collapse-icon
	var collapseIcon = this.liElement.querySelector('i')

	if( hasChildren === false ){
		collapseIcon.className = 'fa fa-minus-square-o'
	}else if( isCollapsed ){
		collapseIcon.className = 'fa fa-plus-square'
	}else{
		collapseIcon.className = 'fa fa-minus-square'
	}
}
//////////////////////////////////////////////////////////////////////////////////
//		Comments
//////////////////////////////////////////////////////////////////////////////////

/**
 * append a child TreeViewItem
 *
 * @param {TreeViewItem} child - the child to add
 */
TreeViewItem.prototype.appendChild = function( childItem ) {
	childItem.detach()

	childItem.treeView = this.treeView;
	childItem.parentItem = this;

	if( this.ulDomElement === null ) {
		this.ulDomElement = document.createElement( 'ul' );
		this.liElement.appendChild( this.ulDomElement );
	}

	this.children.push( childItem );
	this.ulDomElement.appendChild( childItem.liElement );

	this._updateCollapseIcon()
}

/**
 * remove a child TreeViewItem
 *
 * @param {TreeViewItem} child - the child to remove
 */
TreeViewItem.prototype.removeChild = function( childItem ) {

	childItem.treeView = null;
	childItem.parentItem = null;

	// FIXME this seems like crap
	if( this.ulDomElement === null ) {
		this.liElement.removeChild( this.ulDomElement );
	}

	for( var j = 0; j < this.children.length; j++ ) {
		if( this.children[ j ] === childItem ) {
			this.children.splice( j, 1 );
			break;
		}
	}
	this.ulDomElement.removeChild( childItem.liElement );

	this._updateCollapseIcon()
}

TreeViewItem.prototype.detach = function () {
	if( this.parentItem === null )	return

	this.parentItem.removeChild( this );

	this._updateCollapseIcon()
};
<|endoftext|>


<commit_before>/* eslint-disable no-use-before-define, no-loop-func */
import merge from 'lodash.merge';
import pascalCase from 'pascal-case';
import {kebabCase, find, uniq, omit} from 'lodash';
import {transform, buildExternalHelpers} from 'babel-core';
import chalk from 'chalk';
import template from './react-class.tmpl';
import dependencyTemplate from './require.tmpl';

export default function createReactCodeFactory(application) {
	const config = application.configuration.transforms.react || {};
	const TAG_START = '<[-_a-z0-9]+';
	const ATTRIBUTE_NAME = '[-_a-z0-9]+';
	const HTML_ATTRIBUTE_VALUE = `"[^"]*"`;
	const REACT_ATTRIBUTE_VALUE = `{(?:{[^}]*}|[^}]*)}`;
	const ATTRIBUTE_VALUE = `\\s*=\\s*(?:${HTML_ATTRIBUTE_VALUE}|${REACT_ATTRIBUTE_VALUE})`;
	const NAMED_ATTRIBUTE = `(?:${ATTRIBUTE_NAME}(?:${ATTRIBUTE_VALUE})?)`;
	const SPREAD_ATTRIBUTE = '{\\.\\.\\.[^}]+}';
	const ATTRIBUTE = `(?:${NAMED_ATTRIBUTE}|${SPREAD_ATTRIBUTE})`;
	const ATTRIBUTES = `(?:\\s+${ATTRIBUTE})*`;
	const TAG_END = '\\s*\\/?>';
	const EXPR = new RegExp(`\n\s*(${TAG_START}${ATTRIBUTES}${TAG_END}[^]*)`, 'gi');
	const IMPORT = /(import\s+(?:\* as\s)?[^\s]+\s+from\s+["'])([^"']+)(["'];)/g;
	const REQUIRE = /require\(["'](.+?)["']\)/g;

	return async function createReactCode(file, demo, configuration) {
		try {
			const opts = merge({}, config.opts, configuration.opts);

			if (opts.globals && Object.keys(opts.globals).length > 0) {
				application.log.warn(
					[
						`${chalk.yellow('[ ⚠  Deprecation ⚠ ]')}   `,
						'"transforms.react.opts.globals" is deprecated',
						'and will be removed in version 1.0 ',
						`${chalk.grey('[transforms.react]')}`
					].join(' ')
				);
			}

			file.buffer = convertCode(file, configuration.resolveDependencies, opts);
			const helpers = configuration.resolveDependencies ? buildExternalHelpers(undefined, 'var') : '';
			const requireBlock = configuration.resolveDependencies ? createRequireBlock(file, configuration.resolveDependencies, opts) : '';

			file.buffer = [
				helpers,
				requireBlock,
				file.buffer
			].join('\n');

			return file;
		} catch (error) {
			const patternName = file.pattern.id;
			application.log.error(`Unable to run react transform for ${patternName}/${file.name}.`);
			application.log.error(error.stack);
			throw error;
		}
	};

	function convertCode(file, resolveDependencies, opts) {
		const source = file.buffer.toString('utf-8');
		const local = omit(merge({}, opts, {externalHelpers: resolveDependencies}), 'globals');

		// TODO: This is a weak criteria to check if we have to create a wrapper
		// perhaps we could check for dangling jsx expressions on the last line instead?
		const isPlain = source.indexOf('extends React.Component') === -1 || source.indexOf('React.createClass') !== -1;

		// wrap in a render function if plain jsx
		file.buffer = isPlain ? createWrappedRenderFunction(file, source, resolveDependencies, opts) : source;

		// rewrite imports to global names
		file.buffer = rewriteImportsToGlobalNames(file, file.buffer);
		file.buffer = transform(file.buffer, local).code;
		return file.buffer;
	}

	function createWrappedRenderFunction(file, source, resolveDependencies, opts) {
		const dependencies = writeDependencyImports(file, resolveDependencies).join('\n');
		return renderCodeTemplate(source, dependencies, template, pascalCase(file.pattern.id), opts);
	}

	function renderCodeTemplate(source, dependencies, templateCode, className, opts) {
		return templateCode
			.replace('$$dependencies$$', dependencies)
			.replace('$$class-name$$', className)
			.replace('$$render-code$$', matchFirstJsxExpressionAndWrapWithReturn(addImplicitGlobals(source, opts)));
	}

	function addImplicitGlobals(source, opts) {
		let vars = [];
		if (opts && opts.globals) {
			for (let key of Object.keys(opts.globals)) {
				vars.push(`this.${key} = ${JSON.stringify(opts.globals[key])};`);
			}
		}
		return vars.join('\n') + '\n' + source;
	}

	function writeDependencyImports(file) {
		/**
		 * Instead of deprecating the simple jsx templates altogether
		 * we could require the user to provide an import block
		 * to do away with the implicit imports
		 **/
		const tagExpression = /<([A-Z][a-zA-Z0-9]+?)(?:\s|\/|>)/g;
		const source = file.buffer.toString('utf-8');
		const externalTagOccurences = [];
		let match;

		while((match = tagExpression.exec(source)) !== null) {
			externalTagOccurences.push(match[1]);
		}

		const externalTagNames = [...new Set(externalTagOccurences)];

		return externalTagNames.map(tagName => {
			const localName = tagName !== 'Pattern' ? kebabCase(tagName) : tagName;
			const dependency = file.dependencies[localName];

			if (!dependency) {
				const err = new Error(
					[
						'Could not resolve dependency ${localName}',
						'introduced by implicit import for <${tagName}/>',
						'in ${file.path}. Only pattern imports',
						'are supported with plain jsx files'
					].join(' ')
				);
				err.fileName = file.path;
				err.file = file.path;
			}

			return `import ${tagName} from '${localName}';`;
		});
	}

	function matchFirstJsxExpressionAndWrapWithReturn(source) {
		return source.replace(EXPR, (match, jsxExpr) => {
			return 'return (\n' + jsxExpr/*.split('\n').map(line => indent + line).join('\n')*/ + '\n);\n';
		});
	}

	function rewriteImportsToGlobalNames(file, source) {
		return source.replace(IMPORT, (match, before, name, after) => {
			const dependencyFile = file.dependencies[name];
			// must be npm
			if (!dependencyFile) {
				// check if require.resolve finds a match
				try {
					require.resolve(name);
				} catch (err) {
					err.message = [err.message, `It was not found in ${file.pattern.id}'s pattern.json and could not be resolved from npm`].join(' ');
					err.file = file.path;
					err.fileName = file.path;
					throw err;
				}
				// all is well, leave it unchanged
				return match;
			} else {
				// rewrite to global pattern name
				const dependencyName = dependencyFile.pattern.id;
			return `${before}${dependencyName}${after}`;
			}
		});
	}

	function getSquashedDependencies(file) {
		return uniq(
				Object
					.values(file.dependencies)
					.reduce((dependencies, dependency) =>
						[...dependencies, dependency],
					[]),
				dependency => dependency.pattern.id
		);
	}

	function getRequiredDependencies(file, externalHelpers, opts) {
		// search for actual imports
		const code = file.buffer;
		const pool = getSquashedDependencies(file);
		const rawImportNames = [];
		let match;

		while((match = REQUIRE.exec(code)) !== null) {
			rawImportNames.push(match[1]);
		}

		// get deduped list of required names
		const importNames = [...new Set(rawImportNames)];

		// return a hashmap of required globalNames => file
		return importNames
			.reduce((results, importName) => {
				// skip the import if it is already in the hashmap
				if (importName in results) {
					return results;
				}

				// find a matching dependency file
				const dependencyFile = find(pool, (fileItem) => {
					return fileItem.pattern.id === importName;
				});

				// must be npm if we did not find one
				if (!dependencyFile) {
					// check if require.resolve finds a match
					try {
						require.resolve(importName);
					} catch (err) {
						err.message = [
							err.message,
							`It was not found in ${file.pattern.id}'s pattern.json`,
							'and could not be resolved from npm'
						].join(' ');
						err.file = file.path;
						err.pattern = file.pattern.id;
						err.fileName = file.path;
						throw err;
					}
					// nothing to do for npm dependencies
					return results;
				} else {
					// dealing with a local dependency, convert it
					dependencyFile.buffer = convertCode(dependencyFile, externalHelpers, opts);
					// add it to the hashmap, search for more required dependencies recursively
					return {
						...results,
						[importName]: dependencyFile.buffer,
						...getRequiredDependencies(dependencyFile, externalHelpers, opts)
					};
				}
			}, {});
	}

	function createRequireBlock(file, externalHelpers, opts) {
		const requiredDependencies = getRequiredDependencies(file, externalHelpers, opts);

		const results = Object.entries(requiredDependencies)
			.map(requiredEntry => {
				const [name, code] = requiredEntry;
				const formatted = code
					.split('\n')
					.map(line => `${'\t\t\t'}${line}`)
					.join('\n');
				return `'${name}': function(module, exports, require){\n${formatted}\n}`;
			})
			.join(',\n');

		return dependencyTemplate.replace('$$localDependencies$$', results);
	}
}

<commit_msg>wip: fix remaining regressions with react-transform<commit_after>/* eslint-disable no-use-before-define, no-loop-func */
import merge from 'lodash.merge';
import pascalCase from 'pascal-case';
import {kebabCase, find, uniq, omit} from 'lodash';
import {transform, buildExternalHelpers} from 'babel-core';
import chalk from 'chalk';
import template from './react-class.tmpl';
import dependencyTemplate from './require.tmpl';

export default function createReactCodeFactory(application) {
	const config = application.configuration.transforms.react || {};
	const TAG_START = '<[-_a-z0-9]+';
	const ATTRIBUTE_NAME = '[-_a-z0-9]+';
	const HTML_ATTRIBUTE_VALUE = `"[^"]*"`;
	const REACT_ATTRIBUTE_VALUE = `{(?:{[^}]*}|[^}]*)}`;
	const ATTRIBUTE_VALUE = `\\s*=\\s*(?:${HTML_ATTRIBUTE_VALUE}|${REACT_ATTRIBUTE_VALUE})`;
	const NAMED_ATTRIBUTE = `(?:${ATTRIBUTE_NAME}(?:${ATTRIBUTE_VALUE})?)`;
	const SPREAD_ATTRIBUTE = '{\\.\\.\\.[^}]+}';
	const ATTRIBUTE = `(?:${NAMED_ATTRIBUTE}|${SPREAD_ATTRIBUTE})`;
	const ATTRIBUTES = `(?:\\s+${ATTRIBUTE})*`;
	const TAG_END = '\\s*\\/?>';
	const EXPR = new RegExp(`\n\s*(${TAG_START}${ATTRIBUTES}${TAG_END}[^]*)`, 'gi');
	const IMPORT = /(import\s+(?:\* as\s)?[^\s]+\s+from\s+["'])([^"']+)(["'];)/g;
	const REQUIRE = /require\(["'](.+?)["']\)/g;

	return async function createReactCode(file, demo, configuration) {
		try {
			const opts = merge({}, config.opts, configuration.opts);

			if (opts.globals && Object.keys(opts.globals).length > 0) {
				application.log.warn(
					[
						`${chalk.yellow('[ ⚠  Deprecation ⚠ ]')}   `,
						'"transforms.react.opts.globals" is deprecated',
						'and will be removed in version 1.0 ',
						`${chalk.grey('[transforms.react]')}`
					].join(' ')
				);
			}

			file.buffer = convertCode(file, configuration.resolveDependencies, opts);
			const helpers = configuration.resolveDependencies ? buildExternalHelpers(undefined, 'var') : '';
			const requireBlock = configuration.resolveDependencies ? createRequireBlock(file, configuration.resolveDependencies, opts) : '';

			file.buffer = [
				helpers,
				requireBlock,
				file.buffer
			].join('\n');
			return file;
		} catch (error) {
			const patternName = file.pattern.id;
			application.log.error(`Unable to run react transform for ${patternName}/${file.name}.`);
			application.log.error(error.stack);
			throw error;
		}
	};

	function convertCode(file, resolveDependencies, opts) {
		const source = file.buffer.toString('utf-8');
		const local = omit(merge({}, opts, {externalHelpers: resolveDependencies}), 'globals');

		// TODO: This is a weak criteria to check if we have to create a wrapper
		// perhaps we could check for dangling jsx expressions on the last line instead?
		const isPlain = !source.match(/class(.+?)extends(.+?){/g) && // does not contain an es6-class, could possibly checked via babel-ast
			source.indexOf('createClass') === -1; // does not contain an React.createClass call

		// wrap in a render function if plain jsx
		file.buffer = isPlain ? createWrappedRenderFunction(file, source, resolveDependencies, opts) : source;

		// rewrite imports to global names
		file.buffer = rewriteImportsToGlobalNames(file, file.buffer);
		file.buffer = transform(file.buffer, local).code;
		return file.buffer;
	}

	function createWrappedRenderFunction(file, source, resolveDependencies, opts) {
		const dependencies = writeDependencyImports(file, resolveDependencies).join('\n');
		return renderCodeTemplate(source, dependencies, template, pascalCase(file.pattern.id), opts);
	}

	function renderCodeTemplate(source, dependencies, templateCode, className, opts) {
		return templateCode
			.replace('$$dependencies$$', dependencies)
			.replace('$$class-name$$', className)
			.replace('$$render-code$$', matchFirstJsxExpressionAndWrapWithReturn(addImplicitGlobals(source, opts)));
	}

	function addImplicitGlobals(source, opts) {
		let vars = [];
		if (opts && opts.globals) {
			for (let key of Object.keys(opts.globals)) {
				vars.push(`this.${key} = ${JSON.stringify(opts.globals[key])};`);
			}
		}
		return vars.join('\n') + '\n' + source;
	}

	function writeDependencyImports(file) {
		/**
		 * Instead of deprecating the simple jsx templates altogether
		 * we could require the user to provide an import block
		 * to do away with the implicit imports
		 **/
		const tagExpression = /<([A-Z][a-zA-Z0-9]+?)(?:\s|\/|>)/g;
		const source = file.buffer.toString('utf-8');
		const externalTagOccurences = [];
		let match;

		while((match = tagExpression.exec(source)) !== null) {
			externalTagOccurences.push(match[1]);
		}

		const externalTagNames = [...new Set(externalTagOccurences)];

		return externalTagNames.map(tagName => {
			const localName = tagName !== 'Pattern' ? kebabCase(tagName) : tagName;
			const dependency = file.dependencies[localName];

			if (!dependency) {
				const err = new Error(
					[
						'Could not resolve dependency ${localName}',
						'introduced by implicit import for <${tagName}/>',
						'in ${file.path}. Only pattern imports',
						'are supported with plain jsx files'
					].join(' ')
				);
				err.fileName = file.path;
				err.file = file.path;
			}

			return `import ${tagName} from '${localName}';`;
		});
	}

	function matchFirstJsxExpressionAndWrapWithReturn(source) {
		return source.replace(EXPR, (match, jsxExpr) => {
			return 'return (\n' + jsxExpr/*.split('\n').map(line => indent + line).join('\n')*/ + '\n);\n';
		});
	}

	function rewriteImportsToGlobalNames(file, source) {
		return source.replace(IMPORT, (match, before, name, after) => {
			const dependencyFile = file.dependencies[name];
			// must be npm
			if (!dependencyFile) {
				// check if require.resolve finds a match
				try {
					require.resolve(name);
				} catch (err) {
					err.message = [err.message, `It was not found in ${file.pattern.id}'s pattern.json and could not be resolved from npm`].join(' ');
					err.file = file.path;
					err.fileName = file.path;
					throw err;
				}
				// all is well, leave it unchanged
				return match;
			} else {
				// rewrite to global pattern name
				const dependencyName = dependencyFile.pattern.id;
			return `${before}${dependencyName}${after}`;
			}
		});
	}

	function getSquashedDependencies(file) {
		return uniq(
				Object
					.values(file.dependencies)
					.reduce((dependencies, dependency) =>
						[...dependencies, dependency],
					[]),
				dependency => dependency.pattern.id
		);
	}

	function getRequiredDependencies(file, externalHelpers, opts) {
		// search for actual imports
		const code = file.buffer;
		const pool = getSquashedDependencies(file);
		const rawImportNames = [];
		let match;

		while((match = REQUIRE.exec(code)) !== null) {
			rawImportNames.push(match[1]);
		}

		// get deduped list of required names
		const importNames = [...new Set(rawImportNames)];

		// return a hashmap of required globalNames => file
		return importNames
			.reduce((results, importName) => {
				// skip the import if it is already in the hashmap
				if (importName in results) {
					return results;
				}

				// find a matching dependency file
				const dependencyFile = find(pool, (fileItem) => {
					return fileItem.pattern.id === importName;
				});

				// must be npm if we did not find one
				if (!dependencyFile) {
					// check if require.resolve finds a match
					try {
						require.resolve(importName);
					} catch (err) {
						err.message = [
							err.message,
							`It was not found in ${file.pattern.id}'s pattern.json`,
							'and could not be resolved from npm'
						].join(' ');
						err.file = file.path;
						err.pattern = file.pattern.id;
						err.fileName = file.path;
						throw err;
					}
					// nothing to do for npm dependencies
					return results;
				} else {
					// dealing with a local dependency, convert it
					dependencyFile.buffer = convertCode(dependencyFile, externalHelpers, opts);
					// add it to the hashmap, search for more required dependencies recursively
					return {
						...results,
						[importName]: dependencyFile.buffer,
						...getRequiredDependencies(dependencyFile, externalHelpers, opts)
					};
				}
			}, {});
	}

	function createRequireBlock(file, externalHelpers, opts) {
		const requiredDependencies = getRequiredDependencies(file, externalHelpers, opts);

		const results = Object.entries(requiredDependencies)
			.map(requiredEntry => {
				const [name, code] = requiredEntry;
				const formatted = code
					.split('\n')
					.map(line => `${'\t\t\t'}${line}`)
					.join('\n');
				return `'${name}': function(module, exports, require){\n${formatted}\n}`;
			})
			.join(',\n');

		return dependencyTemplate.replace('$$localDependencies$$', results);
	}
}

<|endoftext|>