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

  function flame() {

    var container = null,
      w = 1200, // graph width
      h = 600, // graph height
      c = 18, // cell height
      tooltip = true; // enable tooltip

    function label(d) {
      if (!d.dummy) {
        return d.name + " (" + d3.round(100 * d.dx, 3) + "%, " + d.value + " samples)";
      } else {
        return "";
      }
    }

    function name(d) {
      return d.name;
    }

    function generateHash(name) {
      // Return a vector (0.0->1.0) that is a hash of the input string.
      // The hash is computed to favor early characters over later ones, so
      // that strings with similar starts have similar vectors. Only the first
      // 6 characters are considered.
      var hash = 0, weight = 1, max_hash = 0, mod = 10, max_char = 6;
      if (name) {
        for (var i = 0; i < name.length; i++) {
          if (i > max_char) { break; }
          hash += weight * (name.charCodeAt(i) % mod);
          max_hash += weight * (mod - 1);
          weight *= 0.70;
        }
        if (max_hash > 0) { hash = hash / max_hash; }
      }
      return hash;
    }

    function colorHash(name) {
      // Return an rgb() color string that is a hash of the provided name,
      // and with a warm palette.
      var vector = 0;
      if (name) {
        name = name.replace(/.*`/, "");		// drop module name if present
        name = name.replace(/\(.*/, "");	// drop extra info
        vector = generateHash(name);
      }
      var r = 200 + Math.round(55 * vector);
      var g = 0 + Math.round(230 * (1 - vector));
      var b = 0 + Math.round(55 * (1 - vector));
      return "rgb(" + r + "," + g + "," + b + ")";
    }

    function augment(root) {
      // Augment partitioning layout with "dummy" nodes so that internal nodes'
      // values dictate their width. Annoying, but seems to be least painful
      // option.  https://github.com/mbostock/d3/pull/574
      if (root.children && (root.children.length > 0)) {
        root.children.forEach(augment);
        var childValues = 0;
        root.children.forEach(function(child) {
          childValues += child.value;
        });
        if (childValues < root.value) {
          root.children.push(
            {
              "name": null,
              "value": root.value - childValues,
              "dummy": true
            }
          );
        }
      }
    }

    var partition = d3.layout.partition()
      .sort(function(a, b) {return d3.ascending(a.name, b.name);})
      .value(function(d) {return d.v || d.value;})
      .children(function(d) {return d.c || d.children;});

    function flameGraph(selector) {
      if (!arguments.length) return flameGraph;
        var x = d3.scale.linear().range([0, w]),
            y = d3.scale.linear().range([0, c]);

        selector.each(function(data) {
          container = d3.select(this).append("svg:svg")
            .attr("width", w)
            .attr("height", h)
            .attr("class", "partition");

          augment(data);

          function zoom(d) {
            update(d);
          }

          function update(root) {

            var nodes = partition(root),
                kx = w / root.dx;

            // create new elements as needed
            var svg = container.selectAll("g").data(nodes);

            // update old elements with new data
            svg.attr("width", function(d) { return d.dx * kx; })
             .attr("height", function(d) { return c; })
             .attr("name", function(d) { return d.name; })
             .attr("class", "frame")
             .attr("transform", function(d) { return "translate(" + x(d.x) + "," + (h - y(d.depth) - c) + ")"; });

            svg.select("rect")
             .attr("width", function(d) { return d.dx * kx; })
             .attr("height", function(d) { return c; })
             .attr("fill", function(d) {return colorHash(d.name); })
             .style("opacity", function(d) {return d.dummy ? 0 : 1;});

            svg.select("title").text(label);

            svg.select("foreignObject")
              .attr("width", function (d) { return d.dx * kx; })
              .attr("height", function (d) { return c; })
              .select("div")
              .attr("class", "label")
              .style("display", function (d) { return d.dx * kx < 35 ? "none" : "block";})
              .text(name);

            // and join new data with old elements, if any.
            var g = svg.enter().append("svg:g")
              .attr("width", function(d) { return d.dx * kx; })
              .attr("height", function(d) { return c; })
              .attr("name", function(d) { return d.name; })
              .attr("class", "frame")
              .attr("transform", function(d) { return "translate(" + x(d.x) + "," + (h - y(d.depth) - c) + ")"; })
              .on('click', zoom);

            g.append("svg:rect")
              .attr("width", function(d) { return d.dx * kx; })
              .attr("height", function(d) { return c; })
              .attr("fill", function(d) {return colorHash(d.name); })
              .style("opacity", function(d) {return d.dummy ? 0 : 1;});

            g.append("svg:title")
              .text(label);

            g.append("foreignObject")
              .attr("width", function(d) { return d.dx * kx; })
              .attr("height", function(d) { return c; })
              .append("xhtml:div")
              .attr("class", "label")
              .style("display", function(d) { return (d.dx * kx < 35) || d.dummy ? "none" : "block";})
              .text(name);

            // remove old elements as needed.
            svg.exit().remove();

            // including tooltip
            if (tooltip) {
              var tip = d3.tip()
                .attr('class', 'd3-tip')
                .html(function(d) { return label(d); });
              container.call(tip);
              g.on('mouseover', function(d) {
                if(!d.dummy) { tip.show(d); }

              }).on('mouseout', function(d) {
                if(!d.dummy) { tip.hide(d); }
              });
            }
          }

          // first draw
          update(data);

        });
    }

    flameGraph.height = function (_) {
      if (!arguments.length) { return h; }
      h = _;
      return flameGraph;
    };

    flameGraph.width = function (_) {
      if (!arguments.length) { return w; }
      w = _;
      return flameGraph;
    };

    flameGraph.cellHeight = function (_) {
      if (!arguments.length) { return c; }
      c = _;
      return flameGraph;
    };

    flameGraph.tooltip = function (_) {
      if (!arguments.length) { return tooltip; }
      tooltip = _;
      return flameGraph;
    };

    return flameGraph;
  }

  if (typeof module !== 'undefined' && module.exports){
		module.exports = flame;
	}
	else {
		d3.flame = flame;
	}
})();
<commit_msg>Switching to visibility since it's less computational expensive.<commit_after>(function() {
  'use strict';

  function flame() {

    var container = null,
      w = 1200, // graph width
      h = 600, // graph height
      c = 18, // cell height
      tooltip = true; // enable tooltip

    function label(d) {
      if (!d.dummy) {
        return d.name + " (" + d3.round(100 * d.dx, 3) + "%, " + d.value + " samples)";
      } else {
        return "";
      }
    }

    function name(d) {
      return d.name;
    }

    function generateHash(name) {
      // Return a vector (0.0->1.0) that is a hash of the input string.
      // The hash is computed to favor early characters over later ones, so
      // that strings with similar starts have similar vectors. Only the first
      // 6 characters are considered.
      var hash = 0, weight = 1, max_hash = 0, mod = 10, max_char = 6;
      if (name) {
        for (var i = 0; i < name.length; i++) {
          if (i > max_char) { break; }
          hash += weight * (name.charCodeAt(i) % mod);
          max_hash += weight * (mod - 1);
          weight *= 0.70;
        }
        if (max_hash > 0) { hash = hash / max_hash; }
      }
      return hash;
    }

    function colorHash(name) {
      // Return an rgb() color string that is a hash of the provided name,
      // and with a warm palette.
      var vector = 0;
      if (name) {
        name = name.replace(/.*`/, "");		// drop module name if present
        name = name.replace(/\(.*/, "");	// drop extra info
        vector = generateHash(name);
      }
      var r = 200 + Math.round(55 * vector);
      var g = 0 + Math.round(230 * (1 - vector));
      var b = 0 + Math.round(55 * (1 - vector));
      return "rgb(" + r + "," + g + "," + b + ")";
    }

    function augment(root) {
      // Augment partitioning layout with "dummy" nodes so that internal nodes'
      // values dictate their width. Annoying, but seems to be least painful
      // option.  https://github.com/mbostock/d3/pull/574
      if (root.children && (root.children.length > 0)) {
        root.children.forEach(augment);
        var childValues = 0;
        root.children.forEach(function(child) {
          childValues += child.value;
        });
        if (childValues < root.value) {
          root.children.push(
            {
              "name": null,
              "value": root.value - childValues,
              "dummy": true
            }
          );
        }
      }
    }

    var partition = d3.layout.partition()
      .sort(function(a, b) {return d3.ascending(a.name, b.name);})
      .value(function(d) {return d.v || d.value;})
      .children(function(d) {return d.c || d.children;});

    function flameGraph(selector) {
      if (!arguments.length) return flameGraph;
        var x = d3.scale.linear().range([0, w]),
            y = d3.scale.linear().range([0, c]);

        selector.each(function(data) {
          container = d3.select(this).append("svg:svg")
            .attr("width", w)
            .attr("height", h)
            .attr("class", "partition");

          augment(data);

          function zoom(d) {
            update(d);
          }

          function update(root) {

            var nodes = partition(root),
                kx = w / root.dx;

            // create new elements as needed
            var svg = container.selectAll("g").data(nodes);

            // update old elements with new data
            svg.attr("width", function(d) { return d.dx * kx; })
             .attr("height", function(d) { return c; })
             .attr("name", function(d) { return d.name; })
             .attr("class", "frame")
             .attr("transform", function(d) { return "translate(" + x(d.x) + "," + (h - y(d.depth) - c) + ")"; });

            svg.select("rect")
             .attr("width", function(d) { return d.dx * kx; })
             .attr("height", function(d) { return c; })
             .attr("fill", function(d) {return colorHash(d.name); })
             .style("opacity", function(d) {return d.dummy ? 0 : 1;});

            svg.select("title").text(label);

            svg.select("foreignObject")
              .attr("width", function (d) { return d.dx * kx; })
              .attr("height", function (d) { return c; })
              .select("div")
              .attr("class", "label")
              .style("display", function (d) { return d.dx * kx < 35 ? "none" : "block";})
              .text(name);

            // and join new data with old elements, if any.
            var g = svg.enter().append("svg:g")
              .attr("width", function(d) { return d.dx * kx; })
              .attr("height", function(d) { return c; })
              .attr("name", function(d) { return d.name; })
              .attr("class", "frame")
              .attr("transform", function(d) { return "translate(" + x(d.x) + "," + (h - y(d.depth) - c) + ")"; })
              .on('click', zoom);

            g.append("svg:rect")
              .attr("width", function(d) { return d.dx * kx; })
              .attr("height", function(d) { return c; })
              .attr("fill", function(d) {return colorHash(d.name); })
              .style("visibility", function(d) {return d.dummy ? "hidden" : "visible";});

            g.append("svg:title")
              .text(label);

            g.append("foreignObject")
              .attr("width", function(d) { return d.dx * kx; })
              .attr("height", function(d) { return c; })
              .append("xhtml:div")
              .attr("class", "label")
              .style("display", function(d) { return (d.dx * kx < 35) || d.dummy ? "none" : "block";})
              .text(name);

            // remove old elements as needed.
            svg.exit().remove();

            // including tooltip
            if (tooltip) {
              var tip = d3.tip()
                .attr('class', 'd3-tip')
                .html(function(d) { return label(d); });
              container.call(tip);
              g.on('mouseover', function(d) {
                if(!d.dummy) { tip.show(d); }

              }).on('mouseout', function(d) {
                if(!d.dummy) { tip.hide(d); }
              });
            }
          }

          // first draw
          update(data);

        });
    }

    flameGraph.height = function (_) {
      if (!arguments.length) { return h; }
      h = _;
      return flameGraph;
    };

    flameGraph.width = function (_) {
      if (!arguments.length) { return w; }
      w = _;
      return flameGraph;
    };

    flameGraph.cellHeight = function (_) {
      if (!arguments.length) { return c; }
      c = _;
      return flameGraph;
    };

    flameGraph.tooltip = function (_) {
      if (!arguments.length) { return tooltip; }
      tooltip = _;
      return flameGraph;
    };

    return flameGraph;
  }

  if (typeof module !== 'undefined' && module.exports){
		module.exports = flame;
	}
	else {
		d3.flame = flame;
	}
})();
<|endoftext|>


