<commit_before>(function($, cloudStack, testData) {
  // Admin dashboard
  cloudStack.sections.dashboard = {
    title: 'Dashboard',
    show: cloudStack.uiCustom.dashboard,

    adminCheck: function(args) {
      return isAdmin() ? true : false;
    },

    // User dashboard
    user: {
      dataProvider: function(args) {
        var dataFns = {
          instances: function(data) {
            $.ajax({
              url: createURL('listVirtualMachines'),
              success: function(json) {
                var instances = json.listvirtualmachinesresponse.virtualmachine ?
                      json.listvirtualmachinesresponse.virtualmachine : [];

                dataFns.account($.extend(data, {
                  runningInstances: $.grep(instances, function(instance) {
                    return instance.state == 'Running';
                  }).length,
                  stoppedInstances: $.grep(instances, function(instance) {
                    return instance.state == 'Stopped';
                  }).length,
                  totalInstances: instances.length
                }));
              }
            });
          },

          account: function(data) {
            var user = cloudStack.context.users[0];
            complete($.extend(data, {
              accountID: user.userid,
              accountName: user.account,
              userName: user.username,
              accountType: cloudStack.converters.toRole(user.type),
              accountDomainID: user.domainid
            }));
          }
        };

        var complete = function(data) {
          args.response.success({
            data: data
          });
        };

        dataFns.instances({});
      }
    },

    // Admin dashboard
    admin: {
      dataProvider: function(args) {
        var dataFns = {
          zones: function(data) {
            $.ajax({
              url: createURL('listZones'),
              success: function(json) {
                dataFns.capacity({
                  zones: json.listzonesresponse.zone
                });
              }
            });
          },
          capacity: function(data) {
            if (data.zones) {
              $.ajax({
                url: createURL('listCapacity'),
                success: function(json) {
                  var capacities = json.listcapacityresponse.capacity;

                  var capacity = function(id, converter) {
                    var result = $.grep(capacities, function(capacity) {
                      return capacity.type == id;
                    });
                    return result[0] ? result[0] : {
                      capacityused: 0,
                      capacitytotal: 0,
                      percentused: 0
                    };
                  };

                  dataFns.alerts($.extend(data, {
                    publicIPAllocated: capacity(8).capacityused,
                    publicIPTotal: capacity(8).capacitytotal,
                    publicIPPercentage: parseInt(capacity(8).percentused),
                    privateIPAllocated: capacity(5).capacityused,
                    privateIPTotal: capacity(5).capacitytotal,
                    privateIPPercentage: parseInt(capacity(8).percentused),
                    memoryAllocated: cloudStack.converters.convertBytes(capacity(0).capacityused),
                    memoryTotal: cloudStack.converters.convertBytes(capacity(0).capacitytotal),
                    memoryPercentage: parseInt(capacity(0).percentused),
                    cpuAllocated: cloudStack.converters.convertHz(capacity(1).capacityused),
                    cpuTotal: cloudStack.converters.convertHz(capacity(1).capacitytotal),
                    cpuPercentage: parseInt(capacity(1).percentused)
                  }));
                }
              });
            } else {
              dataFns.alerts($.extend(data, {
                publicIPAllocated: 0,
                publicIPTotal: 0,
                publicIPPercentage: 0,
                privateIPAllocated: 0,
                privateIPTotal: 0,
                privateIPPercentage: 0,
                memoryAllocated: 0,
                memoryTotal: 0,
                memoryPercentage: 0,
                cpuAllocated: 0,
                cpuTotal: 0,
                cpuPercentage: 0
              }));
            }
          },

          alerts: function(data) {
            $.ajax({
              url: createURL('listAlerts'),
              data: {
                page: 1,
                pageSize: 4
              },
              success: function(json) {
                var alerts = json.listalertsresponse.alert ?
                      json.listalertsresponse.alert : [];

                dataFns.hostAlerts($.extend(data, {
                  alerts: $.map(alerts, function(alert) {
                    return {
                      name: cloudStack.converters.toAlertType(alert.type),
                      description: alert.description
                    };
                  })
                }));
              }
            });
          },

          hostAlerts: function(data) {
            $.ajax({
              url: createURL('listHosts'),
              data: {
                state: 'Alert',
                page: 1,
                pageSize: 4
              },
              success: function(json) {
                var hosts = json.listhostsresponse.host ?
                      json.listhostsresponse.host : [];

                dataFns.zoneCapacity($.extend(data, {
                  hostAlerts: $.map(hosts, function(host) {
                    return {
                      name: host.name,
                      description: 'Alert state detected for ' + host.name
                    };
                  })
                }));
              }
            });
          },

          zoneCapacity: function(data) {
            $.ajax({
              url: createURL('listZones'),
              data: {
                showCapacities: true
              },
              success: function(json) {
                var zones = json.listzonesresponse.zone ?
                  json.listzonesresponse.zone : [];

                var zoneCapacities = [];

                $(zones).each(function() {
                  var zone = this;
                  var clusters;

                  // Get cluster-level data
                  $.ajax({
                    url: createURL('listClusters'),
                    data: {
                      zoneId: zone.id,
                      showCapacities: true
                    },
                    async: false,
                    success: function(json) {
                      var cluster = json.listclustersresponse.cluster;

                      $(cluster).each(function() {
                        var cluster = this;

                        $(cluster.capacity).each(function() {
                          var capacity = this;

                          zoneCapacities.push($.extend(capacity, {
                            zoneName: zone.name + '
Cluster: ' + cluster.name
                          }));
                        });
                      });
                    }
                  });
                });

                var sortFn = function(a, b) {
                  return parseInt(a.percentused) < parseInt(b.percentused);
                };

                complete($.extend(data, {
                  zoneCapacities: $.map(zoneCapacities.sort(sortFn), function(capacity) {
                    return {
                      zoneName: capacity.zoneName,
                      type: cloudStack.converters.toAlertType(capacity.type),
                      percent: capacity.percentused,
                      used: cloudStack.converters.convertByType(capacity.type, capacity.capacityused),
                      total: cloudStack.converters.convertByType(capacity.type, capacity.capacitytotal)
                    };
                  })
                }));
              }
            });
          }
        };

        var complete = function(data) {
          args.response.success({
            data: data
          });
        };

        dataFns.zones({});
      }
    }
  };
})(jQuery, cloudStack, testData);
<commit_msg>Show zone-specific data on dashboard<commit_after>(function($, cloudStack, testData) {
  // Admin dashboard
  cloudStack.sections.dashboard = {
    title: 'Dashboard',
    show: cloudStack.uiCustom.dashboard,

    adminCheck: function(args) {
      return isAdmin() ? true : false;
    },

    // User dashboard
    user: {
      dataProvider: function(args) {
        var dataFns = {
          instances: function(data) {
            $.ajax({
              url: createURL('listVirtualMachines'),
              success: function(json) {
                var instances = json.listvirtualmachinesresponse.virtualmachine ?
                      json.listvirtualmachinesresponse.virtualmachine : [];

                dataFns.account($.extend(data, {
                  runningInstances: $.grep(instances, function(instance) {
                    return instance.state == 'Running';
                  }).length,
                  stoppedInstances: $.grep(instances, function(instance) {
                    return instance.state == 'Stopped';
                  }).length,
                  totalInstances: instances.length
                }));
              }
            });
          },

          account: function(data) {
            var user = cloudStack.context.users[0];
            complete($.extend(data, {
              accountID: user.userid,
              accountName: user.account,
              userName: user.username,
              accountType: cloudStack.converters.toRole(user.type),
              accountDomainID: user.domainid
            }));
          }
        };

        var complete = function(data) {
          args.response.success({
            data: data
          });
        };

        dataFns.instances({});
      }
    },

    // Admin dashboard
    admin: {
      dataProvider: function(args) {
        var dataFns = {
          zones: function(data) {
            $.ajax({
              url: createURL('listZones'),
              success: function(json) {
                dataFns.capacity({
                  zones: json.listzonesresponse.zone
                });
              }
            });
          },
          capacity: function(data) {
            if (data.zones) {
              $.ajax({
                url: createURL('listCapacity'),
                success: function(json) {
                  var capacities = json.listcapacityresponse.capacity;

                  var capacity = function(id, converter) {
                    var result = $.grep(capacities, function(capacity) {
                      return capacity.type == id;
                    });
                    return result[0] ? result[0] : {
                      capacityused: 0,
                      capacitytotal: 0,
                      percentused: 0
                    };
                  };

                  dataFns.alerts($.extend(data, {
                    publicIPAllocated: capacity(8).capacityused,
                    publicIPTotal: capacity(8).capacitytotal,
                    publicIPPercentage: parseInt(capacity(8).percentused),
                    privateIPAllocated: capacity(5).capacityused,
                    privateIPTotal: capacity(5).capacitytotal,
                    privateIPPercentage: parseInt(capacity(8).percentused),
                    memoryAllocated: cloudStack.converters.convertBytes(capacity(0).capacityused),
                    memoryTotal: cloudStack.converters.convertBytes(capacity(0).capacitytotal),
                    memoryPercentage: parseInt(capacity(0).percentused),
                    cpuAllocated: cloudStack.converters.convertHz(capacity(1).capacityused),
                    cpuTotal: cloudStack.converters.convertHz(capacity(1).capacitytotal),
                    cpuPercentage: parseInt(capacity(1).percentused)
                  }));
                }
              });
            } else {
              dataFns.alerts($.extend(data, {
                publicIPAllocated: 0,
                publicIPTotal: 0,
                publicIPPercentage: 0,
                privateIPAllocated: 0,
                privateIPTotal: 0,
                privateIPPercentage: 0,
                memoryAllocated: 0,
                memoryTotal: 0,
                memoryPercentage: 0,
                cpuAllocated: 0,
                cpuTotal: 0,
                cpuPercentage: 0
              }));
            }
          },

          alerts: function(data) {
            $.ajax({
              url: createURL('listAlerts'),
              data: {
                page: 1,
                pageSize: 4
              },
              success: function(json) {
                var alerts = json.listalertsresponse.alert ?
                      json.listalertsresponse.alert : [];

                dataFns.hostAlerts($.extend(data, {
                  alerts: $.map(alerts, function(alert) {
                    return {
                      name: cloudStack.converters.toAlertType(alert.type),
                      description: alert.description
                    };
                  })
                }));
              }
            });
          },

          hostAlerts: function(data) {
            $.ajax({
              url: createURL('listHosts'),
              data: {
                state: 'Alert',
                page: 1,
                pageSize: 4
              },
              success: function(json) {
                var hosts = json.listhostsresponse.host ?
                      json.listhostsresponse.host : [];

                dataFns.zoneCapacity($.extend(data, {
                  hostAlerts: $.map(hosts, function(host) {
                    return {
                      name: host.name,
                      description: 'Alert state detected for ' + host.name
                    };
                  })
                }));
              }
            });
          },

          zoneCapacity: function(data) {
            $.ajax({
              url: createURL('listZones'),
              data: {
                showCapacities: true
              },
              success: function(json) {
                var zones = json.listzonesresponse.zone ?
                  json.listzonesresponse.zone : [];

                var zoneCapacities = [];

                $(zones).each(function() {
                  var zone = this;
                  var clusters;

                  // Get cluster-level data
                  $.ajax({
                    url: createURL('listClusters'),
                    data: {
                      zoneId: zone.id,
                      showCapacities: true
                    },
                    async: false,
                    success: function(json) {
                      var cluster = json.listclustersresponse.cluster;

                      // Get cluster-level data
                      $(cluster).each(function() {
                        var cluster = this;

                        $(cluster.capacity).each(function() {
                          var capacity = this;

                          zoneCapacities.push($.extend(capacity, {
                            zoneName: zone.name + '
Cluster: ' + cluster.name
                          }));
                        });
                      });

                      // Get zone-level data
                      $(zone.capacity).each(function() {
                        var capacity = this;
                        var existingCapacityTypes = $.map(zoneCapacities, function(capacity) {
                          return capacity.type;
                        });
                        var isExistingCapacity = $.inArray(capacity.type, existingCapacityTypes) > -1;

                        if (!isExistingCapacity) {
                          zoneCapacities.push($.extend(capacity, {
                            zoneName: zone.name
                          }));
                        }
                      });
                    }
                  });
                });

                var sortFn = function(a, b) {
                  return parseInt(a.percentused) < parseInt(b.percentused);
                };

                complete($.extend(data, {
                  zoneCapacities: $.map(zoneCapacities.sort(sortFn), function(capacity) {
                    return {
                      zoneName: capacity.zoneName,
                      type: cloudStack.converters.toAlertType(capacity.type),
                      percent: capacity.percentused,
                      used: cloudStack.converters.convertByType(capacity.type, capacity.capacityused),
                      total: cloudStack.converters.convertByType(capacity.type, capacity.capacitytotal)
                    };
                  })
                }));
              }
            });
          }
        };

        var complete = function(data) {
          args.response.success({
            data: data
          });
        };

        dataFns.zones({});
      }
    }
  };
})(jQuery, cloudStack, testData);
<|endoftext|>


<commit_before>/**
 * Upgrades
 *
 * Upgrades normally boost cps by boosting a certain building type.
 * As the player buys more buildings, new upgrades are unlocked.
 *
 * Note:
 * building base cps = cps
 * building cps      = count * cps
 *
**/

var upgrades = [];

function Upgrade(name, desc, cost, building, req, cps_function)
{
	// my index
	this.id = 0;
	// attributes
	this.name = name;
	this.desc = desc;
	this.cost = cost;
	// the building this upgrade is connected to
	this.building = building;
	// the requirements for this upgrade showing up
	// such as requiring player to own 1 of building b
	this.requirement = req;
	// 0 = invisible, 1 = visible, 2 = purchased
	this.state = 0;
	// the amount of codelines per second this upgrade
	// permanently gives
	this.getCPS  = cps_function;

	// returns the html element (string) representing this upgrade
	this.getElement = function()
	{
		return "#upgradeIcon" + this.id;
	}

	// for an upgrade to be visible, the requirements must be met
	this.requirementsMet = function()
	{
		return buildings[this.building].count >= this.requirement;
	}

	// enables upgrades that have just become available
	this.enableTest = function()
	{
		if (this.state == 0)
		if (this.requirementsMet())
		{
			// save state
			this.state = 1;
			localStorage.setItem("upgrade" + this.id, this.state);
			// and show it
			$(this.getElement()).show();
		}
	}

	// resumes this upgrade (from page refresh etc.)
	this.resume = function()
	{
		// resume previous state, if any
		this.state = localStorage.getItem("upgrade" + this.id) || 0;

		// if the state is not 2, we will just validate
		if (this.state != 2)
		{
			if (this.requirementsMet())
			{
				// if the requirements are immediately met,
				// just enable the upgrade
				this.state = 1; // available for purchase
				localStorage.setItem("upgrade" + this.id, this.state);
				// and show it
				$(this.getElement()).show();
			}
		}
		else // for state = 2, the upgrade was previously purchased,
		{    // so we will just re-purchase it
			this.buy();
		}
	}

	this.buy = function()
	{
		// enable upgrade by passing building cps through a cps function
		buildings[this.building].cps = this.getCPS(buildings[this.building].cps);

		// remove/hide this upgrade from upgrade list
		$(this.getElement()).hide();

		// save state
		this.state = 2;
		localStorage.setItem("upgrade" + this.id, this.state);

		// update cps to reflect change
		updateCPS();
	}

	this.create = function()
	{
		// creates upgrade image/icon
		$('<img>',
		{
			class: 'upgradeIcon',
			id   : 'upgradeIcon' + this.id,

			src:   'img/upgrades/upgrade' + this.id + '.png',
			alt:   this.name

		}).hide().appendTo($("#upgrades"));

		// hover info popup
		hover(this.getElement(), "Upgrade: " + this.name, this.desc);

		// click (purchase) event
		var self = this;

		$(this.getElement()).click(
		function()
		{
			// check that the upgrade is is cost range
			var cl = getCodelines();
			if (cl < self.cost) return;
			// subtract cost
			setCodelines(cl - self.cost);

			// apply change
			self.buy();
		});

	}
}

function initUpgrades()
{
	// create each upgrade manually

	// xtreme programming (0)
	upgrades.push(
	new Upgrade(
		"Xtreme Programming",
		"Write everything in one go, and never look back. " +
		"Increases programming base cps by 2.",
		100,
		0, 1, // requires 1 of building 0 (Programming)

		// the upgrade function that modifies base cps
		function(base_cps)
		{
			return base_cps + 2;
		}
	));

        // super commenting(1)
	upgrades.push(
	new Upgrade(
		"Commenting freak",
		"Adding unuseful information to the comments! " +
		"Increases commenting lines by 5%",
		20000,
		1, 2, // requires 2 of building 1 (Comment Standard)

		// the upgrade function that modifies base cps
		function(base_cps)
		{
			return base_cps * 1.05;
		}
	));

	// Student Lanprogramming (2)
        upgrades.push(
                new Upgrade(
                    "Student Lanparty",
                    "The students program when they are on a Lan-party all night long! " +
                    "Increases student codlines by 10%",
                    2000,
                    2, 1, // requires 1 of building 2 (Student Programmer)

                    // the upgrade function that modifies base cps
                    function(base_cps){
                        return base_cps * 1.1;
                    }
        ));

	// brogramming (3)
	upgrades.push(
	new Upgrade(
		"Brogramming",
		"Students learn from each other, further convoluting and diluting the codebase! " +
		"Increases student codelines by 50%.",
		20000,
		2, 10, // requires 1 of building 2 (Student Programmer)

		// the upgrade function that modifies base cps
		function(base_cps)
		{
			return base_cps * 1.5;
		}
	));

	// set initial values for upgrades
	for (var i = 0; i < upgrades.length; i++)
	{
		upgrades[i].id = i;
		upgrades[i].create();
		// set initial state for upgrade
		upgrades[i].resume();
	}
}

// each time player purchases a building, we will check
// if each upgrade has suddenly become available
// slightly inefficient O(n), but there are never many upgrades
function testUpgrades()
{
	for (var i = 0; i < upgrades.length; i++)
	{
		upgrades[i].enableTest();
	}
}

<commit_msg>Fixed trabbles in upgrades.js<commit_after>/**
 * Upgrades
 *
 * Upgrades normally boost cps by boosting a certain building type.
 * As the player buys more buildings, new upgrades are unlocked.
 *
 * Note:
 * building base cps = cps
 * building cps      = count * cps
 *
**/

var upgrades = [];

function Upgrade(name, desc, cost, building, req, cps_function)
{
	// my index
	this.id = 0;
	// attributes
	this.name = name;
	this.desc = desc;
	this.cost = cost;
	// the building this upgrade is connected to
	this.building = building;
	// the requirements for this upgrade showing up
	// such as requiring player to own 1 of building b
	this.requirement = req;
	// 0 = invisible, 1 = visible, 2 = purchased
	this.state = 0;
	// the amount of codelines per second this upgrade
	// permanently gives
	this.getCPS  = cps_function;

	// returns the html element (string) representing this upgrade
	this.getElement = function()
	{
		return "#upgradeIcon" + this.id;
	}

	// for an upgrade to be visible, the requirements must be met
	this.requirementsMet = function()
	{
		return buildings[this.building].count >= this.requirement;
	}

	// enables upgrades that have just become available
	this.enableTest = function()
	{
		if (this.state == 0)
		if (this.requirementsMet())
		{
			// save state
			this.state = 1;
			localStorage.setItem("upgrade" + this.id, this.state);
			// and show it
			$(this.getElement()).show();
		}
	}

	// resumes this upgrade (from page refresh etc.)
	this.resume = function()
	{
		// resume previous state, if any
		this.state = localStorage.getItem("upgrade" + this.id) || 0;

		// if the state is not 2, we will just validate
		if (this.state != 2)
		{
			if (this.requirementsMet())
			{
				// if the requirements are immediately met,
				// just enable the upgrade
				this.state = 1; // available for purchase
				localStorage.setItem("upgrade" + this.id, this.state);
				// and show it
				$(this.getElement()).show();
			}
		}
		else // for state = 2, the upgrade was previously purchased,
		{    // so we will just re-purchase it
			this.buy();
		}
	}

	this.buy = function()
	{
		// enable upgrade by passing building cps through a cps function
		buildings[this.building].cps = this.getCPS(buildings[this.building].cps);

		// remove/hide this upgrade from upgrade list
		$(this.getElement()).hide();

		// save state
		this.state = 2;
		localStorage.setItem("upgrade" + this.id, this.state);

		// update cps to reflect change
		updateCPS();
	}

	this.create = function()
	{
		// creates upgrade image/icon
		$('<img>',
		{
			class: 'upgradeIcon',
			id   : 'upgradeIcon' + this.id,

			src:   'img/upgrades/upgrade' + this.id + '.png',
			alt:   this.name

		}).hide().appendTo($("#upgrades"));

		// hover info popup
		hover(this.getElement(), "Upgrade: " + this.name, this.desc);

		// click (purchase) event
		var self = this;

		$(this.getElement()).click(
		function()
		{
			// check that the upgrade is is cost range
			var cl = getCodelines();
			if (cl < self.cost) return;
			// subtract cost
			setCodelines(cl - self.cost);

			// apply change
			self.buy();
		});

	}
}