<commit_before>'use strict';
const debug = require('debug')('app:message-utils');
// TODO:
// If this is a util, it should not have exterior dependencies
// Coudld be a subservice from slack service
// Get it working first though.

const browsers = require('../models/browsers.model');
const RELEVANT_BROWSERS = [
	'chrome',
	'firefox',
	'ie',
	'edge',
	'safari',
	'ios_saf',
	'and_chr'
];
const CURRENT_SUPPORT_ONLY = [
	'chrome',
	'firefox',
	'edge',
	'safari',
	'ios_saf',
	'and_chr'
];
const GRANULAR_SUPPORT = {
	ie: [8, 9, 10, 11]
};
const BROWSER_DISPLAY_NAMES = {
	chrome: 'Chrome',
	firefox: 'Firefox',
	ie: 'IE',
	edge: 'Edge',
	safari: 'Safari',
	ios_saf: 'IOS Safari',
	and_chr: 'Android Chrome'
};

module.exports = (function() {

	return {
		singleFeature,
		mutliFeature
	}

	//public api
	function singleFeature(feature) {
		if (feature.usage_perc_y >= 90) {
			// Broad support everywhere
			return _featureSupportedMessage(feature);
		} else if (feature.usage_perc_y <= 20) {
			// not well supported yet
			return _featureNotSupportedMessage(feature);
		}
		return browsers.getBrowsers().then(browserData => {
			const response = {
				text: `Browser support information for ${feature.data.title} from caniuse.com:`,
				attachments: [
					{
						// fallback: 'Required plain-text summary of the attachment.',
						fallback: `${feature.data.title} is somewhat supported. Visit <http://caniuse.com|caniuse.com> for more details.`,
						color: '#FFCB6B',
						// pretext: 'Optional text that appears above the attachment block',
						// author_name: 'Bobby Tables',
						// author_link: 'http://flickr.com/bobby/',
						// author_icon: 'http://flickr.com/icons/bobby.jpg',
						author_name: 'Bobby Tables',
						author_link: 'http://flickr.com/bobby/',
						author_icon: 'https://platform.slack-edge.com/img/default_application_icon.png',
						title: feature.data.title,
						title_link: `https://caniuse.com/#search=${feature.name}`,
						text: feature.data.description,
						fields: _formatBrowserSupport(feature.data.stats, browserData),
						image_url: 'http://my-website.com/path/to/image.jpg',
						thumb_url: 'http://example.com/path/to/thumb.png',
						footer: 'Slack-Caniuse',
						footer_icon: 'https://platform.slack-edge.com/img/default_application_icon.png',
						ts: 123456789
					}
				]
			}

			return response;
		});
	}

	function mutliFeature(features) {
		return {
			text: 'Your search returned a few results. Which one did you mean?',
			attachments: features.map(feature => {
				return {
					text: feature.data.title,
					fallback: 'uhhh no idea what this is',
					callback_id: 'foo_bar',
					color: '#3AA3E3',
					attachment_type: 'default',
					actions: [{
						name: feature.name,
						text: 'This one!',
						type: 'button',
						value: feature._id
					}]
				}
			})
		}
	}

	function _formatBrowserSupport(featureSupportData, browserData) {
		const relevantStats = Object.keys(featureSupportData)
			.filter(browserName => RELEVANT_BROWSERS.indexOf(browserName.toLowerCase()) >= 0)
			.reduce((statsArray, browserName) => {
				if (CURRENT_SUPPORT_ONLY.indexOf(browserName) >= 0) {
					statsArray.push(_formatCurrentSupport(browserName, featureSupportData[browserName], browserData))
				} else {
					statsArray.push(_formatGranularSupport(browserName, featureSupportData[browserName], browserData))
				}
				return statsArray;
			}, []);

		return relevantStats;
	}

	function _formatCurrentSupport(browserName, supportData, browserData) {
		const status = supportData[browserData[browserName].data.currentVersion];
		let textContent = 'Not supported';
		if (status.includes('y')) {
			textContent = 'Currently supported'
		} else if (status.includes('a')) {
			textContent = 'Partially supported'
		}

		return {
			title: BROWSER_DISPLAY_NAMES[browserName],
			value: textContent,
			short: true
		}
	}

	function _formatGranularSupport(browserName, supportData, browserData) {
		const relevantVersions = GRANULAR_SUPPORT[browserName];
		const supportValues = relevantVersions.map(versionNum => supportData[versionNum]);
		let textContent = '';
		let supportedText = '';
		let partialText = '';

		const supportedVersions = _getVersionsWithStatus(supportData, 'y').join(',').replace(/,/g, ', ');
		const partialVersions = _getVersionsWithStatus(supportData, 'a').join(',').replace(/,/g, ', ');
		if (supportedVersions) {
			supportedText = `Supported in ${BROWSER_DISPLAY_NAMES[browserName]} ${supportedVersions}`
		}
		if (partialVersions) {
			partialText = `Partially supported in ${BROWSER_DISPLAY_NAMES[browserName]} ${partialVersions}}`
		}

		if (supportedText && partialText) {
			textContent = `${supportedText}; ${partialText}`;
		} else if (supportedText && !partialText) {
			textContent = supportedText;
		} else if (partialText && !supportedText) {
			textContent = partialText;
		} else {
			textContent = 'Not supported';
		}

		return {
			title: BROWSER_DISPLAY_NAMES[browserName],
			value: textContent,
			short: true
		}
	}

	function _getVersionsWithStatus(supportData, status) {
		return Object.keys(supportData).reduce((versions, version) => {
			if (supportData[version].includes(status)) {
				versions.push(version);
			}
			return versions;
		}, []);
	}

	function _parseBrowserSupportEntry(entry) {
		let noteIndexes = null;
		if (entry.includes('#')) {
			noteIndexes = entry.split('').reduce((notes, char, currentIndex) => {
				if(char === '#') {
					notes.push(parseInt(entry.split('')[currentIndex + 1]));
				}
				return notes
			}, []);
		}
		return {
			yes: entry.includes('y'),
			almost: entry.includes('a'),
			no: entry.includes('n'),
			polyfill: entry.includes('p'),
			unknown: entry.includes('u'),
			prefixed: entry.includes('x'),
			disabled: entry.includes('p'),
			noteIndexes
		}
	}

})()
<commit_msg>also sending global support data now<commit_after>'use strict';
const debug = require('debug')('app:message-utils');
// TODO:
// If this is a util, it should not have exterior dependencies
// Coudld be a subservice from slack service
// Get it working first though.

const browsers = require('../models/browsers.model');
const RELEVANT_BROWSERS = [
	'chrome',
	'firefox',
	'ie',
	'edge',
	'safari',
	'ios_saf',
	'and_chr'
];
const CURRENT_SUPPORT_ONLY = [
	'chrome',
	'firefox',
	'edge',
	'safari',
	'ios_saf',
	'and_chr'
];
const GRANULAR_SUPPORT = {
	ie: [8, 9, 10, 11]
};
const BROWSER_DISPLAY_NAMES = {
	chrome: 'Chrome',
	firefox: 'Firefox',
	ie: 'IE',
	edge: 'Edge',
	safari: 'Safari',
	ios_saf: 'IOS Safari',
	and_chr: 'Android Chrome'
};