function initUpgrades()
{
	// create each upgrade manually

	// xtreme programming (0)
	upgrades.push(
	new Upgrade(
		"Xtreme Programming",
		"Write everything in one go, and never look back. " +
		"Increases programming base cps by 2.",
		100,
		0, 1, // requires 1 of building 0 (Programming)

		// the upgrade function that modifies base cps
		function(base_cps)
		{
			return base_cps + 2;
		}
	));

    // OCD commenting (1)
	upgrades.push(
	new Upgrade(
		"OCD Commenting",
		"Extra-redundant information added everywhere! " +
		"Increases comment lines by 50%",
		2000,
		1, 2, // requires 2 of building 1 (Comment Standard)

		// the upgrade function that modifies base cps
		function(base_cps)
		{
			return base_cps * 1.5;
		}
	));

	// Student Lan pjartyy (2)
    upgrades.push(
    new Upgrade(
        "Student Lanparty",
        "The students program when they are on a Lan-party all night long! " +
        "Increases student codlines by 50%",
        5000,
        2, 1, // requires 1 of building 2 (Student Programmer)

        // the upgrade function that modifies base cps
        function(base_cps){
            return base_cps * 1.5;
        }
    ));

	// brogramming (3)
	upgrades.push(
	new Upgrade(
		"Brogramming",
		"Students learn from each other, further convoluting and diluting the codebase! " +
		"Increases student codelines by 50%.",
		20000,
		2, 10, // requires 1 of building 2 (Student Programmer)

		// the upgrade function that modifies base cps
		function(base_cps)
		{
			return base_cps * 1.5;
		}
	));

	// set initial values for upgrades
	for (var i = 0; i < upgrades.length; i++)
	{
		upgrades[i].id = i;
		upgrades[i].create();
		// set initial state for upgrade
		upgrades[i].resume();
	}
}

// each time player purchases a building, we will check
// if each upgrade has suddenly become available
// slightly inefficient O(n), but there are never many upgrades
function testUpgrades()
{
	for (var i = 0; i < upgrades.length; i++)
	{
		upgrades[i].enableTest();
	}
}

<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')

const { setWorldConstructor, After } = require('cucumber')
const DatabaseTodoList = require('../../lib/server/DatabaseTodoList')
const HttpTodoList = require('../../lib/client/HttpTodoList')
const BrowserApp = require('../../lib/client/BrowserApp')
const WebApp = require('../../lib/server/WebApp')
const MemoryTodoList = require('../../test_support/MemoryTodoList')
const DomTodoList = require('../../test_support/DomTodoList')
const WebDriverTodoList = require('../../test_support/WebDriverTodoList')
const BrowserStackTodoList = require('../../test_support/BrowserStackTodoList')

const assembly = process.env.CUCUMBER_ASSEMBLY || 'memory'
console.log(`🥒  ${assembly}`)

class TodoWorld {
  constructor() {
    this._truncate = true
    this._stoppables = []

    const memoryTodoList = new MemoryTodoList()

    const makeDomTodoList = async todoList => {
      const publicIndexHtmlPath = path.join(__dirname, '..', '..', 'public', 'index.html')
      const html = fs.readFileSync(publicIndexHtmlPath, 'utf-8')
      const domNode = document.createElement('div')
      domNode.innerHTML = html
      document.body.appendChild(domNode)
      await new BrowserApp({ domNode, todoList }).mount()
      return new DomTodoList(domNode)
    }

    const makeDatabaseTodoList = async () => {
      const databaseTodoList = new DatabaseTodoList()
      await databaseTodoList.start(this._truncate)
      this._truncate = false
      return databaseTodoList
    }

    const makeHttpTodoList = async webApppTodoList => {
      const webApp = new WebApp({ todoList: webApppTodoList, serveClient: false })
      const port = await webApp.listen(0)
      this._stoppables.push(webApp)
      return new HttpTodoList(`http://localhost:${port}`)
    }

    const makeWebDriverTodoList = async webApppTodoList => {
      const webApp = new WebApp({ todoList: webApppTodoList, serveClient: true })
      const port = await webApp.listen(0)
      this._stoppables.push(webApp)
      const webDriverTodoList = new WebDriverTodoList(`http://localhost:${port}`)
      await webDriverTodoList.start()
      this._stoppables.push(webDriverTodoList)
      return webDriverTodoList
    }

    const makeBrowserStackTodoList = async webApppTodoList => {
      const webApp = new WebApp({ todoList: webApppTodoList, serveClient: true })
      const port = await webApp.listen(0)
      this._stoppables.push(webApp)
      const browserStackTodoList = new BrowserStackTodoList(`http://localhost:${port}`)
      await browserStackTodoList.start()
      this._stoppables.push(browserStackTodoList)
      return browserStackTodoList
    }

    const assemblies = {
      'memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => memoryTodoList,
        outcomeTodoList: async () => memoryTodoList,
      },
      'database': {
        contextTodoList: async () => makeDatabaseTodoList(),
        actionTodoList: async () => makeDatabaseTodoList(),
        outcomeTodoList: async () => makeDatabaseTodoList(),
      },
      'http-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeHttpTodoList(memoryTodoList),
        outcomeTodoList: async () => makeHttpTodoList(memoryTodoList),
      },
      'dom-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeDomTodoList(memoryTodoList),
        outcomeTodoList: async () => makeDomTodoList(memoryTodoList),
      },
      'dom-http-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeDomTodoList(await makeHttpTodoList(memoryTodoList)),
        outcomeTodoList: async () => makeDomTodoList(await makeHttpTodoList(memoryTodoList)),
      },
      'webdriver-http-database': {
        contextTodoList: async () => makeDatabaseTodoList(),
        actionTodoList: async () => makeWebDriverTodoList(await makeDatabaseTodoList()),
        outcomeTodoList: async () => makeWebDriverTodoList(await makeDatabaseTodoList())
      },
      'webdriver-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeWebDriverTodoList(memoryTodoList),
        outcomeTodoList: async () => makeWebDriverTodoList(memoryTodoList)
      },
      'browserstack-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeBrowserStackTodoList(memoryTodoList),
        outcomeTodoList: async () => makeBrowserStackTodoList(memoryTodoList)
      }
    }

    Object.assign(this, assemblies[assembly])
  }
}

setWorldConstructor(TodoWorld)

After(async function() {
  return Promise.all(this._stoppables.map(stoppable => stoppable.stop()))
})
<commit_msg>Fix typo<commit_after>const fs = require('fs')
const path = require('path')

const { setWorldConstructor, After } = require('cucumber')
const DatabaseTodoList = require('../../lib/server/DatabaseTodoList')
const HttpTodoList = require('../../lib/client/HttpTodoList')
const BrowserApp = require('../../lib/client/BrowserApp')
const WebApp = require('../../lib/server/WebApp')
const MemoryTodoList = require('../../test_support/MemoryTodoList')
const DomTodoList = require('../../test_support/DomTodoList')
const WebDriverTodoList = require('../../test_support/WebDriverTodoList')
const BrowserStackTodoList = require('../../test_support/BrowserStackTodoList')

const assembly = process.env.CUCUMBER_ASSEMBLY || 'memory'
console.log(`🥒  ${assembly}`)

class TodoWorld {
  constructor() {
    this._truncate = true
    this._stoppables = []

    const memoryTodoList = new MemoryTodoList()

    const makeDomTodoList = async todoList => {
      const publicIndexHtmlPath = path.join(__dirname, '..', '..', 'public', 'index.html')
      const html = fs.readFileSync(publicIndexHtmlPath, 'utf-8')
      const domNode = document.createElement('div')
      domNode.innerHTML = html
      document.body.appendChild(domNode)
      await new BrowserApp({ domNode, todoList }).mount()
      return new DomTodoList(domNode)
    }

    const makeDatabaseTodoList = async () => {
      const databaseTodoList = new DatabaseTodoList()
      await databaseTodoList.start(this._truncate)
      this._truncate = false
      return databaseTodoList
    }

    const makeHttpTodoList = async webAppTodoList => {
      const webApp = new WebApp({ todoList: webAppTodoList, serveClient: false })
      const port = await webApp.listen(0)
      this._stoppables.push(webApp)
      return new HttpTodoList(`http://localhost:${port}`)
    }

    const makeWebDriverTodoList = async webAppTodoList => {
      const webApp = new WebApp({ todoList: webAppTodoList, serveClient: true })
      const port = await webApp.listen(0)
      this._stoppables.push(webApp)
      const webDriverTodoList = new WebDriverTodoList(`http://localhost:${port}`)
      await webDriverTodoList.start()
      this._stoppables.push(webDriverTodoList)
      return webDriverTodoList
    }

    const makeBrowserStackTodoList = async webAppTodoList => {
      const webApp = new WebApp({ todoList: webAppTodoList, serveClient: true })
      const port = await webApp.listen(0)
      this._stoppables.push(webApp)
      const browserStackTodoList = new BrowserStackTodoList(`http://localhost:${port}`)
      await browserStackTodoList.start()
      this._stoppables.push(browserStackTodoList)
      return browserStackTodoList
    }

    const assemblies = {
      'memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => memoryTodoList,
        outcomeTodoList: async () => memoryTodoList,
      },
      'database': {
        contextTodoList: async () => makeDatabaseTodoList(),
        actionTodoList: async () => makeDatabaseTodoList(),
        outcomeTodoList: async () => makeDatabaseTodoList(),
      },
      'http-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeHttpTodoList(memoryTodoList),
        outcomeTodoList: async () => makeHttpTodoList(memoryTodoList),
      },
      'dom-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeDomTodoList(memoryTodoList),
        outcomeTodoList: async () => makeDomTodoList(memoryTodoList),
      },
      'dom-http-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeDomTodoList(await makeHttpTodoList(memoryTodoList)),
        outcomeTodoList: async () => makeDomTodoList(await makeHttpTodoList(memoryTodoList)),
      },
      'webdriver-http-database': {
        contextTodoList: async () => makeDatabaseTodoList(),
        actionTodoList: async () => makeWebDriverTodoList(await makeDatabaseTodoList()),
        outcomeTodoList: async () => makeWebDriverTodoList(await makeDatabaseTodoList())
      },
      'webdriver-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeWebDriverTodoList(memoryTodoList),
        outcomeTodoList: async () => makeWebDriverTodoList(memoryTodoList)
      },
      'browserstack-memory': {
        contextTodoList: async () => memoryTodoList,
        actionTodoList: async () => makeBrowserStackTodoList(memoryTodoList),
        outcomeTodoList: async () => makeBrowserStackTodoList(memoryTodoList)
      }
    }

    Object.assign(this, assemblies[assembly])
  }
}

setWorldConstructor(TodoWorld)

After(async function() {
  return Promise.all(this._stoppables.map(stoppable => stoppable.stop()))
})
<|endoftext|>


<commit_before>"use strict";

//// CONSTANTS /////////////////////////////////////////////////////////////////

var WINDOW_WIDTH = 800;
var WINDOW_HEIGHT = 600;

////////////////////////////////////////////////////////////////////////////////

function canvas() {
	var c = document.createElement("canvas");
	c.width = WINDOW_WIDTH;
	c.height = WINDOW_HEIGHT;
	return c;
}

function sign(x) {
	return x?x<0?-1:1:0;
}

function magnitude(x,y) {
	return Math.sqrt(x * x + y * y);
}

function distance(a,b,x,y) {
	return magnitude(x-a,y-b);
}

function rnd(){
	return Math.random()*2-1;
}

// converts RGBA to HTML color string
function htmlColor(r, g, b, a) {
	// ensure alpha is a number
	if (typeof a != typeof 1) { a = 1; }

	// generate color string
	return ( "rgba("
		+ (r&0xff) +  ","
		+ (g&0xff) + ","
		+ (b&0xff) + ","
		+ a +")"
	);
}

// uniform 2d array populated with value
function gridOfSize(w, h, val){
	var grid = [];
	for(var x = 0; x < w; x++){
		var col = [];
		for(var y = 0; y < h; y++){
			col.push(val);
		}
		grid.push(col);
	}
	return grid;
}

//// Drawing -------------------------------------------------------------------

CanvasRenderingContext2D.prototype.fillCircle = function(x,y,radius){
	this.beginPath();
	this.arc(x,y,radius,0,2*Math.PI);
	this.fill();
}

CanvasRenderingContext2D.prototype.strokeCircle = function(x,y,radius){
	this.beginPath();
	this.arc(x,y,radius,0,2*Math.PI);
	this.stroke();
}

CanvasRenderingContext2D.prototype.fillEllipse = function(x,y,rx,ry){
	// remember current coordinate transform
	this.save();
	// stretch coordinate axes to prepare for ellipse drawing
	this.translate(x-rx, y-ry);
	this.scale(rx, ry);
	// draw circle in stretched coordinates
	this.beginPath();
	this.arc(1, 1, 1, 0, 2 * Math.PI, false);
	this.fill();
	// restore to original, unstretched coordinates
	this.restore();
}

//// Misc ----------------------------------------------------------------------

function assert(condition, message) {
	if (!condition) {
		throw message || "Assertion failed";
	}
}<commit_msg>Added Canvas() helper parameter for scale<commit_after>"use strict";

//// CONSTANTS /////////////////////////////////////////////////////////////////

var WINDOW_WIDTH = 800;
var WINDOW_HEIGHT = 600;

////////////////////////////////////////////////////////////////////////////////

function canvas(scale) {
	scale = scale || 1;
	var c = document.createElement("canvas");
	c.width = WINDOW_WIDTH * scale;
	c.height = WINDOW_HEIGHT * scale;
	return c;
}

function sign(x) {
	return x?x<0?-1:1:0;
}

function magnitude(x,y) {
	return Math.sqrt(x * x + y * y);
}

function distance(a,b,x,y) {
	return magnitude(x-a,y-b);
}

function rnd(){
	return Math.random()*2-1;
}

// converts RGBA to HTML color string
function htmlColor(r, g, b, a) {
	// ensure alpha is a number
	if (typeof a != typeof 1) { a = 1; }

	// generate color string
	return ( "rgba("
		+ (r&0xff) +  ","
		+ (g&0xff) + ","
		+ (b&0xff) + ","
		+ a +")"
	);
}

// uniform 2d array populated with value
function gridOfSize(w, h, val){
	var grid = [];
	for(var x = 0; x < w; x++){
		var col = [];
		for(var y = 0; y < h; y++){
			col.push(val);
		}
		grid.push(col);
	}
	return grid;
}

//// Drawing -------------------------------------------------------------------

CanvasRenderingContext2D.prototype.fillCircle = function(x,y,radius){
	this.beginPath();
	this.arc(x,y,radius,0,2*Math.PI);
	this.fill();
}

CanvasRenderingContext2D.prototype.strokeCircle = function(x,y,radius){
	this.beginPath();
	this.arc(x,y,radius,0,2*Math.PI);
	this.stroke();
}

CanvasRenderingContext2D.prototype.fillEllipse = function(x,y,rx,ry){
	// remember current coordinate transform
	this.save();
	// stretch coordinate axes to prepare for ellipse drawing
	this.translate(x-rx, y-ry);
	this.scale(rx, ry);
	// draw circle in stretched coordinates
	this.beginPath();
	this.arc(1, 1, 1, 0, 2 * Math.PI, false);
	this.fill();
	// restore to original, unstretched coordinates
	this.restore();
}

//// Misc ----------------------------------------------------------------------

function assert(condition, message) {
	if (!condition) {
		throw message || "Assertion failed";
	}
}<|endoftext|>


<commit_before>function init() {
    var form = document.getElementById("question");
    form.onsubmit = function (e) {
        // stop the regular form submission
        e.preventDefault();
    };
}

function handleSubmit(submitButtonName) {
    var form = document.getElementById("question");
    // collect the form data while iterating over the inputs
    var data = {};
    for (var i = 0, ii = form.length; i < ii; ++i) {
        var input = form[i];
        if (input.name) {
            if (input.type !== "radio" || input.checked) {
                data[input.name] = input.value;
            }
        }
    }
    console.log(JSON.stringify(data));
    var request = new Object();
    if (data["id"] !== undefined) {
        request.id = data["id"];
        if (submitButtonName == "Vorige") {
            request.back = true;
        } else {
            request.name = data["name"];
            request.type = data["type"];
            request.value = data["value"];
        }
    }
    console.log("Request: " + JSON.stringify(request));
    // construct an HTTP request
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            console.log(xhr.responseText);
            clearNode();
            if(xhr.responseText == "
500 Internal Error
"){
                document.getElementById("question").appendChild(document.createTextNode("An error occured on the server side"));
                return;
            }
            var response = JSON.parse(xhr.responseText);
            if (response.type === "inquiry") {
                setQuestion(response);
            } else {
                setResult(response);
            }
        }
    };
    xhr.open(form.method, form.action, true);
    xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');

    // send the collected data as JSON
    xhr.send(JSON.stringify(request));

    xhr.onloadend = function () {
        //Done
    };
}

function setQuestion(response) {
    console.log(response.knowledge.origin);
    document.getElementById("question").appendChild(document.createTextNode(response.knowledge.question));
    document.getElementById("question").appendChild(document.createElement("br"));
    setInput("hidden", "id", response.id);
    setInput("hidden", "type", response.knowledge.type);
    setInput("hidden", "name", response.knowledge.name);
    if (response.knowledge.origin === "CHOICESELECTION") {
        for (var i = 0; i < response.knowledge.options.length; i++) {
            setInput("radio", "value", response.knowledge.options[i]);
            document.getElementById("question").appendChild(document.createTextNode(response.knowledge.options[i]));
            document.getElementById("question").appendChild(document.createElement("br"));
        }
        //setChoiceSelection(response.knowledge.options);

    }
    document.getElementById("question").appendChild(document.createElement("br"));
    node = setInput("submit", "Submit1", "Vorige");
    node.setAttribute("onclick", "handleSubmit(\"Vorige\");");
    node = setInput("submit", "Submit2", "Volgende");
    node.setAttribute("onclick", "handleSubmit(\"Volgende\");");
    if (response.knowledge.tip) {
        document.getElementById("tipContent").appendChild(document.createTextNode(response.knowledge.tip));
    }
}

function setResult(response) {
    if (response.goal !== undefined) {
        console.log("Result:");
        document.getElementById("question").appendChild(document.createTextNode(response.goal.goalResponse));
    } else {
        console.log("No result");
        document.getElementById("question").appendChild(document.createTextNode("Het is onbekend of u in aanmerking komt. Contacteer een WMO consulent voor meer informatie."));
    }
}

function clearNode() {
    var node = document.getElementById("question");
    while (node.firstChild) {
        node.removeChild(node.firstChild);
    }
    var tip = document.getElementById("tipContent");
    if (tip.firstChild) {
        tip.removeChild(tip.firstChild);
    }
}

function setInput(type, name, value) {
    var node = document.createElement("input");
    node.setAttribute("type", type);
    node.setAttribute("name", name);
    node.setAttribute("value", value);
    document.getElementById("question").appendChild(node);
    return node;
}

function setChoiceSelection(options) {
    var node = document.createElement("select");
    node.setAttribute("name", "value");
    for (var i = 0; i < options.length; i++) {
        var optionNode = document.createElement("option");
        node.setAttribute("value", i);
        var textNode = document.createTextNode(options[i]);
        optionNode.appendChild(textNode);
        node.appendChild(optionNode);
    }
    document.getElementById("question").appendChild(node);
}<commit_msg>checked<commit_after>function init() {
    var form = document.getElementById("question");
    form.onsubmit = function (e) {
        // stop the regular form submission
        e.preventDefault();
    };
}