module.exports = (function() {

	return {
		singleFeature,
		mutliFeature
	}

	//public api
	function singleFeature(feature) {
		if (feature.usage_perc_y >= 90) {
			// Broad support everywhere
			return _featureSupportedMessage(feature);
		} else if (feature.usage_perc_y <= 20) {
			// not well supported yet
			return _featureNotSupportedMessage(feature);
		}
		return browsers.getBrowsers().then(browserData => {
			const response = {
				text: `Browser support information for ${feature.data.title} from caniuse.com:`,
				attachments: [
					{
						// fallback: 'Required plain-text summary of the attachment.',
						fallback: `${feature.data.title} is somewhat supported. Visit <http://caniuse.com|caniuse.com> for more details.`,
						color: '#FFCB6B',
						// pretext: 'Optional text that appears above the attachment block',
						// author_name: 'Bobby Tables',
						// author_link: 'http://flickr.com/bobby/',
						// author_icon: 'http://flickr.com/icons/bobby.jpg',
						author_name: 'Bobby Tables',
						author_link: 'http://flickr.com/bobby/',
						author_icon: 'https://platform.slack-edge.com/img/default_application_icon.png',
						title: feature.data.title,
						title_link: `https://caniuse.com/#search=${feature.name}`,
						text: feature.data.description,
						fields: _formatBrowserSupport(
							Object.assign(feature.data.stats, {usage_perc_y: feature.data.usage_perc_y}),
							browserData
						),
						image_url: 'http://my-website.com/path/to/image.jpg',
						thumb_url: 'http://example.com/path/to/thumb.png',
						footer: 'Slack-Caniuse',
						footer_icon: 'https://platform.slack-edge.com/img/default_application_icon.png',
						ts: 123456789
					}
				]
			}

			return response;
		});
	}

	function mutliFeature(features) {
		return {
			text: 'Your search returned a few results. Which one did you mean?',
			attachments: features.map(feature => {
				return {
					text: feature.data.title,
					fallback: 'uhhh no idea what this is',
					callback_id: 'foo_bar',
					color: '#3AA3E3',
					attachment_type: 'default',
					actions: [{
						name: feature.name,
						text: 'This one!',
						type: 'button',
						value: feature._id
					}]
				}
			})
		}
	}

	function _formatBrowserSupport(featureSupportData, browserData) {
		const relevantStats = Object.keys(featureSupportData)
			.filter(browserName => RELEVANT_BROWSERS.indexOf(browserName.toLowerCase()) >= 0)
			.reduce((statsArray, browserName) => {
				if (CURRENT_SUPPORT_ONLY.indexOf(browserName) >= 0) {
					statsArray.push(_formatCurrentSupport(browserName, featureSupportData[browserName], browserData))
				} else {
					statsArray.push(_formatGranularSupport(browserName, featureSupportData[browserName], browserData))
				}
				return statsArray;
			}, []);

		debug(featureSupportData.usage_perc_y);
		relevantStats.push({
			title: 'Global support',
			value: `${featureSupportData.usage_perc_y}%`,
			short: true
		})

		return relevantStats;
	}

	function _formatCurrentSupport(browserName, supportData, browserData) {
		const status = supportData[browserData[browserName].data.currentVersion];
		let textContent = 'Not supported';
		if (status.includes('y')) {
			textContent = 'Currently supported'
		} else if (status.includes('a')) {
			textContent = 'Partially supported'
		}

		return {
			title: BROWSER_DISPLAY_NAMES[browserName],
			value: textContent,
			short: true
		}
	}

	function _formatGranularSupport(browserName, supportData, browserData) {
		const relevantVersions = GRANULAR_SUPPORT[browserName];
		const supportValues = relevantVersions.map(versionNum => supportData[versionNum]);
		let textContent = '';
		let supportedText = '';
		let partialText = '';

		const supportedVersions = _getVersionsWithStatus(supportData, 'y').join(',').replace(/,/g, ', ');
		const partialVersions = _getVersionsWithStatus(supportData, 'a').join(',').replace(/,/g, ', ');
		if (supportedVersions) {
			supportedText = `Supported in ${BROWSER_DISPLAY_NAMES[browserName]} ${supportedVersions}`
		}
		if (partialVersions) {
			partialText = `Partially supported in ${BROWSER_DISPLAY_NAMES[browserName]} ${partialVersions}}`
		}

		if (supportedText && partialText) {
			textContent = `${supportedText}; ${partialText}`;
		} else if (supportedText && !partialText) {
			textContent = supportedText;
		} else if (partialText && !supportedText) {
			textContent = partialText;
		} else {
			textContent = 'Not supported';
		}

		return {
			title: BROWSER_DISPLAY_NAMES[browserName],
			value: textContent,
			short: true
		}
	}

	function _getVersionsWithStatus(supportData, status) {
		return Object.keys(supportData).reduce((versions, version) => {
			if (supportData[version].includes(status)) {
				versions.push(version);
			}
			return versions;
		}, []);
	}

	function _parseBrowserSupportEntry(entry) {
		let noteIndexes = null;
		if (entry.includes('#')) {
			noteIndexes = entry.split('').reduce((notes, char, currentIndex) => {
				if(char === '#') {
					notes.push(parseInt(entry.split('')[currentIndex + 1]));
				}
				return notes
			}, []);
		}
		return {
			yes: entry.includes('y'),
			almost: entry.includes('a'),
			no: entry.includes('n'),
			polyfill: entry.includes('p'),
			unknown: entry.includes('u'),
			prefixed: entry.includes('x'),
			disabled: entry.includes('p'),
			noteIndexes
		}
	}

})()
<|endoftext|>


<commit_before>var Entity=require("prismarine-entity");
var Vec3 = require("vec3").Vec3;

var path = require('path');
var requireIndex = require('requireindex');
var plugins = requireIndex(path.join(__dirname,'..', 'plugins'));
var Command = require('flying-squid').Command;

module.exports.server=function(serv,options)
{
  serv._server.on('connection', client =>
    client.on('error',error => serv.emit('clientError',client,error)));

  serv._server.on('login', async (client) => {
    if(client.socket.listenerCount('end')==0) return; // TODO: should be fixed properly in nmp instead
    try {
      var player = serv.initEntity('player', null, serv.overworld, new Vec3(0,0,0));
      player._client=client;

      player.profileProperties=player._client.profile ? player._client.profile.properties : [];
      player.commands = new Command({});
      Object.keys(plugins)
        .filter(pluginName => plugins[pluginName].player!=undefined)
        .forEach(pluginName => plugins[pluginName].player(player, serv, options));

      serv.emit("newPlayer",player);
      player.emit('asap');
      await player.login();
    }
    catch(err){
      setTimeout(() => {throw err;},0)
    }
  });
};

module.exports.player=function(player,serv)
{
  function addPlayer()
  {
    player.type = 'player';
    player.health = 20;
    player.food = 20;
    player.crouching = false; // Needs added in prismarine-entity later
    player.op = true; // REMOVE THIS WHEN OUT OF TESTING
    player.username=player._client.username;
    serv.players.push(player);
    serv.uuidToPlayer[player._client.uuid] = player;
    player.heldItemSlot=36;
    player.loadedChunks={};
  }

  function sendLogin()
  {
    // send init data so client will start rendering world
    player._client.write('login', {
      entityId: player.id,
      levelType: 'default',
      gameMode: player.gameMode,
      dimension: 0,
      difficulty: 0,
      reducedDebugInfo: false,
      maxPlayers: serv._server.maxPlayers
    });
    player.position=player.spawnPoint.toFixedPosition();
  }

  function sendChunkWhenMove()
  {
    player.on("move", () => {
      if(!player.sendingChunks && player.position.distanceTo(player.lastPositionChunkUpdated)>16*32)
        player.sendRestMap();
    });
  }

  function updateTime()
  {
    player._client.write('update_time', {
      age: [0, 0],
      time: [0, serv.time]
    });
  }

  player.setGameMode = gameMode =>
  {
    player.gameMode=gameMode;
    player._client.write('game_state_change', {
      reason: 3,
      gameMode: player.gameMode
    });
    serv._writeAll('player_info',{
      action: 1,
      data: [{
        UUID: player._client.uuid,
        gamemode: player.gameMode
      }]
    });
  };

  function fillTabList()
  {
    player._writeOthers('player_info',{
      action: 0,
      data: [{
        UUID: player._client.uuid,
        name: player.username,
        properties: player.profileProperties,
        gamemode: player.gameMode,
        ping: 1
      }]
    });

    player._client.write('player_info', {
      action: 0,
      data: serv.players.map((otherPlayer) => ({
        UUID: otherPlayer._client.uuid,
        name: otherPlayer.username,
        properties: otherPlayer.profileProperties,
        gamemode: otherPlayer.gameMode,
        ping: 1
      }))
    });
  }

  function announceJoin()
  {
    serv.broadcast(player.username + ' joined the game.', "yellow");
    player.emit("connected");
  }

  player.waitPlayerLogin = () => {
    var events=["flying","look"];
    return new Promise(function(resolve){

      var listener=()=> {
        events.map(event => player._client.removeListener(event,listener));
        resolve();
      };
      events.map(event =>player._client.on(event,listener));
    });
  };

  player.login = async () =>
  {
    if (serv.uuidToPlayer[player._client.uuid]) {
      player._client.end("You are already connected");
      return;
    }
    if (serv.bannedPlayers[player._client.uuid]) {
      player.kick(serv.bannedPlayers[player._client.uuid].reason);
      return;
    }
    if(serv.bannedIPs[player._client.socket.remoteAddress]){
        player.kick(serv.bannedIPs[player._client.socket.remoteAddress].reason)
        return
    }

    addPlayer();
    sendLogin();
    await player.sendMap();
    player.sendSpawnPosition();
    player.sendSelfPosition();
    player.updateHealth(player.health);

    updateTime();
    fillTabList();
    player.updateAndSpawn();

    announceJoin();
    player.emit("spawned");

    await player.waitPlayerLogin();
    player.sendRestMap();
    sendChunkWhenMove();
  };
};<commit_msg>node 0.10 doesn't have listenerCount, don't use it<commit_after>var Entity=require("prismarine-entity");
var Vec3 = require("vec3").Vec3;

var path = require('path');
var requireIndex = require('requireindex');
var plugins = requireIndex(path.join(__dirname,'..', 'plugins'));
var Command = require('flying-squid').Command;

module.exports.server=function(serv,options)
{
  serv._server.on('connection', client =>
    client.on('error',error => serv.emit('clientError',client,error)));

  serv._server.on('login', async (client) => {
    if(client.socket.listeners('end').length==0) return; // TODO: should be fixed properly in nmp instead
    try {
      var player = serv.initEntity('player', null, serv.overworld, new Vec3(0,0,0));
      player._client=client;

      player.profileProperties=player._client.profile ? player._client.profile.properties : [];
      player.commands = new Command({});
      Object.keys(plugins)
        .filter(pluginName => plugins[pluginName].player!=undefined)
        .forEach(pluginName => plugins[pluginName].player(player, serv, options));

      serv.emit("newPlayer",player);
      player.emit('asap');
      await player.login();
    }
    catch(err){
      setTimeout(() => {throw err;},0)
    }
  });
};