function handleSubmit(submitButtonName) {
    var form = document.getElementById("question");
    // collect the form data while iterating over the inputs
    var data = {};
    for (var i = 0, ii = form.length; i < ii; ++i) {
        var input = form[i];
        if (input.name) {
            if (input.type !== "radio" || input.checked) {
                data[input.name] = input.value;
            }
        }
    }
    console.log(JSON.stringify(data));
    var request = new Object();
    if (data["id"] !== undefined) {
        request.id = data["id"];
        if (submitButtonName == "Vorige") {
            request.back = true;
        } else {
            request.name = data["name"];
            request.type = data["type"];
            request.value = data["value"];
        }
    }
    console.log("Request: " + JSON.stringify(request));
    // construct an HTTP request
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            console.log(xhr.responseText);
            clearNode();
            if (xhr.responseText == "
500 Internal Error
") {
                document.getElementById("question").appendChild(document.createTextNode("An error occured on the server side"));
                return;
            }
            var response = JSON.parse(xhr.responseText);
            if (response.type === "inquiry") {
                setQuestion(response);
            } else {
                setResult(response);
            }
        }
    };
    xhr.open(form.method, form.action, true);
    xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');

    // send the collected data as JSON
    xhr.send(JSON.stringify(request));

    xhr.onloadend = function () {
        //Done
    };
}

function setQuestion(response) {
    console.log(response.knowledge.origin);
    document.getElementById("question").appendChild(document.createTextNode(response.knowledge.question));
    document.getElementById("question").appendChild(document.createElement("br"));
    setInput("hidden", "id", response.id);
    setInput("hidden", "type", response.knowledge.type);
    setInput("hidden", "name", response.knowledge.name);
    if (response.knowledge.origin === "CHOICESELECTION") {
        for (var i = 0; i < response.knowledge.options.length; i++) {
            node = setInput("radio", "value", response.knowledge.options[i]);
            if (i == 0) {
                node.setAttribute("checked", "checked");
            }
            document.getElementById("question").appendChild(document.createTextNode(response.knowledge.options[i]));
            document.getElementById("question").appendChild(document.createElement("br"));
        }
        //setChoiceSelection(response.knowledge.options);
    }
    document.getElementById("question").appendChild(document.createElement("br"));
    node = setInput("submit", "Submit1", "Vorige");
    node.setAttribute("onclick", "handleSubmit(\"Vorige\");");
    node = setInput("submit", "Submit2", "Volgende");
    node.setAttribute("onclick", "handleSubmit(\"Volgende\");");
    if (response.knowledge.tip) {
        document.getElementById("tipContent").appendChild(document.createTextNode(response.knowledge.tip));
    }
}

function setResult(response) {
    if (response.goal !== undefined) {
        console.log("Result:");
        document.getElementById("question").appendChild(document.createTextNode(response.goal.goalResponse));
    } else {
        console.log("No result");
        document.getElementById("question").appendChild(document.createTextNode("Het is onbekend of u in aanmerking komt. Contacteer een WMO consulent voor meer informatie."));
    }
}

function clearNode() {
    var node = document.getElementById("question");
    while (node.firstChild) {
        node.removeChild(node.firstChild);
    }
    var tip = document.getElementById("tipContent");
    if (tip.firstChild) {
        tip.removeChild(tip.firstChild);
    }
}

function setInput(type, name, value) {
    var node = document.createElement("input");
    node.setAttribute("type", type);
    node.setAttribute("name", name);
    node.setAttribute("value", value);
    document.getElementById("question").appendChild(node);
    return node;
}

function setChoiceSelection(options) {
    var node = document.createElement("select");
    node.setAttribute("name", "value");
    for (var i = 0; i < options.length; i++) {
        var optionNode = document.createElement("option");
        node.setAttribute("value", i);
        var textNode = document.createTextNode(options[i]);
        optionNode.appendChild(textNode);
        node.appendChild(optionNode);
    }
    document.getElementById("question").appendChild(node);
}<|endoftext|>


<commit_before>// Initialize and define elements
var self = require("sdk/self");
var tmr = require("sdk/timers");
var panels = require("sdk/panel");
var buttons = require("sdk/ui/button/toggle");
var tabs = require("sdk/tabs");

var act, de_act;

// Create a button
var button = buttons.ToggleButton({
	id: "show-panel",
	label: "Show Panel",
	icon: {
	"16": "./m_icon-16.png",
	"32": "./m_icon-32.png",
	"64": "./m_icon-64.png"
	},
	onChange: activate_al
});

// Create Panels
var panel = panels.Panel({
	width: 640,
	height: 400,
	contentURL: self.data.url("text-entry.html"),
	onHide: deactivate_al
});

// setInterval(activate_al, 1800000)

function activate_al(state) {

	panel.show({
		// Pass the button as arg so that panel
		// attaches itself to the button.
		position: button
	});
	console.log("Show Al!");
	act = tmr.setTimeout(deactivate_al, 5000);
	tmr.clearTimeout(de_act);
}

function deactivate_al() {
	panel.hide();
	de_act = tmr.setTimeout(activate_al, 1800000);
	tmr.clearTimeout(act);
	console.log("Hid Al!");
}

/**
Scarface dialogue: (To be confirmed)

"So you wanna dance,
Frank, or do you wanna sit here and have a heart attack?"

*/<commit_msg>updated with URL<commit_after>// Initialize and define elements
var self = require("sdk/self");
var tmr = require("sdk/timers");
var panels = require("sdk/panel");
var buttons = require("sdk/ui/button/toggle");
var tabs = require("sdk/tabs");

var act, de_act;

// Create a button
var button = buttons.ToggleButton({
	id: "show-panel",
	label: "Show Panel",
	icon: {
	"16": "./m_icon-16.png",
	"32": "./m_icon-32.png",
	"64": "./m_icon-64.png"
	},
	onChange: activate_al
});

// Create Panels
var panel = panels.Panel({
	width: 640,
	height: 400,
	contentURL: self.data.url("text-entry.html"),
	onHide: deactivate_al
});

// setInterval(activate_al, 1800000)

function activate_al(state) {

	panel.show({
		// Pass the button as arg so that panel
		// attaches itself to the button.
		position: button
	});
	console.log("Show Al!");
	act = tmr.setTimeout(deactivate_al, 5000);
	tmr.clearTimeout(de_act);
}

function deactivate_al() {
	panel.hide();
	de_act = tmr.setTimeout(activate_al, 1800000);
	tmr.clearTimeout(act);
	console.log("Hid Al!");
}

/**
Scarface dialogue: (To be confirmed)

"So you wanna dance,
Frank, or do you wanna sit here and have a heart attack?"

URL: https://addons.mozilla.org/en-US/firefox/addon/sit-upright/
*/<|endoftext|>