module.exports.player=function(player,serv)
{
  function addPlayer()
  {
    player.type = 'player';
    player.health = 20;
    player.food = 20;
    player.crouching = false; // Needs added in prismarine-entity later
    player.op = true; // REMOVE THIS WHEN OUT OF TESTING
    player.username=player._client.username;
    serv.players.push(player);
    serv.uuidToPlayer[player._client.uuid] = player;
    player.heldItemSlot=36;
    player.loadedChunks={};
  }

  function sendLogin()
  {
    // send init data so client will start rendering world
    player._client.write('login', {
      entityId: player.id,
      levelType: 'default',
      gameMode: player.gameMode,
      dimension: 0,
      difficulty: 0,
      reducedDebugInfo: false,
      maxPlayers: serv._server.maxPlayers
    });
    player.position=player.spawnPoint.toFixedPosition();
  }

  function sendChunkWhenMove()
  {
    player.on("move", () => {
      if(!player.sendingChunks && player.position.distanceTo(player.lastPositionChunkUpdated)>16*32)
        player.sendRestMap();
    });
  }

  function updateTime()
  {
    player._client.write('update_time', {
      age: [0, 0],
      time: [0, serv.time]
    });
  }

  player.setGameMode = gameMode =>
  {
    player.gameMode=gameMode;
    player._client.write('game_state_change', {
      reason: 3,
      gameMode: player.gameMode
    });
    serv._writeAll('player_info',{
      action: 1,
      data: [{
        UUID: player._client.uuid,
        gamemode: player.gameMode
      }]
    });
  };

  function fillTabList()
  {
    player._writeOthers('player_info',{
      action: 0,
      data: [{
        UUID: player._client.uuid,
        name: player.username,
        properties: player.profileProperties,
        gamemode: player.gameMode,
        ping: 1
      }]
    });

    player._client.write('player_info', {
      action: 0,
      data: serv.players.map((otherPlayer) => ({
        UUID: otherPlayer._client.uuid,
        name: otherPlayer.username,
        properties: otherPlayer.profileProperties,
        gamemode: otherPlayer.gameMode,
        ping: 1
      }))
    });
  }

  function announceJoin()
  {
    serv.broadcast(player.username + ' joined the game.', "yellow");
    player.emit("connected");
  }

  player.waitPlayerLogin = () => {
    var events=["flying","look"];
    return new Promise(function(resolve){

      var listener=()=> {
        events.map(event => player._client.removeListener(event,listener));
        resolve();
      };
      events.map(event =>player._client.on(event,listener));
    });
  };

  player.login = async () =>
  {
    if (serv.uuidToPlayer[player._client.uuid]) {
      player._client.end("You are already connected");
      return;
    }
    if (serv.bannedPlayers[player._client.uuid]) {
      player.kick(serv.bannedPlayers[player._client.uuid].reason);
      return;
    }
    if(serv.bannedIPs[player._client.socket.remoteAddress]){
        player.kick(serv.bannedIPs[player._client.socket.remoteAddress].reason)
        return
    }

    addPlayer();
    sendLogin();
    await player.sendMap();
    player.sendSpawnPosition();
    player.sendSelfPosition();
    player.updateHealth(player.health);

    updateTime();
    fillTabList();
    player.updateAndSpawn();

    announceJoin();
    player.emit("spawned");

    await player.waitPlayerLogin();
    player.sendRestMap();
    sendChunkWhenMove();
  };
};<|endoftext|>


<commit_before>/*!
 * CSS Modal
 * http://drublic.github.com/css-modal
 *
 * @author Hans Christian Reinl - @drublic
 * @version 1.1.0
 */

(function (global) {

	'use strict';

	// Storage variable
	var modal = {};

	// Store for currently active element
	modal.lastActive = undefined;
	modal.activeElement = undefined;

	// Polyfill addEventListener for IE8 (only very basic)
	modal._addEventListener = function (element, event, callback) {
		if (element.addEventListener) {
			element.addEventListener(event, callback, false);
		} else {
			element.attachEvent('on' + event, callback);
		}
	};

	// Hide overlay when ESC is pressed
	modal._addEventListener(document, 'keyup', function (event) {
		var hash = window.location.hash.replace('#', '');

		// If hash is not set
		if (hash === '' || hash === '!') {
			return;
		}

		// If key ESC is pressed
		if (event.keyCode === 27) {
			window.location.hash = '!';

			if (modal.lastActive) {
				return false;
			}

			// Unfocus
			modal.removeFocus();
		}
	}, false);

	// Convenience function to trigger event
	modal._dispatchEvent = function (event, modal) {
		var eventTigger;

		if (!document.createEvent) {
			return;
		}

		eventTigger = document.createEvent('Event');

		eventTigger.initEvent(event, true, true);
		eventTigger.customData = { 'modal': modal };

		document.dispatchEvent(eventTigger);
	};

	// Convenience function to add a class to an element
	modal.addClass = function (element, className) {
		element.className += className;
	};

	// Convenience function to remove a class from an element
	modal.removeClass = function (element, className) {
		element.className = element.className.replace(className, '');
	};

	// Mark modal as active
	modal.setActive = function (element) {
		modal.addClass(element, 'is-active');
		modal.activeElement = element;
	};

	// Unset previous active modal
	modal.unsetActive = function () {
		if (modal.activeElement) {
			modal.removeClass(modal.activeElement, 'is-active');
		}
	};

	// When showing overlay, prevent background from scrolling
	modal.mainHandler = function () {
		var htmlClasses = document.documentElement.className;
		var hash = window.location.hash.replace('#', '');
		var modalElement = document.getElementById(hash);
		var modalChild;

		// If the hash element exists
		if (modalElement) {

			// Get first element in selected element
			modalChild = modalElement.children[0];

			// When we deal with a modal and body-class `has-overlay` is not set
			if (modalChild && modalChild.className.match(/modal-inner/)) {
				if (!htmlClasses.match(/has-overlay/)) {

					// Set an html class to prevent scrolling
					modal.addClass(document.documentElement, 'has-overlay');
				}

				// Unmark previous active element
				modal.unsetActive(modalElement);

				// Mark the active element
				modal.setActive(modalElement);

				// Set the focus to the modal
				modal.setFocus(hash);

				// Fire an event
				modal._dispatchEvent('cssmodal:show', modal.activeElement);
			}
		} else {
			modal.removeClass(document.documentElement, 'has-overlay');

			// If activeElement is already defined, delete it
			if (modal.activeElement) {
				modal.removeClass(modal.activeElement, 'is-active');

				// Fire an event
				modal._dispatchEvent('cssmodal:hide', modal.activeElement);

				// Reset active element
				modal.activeElement = null;

				// Unfocus
				modal.removeFocus();
			}
		}
	};

	// Trigger main handler on load and hashchange
	modal._addEventListener(window, 'hashchange', modal.mainHandler);
	modal._addEventListener(window, 'load', modal.mainHandler);

	/*
	 * Accessibility
	 */

	// Focus modal
	modal.setFocus = function () {
		if (modal.activeElement) {

			// Set element with last focus
			modal.lastActive = document.activeElement;

			// New focussing
			modal.activeElement.focus();
		}
	};

	// Unfocus
	modal.removeFocus = function () {
		if (modal.lastActive) {
			modal.lastActive.focus();
		}
	};

	// Export CSSModal into global space
	global.CSSModal = modal;

}(window));
<commit_msg>Check if class is already set on element before applying again<commit_after>/*!
 * CSS Modal
 * http://drublic.github.com/css-modal
 *
 * @author Hans Christian Reinl - @drublic
 * @version 1.1.0
 */

(function (global) {

	'use strict';

	// Storage variable
	var modal = {};

	// Store for currently active element
	modal.lastActive = undefined;
	modal.activeElement = undefined;

	// Polyfill addEventListener for IE8 (only very basic)
	modal._addEventListener = function (element, event, callback) {
		if (element.addEventListener) {
			element.addEventListener(event, callback, false);
		} else {
			element.attachEvent('on' + event, callback);
		}
	};

	// Hide overlay when ESC is pressed
	modal._addEventListener(document, 'keyup', function (event) {
		var hash = window.location.hash.replace('#', '');

		// If hash is not set
		if (hash === '' || hash === '!') {
			return;
		}

		// If key ESC is pressed
		if (event.keyCode === 27) {
			window.location.hash = '!';

			if (modal.lastActive) {
				return false;
			}

			// Unfocus
			modal.removeFocus();
		}
	}, false);

	// Convenience function to trigger event
	modal._dispatchEvent = function (event, modal) {
		var eventTigger;

		if (!document.createEvent) {
			return;
		}

		eventTigger = document.createEvent('Event');

		eventTigger.initEvent(event, true, true);
		eventTigger.customData = { 'modal': modal };

		document.dispatchEvent(eventTigger);
	};

	// Convenience function to add a class to an element
	modal.addClass = function (element, className) {
		if (element && !element.className.match(/className/)) {
			element.className += className;
		}
	};

	// Convenience function to remove a class from an element
	modal.removeClass = function (element, className) {
		element.className = element.className.replace(className, '');
	};

	// Mark modal as active
	modal.setActive = function (element) {
		modal.addClass(element, 'is-active');
		modal.activeElement = element;
	};

	// Unset previous active modal
	modal.unsetActive = function () {
		if (modal.activeElement) {
			modal.removeClass(modal.activeElement, 'is-active');
		}
	};

	// When showing overlay, prevent background from scrolling
	modal.mainHandler = function () {
		var hash = window.location.hash.replace('#', '');
		var modalElement = document.getElementById(hash);
		var modalChild;

		// If the hash element exists
		if (modalElement) {

			// Get first element in selected element
			modalChild = modalElement.children[0];

			// When we deal with a modal and body-class `has-overlay` is not set
			if (modalChild && modalChild.className.match(/modal-inner/)) {

				// Set an html class to prevent scrolling
				modal.addClass(document.documentElement, 'has-overlay');

				// Unmark previous active element
				modal.unsetActive(modalElement);

				// Mark the active element
				modal.setActive(modalElement);

				// Set the focus to the modal
				modal.setFocus(hash);

				// Fire an event
				modal._dispatchEvent('cssmodal:show', modal.activeElement);
			}
		} else {
			modal.removeClass(document.documentElement, 'has-overlay');

			// If activeElement is already defined, delete it
			if (modal.activeElement) {
				modal.removeClass(modal.activeElement, 'is-active');

				// Fire an event
				modal._dispatchEvent('cssmodal:hide', modal.activeElement);

				// Reset active element
				modal.activeElement = null;

				// Unfocus
				modal.removeFocus();
			}
		}
	};

	// Trigger main handler on load and hashchange
	modal._addEventListener(window, 'hashchange', modal.mainHandler);
	modal._addEventListener(window, 'load', modal.mainHandler);

	/*
	 * Accessibility
	 */

	// Focus modal
	modal.setFocus = function () {
		if (modal.activeElement) {

			// Set element with last focus
			modal.lastActive = document.activeElement;

			// New focussing
			modal.activeElement.focus();
		}
	};

	// Unfocus
	modal.removeFocus = function () {
		if (modal.lastActive) {
			modal.lastActive.focus();
		}
	};

	// Export CSSModal into global space
	global.CSSModal = modal;

}(window));
<|endoftext|>


<commit_before>/// @file
/// @author John Tooker
/// @license MIT, see `LICENSE.txt`
///
/// The "model" of this app

/*global chrome, console, settings*/