<commit_before>/**
	_moon.Drawers_ is a container kind designed to hold a set of
	[moon.Drawer](#moon.Drawer) objects and client content. The _drawers_ property
	accepts an array of _moon.Drawer_ controls. The associated [drawer
	handles](#moon.DrawerHandle) are positioned in their own small drawer,
	centered at the top of the "dresser"--the region containing the array of
	Drawer controls and the activator nub.

	When a handle is selected, it opens the corresponding Drawer object's main
	drawer or control drawer, depending on how the Drawer object is configured.

	The control's child components may be of any kind.

		{
			kind: "moon.Drawers",
			drawers: [
				{
					name: "musicDrawer",
					kind: "moon.Drawer",
					handle: {kind: "moon.DrawerHandle", content: "Handle"},
					components: [
						{content: "Drawer Content"}
					],
					controlDrawerComponents: [
						{content: "Controls"}
					]
				}
			],
			components: [
				{content: "Content Area"}
			]
		}
*/
enyo.kind({
	name: "moon.Drawers",
	kind: "enyo.Control",
	//* @protected
	classes: "moon-drawers enyo-fit",
	//* @public
	published: {
		//* Populate with an array of _moon.Drawer_ components
		drawers: null
	},
	//* @protected
	handlers: {
		//* Handlers to update the activator when the state of the contained drawers changes
		onActivate: "drawerActivated",
		onDeactivate: "drawerDeactivated",
		onSpotlightDown:"spotDown",
		onSpotlightUp:"spotUp"
	},
	components: [
		{name:"activatorWrapper", classes:"moon-drawers-activator-wrapper", spotlight:true, ontap:"activatorHandler", components: [
			{name:"activator", classes:"moon-drawers-activator", components:[
				{classes:"moon-drawers-activator-nub", components:[
					{name:"nubArrow", classes:"nub-arrow down"}
				]}
			]}
		]},
		{name:"handleContainer", classes:"moon-drawers-handle-container", kind:"enyo.Drawer", resizeContainer:false, open:false, onpostresize:"resizeHandleContainer", components:[
			{name:"handles", classes:"moon-neutral moon-drawers-handles"}
		]},
		{name: "drawers", classes:"moon-drawers-drawer-container"},
		{name: "client", classes:"moon-drawers-client"}
	],
	eventsToCapture: {tap:1, onSpotlightFocus:1, onSpotlightSelect:1},
	create: function() {
		this.inherited(arguments);
		this.$.drawers.createComponents(this.drawers, {kind: "moon.Drawer", owner:this.owner});
		this.setupHandles();
	},
	rendered: function() {
		this.inherited(arguments);
		var dh = document.body.getBoundingClientRect().height;
		this.waterfall("onDrawersRendered", {drawersHeight: dh});
	},
	setupHandles: function() {
		var handles = [];
		for (var index in this.drawers){
			handles.push(this.drawers[index].handle);
		}
		this.$.handles.createComponents(handles, {kind: "moon.DrawerHandle", owner:this});
		for (index in handles) {
			this.$.handles.getControls()[index].addClass('moon-drawers-handle');
			this.$.handles.getControls()[index].tap = this.bindSafely(this.handleTapped);
		}
	},
	activatorHandler: function(){
		if (this.drawerOpen()) {
			this.closeDrawers();
		} else {
			if (this.$.handles.getControls().length == 1) {
				this.openDrawer(this.$.handles.getControls()[0]);
			} else {
				if (this.$.handleContainer.getOpen()) {
					this.closeHandleContainer();
				} else {
					this.openHandleContainer();
				}
			}
		}
	},
	openHandleContainer: function() {
		this.$.handleContainer.setOpen(true);
		enyo.Spotlight.spot(this.$.handleContainer);
		this.updateActivator(true);
		enyo.dispatcher.capture(this.$.handleContainer, this.eventsToCapture, enyo.bind(this, "capturedEvent"), true);
	},
	closeHandleContainer: function() {
		enyo.dispatcher.release(this.$.handleContainer);
		this.$.handleContainer.setOpen(false);
		this.updateActivator(false);
	},
	handleTapped: function(inSender, inEvent) {
		this.openDrawer(inEvent.originator);
		return true;
	},
	openDrawer: function(drawer) {
		var handles = this.$.handles.getControls();
		for (var index in handles)
		{
			if (handles[index] == drawer || enyo.Spotlight.Util.isChild(handles[index],drawer)) {
				drawer = this.$.drawers.getControls()[index];
				drawer.toggleDrawer();
				this.closeHandleContainer();
				enyo.dispatcher.capture(drawer, this.eventsToCapture, enyo.bind(this, "capturedEvent"), true);
				return;
			}
		}
	},
	drawerOpen: function() {
		var drawers = this.$.drawers.getControls();
		for (var index in drawers){
			if (drawers[index].getOpen() || drawers[index].getControlsOpen()) {
				return true;
			}
		}
		return false;
	},
	closeDrawers: function() {
		var drawers = this.$.drawers.getControls();
		for (var index in drawers){
			var drawer = drawers[index];
			if (drawer.getOpen() || drawer.getControlsOpen()) {
				enyo.dispatcher.release(drawer);
				drawer.setOpen(false);
				drawer.setControlsOpen(false);
			}
		}
		this.updateActivator(false);
	},
	capturedEvent: function(inEventName, inEvent, inSender) {
		// Any tap, select, or 5-way focus into the client area closes the dresser/drawer
		if ((inEvent.dir || (inEventName != "onSpotlightFocus")) &&
			(inEvent.dispatchTarget.isDescendantOf(this.$.client))) {
			this.closeDrawers();
			this.closeHandleContainer();
		}
	},
	drawerActivated: function(inSender, inEvent) {
		if (inEvent.originator instanceof moon.Drawer) {
			this.updateActivator(true);
			// Hide client when fullscreen drawer is open so it is not focusable
			if (inEvent.originator.getOpen()) {
				this.$.client.hide();
			}
		}
	},
	drawerDeactivated: function(inSender, inEvent) {
		if (inEvent.originator instanceof moon.Drawer) {
			if (!inEvent.originator.getOpen() && !inEvent.originator.getControlsOpen()) {
				this.updateActivator(false);
			}
			// Re-show client when closing fullscreen drawer
			if (!inEvent.originator.getOpen()) {
				this.$.client.show();
			}
		}
	},
	updateActivator: function(up) {
		this.$.nubArrow.addRemoveClass("up",up);
		this.$.nubArrow.addRemoveClass("down",!up);
		if (!up) {
			this.$.activator.addRemoveClass("drawer-open", false);
		}
	},
	resizeHandler: function() {
		this.inherited(arguments);
		var dh = document.body.getBoundingClientRect().height;
		this.waterfall("onDrawersResized", {drawersHeight: dh});
		this.updateActivator(false);
	},
	/**
		Updates the activator's style only when it is not animating, so that there
		are no visual artifacts.
	*/
	resizeHandleContainer: function(inSender, inEvent) {
		enyo.asyncMethod(inEvent.delegate.bindSafely(function(){
			if (!this.$.animator.isAnimating()) {
				this.parent.$.activator.addRemoveClass("drawer-open", this.parent.drawerOpen() ? true : false);
			}
		}));
	},
	handleAtIndex: function(inIndex) {
		return this.$.handles.getControls()[inIndex];
	}
});
<commit_msg>GF-41746: Update to use new capture API. DCO-1.1-Signed-Off-By: Kevin Schaaf <kevin.schaaf@lge.com><commit_after>/**
	_moon.Drawers_ is a container kind designed to hold a set of
	[moon.Drawer](#moon.Drawer) objects and client content. The _drawers_ property
	accepts an array of _moon.Drawer_ controls. The associated [drawer
	handles](#moon.DrawerHandle) are positioned in their own small drawer,
	centered at the top of the "dresser"--the region containing the array of
	Drawer controls and the activator nub.

	When a handle is selected, it opens the corresponding Drawer object's main
	drawer or control drawer, depending on how the Drawer object is configured.

	The control's child components may be of any kind.

		{
			kind: "moon.Drawers",
			drawers: [
				{
					name: "musicDrawer",
					kind: "moon.Drawer",
					handle: {kind: "moon.DrawerHandle", content: "Handle"},
					components: [
						{content: "Drawer Content"}
					],
					controlDrawerComponents: [
						{content: "Controls"}
					]
				}
			],
			components: [
				{content: "Content Area"}
			]
		}
*/
enyo.kind({
	name: "moon.Drawers",
	kind: "enyo.Control",
	//* @protected
	classes: "moon-drawers enyo-fit",
	//* @public
	published: {
		//* Populate with an array of _moon.Drawer_ components
		drawers: null
	},
	//* @protected
	handlers: {
		//* Handlers to update the activator when the state of the contained drawers changes
		onActivate: "drawerActivated",
		onDeactivate: "drawerDeactivated",
		onSpotlightDown:"spotDown",
		onSpotlightUp:"spotUp"
	},
	components: [
		{name:"activatorWrapper", classes:"moon-drawers-activator-wrapper", spotlight:true, ontap:"activatorHandler", components: [
			{name:"activator", classes:"moon-drawers-activator", components:[
				{classes:"moon-drawers-activator-nub", components:[
					{name:"nubArrow", classes:"nub-arrow down"}
				]}
			]}
		]},
		{name:"handleContainer", classes:"moon-drawers-handle-container", kind:"enyo.Drawer", resizeContainer:false, open:false, onpostresize:"resizeHandleContainer", components:[
			{name:"handles", classes:"moon-neutral moon-drawers-handles"}
		]},
		{name: "drawers", classes:"moon-drawers-drawer-container"},
		{name: "client", classes:"moon-drawers-client"}
	],
	eventsToCapture: {
		ontap: "captureTapSelect",
		onSpotlightFocus: "captureSpotFocus",
		onSpotlightSelect: "captureTapSelect"
	},
	create: function() {
		this.inherited(arguments);
		this.$.drawers.createComponents(this.drawers, {kind: "moon.Drawer", owner:this.owner});
		this.setupHandles();
	},
	rendered: function() {
		this.inherited(arguments);
		var dh = document.body.getBoundingClientRect().height;
		this.waterfall("onDrawersRendered", {drawersHeight: dh});
	},
	setupHandles: function() {
		var handles = [];
		for (var index in this.drawers){
			handles.push(this.drawers[index].handle);
		}
		this.$.handles.createComponents(handles, {kind: "moon.DrawerHandle", owner:this});
		for (index in handles) {
			this.$.handles.getControls()[index].addClass('moon-drawers-handle');
			this.$.handles.getControls()[index].tap = this.bindSafely(this.handleTapped);
		}
	},
	activatorHandler: function(){
		if (this.drawerOpen()) {
			this.closeDrawers();
		} else {
			if (this.$.handles.getControls().length == 1) {
				this.openDrawer(this.$.handles.getControls()[0]);
			} else {
				if (this.$.handleContainer.getOpen()) {
					this.closeHandleContainer();
				} else {
					this.openHandleContainer();
				}
			}
		}
	},
	openHandleContainer: function() {
		this.$.handleContainer.setOpen(true);
		enyo.Spotlight.spot(this.$.handleContainer);
		this.updateActivator(true);
		enyo.dispatcher.capture(this.$.handleContainer, this.eventsToCapture, this);
	},
	closeHandleContainer: function() {
		enyo.dispatcher.release(this.$.handleContainer);
		this.$.handleContainer.setOpen(false);
		this.updateActivator(false);
	},
	handleTapped: function(inSender, inEvent) {
		this.openDrawer(inEvent.originator);
		return true;
	},
	openDrawer: function(drawer) {
		var handles = this.$.handles.getControls();
		for (var index in handles)
		{
			if (handles[index] == drawer || enyo.Spotlight.Util.isChild(handles[index],drawer)) {
				drawer = this.$.drawers.getControls()[index];
				drawer.toggleDrawer();
				this.closeHandleContainer();
				enyo.dispatcher.capture(drawer, this.eventsToCapture, this);
				return;
			}
		}
	},
	drawerOpen: function() {
		var drawers = this.$.drawers.getControls();
		for (var index in drawers){
			if (drawers[index].getOpen() || drawers[index].getControlsOpen()) {
				return true;
			}
		}
		return false;
	},
	closeDrawers: function() {
		var drawers = this.$.drawers.getControls();
		for (var index in drawers){
			var drawer = drawers[index];
			if (drawer.getOpen() || drawer.getControlsOpen()) {
				enyo.dispatcher.release(drawer);
				drawer.setOpen(false);
				drawer.setControlsOpen(false);
			}
		}
		this.updateActivator(false);
	},
	captureSpotFocus: function(inSender, inEvent) {
		// Only close drawers on 5-way focus in the client (not pointer focus)
		if (inEvent.dir && inEvent.dispatchTarget.isDescendantOf(this.$.client)) {
			this.closeDrawers();
			this.closeHandleContainer();
		}
	},
	captureTapSelect: function(inSender, inEvent) {
		// Any tap or select in the client area closes the dresser/drawer
		if (inEvent.dispatchTarget.isDescendantOf(this.$.client)) {
			this.closeDrawers();
			this.closeHandleContainer();
		}
	},
	drawerActivated: function(inSender, inEvent) {
		if (inEvent.originator instanceof moon.Drawer) {
			this.updateActivator(true);
			// Hide client when fullscreen drawer is open so it is not focusable
			if (inEvent.originator.getOpen()) {
				this.$.client.hide();
			}
		}
	},
	drawerDeactivated: function(inSender, inEvent) {
		if (inEvent.originator instanceof moon.Drawer) {
			if (!inEvent.originator.getOpen() && !inEvent.originator.getControlsOpen()) {
				this.updateActivator(false);
			}
			// Re-show client when closing fullscreen drawer
			if (!inEvent.originator.getOpen()) {
				this.$.client.show();
			}
		}
	},
	updateActivator: function(up) {
		this.$.nubArrow.addRemoveClass("up",up);
		this.$.nubArrow.addRemoveClass("down",!up);
		if (!up) {
			this.$.activator.addRemoveClass("drawer-open", false);
		}
	},
	resizeHandler: function() {
		this.inherited(arguments);
		var dh = document.body.getBoundingClientRect().height;
		this.waterfall("onDrawersResized", {drawersHeight: dh});
		this.updateActivator(false);
	},
	/**
		Updates the activator's style only when it is not animating, so that there
		are no visual artifacts.
	*/
	resizeHandleContainer: function(inSender, inEvent) {
		enyo.asyncMethod(inEvent.delegate.bindSafely(function(){
			if (!this.$.animator.isAnimating()) {
				this.parent.$.activator.addRemoveClass("drawer-open", this.parent.drawerOpen() ? true : false);
			}
		}));
	},
	handleAtIndex: function(inIndex) {
		return this.$.handles.getControls()[inIndex];
	}
});
<|endoftext|>


<commit_before>/**
	_moon.MarqueeText_ is a basic text control that supports marquee animation.
	When _moon.MarqueeText_ objects are used inside a
	[moon.MarqueeDecorator](#moon.MarqueeDecorator), the decorator synchronizes
	their start times; the user may start a marquee programmatically by calling
	_startMarquee()_.

		enyo.kind({
			name: "moon.Header",
			mixins: ["moon.MarqueeSupport"],
			marqueeSpeed: 100,
			components: [
				{kind: "moon.MarqueeText", content: "longText+longText"},
				{kind: "moon.MarqueeText", content: "longText"}
			],
			rendered: function() {
				this.startMarquee();
			}
		});

	To add the marquee feature to a kind, simply use the MarqueeSupport mixin:

		enyo.kind({
			name: "moon.MarqueeButton",
			kind: "enyo.Button",
			mixins: ["moon.MarqueeSupport"],
			components: [
				{kind:"moon.MarqueeText"}
			],
			contentChanged: function() {
				this.$.marqueeText.setContent(this.content);
			}
		});
*/

enyo.kind({
	name: "moon.MarqueeText",
	mixins: ["moon.MarqueeItem"],
	published: {
		//* Speed of marquee animation, in pixels per second
		marqueeSpeed: 60,
		/**
			Time in milliseconds that the marquee will pause at the end of the
			animation, before resetting to the beginning
		*/
		marqueePause: 1000,
		//* When true, marqueeing will not occur
		disabled: false
	}
});

/**
	@public

	The _moon.MarqueeSupport_ mixin should be used with controls that contain multiple marquees
	whose animation behavior should be synchronized. Calling _this.startMarquee()_ or
	_this.stopMarquee()_ starts/stops all contained marquees.
*/
moon.MarqueeSupport = {
	name: "MarqueeSupport",
	marqueeOnSpotlight: true,

	//* @protected

	handlers: {
		onSpotlightFocus: "_marqueeSpotlightFocus",
		onSpotlightBlur: "_marqueeSpotlightBlur",
		onMarqueeEnded: "_marqueeEnded",
		onresize: "_marqueeResize"
	},
	marqueeActive: false,

	//* Initialize marquee timings during _create_
	create: enyo.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			this.marqueeOnSpotlight = (this.marqueeOnSpotlight === undefined) ? true : this.marqueeOnSpotlight;
			this.marqueeSpeed =    (this.marqueeSpeed ===     undefined) ? 60 :    this.marqueeSpeed;
			this.marqueeDelay =    (this.marqueeDelay ===     undefined) ? 1000 :  this.marqueeDelay;
			this.marqueePause =    (this.marqueePause ===     undefined) ? 1000 :  this.marqueePause;
			this.marqueeHold  =    (this.marqueeHold  ===     undefined) ? 2000 :  this.marqueeHold;
			this.marqueeOnRender = (this.marqueeOnRender  === undefined) ? false : this.marqueeOnRender;
		};
	}),
	//* If _this.marqueeOnRender_ is true, kick off marquee animation
	rendered: enyo.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			if (this.marqueeOnRender) {
				this.startMarquee();
			}
		};
	}),
	//* On focus, start child marquees
	_marqueeSpotlightFocus: function(inSender, inEvent) {
		if (this.marqueeOnSpotlight) {
			this.startMarquee();
		}
	},
	//* On blur, halt child marquees
	_marqueeSpotlightBlur: function(inSender, inEvent) {
		if (this.marqueeOnSpotlight) {
			this.stopMarquee();
		}
	},
	//* When a child marquee animation completes, remove the child from _this.marqueeWaitList_
	_marqueeEnded: function(inSender, inEvent) {
		if (this.marqueeActive) {
			enyo.remove(inEvent.originator, this.marqueeWaitList);
			if (this.marqueeWaitList.length === 0) {
				this._marqueeStartHold();
				this.marqueeActive = false;
			}
		}
		return true;
	},
	_marqueeResize: function(inSender, inEvent) {
		if (this.marqueeOnSpotlight) {
			this._marqueeStartHold();
		}
	},

	//* @public

	//* Start timer to waterfall an _onRequestMarqueeStart_ event that kicks off marquee animation on all child marquees
	startMarquee: function() {
		this._buildMarqueeWaitList();

		if (this.marqueeWaitList.length === 0) {
			return;
		}

		this.marqueeActive = true;
		this.startJob("marqueeSupportJob", "_startChildMarquees", this.marqueeDelay);
	},
	//* Waterfall an _onRequestMarqueeStop_ event to halt all running child marquees
	stopMarquee: function() {
		this.stopJob("marqueeSupportJob");
		this.marqueeActive = false;
		this._stopChildMarquees();
	},
	//* Add _inControl_ to _this.marqueeWaitList_
	addMarqueeItem: function(inControl) {
		this.marqueeWaitList.push(inControl);
	},

	//* @protected

	//* Waterfall request for child animations to build up _this.marqueeWaitList_
	_buildMarqueeWaitList: function() {
		this.marqueeWaitList = [];
		this.waterfall("onRequestMarquee", {originator: this, marqueePause: this.marqueePause, marqueeSpeed: this.marqueeSpeed});
	},
	//* Waterfall event to kick off child marquee animations
	_startChildMarquees: function() {
		this.waterfall("onRequestMarqueeStart");
	},
	//* Waterfall event to halt child marquee animations
	_stopChildMarquees: function() {
		this.waterfall("onRequestMarqueeStop");
	},
	//* Begin delayed restart of child marquee animations
	_marqueeStartHold: function() {
		this.startJob("marqueeSupportJob", "startMarquee", this.marqueeHold);
	}
};

/**
	@public

	The _moon.MarqueeSupport_ mixin is used to add marquee animation functionality
	to a control.
*/
moon.MarqueeItem = {
	events: {
		onMarqueeEnded:""
	},

	//* @protected

	handlers: {
		onRequestMarquee: "requestMarquee",
		onRequestMarqueeStart: "startMarqueeAnimation",
		onRequestMarqueeStop: "stopMarqueeAnimation",
		ontransitionend: "marqueeAnimationEnded"
	},
	observers: {
		marqueeContentChanged: ["content"]
	},
	classes: "moon-marquee",
	//* When the content of this control changes, update the content of _this.$.marqueeText_ (if it exists)
	marqueeContentChanged: function() {
		if (this.$.marqueeText) {
			this.$.marqueeText.setContent(this.content);
		}
	},
	//* If this control needs to marquee, let the event originator know
	requestMarquee: function(inSender, inEvent) {
		if (!inEvent || !this.shouldMarqueeAnimate()) {
			return true;
		}

		inEvent.originator.addMarqueeItem(this);

		this.marqueePause = inEvent.marqueePause || 1000;
		this.marqueeSpeed = inEvent.marqueeSpeed || 60;

		return true;
	},
	//* Start marquee animation
	startMarqueeAnimation: function() {
		var distance = this.calcMarqueeDistance();

		// If there is no need to animate, return early
		if (!this.shouldMarqueeAnimate(distance)) {
			return;
		}

		// Lazy creation of _this.$.marqueeText_
		if (!this.$.marqueeText) {
			this.createMarquee();
		}

		this.addClass("animate-marquee");
		this.updateMarqueeNodeCSSText(this.generateMarqueeAnimationCSSText(distance));

		return true;
	},
	//* Stop marquee animation
	stopMarqueeAnimation: function(inSender, inEvent) {
		this.stopJob("stopMarquee");
		this.removeClass("animate-marquee");
		this.updateMarqueeNodeCSSText("");

		this.doMarqueeEnded();
		return true;
	},
	//* When animation ends, start _this.stopMarquee_ job
	marqueeAnimationEnded: function(inSender, inEvent) {
		if (inEvent.originator !== this.$.marqueeText) {
			return;
		}

		this.startJob("stopMarquee", "stopMarqueeAnimation", this.marqueePause);
        return true;
	},
	//* Return _true_ if this control has enough content that it needs to animate
	shouldMarqueeAnimate: function(inDistance) {
		inDistance = (inDistance && inDistance >= 0) ? inDistance : this.calcMarqueeDistance();
		return (!this.disabled && inDistance > 0);
	},
	//* Determine how far the marquee needs to scroll
	calcMarqueeDistance: function() {
		var node = this.getMarqueeNode() || this.hasNode();
		return node.scrollWidth - node.clientWidth;
	},
	//* Return duration based on _inDistance_ and _this.marqueeSpeed_
	calcMarqueeDuration: function(inDistance) {
		return inDistance / this.marqueeSpeed;
	},
	//* Return the marquee-able node
	getMarqueeNode: function() {
		this.marqueeNode = this.marqueeNode || (this.$.marqueeText ? this.$.marqueeText.hasNode() : null);
		return this.marqueeNode;
	},
	//* Create a marquee-able div inside of _this_
	createMarquee: function() {
		this.createComponent({classes: "moon-marquee-text-wrapper", components: [{name: "marqueeText", classes: "moon-marquee-text", allowHtml: this.allowHtml, content: this.content}]});
		this.render();
		return true;
	},
	//* Generate the CSS text for the marquee animation based on _inDistance_
	generateMarqueeAnimationCSSText: function(inDistance) {
		var duration = this.calcMarqueeDuration(inDistance),
			transformProp = enyo.dom.getCssTransformProp();

		return enyo.dom.transition + ": " + transformProp + " " + duration + "s linear; " + transformProp + ": translateX( " + (-1 * inDistance) + "px);";
	},
	//* Set the cssText of marquee node to _inCSSString_
	updateMarqueeNodeCSSText: function(inCSSString) {
		var node = this.getMarqueeNode();
		if (!node) {
			return;
		}
		node.style.cssText = inCSSString;
	}
};