function model(callback) {
    "use strict";

    // State
    var m_loading = true;
    var m_directoryEntry;
    var m_fileInfos = [];
    var m_extFilter = [];   /// [] means all are valid, else whitelist
    var m_findAndReplaces = [];
    var m_settings;
    var m_onStaleData;      /// @see setOnRefresh

    // private -----------------------------------------------------------------
    function isFunction(f) {
        return typeof f === "function";
    }

    // private -----------------------------------------------------------------
    function capitalizeFirstLetter(text) {
        text = text.toLowerCase();

        return text.replace(/^./, function (first) {
            return first.toUpperCase();
        });
    }

    // private -----------------------------------------------------------------
    function capitalizeEachWord(text) {
        // http://stackoverflow.com/questions/15150168/
        return text.replace(/[^\s]+/g, capitalizeFirstLetter);
    }

    // private -----------------------------------------------------------------
    function calculateName(name) {
        var stem;       // up to last period
        var extension;  // includes period
        var lastPeriodIndex = name.lastIndexOf(".");

        // treat names that start with a period as not having an extension
        if (lastPeriodIndex < 1) {
            stem = name;
            extension = "";
        } else {
            stem = name.substring(0, lastPeriodIndex);
            extension = name.substring(lastPeriodIndex);
        }

        // stem modifications
        m_findAndReplaces.forEach(function (fr) {
            stem = stem.replace(fr.find, fr.replace);
        });

        if (m_settings.usePrefix) {
            stem = m_settings.prefix + stem;
        }
        if (m_settings.usePostfix) {
            stem = stem + m_settings.postfix;
        }
        switch (m_settings.changeCase) {
        case "unchanged":
            break;
        case "uppercase":
            stem = stem.toUpperCase();
            break;
        case "lowercase":
            stem = stem.toLowerCase();
            break;
        case "capitalize_words":
            stem = capitalizeEachWord(stem);
            break;
        case "capitalize_first_letter":
            stem = capitalizeFirstLetter(stem);
            break;
        }

        // extension modifications
        if (m_settings.lowercaseExtensions) {
            extension = extension.toLowerCase();
        } else if (m_settings.uppercaseExtensions) {
            extension = extension.toUpperCase();
        } else if (m_settings.haveCustomExtensions) {
            if (m_settings.customExtension === "") {
                extension = "";
            } else if (m_settings.customExtension.indexOf(".") === 0) {
                extension = m_settings.customExtension;
            } else {
                extension = "." + m_settings.customExtension;
            }
        }

        return stem + extension;
    }

    // private -----------------------------------------------------------------
    function incrementKey(object, key) {
        if (object[key] > 0) {
            object[key] += 1;
        } else {
            object[key] = 1;
        }
    }

    // private -----------------------------------------------------------------
    function calculateNames() {
        var usedNames = {};

        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.newName = fileInfo.enabled
                ? calculateName(fileInfo.fileName)
                : fileInfo.fileName;

            if (fileInfo.fileName !== fileInfo.newName) {
                // otherwise it may be overwritten
                incrementKey(usedNames, fileInfo.fileName);
            }
            incrementKey(usedNames, fileInfo.newName);
        });

        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.error = usedNames[fileInfo.newName] > 1
                ? "Another file has this name"
                : undefined;
        });
    }

    // private -----------------------------------------------------------------
    function refresh(staleParts) {
        calculateNames();

        if (isFunction(m_onStaleData)) {
            m_onStaleData(staleParts);
        }
    }

    // public ------------------------------------------------------------------
    function areLoading() {
        return m_loading;
    }

    // private -----------------------------------------------------------------
    function reportError(message) {
        refresh({errorMessage: message});
    }

    // private -----------------------------------------------------------------
    function getExtFilterString() {
        if (m_extFilter.length === 0) {
            return "*.*";
        }

        return "*." + m_extFilter.join(", *.");
    }

    // private -----------------------------------------------------------------
    function getDirectoryPath() {
        return (m_directoryEntry && m_directoryEntry.fullPath) || "";
    }

    // private -----------------------------------------------------------------
    function translateFileInfoForView() {
        var simpleInfos = [];

        m_fileInfos.forEach(function (fileInfo) {
            simpleInfos.push({
                checked: fileInfo.enabled,
                fileName: fileInfo.fileName
            });
        });

        return Object.freeze(simpleInfos);
    }

    // pubic -------------------------------------------------------------------
    function getFileInfo() {
        return Object.freeze({
            directoryPath: getDirectoryPath(),
            files: translateFileInfoForView()
        });
    }

    // pubic -------------------------------------------------------------------
    function getOptions() {
        return Object.freeze({
            extFilter: getExtFilterString(),
            usePrefix: m_settings.usePrefix,
            prefix: m_settings.prefix,
            usePostfix: m_settings.usePostfix,
            postfix: m_settings.postfix,
            changeCase: m_settings.changeCase,
            lowercaseExtensions: m_settings.lowercaseExtensions,
            uppercaseExtensions: m_settings.uppercaseExtensions,
            haveCustomExtensions: m_settings.haveCustomExtensions,
            customExtension: m_settings.customExtension,
            findAndReplaces: m_findAndReplaces
        });
    }

    // pubic -------------------------------------------------------------------
    function getPreview() {
        var changes = [];

        m_fileInfos.forEach(function (fileInfo) {
            if (fileInfo.enabled) {
                changes.push({
                    oldName: fileInfo.fileName,
                    newName: fileInfo.newName,
                    error: fileInfo.error
                });
            }
        });

        return changes;
    }

    // private -----------------------------------------------------------------
    function isFileTypeEnabled(fileName) {
        var found = false;

        if (m_extFilter.length === 0) {
            return true;
        }

        m_extFilter.forEach(function (ext) {
            found = found || fileName.endsWith("." + ext);
        });

        return found;
    }

    // private -----------------------------------------------------------------
    function refilterFiles() {
        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.enabled = isFileTypeEnabled(fileInfo.fileName);
        });
    }

    // private -----------------------------------------------------------------
    function createFileInfo(fileEntry) {
        return {"fileEntry": fileEntry,
                "fileName": fileEntry.name,
                "newName": fileEntry.name,
                "enabled": isFileTypeEnabled(fileEntry.name)};
    }

    // private -----------------------------------------------------------------
    function loadDirectory(directoryEntry) {
        var directoryReader;

        if (!directoryEntry.isDirectory) {
            return;
        }

        m_directoryEntry = directoryEntry;

        m_fileInfos = [];
        directoryReader = directoryEntry.createReader();
        function readEntries() {
            directoryReader.readEntries(
                function (results) {
                    if (!results.length) {
                        refresh();
                        return;
                    }

                    results.forEach(function (fileEntry) {
                        if (fileEntry.isFile) {
                            m_fileInfos.push(createFileInfo(fileEntry));
                        }
                    });
                    readEntries();
                },
                function (error) {
                    console.error(error);
                }
            );
        }
        readEntries();

        // refresh is done on the last `readEntries`
    }

    // pubic -------------------------------------------------------------------
    function setDirectory(entry) {
        if (!entry) {
            return;
        }

        // use local storage to retain access to this file
        chrome.storage.local.set({"directory": chrome.fileSystem.retainEntry(entry)});
        loadDirectory(entry);
    }

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

    // public ------------------------------------------------------------------
    function setExtensionFilter(filterText) {
        filterText = filterText || "";

        m_settings.extFilter = filterText;
        m_extFilter = [];

        filterText = filterText.trim();

        if (filterText === "" || filterText === "*.*") {
            return;
        }

        m_extFilter = filterText.split(",");
        m_extFilter.forEach(function (value, i) {
            value = value.trim();
            value = value.replace("*.", "");
            m_extFilter[i] = value;
        });

        refilterFiles();
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleUsePrefix() {
        m_settings.usePrefix = !m_settings.usePrefix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleUsePostfix() {
        m_settings.usePostfix = !m_settings.usePostfix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function setPrefix(prefix) {
        m_settings.prefix = prefix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function setPostfix(postfix) {
        m_settings.postfix = postfix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleLowerExtension() {
        m_settings.lowercaseExtensions = !m_settings.lowercaseExtensions;
        m_settings.uppercaseExtensions = false;
        m_settings.haveCustomExtensions = false;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleUpperExtension() {
        m_settings.lowercaseExtensions = false;
        m_settings.uppercaseExtensions = !m_settings.uppercaseExtensions;
        m_settings.haveCustomExtensions = false;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleCustomExtension() {
        m_settings.lowercaseExtensions = false;
        m_settings.uppercaseExtensions = false;
        m_settings.haveCustomExtensions = !m_settings.haveCustomExtensions;
        refresh();
    }

    // public ------------------------------------------------------------------
    function setExtensionToUse(customExtension) {
        m_settings.customExtension = customExtension;
        refresh();
    }

    // private -----------------------------------------------------------------
    function setFindReplace(pair) {
        // pair.find and pair.replace are strings
        m_findAndReplaces = [pair];
        m_settings.findReplace = JSON.stringify(m_findAndReplaces);
        refresh();
    }

    // private -----------------------------------------------------------------
    function setChangeCase(changeCase) {
        m_settings.changeCase = changeCase;
        refresh();
    }

    // public ------------------------------------------------------------------
    function executeRename() {
        m_fileInfos.forEach(function (fileInfo) {
            if (fileInfo.enabled && fileInfo.fileName !== fileInfo.newName &&
                    fileInfo.error === undefined) {
                fileInfo.fileEntry.moveTo(
                    m_directoryEntry,
                    fileInfo.newName,
                    function (fileEntry) {
                        fileInfo.enabled = false;
                        fileInfo.fileName = fileEntry.name;
                        fileInfo.fileEntry = fileEntry;
                        refresh();
                    },
                    function (fileError) {
                        fileInfo.error = fileError.message;
                        refresh();
                    }
                );
            } else {
                fileInfo.enabled = false;
            }
        });

        refresh();  // for the non-callback changes
    }

    // public ------------------------------------------------------------------
    function setSelection(selectAll) {
        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.enabled = selectAll;
        });

        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleSelection(filename) {
        m_fileInfos.forEach(function (fileInfo) {
            if (fileInfo.fileName === filename) {
                fileInfo.enabled = !fileInfo.enabled;
            }
        });

        refresh();
    }

    // private -----------------------------------------------------------------
    function initializeSetting(name, value) {
        if (m_settings[name] === undefined) {
            m_settings[name] = value;
        }
    }

    // private -----------------------------------------------------------------
    function populateState() {
        console.log("Entered `populateOptions`");

        if (chrome.runtime.lastError) {
            reportError(chrome.runtime.lastError.message);
            return;
        }

        setExtensionFilter(m_settings.extFilter);

        initializeSetting("usePrefix", true);
        initializeSetting("usePostfix", true);
        initializeSetting("prefix", "");
        initializeSetting("postfix", "");
        initializeSetting("changeCase", "unchanged");
        initializeSetting("lowercaseExtensions", false);
        initializeSetting("uppercaseExtensions", false);
        initializeSetting("haveCustomExtensions", false);
        initializeSetting("customExtension", "");

        m_findAndReplaces = JSON.parse(m_settings.findReplace ||
                "[{\"find\": \"\", \"replace\": \"\"}]");

        m_loading = false;
        refresh();
    }

    ////////////////////////////////////////////////////////////////////////////
    // Start of code that is executed upon inclusion
    ////////////////////////////////////////////////////////////////////////////

    m_onStaleData = callback;
    m_settings = settings(populateState);

    return Object.freeze({
        areLoading: areLoading,
        getFileInfo: getFileInfo,
        getOptions: getOptions,
        getPreview: getPreview,
        setDirectory: setDirectory,
        setExtensionFilter: setExtensionFilter,
        toggleUsePrefix: toggleUsePrefix,
        toggleUsePostfix: toggleUsePostfix,
        setPrefix: setPrefix,
        setPostfix: setPostfix,
        setFindReplace: setFindReplace,
        setChangeCase: setChangeCase,
        executeRename: executeRename,
        setSelection: setSelection,
        toggleSelection: toggleSelection,
        toggleLowerExtension: toggleLowerExtension,
        toggleUpperExtension: toggleUpperExtension,
        toggleCustomExtension: toggleCustomExtension,
        setExtensionToUse: setExtensionToUse
    });
}<commit_msg>Sort files naturally by filename; fixes #16<commit_after>/// @file
/// @author John Tooker
/// @license MIT, see `LICENSE.txt`
///
/// The "model" of this app

/*global chrome, console, settings*/

function model(callback) {
    "use strict";

    // State
    var m_loading = true;
    var m_directoryEntry;
    var m_fileInfos = [];
    var m_extFilter = [];   /// [] means all are valid, else whitelist
    var m_findAndReplaces = [];
    var m_settings;
    var m_onStaleData;      /// @see setOnRefresh

    // private -----------------------------------------------------------------
    function isFunction(f) {
        return typeof f === "function";
    }

    // private -----------------------------------------------------------------
    function capitalizeFirstLetter(text) {
        text = text.toLowerCase();

        return text.replace(/^./, function (first) {
            return first.toUpperCase();
        });
    }

    // private -----------------------------------------------------------------
    function capitalizeEachWord(text) {
        // http://stackoverflow.com/questions/15150168/
        return text.replace(/[^\s]+/g, capitalizeFirstLetter);
    }

    // private -----------------------------------------------------------------
    function calculateName(name) {
        var stem;       // up to last period
        var extension;  // includes period
        var lastPeriodIndex = name.lastIndexOf(".");

        // treat names that start with a period as not having an extension
        if (lastPeriodIndex < 1) {
            stem = name;
            extension = "";
        } else {
            stem = name.substring(0, lastPeriodIndex);
            extension = name.substring(lastPeriodIndex);
        }

        // stem modifications
        m_findAndReplaces.forEach(function (fr) {
            stem = stem.replace(fr.find, fr.replace);
        });

        if (m_settings.usePrefix) {
            stem = m_settings.prefix + stem;
        }
        if (m_settings.usePostfix) {
            stem = stem + m_settings.postfix;
        }
        switch (m_settings.changeCase) {
        case "unchanged":
            break;
        case "uppercase":
            stem = stem.toUpperCase();
            break;
        case "lowercase":
            stem = stem.toLowerCase();
            break;
        case "capitalize_words":
            stem = capitalizeEachWord(stem);
            break;
        case "capitalize_first_letter":
            stem = capitalizeFirstLetter(stem);
            break;
        }

        // extension modifications
        if (m_settings.lowercaseExtensions) {
            extension = extension.toLowerCase();
        } else if (m_settings.uppercaseExtensions) {
            extension = extension.toUpperCase();
        } else if (m_settings.haveCustomExtensions) {
            if (m_settings.customExtension === "") {
                extension = "";
            } else if (m_settings.customExtension.indexOf(".") === 0) {
                extension = m_settings.customExtension;
            } else {
                extension = "." + m_settings.customExtension;
            }
        }

        return stem + extension;
    }

    // private -----------------------------------------------------------------
    function incrementKey(object, key) {
        if (object[key] > 0) {
            object[key] += 1;
        } else {
            object[key] = 1;
        }
    }

    // private -----------------------------------------------------------------
    function calculateNames() {
        var usedNames = {};

        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.newName = fileInfo.enabled
                ? calculateName(fileInfo.fileName)
                : fileInfo.fileName;

            if (fileInfo.fileName !== fileInfo.newName) {
                // otherwise it may be overwritten
                incrementKey(usedNames, fileInfo.fileName);
            }
            incrementKey(usedNames, fileInfo.newName);
        });

        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.error = usedNames[fileInfo.newName] > 1
                ? "Another file has this name"
                : undefined;
        });
    }

    // private -----------------------------------------------------------------
    function refresh(staleParts) {
        calculateNames();

        if (isFunction(m_onStaleData)) {
            m_onStaleData(staleParts);
        }
    }

    // public ------------------------------------------------------------------
    function areLoading() {
        return m_loading;
    }

    // private -----------------------------------------------------------------
    function reportError(message) {
        refresh({errorMessage: message});
    }

    // private -----------------------------------------------------------------
    function getExtFilterString() {
        if (m_extFilter.length === 0) {
            return "*.*";
        }

        return "*." + m_extFilter.join(", *.");
    }

    // private -----------------------------------------------------------------
    function getDirectoryPath() {
        return (m_directoryEntry && m_directoryEntry.fullPath) || "";
    }

    // private -----------------------------------------------------------------
    function translateFileInfoForView() {
        var simpleInfos = [];

        m_fileInfos.forEach(function (fileInfo) {
            simpleInfos.push({
                checked: fileInfo.enabled,
                fileName: fileInfo.fileName
            });
        });

        return Object.freeze(simpleInfos);
    }

    // pubic -------------------------------------------------------------------
    function getFileInfo() {
        return Object.freeze({
            directoryPath: getDirectoryPath(),
            files: translateFileInfoForView()
        });
    }

    // pubic -------------------------------------------------------------------
    function getOptions() {
        return Object.freeze({
            extFilter: getExtFilterString(),
            usePrefix: m_settings.usePrefix,
            prefix: m_settings.prefix,
            usePostfix: m_settings.usePostfix,
            postfix: m_settings.postfix,
            changeCase: m_settings.changeCase,
            lowercaseExtensions: m_settings.lowercaseExtensions,
            uppercaseExtensions: m_settings.uppercaseExtensions,
            haveCustomExtensions: m_settings.haveCustomExtensions,
            customExtension: m_settings.customExtension,
            findAndReplaces: m_findAndReplaces
        });
    }

    // pubic -------------------------------------------------------------------
    function getPreview() {
        var changes = [];

        m_fileInfos.forEach(function (fileInfo) {
            if (fileInfo.enabled) {
                changes.push({
                    oldName: fileInfo.fileName,
                    newName: fileInfo.newName,
                    error: fileInfo.error
                });
            }
        });

        return changes;
    }

    // private -----------------------------------------------------------------
    function isFileTypeEnabled(fileName) {
        var found = false;

        if (m_extFilter.length === 0) {
            return true;
        }

        m_extFilter.forEach(function (ext) {
            found = found || fileName.endsWith("." + ext);
        });

        return found;
    }

    // private -----------------------------------------------------------------
    function refilterFiles() {
        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.enabled = isFileTypeEnabled(fileInfo.fileName);
        });
    }

    // private -----------------------------------------------------------------
    function onDirectoryLoaded() {
        function compare(left, right) {
            var leftName = left.fileName;
            var rightName = right.fileName;
            return leftName.localeCompare(rightName, undefined,
                    {sensitivity: "case", numeric: true});
        }

        m_fileInfos.sort(compare);
        refresh();
    }

    // private -----------------------------------------------------------------
    function createFileInfo(fileEntry) {
        return {"fileEntry": fileEntry,
                "fileName": fileEntry.name,
                "newName": fileEntry.name,
                "enabled": isFileTypeEnabled(fileEntry.name)};
    }

    // private -----------------------------------------------------------------
    function loadDirectory(directoryEntry) {
        var directoryReader;

        if (!directoryEntry.isDirectory) {
            return;
        }

        m_directoryEntry = directoryEntry;

        m_fileInfos = [];
        directoryReader = directoryEntry.createReader();
        function readEntries() {
            directoryReader.readEntries(
                function (results) {
                    if (!results.length) {
                        onDirectoryLoaded();
                        return;
                    }

                    results.forEach(function (fileEntry) {
                        if (fileEntry.isFile) {
                            m_fileInfos.push(createFileInfo(fileEntry));
                        }
                    });
                    readEntries();
                },
                function (error) {
                    console.error(error);
                }
            );
        }
        readEntries();

        // refresh is done on the last `readEntries`
    }

    // pubic -------------------------------------------------------------------
    function setDirectory(entry) {
        if (!entry) {
            return;
        }

        // use local storage to retain access to this file
        chrome.storage.local.set({"directory": chrome.fileSystem.retainEntry(entry)});
        loadDirectory(entry);
    }

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

    // public ------------------------------------------------------------------
    function setExtensionFilter(filterText) {
        filterText = filterText || "";

        m_settings.extFilter = filterText;
        m_extFilter = [];

        filterText = filterText.trim();

        if (filterText === "" || filterText === "*.*") {
            return;
        }

        m_extFilter = filterText.split(",");
        m_extFilter.forEach(function (value, i) {
            value = value.trim();
            value = value.replace("*.", "");
            m_extFilter[i] = value;
        });

        refilterFiles();
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleUsePrefix() {
        m_settings.usePrefix = !m_settings.usePrefix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleUsePostfix() {
        m_settings.usePostfix = !m_settings.usePostfix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function setPrefix(prefix) {
        m_settings.prefix = prefix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function setPostfix(postfix) {
        m_settings.postfix = postfix;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleLowerExtension() {
        m_settings.lowercaseExtensions = !m_settings.lowercaseExtensions;
        m_settings.uppercaseExtensions = false;
        m_settings.haveCustomExtensions = false;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleUpperExtension() {
        m_settings.lowercaseExtensions = false;
        m_settings.uppercaseExtensions = !m_settings.uppercaseExtensions;
        m_settings.haveCustomExtensions = false;
        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleCustomExtension() {
        m_settings.lowercaseExtensions = false;
        m_settings.uppercaseExtensions = false;
        m_settings.haveCustomExtensions = !m_settings.haveCustomExtensions;
        refresh();
    }

    // public ------------------------------------------------------------------
    function setExtensionToUse(customExtension) {
        m_settings.customExtension = customExtension;
        refresh();
    }

    // private -----------------------------------------------------------------
    function setFindReplace(pair) {
        // pair.find and pair.replace are strings
        m_findAndReplaces = [pair];
        m_settings.findReplace = JSON.stringify(m_findAndReplaces);
        refresh();
    }

    // private -----------------------------------------------------------------
    function setChangeCase(changeCase) {
        m_settings.changeCase = changeCase;
        refresh();
    }

    // public ------------------------------------------------------------------
    function executeRename() {
        m_fileInfos.forEach(function (fileInfo) {
            if (fileInfo.enabled && fileInfo.fileName !== fileInfo.newName &&
                    fileInfo.error === undefined) {
                fileInfo.fileEntry.moveTo(
                    m_directoryEntry,
                    fileInfo.newName,
                    function (fileEntry) {
                        fileInfo.enabled = false;
                        fileInfo.fileName = fileEntry.name;
                        fileInfo.fileEntry = fileEntry;
                        refresh();
                    },
                    function (fileError) {
                        fileInfo.error = fileError.message;
                        refresh();
                    }
                );
            } else {
                fileInfo.enabled = false;
            }
        });

        refresh();  // for the non-callback changes
    }

    // public ------------------------------------------------------------------
    function setSelection(selectAll) {
        m_fileInfos.forEach(function (fileInfo) {
            fileInfo.enabled = selectAll;
        });

        refresh();
    }

    // public ------------------------------------------------------------------
    function toggleSelection(filename) {
        m_fileInfos.forEach(function (fileInfo) {
            if (fileInfo.fileName === filename) {
                fileInfo.enabled = !fileInfo.enabled;
            }
        });

        refresh();
    }

    // private -----------------------------------------------------------------
    function initializeSetting(name, value) {
        if (m_settings[name] === undefined) {
            m_settings[name] = value;
        }
    }

    // private -----------------------------------------------------------------
    function populateState() {
        console.log("Entered `populateOptions`");

        if (chrome.runtime.lastError) {
            reportError(chrome.runtime.lastError.message);
            return;
        }

        setExtensionFilter(m_settings.extFilter);

        initializeSetting("usePrefix", true);
        initializeSetting("usePostfix", true);
        initializeSetting("prefix", "");
        initializeSetting("postfix", "");
        initializeSetting("changeCase", "unchanged");
        initializeSetting("lowercaseExtensions", false);
        initializeSetting("uppercaseExtensions", false);
        initializeSetting("haveCustomExtensions", false);
        initializeSetting("customExtension", "");

        m_findAndReplaces = JSON.parse(m_settings.findReplace ||
                "[{\"find\": \"\", \"replace\": \"\"}]");

        m_loading = false;
        refresh();
    }

    ////////////////////////////////////////////////////////////////////////////
    // Start of code that is executed upon inclusion
    ////////////////////////////////////////////////////////////////////////////

    m_onStaleData = callback;
    m_settings = settings(populateState);

    return Object.freeze({
        areLoading: areLoading,
        getFileInfo: getFileInfo,
        getOptions: getOptions,
        getPreview: getPreview,
        setDirectory: setDirectory,
        setExtensionFilter: setExtensionFilter,
        toggleUsePrefix: toggleUsePrefix,
        toggleUsePostfix: toggleUsePostfix,
        setPrefix: setPrefix,
        setPostfix: setPostfix,
        setFindReplace: setFindReplace,
        setChangeCase: setChangeCase,
        executeRename: executeRename,
        setSelection: setSelection,
        toggleSelection: toggleSelection,
        toggleLowerExtension: toggleLowerExtension,
        toggleUpperExtension: toggleUpperExtension,
        toggleCustomExtension: toggleCustomExtension,
        setExtensionToUse: setExtensionToUse
    });
}<|endoftext|>


<commit_before>
export default class Eventhandler {

	constructor(config = {}) {
		this._init(config);
	}

	/**
	 * Passed config object
	 * @type {Object}
	 */
	config = {
		events: {},
		element: null,
		bind: null,
	};

	/**
	 * Main event callback wrapper
	 * @type {Object}
	 */
	mainEventCallackContainer = {};

	/**
	 * Initialize Eventhandler
	 * @return {undefined}
	 */
	_init(config){

		/**
		* Throw error if no element passed
		*/
		if(!config.element){
			throw new Error('Please pass an element. See documentation!');
		}
		// Assign element
		this.config.element = config.element;

		// bind bindObject
		if(config.bind){
			this.config.bind = config.bind;
			Eventhandler.bindObjectToEventList(config.events, config.bind);
		}
		// group events
		this.config.events = Eventhandler.groupEvents(config.events);

		// add events eventListener
		this._addEventCollection(this.config.events);

	}

	/**
	 * Create and return an instace of Eventhandler
	 * @param  {Object} ...args
	 * @return {Object}
	 */
	static create(...args){
		return new Eventhandler(...args);
	}

	/**
	 * Convert eventDomain into seperate item
	 * @param  {String} eventDomain
	 * @return {Array} [ type, delegateSelector ]
	 */
	static prepareEventdomain(eventDomain) {

		if(Object.classof(eventDomain) !== 'String'){
			throw new Error('Passed argument must be a string');
		}

		let splitedEventDomain = eventDomain.split(' ');
		let type = splitedEventDomain[0];
		let delegateSelector = null;
		if(splitedEventDomain[1]){
			delegateSelector = splitedEventDomain.splice(1).join(' ');
		}

		return [ type, delegateSelector ];

	}

	/**
	 * Group events
	 * @param  {Object} config
	 * @return {Object}
	 */
	static groupEvents(events = {}){

		let tmpConfig = {};

		for(let eventDomain in events){
			if(!events.hasOwnProperty(eventDomain)){
				continue;
			}

			let [ type, delegateSelector ] = Eventhandler.prepareEventdomain(eventDomain);
			if(!tmpConfig[type]){
				tmpConfig[type] = [];
			}

			tmpConfig[type].push({
				[ delegateSelector ]: events[eventDomain],
			});
		}

		return tmpConfig;

	}

	/**
	 * Bind object to event value
	 * @param  {Object} events
	 * @param  {Object} bindObject
	 * @return {undefined}
	 */
	static bindObjectToEventList(events = {}, bindObject){

		for(let eventDomain in events){
			if(!events.hasOwnProperty(eventDomain)){
				continue;
			}
			// bind and assign bindObject
			events[eventDomain] = bindObject::events[eventDomain];
		}

	}

	/**
	 * on
	 * @param  {String} eventDomain
	 * @param  {Function} handler
	 * @return {Undefined}
	 */
	on(eventDomain, handler){

		let [ type, delegateSelector ] = Eventhandler.prepareEventdomain(eventDomain);
		// if event type not registered, add to eventlistener
		if(!this.config.events[type]){
			this.config.events[type] = [];
			this._addEvent(this.config.element, type, this.config.events);
		}

		// bind bindObject
		if(this.config.bind){
			// bind and assign bindObject
			handler = this.config.bind::handler;
		}

		// add delegate selector to event list
		this.config.events[type].push({
			[ delegateSelector ]: handler,
		});

	}

	/**
	 * trigger
	 * @param  {String} event
	 * @param  {Any} value
	 * @return {Undefined}
	 */
	trigger(event, value = null){

		this.config.element.dispatchEvent(
			new Event(event, { detail: value })
		);

	}

	/**
	 * Return event callbacks
	 * @param  {String} eventType
	 * @return {Array|null}
	 */
	getHandlers(eventType){
		return this.config.events[eventType] || null;
	}

	/**
	 * Add collection of events
	 * Example: see above constructor config.events
	 * @param {Objbect} events
	 */
	_addEventCollection(events){

		// add events eventlistener
		for(let type in events){
			if(!events.hasOwnProperty(type)){
				continue;
			}
			this._addEvent(this.config.element, type, this.config.events);
		}

	}

	/**
	 * Add single event
	 * @param {HTMLElement} element
	 * @param {String} type
	 * @param {Function} callback
	 */
	_addEvent(element, type, configEvents) {

		this.mainEventCallackContainer[type] = ( event ) => {

			let delegateSelectors = configEvents[type];
			for(let delegateObject of delegateSelectors){

				// received key (delegateSelector) is always a key
				// therefore (see below) delegateSelector === "null"
				let delegateSelector = Object.keys(delegateObject)[0];
				let callback = delegateObject[delegateSelector];
				let matchedSelector = false;

				if(delegateSelector){
					matchedSelector = event.target.matches(delegateSelector);
				}

				if(delegateSelector === "null" || matchedSelector){
					callback(event);
				}

			}
		};

		element.addEventListener(type, this.mainEventCallackContainer[type], false);

	}

	/**
	 * Remove event from listener by given eventdomain
	 * @param  {String} eventDomain
	 * @return {undefined}
	 */
	removeEvent(eventDomain) {

		let [ type, delegateSelector ] = Eventhandler.prepareEventdomain(eventDomain);

		if(!this.config.events[type]) {
			throw new Error(`Event: ${type} not exists!`);
		}

		let index = 0;
		for(let delegateObject of this.config.events[type]){
			// remove delegate callback if passed e.g. "click .foo"
			if(delegateSelector && delegateObject[delegateSelector]){
				this.config.events[type].splice(index, 1);
			}
			// remove all callbacks if passed type e.g. "click" or e.g. "click" is empty
			if((type && delegateSelector === null) || this.config.events[type].length === 0){
				delete this.config.events[type];
			}
			index++;
		}

		// remove listener if eventsList empty or not exists
		if(!this.config.events[type]){
			this.config.element.removeEventListener(type, this.mainEventCallackContainer[type]);
			delete this.config.events[type];
		}

	}

	/**
	 * Removes all eventhandler
	 * @return {undefined}
	 */
	reset() {

		if(!Object.keys(this.config.events).length){
			return;
		}

		// add events eventlistener
		for(let type in this.config.events){
			if(!events.hasOwnProperty(type)){
				continue;
			}
			this.removeEvent(type);
		}

	}

}
<commit_msg>Eventhandler: refactored binding of event handler by passed bindObject<commit_after>
export default class Eventhandler {

	constructor(config = {}) {
		this._init(config);
	}

	/**
	 * Passed config object
	 * @type {Object}
	 */
	config = {
		events: {},
		element: null,
		bind: null,
	};

	/**
	 * Main event callback wrapper
	 * @type {Object}
	 */
	mainEventCallackContainer = {};

	/**
	 * Initialize Eventhandler
	 * @return {undefined}
	 */
	_init(config){

		/**
		* Throw error if no element passed
		*/
		if(!config.element){
			throw new Error('Please pass an element. See documentation!');
		}
		// Assign element
		this.config.element = config.element;

		// bind bindObject
		if(config.bind){
			this.config.bind = config.bind;
			Eventhandler.bindObjectToEventList(config.events, config.bind);
		}
		// group events
		this.config.events = Eventhandler.groupEvents(config.events);

		// add events eventListener
		this._addEventCollection(this.config.events);

	}

	/**
	 * Create and return an instace of Eventhandler
	 * @param  {Object} ...args
	 * @return {Object}
	 */
	static create(...args){
		return new Eventhandler(...args);
	}

	/**
	 * Convert eventDomain into seperate item
	 * @param  {String} eventDomain
	 * @return {Array} [ type, delegateSelector ]
	 */
	static prepareEventdomain(eventDomain) {

		if(Object.classof(eventDomain) !== 'String'){
			throw new Error('Passed argument must be a string');
		}

		let splitedEventDomain = eventDomain.split(' ');
		let type = splitedEventDomain[0];
		let delegateSelector = null;
		if(splitedEventDomain[1]){
			delegateSelector = splitedEventDomain.splice(1).join(' ');
		}

		return [ type, delegateSelector ];

	}

	/**
	 * Group events
	 * @param  {Object} config
	 * @return {Object}
	 */
	static groupEvents(events = {}){

		let tmpConfig = {};

		for(let eventDomain in events){
			if(!events.hasOwnProperty(eventDomain)){
				continue;
			}

			let [ type, delegateSelector ] = Eventhandler.prepareEventdomain(eventDomain);
			if(!tmpConfig[type]){
				tmpConfig[type] = [];
			}

			tmpConfig[type].push({
				[ delegateSelector ]: events[eventDomain],
			});
		}

		return tmpConfig;

	}

	/**
	 * Bind object to event value
	 * @param  {Object} events
	 * @param  {Object} bindObject
	 * @return {undefined}
	 */
	static bindObjectToEventList(events = {}, bindObject){

		for(let eventDomain in events){
			if(!events.hasOwnProperty(eventDomain)){
				continue;
			}
			// bind and assign bindObject
			events[eventDomain] = bindObject::events[eventDomain];
		}

	}

	/**
	 * on
	 * @param  {String} eventDomain
	 * @param  {Function} handler
	 * @return {Undefined}
	 */
	on(eventDomain, handler){

		let [ type, delegateSelector ] = Eventhandler.prepareEventdomain(eventDomain);
		// if event type not registered, add to eventlistener
		if(!this.config.events[type]){
			this.config.events[type] = [];
			this._addEvent(this.config.element, type, this.config.events);
		}

		// add delegate selector to event list
		this.config.events[type].push({
			[ delegateSelector ]: this.config.bind ? this.config.bind::handler: handler,
		});

	}

	/**
	 * trigger
	 * @param  {String} event
	 * @param  {Any} value
	 * @return {Undefined}
	 */
	trigger(event, value = null){

		this.config.element.dispatchEvent(
			new Event(event, { detail: value })
		);

	}

	/**
	 * Return event callbacks
	 * @param  {String} eventType
	 * @return {Array|null}
	 */
	getHandlers(eventType){
		return this.config.events[eventType] || null;
	}

	/**
	 * Add collection of events
	 * Example: see above constructor config.events
	 * @param {Objbect} events
	 */
	_addEventCollection(events){

		// add events eventlistener
		for(let type in events){
			if(!events.hasOwnProperty(type)){
				continue;
			}
			this._addEvent(this.config.element, type, this.config.events);
		}

	}

	/**
	 * Add single event
	 * @param {HTMLElement} element
	 * @param {String} type
	 * @param {Function} callback
	 */
	_addEvent(element, type, configEvents) {

		this.mainEventCallackContainer[type] = ( event ) => {

			let delegateSelectors = configEvents[type];
			for(let delegateObject of delegateSelectors){

				// received key (delegateSelector) is always a key
				// therefore (see below) delegateSelector === "null"
				let delegateSelector = Object.keys(delegateObject)[0];
				let callback = delegateObject[delegateSelector];
				let matchedSelector = false;

				if(delegateSelector){
					matchedSelector = event.target.matches(delegateSelector);
				}

				if(delegateSelector === "null" || matchedSelector){
					callback(event);
				}

			}
		};

		element.addEventListener(type, this.mainEventCallackContainer[type], false);

	}

	/**
	 * Remove event from listener by given eventdomain
	 * @param  {String} eventDomain
	 * @return {undefined}
	 */
	removeEvent(eventDomain) {

		let [ type, delegateSelector ] = Eventhandler.prepareEventdomain(eventDomain);

		if(!this.config.events[type]) {
			throw new Error(`Event: ${type} not exists!`);
		}

		let index = 0;
		for(let delegateObject of this.config.events[type]){
			// remove delegate callback if passed e.g. "click .foo"
			if(delegateSelector && delegateObject[delegateSelector]){
				this.config.events[type].splice(index, 1);
			}
			// remove all callbacks if passed type e.g. "click" or e.g. "click" is empty
			if((type && delegateSelector === null) || this.config.events[type].length === 0){
				delete this.config.events[type];
			}
			index++;
		}

		// remove listener if eventsList empty or not exists
		if(!this.config.events[type]){
			this.config.element.removeEventListener(type, this.mainEventCallackContainer[type]);
			delete this.config.events[type];
		}

	}

	/**
	 * Removes all eventhandler
	 * @return {undefined}
	 */
	reset() {

		if(!Object.keys(this.config.events).length){
			return;
		}

		// add events eventlistener
		for(let type in this.config.events){
			if(!events.hasOwnProperty(type)){
				continue;
			}
			this.removeEvent(type);
		}

	}

}
<|endoftext|>


<commit_before>module.exports = function(config){
    config.set({
    basePath : '../',
    files : [
      '../bower_components/angular/angular.js',
      '../bower_components/angular-mocks/angular-mocks.js',
      'src/js/**/*.js',
      'test/unit/**/*.js'
    ],
    autoWatch : true,
    frameworks: ['jasmine'],
    browsers : ['Firefox'],
    plugins : ['karma-firefox-launcher', 'karma-jasmine']
})};
<commit_msg>Another pathing attempt<commit_after>module.exports = function(config){
    config.set({
    basePath : '../',
    files : [
      '/bower_components/angular/angular.js',
      '/bower_components/angular-mocks/angular-mocks.js',
      'src/js/**/*.js',
      'test/unit/**/*.js'
    ],
    autoWatch : true,
    frameworks: ['jasmine'],
    browsers : ['Firefox'],
    plugins : ['karma-firefox-launcher', 'karma-jasmine']
})};
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Hippo B.V. (http://www.onehippo.com)
 *
 * 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.
 */

const conf = require('./gulp.conf');
const karmaFixtureProxyPath = '/base/src/angularjs/';
const customKarma = conf.custom && conf.custom.karma ? conf.custom.karma : {};

module.exports = function karmaConfig(config) {
  const configuration = {
    basePath: conf.paths.basePath,
    browsers: [
      'Chrome',
    ],
    frameworks: [
      'jasmine-jquery',
      'jasmine',
    ],
    files: [
      conf.path.src('index.spec.js'),
      {
        pattern: conf.path.src('**/*.fixture.+(js|html|css|json)'),
        included: false,
      },
    ].concat(customKarma.files || []),
    preprocessors: {
      [conf.path.src('index.spec.js')]: [
        'webpack',
        'sourcemap',
      ],
      [conf.path.src('**/!(*fixture).html')]: [
        'ng-html2js',
      ],
    },
    proxies: Object.assign({
      '/spec/javascripts/fixtures/': karmaFixtureProxyPath,
      '/spec/javascripts/fixtures/json/': karmaFixtureProxyPath,
    }, customKarma.proxies),
    ngHtml2JsPreprocessor: {
      stripPrefix: `${conf.paths.src}/`,
    },
    reporters: [
      'mocha',
      'coverage',
    ],
    coverageReporter: {
      reporters: [
        { type: 'html' },
        { type: 'text-summary' },
      ],
    },
    mochaReporter: {
      output: 'minimal',
    },
    webpack: require('./webpack-test.conf'),
    webpackMiddleware: {
      noInfo: true,
    },
  };

  config.set(configuration);
};
<commit_msg>FEBUILD-24 extract custom karma files logic<commit_after>/*
 * Copyright 2016 Hippo B.V. (http://www.onehippo.com)
 *
 * 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.
 */

const conf = require('./gulp.conf');
const karmaFixtureProxyPath = '/base/src/angularjs/';
const customKarma = conf.custom && conf.custom.karma ? conf.custom.karma : {};
const customKarmaFiles = customKarma.files || [];

module.exports = function karmaConfig(config) {
  const configuration = {
    basePath: conf.paths.basePath,
    browsers: [
      'Chrome',
    ],
    frameworks: [
      'jasmine-jquery',
      'jasmine',
    ],
    files: [].concat(
      conf.path.src('index.spec.js'),
      {
        pattern: conf.path.src('**/*.fixture.+(js|html|css|json)'),
        included: false,
      },
      customKarmaFiles
    ),
    preprocessors: {
      [conf.path.src('index.spec.js')]: [
        'webpack',
        'sourcemap',
      ],
      [conf.path.src('**/!(*fixture).html')]: [
        'ng-html2js',
      ],
    },
    proxies: Object.assign({
      '/spec/javascripts/fixtures/': karmaFixtureProxyPath,
      '/spec/javascripts/fixtures/json/': karmaFixtureProxyPath,
    }, customKarma.proxies),
    ngHtml2JsPreprocessor: {
      stripPrefix: `${conf.paths.src}/`,
    },
    reporters: [
      'mocha',
      'coverage',
    ],
    coverageReporter: {
      reporters: [
        { type: 'html' },
        { type: 'text-summary' },
      ],
    },
    mochaReporter: {
      output: 'minimal',
    },
    webpack: require('./webpack-test.conf'),
    webpackMiddleware: {
      noInfo: true,
    },
  };

  config.set(configuration);
};
<|endoftext|>