//* @public
enyo.kind({
	name: "moon.MarqueeDecorator",
	//* @protected
	mixins: ["moon.MarqueeSupport"],
	style: "overflow: hidden;"
});
<commit_msg>Don't stop marquee events from bubbling (bug fix for nested marquees).<commit_after>/**
	_moon.MarqueeText_ is a basic text control that supports marquee animation.
	When _moon.MarqueeText_ objects are used inside a
	[moon.MarqueeDecorator](#moon.MarqueeDecorator), the decorator synchronizes
	their start times; the user may start a marquee programmatically by calling
	_startMarquee()_.

		enyo.kind({
			name: "moon.Header",
			mixins: ["moon.MarqueeSupport"],
			marqueeSpeed: 100,
			components: [
				{kind: "moon.MarqueeText", content: "longText+longText"},
				{kind: "moon.MarqueeText", content: "longText"}
			],
			rendered: function() {
				this.startMarquee();
			}
		});

	To add the marquee feature to a kind, simply use the MarqueeSupport mixin:

		enyo.kind({
			name: "moon.MarqueeButton",
			kind: "enyo.Button",
			mixins: ["moon.MarqueeSupport"],
			components: [
				{kind:"moon.MarqueeText"}
			],
			contentChanged: function() {
				this.$.marqueeText.setContent(this.content);
			}
		});
*/

enyo.kind({
	name: "moon.MarqueeText",
	mixins: ["moon.MarqueeItem"],
	published: {
		//* Speed of marquee animation, in pixels per second
		marqueeSpeed: 60,
		/**
			Time in milliseconds that the marquee will pause at the end of the
			animation, before resetting to the beginning
		*/
		marqueePause: 1000,
		//* When true, marqueeing will not occur
		disabled: false
	}
});

/**
	@public

	The _moon.MarqueeSupport_ mixin should be used with controls that contain multiple marquees
	whose animation behavior should be synchronized. Calling _this.startMarquee()_ or
	_this.stopMarquee()_ starts/stops all contained marquees.
*/
moon.MarqueeSupport = {
	name: "MarqueeSupport",
	marqueeOnSpotlight: true,
	//* @protected
	handlers: {
		onSpotlightFocus: "_marquee_spotlightFocus",
		onSpotlightBlur: "_marquee_spotlightBlur",
		onMarqueeEnded: "_marquee_marqueeEnded",
		onresize: "_marquee_resize"
	},
	marqueeActive: false,
	//* Initialize marquee timings during _create_
	create: enyo.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			this.marqueeOnSpotlight = (this.marqueeOnSpotlight === undefined) ? true : this.marqueeOnSpotlight;
			this.marqueeSpeed =    (this.marqueeSpeed ===     undefined) ? 60 :    this.marqueeSpeed;
			this.marqueeDelay =    (this.marqueeDelay ===     undefined) ? 1000 :  this.marqueeDelay;
			this.marqueePause =    (this.marqueePause ===     undefined) ? 1000 :  this.marqueePause;
			this.marqueeHold  =    (this.marqueeHold  ===     undefined) ? 2000 :  this.marqueeHold;
			this.marqueeOnRender = (this.marqueeOnRender  === undefined) ? false : this.marqueeOnRender;
		};
	}),
	//* If _this.marqueeOnRender_ is true, kick off marquee animation
	rendered: enyo.inherit(function (sup) {
		return function() {
			sup.apply(this, arguments);
			if (this.marqueeOnRender) {
				this.startMarquee();
			}
		};
	}),
	//* On focus, start child marquees
	_marquee_spotlightFocus: function(inSender, inEvent) {
		if (this.marqueeOnSpotlight) {
			this.startMarquee();
		}
	},
	//* On blur, halt child marquees
	_marquee_spotlightBlur: function(inSender, inEvent) {
		if (this.marqueeOnSpotlight) {
			this.stopMarquee();
		}
	},
	//* When a child marquee animation completes, remove the child from _this.marqueeWaitList_
	_marquee_marqueeEnded: function(inSender, inEvent) {
		if (this.marqueeActive) {
			enyo.remove(inEvent.originator, this.marqueeWaitList);
			if (this.marqueeWaitList.length === 0) {
				this._marquee_startHold();
				this.marqueeActive = false;
			}
		}
		return true;
	},
	_marquee_resize: function(inSender, inEvent) {
		if (this.marqueeOnSpotlight) {
			this._marquee_startHold();
		}
	},

	//* @public

	//* Start timer to waterfall an _onRequestMarqueeStart_ event that kicks off marquee animation on all child marquees
	startMarquee: function() {
		this._marquee_buildWaitList();

		if (this.marqueeWaitList.length === 0) {
			return;
		}

		this.marqueeActive = true;
		this.startJob("marqueeSupportJob", "_marquee_startChildMarquees", this.marqueeDelay);
	},
	//* Waterfall an _onRequestMarqueeStop_ event to halt all running child marquees
	stopMarquee: function() {
		this.stopJob("marqueeSupportJob");
		this.marqueeActive = false;
		this._marquee_stopChildMarquees();
	},
	//* Add _inControl_ to _this.marqueeWaitList_
	addMarqueeItem: function(inControl) {
		this.marqueeWaitList.push(inControl);
	},

	//* @protected

	//* Waterfall request for child animations to build up _this.marqueeWaitList_
	_marquee_buildWaitList: function() {
		this.marqueeWaitList = [];
		this.waterfall("onRequestMarquee", {originator: this, marqueePause: this.marqueePause, marqueeSpeed: this.marqueeSpeed});
	},
	//* Waterfall event to kick off child marquee animations
	_marquee_startChildMarquees: function() {
		this.waterfall("onRequestMarqueeStart");
	},
	//* Waterfall event to halt child marquee animations
	_marquee_stopChildMarquees: function() {
		this.waterfall("onRequestMarqueeStop");
	},
	//* Begin delayed restart of child marquee animations
	_marquee_startHold: function() {
		this.startJob("marqueeSupportJob", "startMarquee", this.marqueeHold);
	}
};

/**
	@public

	The _moon.MarqueeSupport_ mixin is used to add marquee animation functionality
	to a control.
*/
moon.MarqueeItem = {
	events: {
		onMarqueeEnded:""
	},
	//* @protected
	handlers: {
		onRequestMarquee: "_marquee_requestMarquee",
		onRequestMarqueeStart: "_marquee_startAnimation",
		onRequestMarqueeStop: "_marquee_stopAnimation",
		ontransitionend: "_marquee_animationEnded"
	},
	observers: {
		_marquee_contentChanged: ["content"]
	},
	classes: "moon-marquee",
	//* When the content of this control changes, update the content of _this.$.marqueeText_ (if it exists)
	_marquee_contentChanged: function() {
		if (this.$.marqueeText) {
			this.$.marqueeText.setContent(this.content);
		}
	},
	//* If this control needs to marquee, let the event originator know
	_marquee_requestMarquee: function(inSender, inEvent) {
		if (!inEvent || !this._marquee_shouldAnimate()) {
			return;
		}

		inEvent.originator.addMarqueeItem(this);

		this.marqueePause = inEvent.marqueePause || 1000;
		this.marqueeSpeed = inEvent.marqueeSpeed || 60;
	},
	//* Start marquee animation
	_marquee_startAnimation: function() {
		var distance = this._marquee_calcDistance();

		// If there is no need to animate, return early
		if (!this._marquee_shouldAnimate(distance)) {
			return;
		}

		// Lazy creation of _this.$.marqueeText_
		if (!this.$.marqueeText) {
			this._marquee_createMarquee();
		}

		this.addClass("animate-marquee");
		this._marquee_updateNodeCSSText(this._marquee_generateAnimationCSSText(distance));

		return true;
	},
	//* Stop marquee animation
	_marquee_stopAnimation: function(inSender, inEvent) {
		this.stopJob("stopMarquee");
		this.removeClass("animate-marquee");
		this._marquee_updateNodeCSSText("");
		this.doMarqueeEnded();
	},
	//* When animation ends, start _this.stopMarquee_ job
	_marquee_animationEnded: function(inSender, inEvent) {
		if (inEvent.originator !== this.$.marqueeText) {
			return;
		}

		this.startJob("stopMarquee", "_marquee_stopAnimation", this.marqueePause);
        return true;
	},
	//* Return _true_ if this control has enough content that it needs to animate
	_marquee_shouldAnimate: function(inDistance) {
		inDistance = (inDistance && inDistance >= 0) ? inDistance : this._marquee_calcDistance();
		return (!this.disabled && inDistance > 0);
	},
	//* Determine how far the marquee needs to scroll
	_marquee_calcDistance: function() {
		var node = this._marquee_getMarqueeNode() || this.hasNode();
		return node.scrollWidth - node.clientWidth;
	},
	//* Return duration based on _inDistance_ and _this.marqueeSpeed_
	_marquee_calcDuration: function(inDistance) {
		return inDistance / this.marqueeSpeed;
	},
	//* Return the marquee-able node
	_marquee_getMarqueeNode: function() {
		this.marqueeNode = this.marqueeNode || (this.$.marqueeText ? this.$.marqueeText.hasNode() : null);
		return this.marqueeNode;
	},
	//* Create a marquee-able div inside of _this_
	_marquee_createMarquee: function() {
		this.createComponent({classes: "moon-marquee-text-wrapper", components: [{name: "marqueeText", classes: "moon-marquee-text", allowHtml: this.allowHtml, content: this.content}]});
		this.render();
		return true;
	},
	//* Generate the CSS text for the marquee animation based on _inDistance_
	_marquee_generateAnimationCSSText: function(inDistance) {
		var duration = this._marquee_calcDuration(inDistance),
			transformProp = enyo.dom.getCssTransformProp();

		return enyo.dom.transition + ": " + transformProp + " " + duration + "s linear; " + transformProp + ": translateX( " + (-1 * inDistance) + "px);";
	},
	//* Set the cssText of marquee node to _inCSSString_
	_marquee_updateNodeCSSText: function(inCSSString) {
		var node = this._marquee_getMarqueeNode();
		if (!node) {
			return;
		}
		node.style.cssText = inCSSString;
	}
};

//* @public
enyo.kind({
	name: "moon.MarqueeDecorator",
	//* @protected
	mixins: ["moon.MarqueeSupport"],
	style: "overflow: hidden;"
});
<|endoftext|>