<commit_before>/**
 * Copyright (c) 2017 NAVER Corp.
 * billboard.js project is licensed under the MIT license
 */
 /* eslint-disable */
import util from "../assets/util";
import CLASS from "../../src/config/classes";

describe("API show", () => {
	let chart;
	let args = {
		data: {
			columns: [
				["data1", 30, 200, 100, 400],
				["data2", 500, 800, 500, 2000]
			]
		}
	};

	beforeEach(() => {
		chart = util.generate(args);
	});

	describe("hide()", () => {
		it("Hide partial data", done => {
			const internal = chart.internal;
			const main = internal.main;

			chart.hide("data1");

			setTimeout(() => {
				const selector = `.${CLASS.chartLine}.${CLASS.target}`;

				expect(+main.select(`${selector}-data1`).style("opacity")).to.be.equal(0);
				expect(+main.select(`${selector}-data2`).style("opacity")).to.be.equal(1);

				expect(+internal.svg.selectAll(`.${CLASS.legendItemHidden}`).size()).to.be.equal(1);

				done();
			}, 500);
		});

		it("Hide all data", () => {
			const internal = chart.internal;
			const main = internal.main;

			// hide all data
			chart.hide();

			main.selectAll(`.${CLASS.chartLine}`).each(function() {
				expect(+this.style.opacity).to.be.equal(0);
			});

			const legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

			expect(+legend.size()).to.be.equal(chart.data().length);

			legend.each(function() {
				expect(+d3.select(this).style("opacity")).to.be.equal(0.15);
			});

			// hide legend
			chart.hide(null, {withLegend:true});

			legend.each(function() {
				expect(+d3.select(this).style("opacity")).to.be.equal(0);
			});
		});
	});

	describe("show()", () => {
		it("Show partial data", done => {
			const internal = chart.internal;
			const main = internal.main;

			chart.hide();
			chart.show("data1");

			setTimeout(() => {
				const selector = `.${CLASS.chartLine}.${CLASS.target}`;

				expect(+main.select(`${selector}-data1`).style("opacity")).to.be.equal(1);
				expect(+main.select(`${selector}-data2`).style("opacity")).to.be.equal(0);

				expect(+internal.svg.selectAll(`.${CLASS.legendItemHidden}`).size()).to.be.equal(1);

				done();
			}, 500);
		});

		it("Show all data", done => {
			const internal = chart.internal;
			const main = internal.main;
			let legend;

			// hide all data
			chart.hide();

			legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);
			expect(+legend.size()).to.be.equal(chart.data().length);

			// show all data
			chart.show();

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(1);
				});

				legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(0);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(1);
				});

				done();
			}, 500);
		});

		it("Show all data using 'withLegend' option", done => {
			const internal = chart.internal;
			const main = internal.main;

			// hide all data and legend
			chart.hide(null, {withLegend: true});

			// show all data without legend
			chart.show(null, {withLegend: false});

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(1);
				});

				const legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(0);

				internal.svg.selectAll(`.${CLASS.legendItem}`).each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(0);
				});

				done();
			}, 500);
		});
	});

	describe("toggle()", () => {
		it("should be toggled hiding and showing data", done => {
			const internal = chart.internal;
			const main = internal.main;
			let legend;

			// hide data
			chart.toggle();

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.below(1);
				});

				legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(chart.data().length);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(0.15);
				});

				// show data
				chart.toggle();
			}, 100);

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(1);
				});

				legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(0);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(1);
				});

				done();
			}, 500);
		});
	});

	describe("check toggle interaction", () => {
		const ids = ["FirstPercentage", "SecondPercentage", "ThirdPercentage"];

		it("set options", () => {
			args = {
				data: {
					json: [{
						Name: "Some Data",
						FirstPercentage: 0.20,
						SecondPercentage: 0.10,
						ThirdPercentage: 0.15
					}],
					keys: {
						x: "Name",
						value: ids
					},
					type: "bar",
					hide: [ids[2]]
				},
				axis: {
					x: {
						type: "category"
					}
				},
				transition: {
					duration: 10
				}
			};
		});

		it("should correctly rendered having same width", done => {
			const main = chart.internal.main;
			const barWidth = Math.round(main.select(`.${CLASS.bars}-${ids[0]}`).node().getBBox().width);

			chart.toggle(ids.concat().splice(1));

			setTimeout(() => {
				main.selectAll(`.${CLASS.bars}-${ids[0]}, .${CLASS.bars}-${ids[2]}`)
					.each(function() {
						expect(Math.round(this.getBBox().width)).to.be.equal(barWidth);
					});

				done();
			}, 500);
		});
	});
});
<commit_msg>test(api): Fix intermittent fails (#733)<commit_after>/**
 * Copyright (c) 2017 NAVER Corp.
 * billboard.js project is licensed under the MIT license
 */
 /* eslint-disable */
import util from "../assets/util";
import CLASS from "../../src/config/classes";

describe("API show", () => {
	let chart;
	let args = {
		data: {
			columns: [
				["data1", 30, 200, 100, 400],
				["data2", 500, 800, 500, 2000]
			]
		}
	};

	beforeEach(() => {
		chart = util.generate(args);
	});

	describe("hide()", () => {
		it("Hide partial data", done => {
			const internal = chart.internal;
			const main = internal.main;

			chart.hide("data1");

			setTimeout(() => {
				const selector = `.${CLASS.chartLine}.${CLASS.target}`;

				expect(+main.select(`${selector}-data1`).style("opacity")).to.be.equal(0);
				expect(+main.select(`${selector}-data2`).style("opacity")).to.be.equal(1);

				expect(+internal.svg.selectAll(`.${CLASS.legendItemHidden}`).size()).to.be.equal(1);

				done();
			}, 500);
		});

		it("Hide all data", done => {
			const main = chart.internal.main;

			// hide all data
			chart.hide();

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(0);
				});

				const legend = chart.$.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(chart.data().length);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(0.15);
				});

				done();
			}, 500);
		});

		it("Hide all data with legend", done => {
			// hide legend
			chart.hide(null, {withLegend:true});

			setTimeout(() => {
				chart.$.svg
					.selectAll(`.${CLASS.legendItemHidden}`)
					.each(function() {
						expect(+d3.select(this).style("opacity")).to.be.equal(0);
					});

				done();
			}, 500);
		});
	});

	describe("show()", () => {
		it("Show partial data", done => {
			const internal = chart.internal;
			const main = internal.main;

			chart.hide();
			chart.show("data1");

			setTimeout(() => {
				const selector = `.${CLASS.chartLine}.${CLASS.target}`;

				expect(+main.select(`${selector}-data1`).style("opacity")).to.be.equal(1);
				expect(+main.select(`${selector}-data2`).style("opacity")).to.be.equal(0);

				expect(+internal.svg.selectAll(`.${CLASS.legendItemHidden}`).size()).to.be.equal(1);

				done();
			}, 500);
		});

		it("Show all data", done => {
			const internal = chart.internal;
			const main = internal.main;
			let legend;

			// hide all data
			chart.hide();

			legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);
			expect(+legend.size()).to.be.equal(chart.data().length);

			// show all data
			chart.show();

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(1);
				});

				legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(0);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(1);
				});

				done();
			}, 500);
		});

		it("Show all data using 'withLegend' option", done => {
			const internal = chart.internal;
			const main = internal.main;

			// hide all data and legend
			chart.hide(null, {withLegend: true});

			// show all data without legend
			chart.show(null, {withLegend: false});

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(1);
				});

				const legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(0);

				internal.svg.selectAll(`.${CLASS.legendItem}`).each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(0);
				});

				done();
			}, 500);
		});
	});

	describe("toggle()", () => {
		it("should be toggled hiding and showing data", done => {
			const internal = chart.internal;
			const main = internal.main;
			let legend;

			// hide data
			chart.toggle();

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.below(1);
				});

				legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(chart.data().length);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(0.15);
				});

				// show data
				chart.toggle();
			}, 100);

			setTimeout(() => {
				main.selectAll(`.${CLASS.chartLine}`).each(function() {
					expect(+this.style.opacity).to.be.equal(1);
				});

				legend = internal.svg.selectAll(`.${CLASS.legendItemHidden}`);

				expect(+legend.size()).to.be.equal(0);

				legend.each(function() {
					expect(+d3.select(this).style("opacity")).to.be.equal(1);
				});

				done();
			}, 500);
		});
	});

	describe("check toggle interaction", () => {
		const ids = ["FirstPercentage", "SecondPercentage", "ThirdPercentage"];

		it("set options", () => {
			args = {
				data: {
					json: [{
						Name: "Some Data",
						FirstPercentage: 0.20,
						SecondPercentage: 0.10,
						ThirdPercentage: 0.15
					}],
					keys: {
						x: "Name",
						value: ids
					},
					type: "bar",
					hide: [ids[2]]
				},
				axis: {
					x: {
						type: "category"
					}
				},
				transition: {
					duration: 10
				}
			};
		});

		it("should correctly rendered having same width", done => {
			const main = chart.internal.main;
			const barWidth = Math.round(main.select(`.${CLASS.bars}-${ids[0]}`).node().getBBox().width);

			chart.toggle(ids.concat().splice(1));

			setTimeout(() => {
				main.selectAll(`.${CLASS.bars}-${ids[0]}, .${CLASS.bars}-${ids[2]}`)
					.each(function() {
						expect(Math.round(this.getBBox().width)).to.be.equal(barWidth);
					});

				done();
			}, 500);
		});
	});
});
<|endoftext|>


<commit_before>module.exports = function(api) {
  const config = {};

  if (api.env('cjs')) {
    config.compact = false;
    config.minified = false;
    config.presets = [
      [
        '@babel/preset-env',
        {
          targets: {
            node: '6'
          },
          modules: 'commonjs'
        }
      ]
    ];
    config.plugins = [
      [
        'transform-rename-import',
        {
          replacements: [
            {
              original: '(.*)\\.mjs$',
              replacement: '$1.js'
            },
            {
              original: '^\\./src',
              replacement: './cjs'
            }
          ]
        }
      ]
    ];
  }

  return config;
};
<commit_msg>updated .babelrc.js<commit_after>module.exports = function(api) {
  const config = {};

  if (api.env('cjs')) {
    config.compact = false;
    config.minified = false;
    config.presets = [
      [
        '@babel/preset-env',
        {
          debug: true,
          modules: 'commonjs',
          targets: {
            node: '6'
          }
        }
      ]
    ];
    config.plugins = [
      [
        'transform-rename-import',
        {
          replacements: [
            {
              original: '(.*)\\.mjs$',
              replacement: '$1.js'
            },
            {
              original: '^\\./src',
              replacement: './cjs'
            }
          ]
        }
      ]
    ];
  }

  return config;
};
<|endoftext|>


<commit_before>"use strict";

module.exports = function(api) {
  const env = api.env();

  const includeCoverage = process.env.BABEL_COVERAGE === "true";

  const envOpts = {
    loose: true,
    modules: false,
    exclude: ["transform-typeof-symbol"],
  };

  let convertESM = true;

  switch (env) {
    // Configs used during bundling builds.
    case "babylon":
    case "standalone":
      convertESM = false;
      break;
    case "production":
      // Config during builds before publish.
      break;
    case "development":
      envOpts.debug = true;
      envOpts.targets = {
        node: "current",
      };
      break;
    case "test":
      envOpts.targets = {
        node: "current",
      };
      break;
  }

  const config = {
    comments: false,
    presets: [["@babel/env", envOpts]],
    plugins: [
      // TODO: Use @babel/preset-flow when
      // https://github.com/babel/babel/issues/7233 is fixed
      "@babel/plugin-transform-flow-strip-types",
      ["@babel/proposal-class-properties", { loose: true }],
      "@babel/proposal-export-namespace-from",
      "@babel/proposal-numeric-separator",
      ["@babel/proposal-object-rest-spread", { useBuiltIns: true }],

      // Explicitly use the lazy version of CommonJS modules.
      convertESM ? ["@babel/transform-modules-commonjs", { lazy: true }] : null,
    ].filter(Boolean),
    overrides: [
      {
        test: "packages/babylon",
        plugins: [
          "babel-plugin-transform-charcodes",
          ["@babel/transform-for-of", { assumeArray: true }],
        ],
      },
      {
        test: "./packages/babel-register",
        plugins: [
          // Override the root options to disable lazy imports for babel-register
          // because otherwise the require hook will try to lazy-import things
          // leading to dependency cycles.
          convertESM ? "@babel/transform-modules-commonjs" : null,
        ].filter(Boolean),
      },
    ],
  };

  // we need to do this as long as we do not test everything from source
  if (includeCoverage) {
    config.auxiliaryCommentBefore = "istanbul ignore next";
    config.plugins.push("babel-plugin-istanbul");
  }

  return config;
};
<commit_msg>Target Node 6 in production (#7782)<commit_after>"use strict";

module.exports = function(api) {
  const env = api.env();

  const includeCoverage = process.env.BABEL_COVERAGE === "true";

  const envOpts = {
    loose: true,
    modules: false,
    exclude: ["transform-typeof-symbol"],
  };

  let convertESM = true;

  switch (env) {
    // Configs used during bundling builds.
    case "babylon":
    case "standalone":
      convertESM = false;
      break;
    case "production":
      // Config during builds before publish.
      envOpts.targets = {
        node: 6,
      };
      break;
    case "development":
      envOpts.debug = true;
      envOpts.targets = {
        node: "current",
      };
      break;
    case "test":
      envOpts.targets = {
        node: "current",
      };
      break;
  }

  const config = {
    comments: false,
    presets: [["@babel/env", envOpts]],
    plugins: [
      // TODO: Use @babel/preset-flow when
      // https://github.com/babel/babel/issues/7233 is fixed
      "@babel/plugin-transform-flow-strip-types",
      ["@babel/proposal-class-properties", { loose: true }],
      "@babel/proposal-export-namespace-from",
      "@babel/proposal-numeric-separator",
      ["@babel/proposal-object-rest-spread", { useBuiltIns: true }],

      // Explicitly use the lazy version of CommonJS modules.
      convertESM ? ["@babel/transform-modules-commonjs", { lazy: true }] : null,
    ].filter(Boolean),
    overrides: [
      {
        test: "packages/babylon",
        plugins: [
          "babel-plugin-transform-charcodes",
          ["@babel/transform-for-of", { assumeArray: true }],
        ],
      },
      {
        test: "./packages/babel-register",
        plugins: [
          // Override the root options to disable lazy imports for babel-register
          // because otherwise the require hook will try to lazy-import things
          // leading to dependency cycles.
          convertESM ? "@babel/transform-modules-commonjs" : null,
        ].filter(Boolean),
      },
    ],
  };

  // we need to do this as long as we do not test everything from source
  if (includeCoverage) {
    config.auxiliaryCommentBefore = "istanbul ignore next";
    config.plugins.push("babel-plugin-istanbul");
  }

  return config;
};
<|endoftext|>


<commit_before>describe('Herotabs', function() {

    describe('Basic HTML', function() {
        testSuite('basic.html');
    });

    describe('Heavily nested HTML', function() {
        testSuite('nesting.html');
    });

    describe('Mixed HTML elements', function() {
        testSuite('mixed-elements.html');
    });

    function testSuite(fixture) {
        var tabs, nav, tabPanels, instance;

        describe('Object init', function() {
            beforeEach(function() {
                loadFixtures(fixture);
                tabs = $('.tabs').herotabs({
                    interactEvent: 'hover',
                    css: {
                        navId: 'test'
                    }
                });
                instance = tabs.data('herotabs');
            });

            it('should expose the constructor', function() {
                expect($.fn.herotabs.Herotabs).toBeOfType('function');
            });

            it('should create an instance in data storage', function() {
                expect(instance.constructor).toEqual($.fn.herotabs.Herotabs);
            });

            it('should expose default options', function() {
                expect($.fn.herotabs.defaults).toBeOfType('object');
            });

            it('should allow user to override default options', function() {
                expect(instance.options.interactEvent).toBe('hover');
                expect(instance.options.css.navId).toBe('test');
                expect(instance.options.css.current).toBe('tab-current');
            });

            it('should set an active class', function() {
                expect(tabs).toHaveClass($.fn.herotabs.defaults.css.active);
            });
        });

        beforeEach(function() {
            loadFixtures(fixture);
            tabs = $('.tabs').herotabs();
            nav = $('.js-nav');
            tabPanels = $('.js-tab');
            instance = tabs.data('herotabs');
        });

        describe('Initial nav state', function() {
            it('should set roles on the navigation', function() {
                expect(nav).toHaveAttr('role', 'tablist');
                expect(nav.find('.js-nav-item')).toHaveAttr('role', 'presentation');
                expect(nav.find('.js-nav-item a')).toHaveAttr('role', 'tab');
            });

            it('should generate ids on the nav links', function() {
                nav.find('a').each(function(index) {
                    expect($(this)).toHaveId($.fn.herotabs.defaults.css.navId + instance._instanceId + '-' + (index + 1));
                });
            });

            it('should set the first nav item to current by default', function() {
                expect(nav.find('.js-nav-item').eq(0)).toHaveClass($.fn.herotabs.defaults.css.navCurrent);
                expect(nav.find('a').eq(0)).toHaveAttr('aria-selected', 'true');
                expect(nav.find('a').eq(0)).toHaveAttr('tabindex', '0');

                expect(nav.find('.js-nav-item').slice(1)).not.toHaveClass($.fn.herotabs.defaults.css.navCurrent);
                expect(nav.find('a').slice(1)).toHaveAttr('tabindex', '-1');
                expect(nav.find('a').slice(1)).toHaveAttr('aria-selected', 'false');
            });
        });

        describe('Initial tab panel state', function() {
            it('should set roles on the tab panels', function() {
                expect(tabPanels).toHaveAttr('role', 'tabpanel');
            });

            it('should link tab panels to their nav item ids', function() {
                tabPanels.each(function(index) {
                    expect($(this)).toHaveAttr('aria-labelledby', $.fn.herotabs.defaults.css.navId + instance._instanceId + '-' + (index + 1));
                });
            });

            it('should set the first tab panel to current by default', function() {
                expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
                expect(tabPanels.eq(0)).toHaveAttr('aria-hidden', 'false');
                expect(tabPanels.eq(0)).toHaveAttr('tabindex', '0');
                expect(tabPanels.eq(0)).toBeVisible();

                expect(tabPanels.slice(1)).toHaveAttr('aria-hidden', 'true');
                expect(tabPanels.slice(1)).toHaveAttr('tabindex', '-1');
                expect(tabPanels.slice(1)).toBeHidden();
            });

            it('should set correct tabindex to links within the tab panels', function() {
                expect(tabPanels.eq(0).find('a')).toHaveAttr('tabindex', '0');
                expect(tabPanels.slice(1).find('a')).toHaveAttr('tabindex', '-1');
            });
        });

        describe('Show tab', function() {
            it('should show the selected tab by using an index', function() {
                instance.showTab(1);

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show the selected tab by using an jQuery object of the element', function() {
                instance.showTab(tabPanels.eq(1));

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should do nothing if an index is greater than the total elements', function() {
                instance.showTab(5);

                expect(tabPanels.eq(0)).toBeVisible();
                expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should return the instance', function() {
                var ret = instance.showTab(2);
                expect(ret.constructor).toEqual($.fn.herotabs.Herotabs);
            });
        });

        describe('Next tab', function() {
            it('should show the next tab', function() {
                instance.nextTab();

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show the first tab if the last one is already visible', function() {
                instance.showTab(2);
                instance.nextTab();

                expect(tabPanels.eq(2)).toBeHidden();
                expect(tabPanels.eq(2)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(0)).toBeVisible();
                expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should return the instance', function() {
                var ret = instance.nextTab();
                expect(ret.constructor).toEqual($.fn.herotabs.Herotabs);
            });
        });

        describe('Previous tab', function() {
            it('should show the previous tab', function() {
                instance.prevTab();

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(2)).toBeVisible();
                expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should return the instance', function() {
                var ret = instance.nextTab();
                expect(ret.constructor).toEqual($.fn.herotabs.Herotabs);
            });
        });

        describe('Timer', function() {
            it('should show the next tab after the delay', function() {
                var done = false;
                tabs = $('.tabs').herotabs({
                    delay: 300
                });

                runs(function() {
                    setTimeout(function() {
                        done = true;
                    }, 300);
                });

                waitsFor(function() {
                    return done;
                }, "The next tab should be visible", 600);

                runs(function() {
                    expect(tabPanels.eq(0)).toBeHidden();
                    expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                    expect(tabPanels.eq(1)).toBeVisible();
                    expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
                });
            });

            it('should show the previous tab if reverse is set', function() {
                var done = false;
                tabs = $('.tabs').herotabs({
                    delay: 300,
                    reverse: true
                });

                runs(function() {
                    setTimeout(function() {
                        done = true;
                    }, 300);
                });

                waitsFor(function() {
                    return done;
                }, "The previous tab should be visible", 600);

                runs(function() {
                    expect(tabPanels.eq(0)).toBeHidden();
                    expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                    expect(tabPanels.eq(2)).toBeVisible();
                    expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
                });
            });

            it('should allow the timer to be stopped', function() {
                var done = false;
                tabs = $('.tabs').herotabs({
                    delay: 500
                });
                instance = tabs.data('herotabs');

                runs(function() {
                    setTimeout(function() {
                        done = true;
                        instance.stop();
                    }, 300);
                });

                waitsFor(function() {
                    return done;
                }, "The first tab should still be visible", 400);

                runs(function() {
                    expect(tabPanels.eq(0)).toBeVisible();
                    expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
                });
            });
        });

        describe('Custom events', function() {
            it('should fire an event when a tab is shown', function() {
                spyOnEvent(tabs, 'herotabs.show');
                instance.showTab(1);
                expect('herotabs.show').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire an event when the next tab is shown', function() {
                spyOnEvent(tabs, 'herotabs.next');
                instance.nextTab();
                expect('herotabs.next').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire an event when the previous tab is shown', function() {
                spyOnEvent(tabs, 'herotabs.prev');
                instance.prevTab();
                expect('herotabs.prev').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire events when the start method is called', function() {
                spyOnEvent(tabs, 'herotabs.start');

                instance.options.delay = 200;
                instance.start();

                expect('herotabs.start').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire events when the stop method is called', function() {
                spyOnEvent(tabs, 'herotabs.stop');

                instance.options.delay = 200;
                instance.start();
                instance.stop();

                expect('herotabs.stop').toHaveBeenTriggeredOn(tabs);
            });
        });

        describe('Browser events', function() {
            it('should change tab when a nav item is clicked', function() {
                var event = $.Event('click');
                event.target = nav.find('a')[1];
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should change tab when a nav item is hovered', function() {
                tabs = $('.tabs').herotabs({
                    interactEvent: 'hover'
                });
                var event = $.Event('mouseenter');
                event.target = nav.find('a')[1];
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show next tab when the right arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 39;
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show next tab when the down arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 40;
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show previous tab when the left arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 37;
                nav.trigger(event);

                expect(tabPanels.eq(2)).toBeVisible();
                expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show previous tab when the up arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 38;
                nav.trigger(event);

                expect(tabPanels.eq(2)).toBeVisible();
                expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
            });
        });

        describe('checkUrlIsAnchor regex', function() {
            it('should match hrefs that are anchor links', function() {
                expect(instance._checkUrlIsAnchor('#href')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('#href_foo')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('#href-foo')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('#hREffoo')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('/#hREffoo')).toBeTruthy();

                expect(instance._checkUrlIsAnchor('http://google.com')).toBeFalsy();
                expect(instance._checkUrlIsAnchor('fooo')).toBeFalsy();
                expect(instance._checkUrlIsAnchor('')).toBeFalsy();
            });
        });
    }

});

<commit_msg>Remove some of the test duplication<commit_after>describe('Herotabs', function() {

    describe('Setup', function() {
        var tabs, instance;

        describe('Object init', function() {
            beforeEach(function() {
                loadFixtures('basic.html');
                tabs = $('.tabs').herotabs({
                    interactEvent: 'hover',
                    css: {
                        navId: 'test'
                    }
                });
                instance = tabs.data('herotabs');
            });

            it('should expose the constructor', function() {
                expect($.fn.herotabs.Herotabs).toBeOfType('function');
            });

            it('should create an instance in data storage', function() {
                expect(instance.constructor).toEqual($.fn.herotabs.Herotabs);
            });

            it('should expose default options', function() {
                expect($.fn.herotabs.defaults).toBeOfType('object');
            });

            it('should allow user to override default options', function() {
                expect(instance.options.interactEvent).toBe('hover');
                expect(instance.options.css.navId).toBe('test');
                expect(instance.options.css.current).toBe('tab-current');
            });

            it('should set an active class', function() {
                expect(tabs).toHaveClass($.fn.herotabs.defaults.css.active);
            });
        });

        describe('checkUrlIsAnchor regex', function() {
            it('should match hrefs that are anchor links', function() {
                expect(instance._checkUrlIsAnchor('#href')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('#href_foo')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('#href-foo')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('#hREffoo')).toBeTruthy();
                expect(instance._checkUrlIsAnchor('/#hREffoo')).toBeTruthy();

                expect(instance._checkUrlIsAnchor('http://google.com')).toBeFalsy();
                expect(instance._checkUrlIsAnchor('fooo')).toBeFalsy();
                expect(instance._checkUrlIsAnchor('')).toBeFalsy();
            });
        });
    });

    /**
     * Run the same tests against different HTML structures
     * ----------------------------------------------------
     */

    describe('Basic HTML', function() {
        testSuite('basic.html');
    });

    describe('Heavily nested HTML', function() {
        testSuite('nesting.html');
    });

    describe('Mixed HTML elements', function() {
        testSuite('mixed-elements.html');
    });

    function testSuite(fixture) {
        var tabs, nav, tabPanels, instance;

        beforeEach(function() {
            loadFixtures(fixture);
            tabs = $('.tabs').herotabs();
            nav = $('.js-nav');
            tabPanels = $('.js-tab');
            instance = tabs.data('herotabs');
        });

        describe('Initial nav state', function() {
            it('should set roles on the navigation', function() {
                expect(nav).toHaveAttr('role', 'tablist');
                expect(nav.find('.js-nav-item')).toHaveAttr('role', 'presentation');
                expect(nav.find('.js-nav-item a')).toHaveAttr('role', 'tab');
            });

            it('should generate ids on the nav links', function() {
                nav.find('a').each(function(index) {
                    expect($(this)).toHaveId($.fn.herotabs.defaults.css.navId + instance._instanceId + '-' + (index + 1));
                });
            });

            it('should set the first nav item to current by default', function() {
                expect(nav.find('.js-nav-item').eq(0)).toHaveClass($.fn.herotabs.defaults.css.navCurrent);
                expect(nav.find('a').eq(0)).toHaveAttr('aria-selected', 'true');
                expect(nav.find('a').eq(0)).toHaveAttr('tabindex', '0');

                expect(nav.find('.js-nav-item').slice(1)).not.toHaveClass($.fn.herotabs.defaults.css.navCurrent);
                expect(nav.find('a').slice(1)).toHaveAttr('tabindex', '-1');
                expect(nav.find('a').slice(1)).toHaveAttr('aria-selected', 'false');
            });
        });

        describe('Initial tab panel state', function() {
            it('should set roles on the tab panels', function() {
                expect(tabPanels).toHaveAttr('role', 'tabpanel');
            });

            it('should link tab panels to their nav item ids', function() {
                tabPanels.each(function(index) {
                    expect($(this)).toHaveAttr('aria-labelledby', $.fn.herotabs.defaults.css.navId + instance._instanceId + '-' + (index + 1));
                });
            });

            it('should set the first tab panel to current by default', function() {
                expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
                expect(tabPanels.eq(0)).toHaveAttr('aria-hidden', 'false');
                expect(tabPanels.eq(0)).toHaveAttr('tabindex', '0');
                expect(tabPanels.eq(0)).toBeVisible();

                expect(tabPanels.slice(1)).toHaveAttr('aria-hidden', 'true');
                expect(tabPanels.slice(1)).toHaveAttr('tabindex', '-1');
                expect(tabPanels.slice(1)).toBeHidden();
            });

            it('should set correct tabindex to links within the tab panels', function() {
                expect(tabPanels.eq(0).find('a')).toHaveAttr('tabindex', '0');
                expect(tabPanels.slice(1).find('a')).toHaveAttr('tabindex', '-1');
            });
        });

        describe('Show tab', function() {
            it('should show the selected tab by using an index', function() {
                instance.showTab(1);

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show the selected tab by using an jQuery object of the element', function() {
                instance.showTab(tabPanels.eq(1));

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should do nothing if an index is greater than the total elements', function() {
                instance.showTab(5);

                expect(tabPanels.eq(0)).toBeVisible();
                expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should return the instance', function() {
                var ret = instance.showTab(2);
                expect(ret.constructor).toEqual($.fn.herotabs.Herotabs);
            });
        });

        describe('Next tab', function() {
            it('should show the next tab', function() {
                instance.nextTab();

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show the first tab if the last one is already visible', function() {
                instance.showTab(2);
                instance.nextTab();

                expect(tabPanels.eq(2)).toBeHidden();
                expect(tabPanels.eq(2)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(0)).toBeVisible();
                expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should return the instance', function() {
                var ret = instance.nextTab();
                expect(ret.constructor).toEqual($.fn.herotabs.Herotabs);
            });
        });

        describe('Previous tab', function() {
            it('should show the previous tab', function() {
                instance.prevTab();

                expect(tabPanels.eq(0)).toBeHidden();
                expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                expect(tabPanels.eq(2)).toBeVisible();
                expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should return the instance', function() {
                var ret = instance.nextTab();
                expect(ret.constructor).toEqual($.fn.herotabs.Herotabs);
            });
        });

        describe('Timer', function() {
            it('should show the next tab after the delay', function() {
                var done = false;
                tabs = $('.tabs').herotabs({
                    delay: 300
                });

                runs(function() {
                    setTimeout(function() {
                        done = true;
                    }, 300);
                });

                waitsFor(function() {
                    return done;
                }, "The next tab should be visible", 600);

                runs(function() {
                    expect(tabPanels.eq(0)).toBeHidden();
                    expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                    expect(tabPanels.eq(1)).toBeVisible();
                    expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
                });
            });

            it('should show the previous tab if reverse is set', function() {
                var done = false;
                tabs = $('.tabs').herotabs({
                    delay: 300,
                    reverse: true
                });

                runs(function() {
                    setTimeout(function() {
                        done = true;
                    }, 300);
                });

                waitsFor(function() {
                    return done;
                }, "The previous tab should be visible", 600);

                runs(function() {
                    expect(tabPanels.eq(0)).toBeHidden();
                    expect(tabPanels.eq(0)).not.toHaveClass($.fn.herotabs.defaults.css.current);

                    expect(tabPanels.eq(2)).toBeVisible();
                    expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
                });
            });

            it('should allow the timer to be stopped', function() {
                var done = false;
                tabs = $('.tabs').herotabs({
                    delay: 500
                });
                instance = tabs.data('herotabs');

                runs(function() {
                    setTimeout(function() {
                        done = true;
                        instance.stop();
                    }, 300);
                });

                waitsFor(function() {
                    return done;
                }, "The first tab should still be visible", 400);

                runs(function() {
                    expect(tabPanels.eq(0)).toBeVisible();
                    expect(tabPanels.eq(0)).toHaveClass($.fn.herotabs.defaults.css.current);
                });
            });
        });

        describe('Custom events', function() {
            it('should fire an event when a tab is shown', function() {
                spyOnEvent(tabs, 'herotabs.show');
                instance.showTab(1);
                expect('herotabs.show').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire an event when the next tab is shown', function() {
                spyOnEvent(tabs, 'herotabs.next');
                instance.nextTab();
                expect('herotabs.next').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire an event when the previous tab is shown', function() {
                spyOnEvent(tabs, 'herotabs.prev');
                instance.prevTab();
                expect('herotabs.prev').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire events when the start method is called', function() {
                spyOnEvent(tabs, 'herotabs.start');

                instance.options.delay = 200;
                instance.start();

                expect('herotabs.start').toHaveBeenTriggeredOn(tabs);
            });

            it('should fire events when the stop method is called', function() {
                spyOnEvent(tabs, 'herotabs.stop');

                instance.options.delay = 200;
                instance.start();
                instance.stop();

                expect('herotabs.stop').toHaveBeenTriggeredOn(tabs);
            });
        });

        describe('Browser events', function() {
            it('should change tab when a nav item is clicked', function() {
                var event = $.Event('click');
                event.target = nav.find('a')[1];
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should change tab when a nav item is hovered', function() {
                tabs = $('.tabs').herotabs({
                    interactEvent: 'hover'
                });
                var event = $.Event('mouseenter');
                event.target = nav.find('a')[1];
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show next tab when the right arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 39;
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show next tab when the down arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 40;
                nav.trigger(event);

                expect(tabPanels.eq(1)).toBeVisible();
                expect(tabPanels.eq(1)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show previous tab when the left arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 37;
                nav.trigger(event);

                expect(tabPanels.eq(2)).toBeVisible();
                expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
            });

            it('should show previous tab when the up arrow is pressed', function() {
                var event = $.Event('keydown');
                event.target = nav.find('a')[1];
                event.keyCode = 38;
                nav.trigger(event);

                expect(tabPanels.eq(2)).toBeVisible();
                expect(tabPanels.eq(2)).toHaveClass($.fn.herotabs.defaults.css.current);
            });
        });
    }
});

<|endoftext|>


<commit_before>/*
 * jQuery-Bootstrap-Purr
 * https://github.com/softwarespot/jquery-bootstrap-purr
 * Author: softwarespot
 * Licensed under the MIT license
 * Version: 1.1.1
 */
; // jshint ignore:line
(function (window, $) {

    // Plugin Logic

    $.bootstrapPurr = function (message, options) {
        // Set our options from the defaults, overriding with the
        // parameter we pass into this function
        options = $.extend({}, $.bootstrapPurr.options, options);

        // Create a temporary div element
        var $alert = $('<div/>')
            // Add the 'alert' and 'bootstrap-purr' classes for distinguishing
            // other Bootstrap alerts
            .addClass('alert bootstrap-purr')
            .attr('role', 'alert');

        // If the 'type' is set, then add the relevant alert-* class name
        if (isString(options.type) && /^DANGER|INFO|SUCCESS|WARNING$/i.test(options.type)) {
            $alert.addClass('alert-' + options.type.toLowerCase());
        }

        // Set the default value of 'allow_dismiss' if not a boolean datatype
        options.allow_dismiss = isBoolean(options.allow_dismiss) ? options.allow_dismiss : true;

        // Set the default value of 'allow_dismiss_type' if not defined
        options.allow_dismiss_type = isString(options.allow_dismiss_type) && /^CLICK|HOVER$/i.test(options.allow_dismiss_type) ?
            options.allow_dismiss_type.toUpperCase() :
            'CLICK';

        var isClick = options.allow_dismiss_type === 'CLICK';
        var isHover = options.allow_dismiss_type === 'HOVER';

        // If 'allow dismissal' is set to true, then add the relevant class and append a button element
        if (options.allow_dismiss && isClick) {
            // Close button
            var $button = $('<button/>')
                .attr('type', 'button')
                .addClass('close')
                .attr('data-dismiss', 'alert')
                .attr('aria-label', 'Close');

            // The small 'x' in the top right hand corner
            var $cross = $('<span/>')
                .attr('aria-hidden', 'true')
                .html('×');

            // Append the cross to the button element
            $button.append($cross);

            // Append the close button to the alert and add the class
            // that specifies it's dimissible
            $alert.append($button)
                .addClass('alert-dismissible');
        }

        // Append the message to the alert. This could be HTML or simple a TEXT node
        if (message) {
            $alert.append(message);
        }

        // Check if the options.offset is correctly formatted
        options.offset.amount = $.isNumeric(options.offset.amount) ? options.offset.amount : 20;
        options.offset.from = /^TOP|BOTTOM$/i.test(options.offset.from) ? options.offset.from.toLowerCase() : 'top';

        // If 'stack spacing' is not numeric, then set the default to 10
        if (!$.isNumeric(options.stackup_spacing)) {
            options.stackup_spacing = 10;
        }

        // Store the offset amount for use outside the forEach()
        var offsetTotal = options.offset.amount;

        // For each element with the class name of '.bootstrap-purr', calculate the total offset
        $('.bootstrap-purr').each(function () {
            // Cache the jQuery selector
            var $this = $(this);
            // ES2015 use Number.parseInt
            offsetTotal = Math.max(offsetTotal, parseInt($this.css(options.offset.from)) + $this.outerHeight() + options.stackup_spacing);
        });

        // Set the default 'element' to 'body', if it's an invalid string
        if (!isString(options.element)) {
            options.element = 'body';
        }

        // Create a css object literal
        var css = {
            display: 'none',
            margin: 0,
            position: (options.element === 'body' ? 'fixed' : 'absolute'),
            width: 'auto',
            'z-index': '9999'
        };

        // Set the css property i.e. top or bottom, with the offset total
        css[options.offset.from] = offsetTotal + 'px';

        // Convert to lowercase
        options.width = ('' + options.width).toLowerCase();

        if (options.width !== 'auto' && $.isNumeric(options.width)) {
            css.width = options.width + 'px';
        }

        // Apply the css styles from above
        $alert.css(css);

        // Get the parent jQuery selector
        var $parent = $(options.element);

        // If the jQuery selector is empty, then default to 'body'
        if ($parent.length === 0) {
            $parent = $('body');
        }

        // Append the alert to the parent element
        $parent.append($alert);

        // Convert to uppercase for case-insensitive matching
        if (isString(options.align)) {
            options.align = options.align.toUpperCase();
        }

        // Apply the css styles with regards to alignment in the parent element
        switch (options.align) {
            case 'CENTER':
                $alert.css({
                    'left': '50%',
                    'margin-left': '-' + ($alert.outerWidth() / 2) + 'px'
                });
                break;

            case 'LEFT':
                $alert.css('left', '20px');
                break;

            default:
                $alert.css('right', '20px');
        }

        // Set the default value of 'draggable' if not a boolean datatype
        options.draggable = isBoolean(options.draggable) ? options.draggable : true;

        // Create variables to store anonymous functions
        var mouseDown = null;
        var mouseMove = null;

        // If 'draggable' is set to true
        if (options.draggable && !isHover) {
            // Add a moving cursor to signify the alert can be moved
            $alert.css('cursor', 'move');

            // Object to store the mouse co-ordinates
            var mouse = {
                x: 0,
                y: 0,
                // Update function
                update: function (event) {
                    this.x = event.pageX;
                    this.y = event.pageY;
                }
            };

            // Create a function expression to reference at a later stage
            mouseDown = function (event) {
                event.preventDefault();

                // If not absolute, fixed or relative, then set the position to relative by default
                if (!/^(ABSOLUTE|FIXED|RELATIVE)$/i.test($alert.css('position'))) {
                    $alert.css('position', 'relative');
                }

                // Update the mouse coordinates
                mouse.update(event);

                // Create a function expression to reference at a later stage
                mouseMove = function (event) {
                    event.preventDefault();

                    // Get the offset object relative to the document
                    var offset = $alert.offset();

                    // Set the offset of the alert element
                    $alert.offset({
                        left: (offset.left + (event.pageX - mouse.x)),
                        top: (offset.top + (event.pageY - mouse.y))
                    });

                    // Update the mouse coordinates
                    mouse.update(event);
                };

                // Register an event for 'MOUSE_MOVE' on the parent element
                $parent.on(Events.MOUSE_MOVE, mouseMove);

                // Tidy up registered events (good housekeeping)

                // Register an event for 'MOUSE_UP' on the parent element
                $parent.one(Events.MOUSE_UP, function () {
                    // 'MOUSE_UP' will automatically be unregistered, due to using .one()

                    // Unregister the 'MOUSE_MOVE' event on the parent element
                    $parent.off(Events.MOUSE_MOVE, mouseMove);
                });
            };

            // Register an event for 'MOUSE_DOWN' on the alert element
            $alert.on(Events.MOUSE_DOWN, mouseDown);
        }

        // Create variable to store anonymous functions
        var mouseHover = null;
        var mouseLeave = null;

        // Create a function expression to reference at a later stage
        // This close the alert
        var alertClose = function () {
            $alert.alert('close');
        };

        // Create a function expression to reference at a later stage
        // Unregister events
        var unregisterEvents = function () {
            // Tidy up registered events (good housekeeping)
            if (options.draggable && !isHover) {
                // Tidy up registered events (good housekeeping)

                // Unregister the 'MOUSE_MOVE' event applied to the parent element
                $parent.off(Events.MOUSE_MOVE, mouseMove);

                // Unregister the 'MOUSE_DOWN' event applied to the alert element
                $alert.off(Events.MOUSE_DOWN, mouseDown);
            }

            // Unregister the 'MOUSE_HOVER' event applied to the alert element
            $alert.off(Events.MOUSE_HOVER, mouseHover);

            // Unregister the 'MOUSE_LEAVE' event applied to the alert element
            $alert.off(Events.MOUSE_LEAVE, mouseLeave);
        };

        // Add the complete function to the 'animate hide' options
        $.extend(options.animate_hide, {
            complete: alertClose
        });

        // Set the default value of 'delay' if not a number datatype or greater than zero
        options.delay = $.isNumeric(options.delay) && options.delay >= 0 ? options.delay : 5000;

        // Set the default value of 'draggable' if not a boolean datatype
        options.delay_pause = isBoolean(options.delay_pause) ? options.delay_pause : false;

        // Store if to dismiss the alert on hover
        var isDismissOnHover = options.allow_dismiss && isHover;

        // Store if to pause the dismissal on hover
        var isPauseOnHover = options.delay > 0 && options.delay_pause;

        // Store whether or not the pause alert has taken place or the timeout has been cancelled
        var isPaused = false;
        var hasTimedout = false;

        // If 'allow_dismiss' is true and the type is 'hover' OR
        // if delay on hover, then register the 'MOUSE_HOVER' event
        if (isDismissOnHover || isPauseOnHover) {
            // Create a function expression to reference at a later stage
            mouseHover = function () {
                if (isDismissOnHover) {
                    $alert.animate(options.animate_hide, options.animate_hide);
                } else if (isPauseOnHover) {
                    isPaused = true;

                    // Register a 'MOUSE_LEAVE' event only once
                    $alert.one(Events.MOUSE_LEAVE, function () {
                        isPaused = false;
                        if (hasTimedout) {
                            $alert.animate(options.animate_hide, options.animate_hide);
                        }
                    });
                }
            };

            // When the alert is hovered over. Register the 'MOUSE_HOVER' event
            $alert.on(Events.MOUSE_HOVER, mouseHover);
        }

        // When the alert is closed, unregister registered events
        $alert.one(Events.ALERT_CLOSED, unregisterEvents);

        // Display the alert
        $alert.animate(options.animate_show);

        // Create a delay on fade out if greater than zero,
        // otherwise the alert will stay there indefinitely
        if (options.delay > 0) {
            setTimeout(function () {
                hasTimedout = true;
                if (!isPaused) {
                    $alert.animate(options.animate_hide, options.animate_hide);
                }
            }, options.delay);
        }

        // Return the alert selector
        return $alert;
    };

    // Constants

    var Events = {
        // Fired by Bootstrap when the alert has finally closed
        ALERT_CLOSED: 'closed.bs.alert',

        // When the primary mouse button is pushed down on the alert
        MOUSE_DOWN: 'mousedown.bootstrap.purr',

        // When the mouse starts hovering over the alert
        MOUSE_HOVER: 'mouseenter.bootstrap.purr',

        // When the mouse stops hovering over the alert
        MOUSE_LEAVE: 'mouseleave.boostrap.purr',

        // When the mouse is moved whilst the primary mouse button is down. This is only created when 'MOUSE_DOWN' is invoked
        MOUSE_MOVE: 'mousemove.bootstrap.purr',

        // When the primary mouse button is released. This is only called once using .one()
        MOUSE_UP: 'mouseup.bootstrap.purr'
    };

    // Fields

    // Methods (Private)

    // Check if value is a boolean datatype
    function isBoolean(value) {
        return $.type(value) === 'boolean';
    }

    // Check if a value is a string datatype with a length greater than zero when whitespace is stripped
    function isString(value) {
        return $.type(value) === 'string' && value.trim().length > 0;
    }

    // Defaults

    $.bootstrapPurr.options = {
        // Default parent element to append the alert to
        element: 'body',

        // Type of alert. See Bootstrap documentation for any additional supported formats
        type: 'info', // (null|'default', 'info', 'danger', 'success')

        // Alert offset
        offset: {
            amount: 20, // (number)
            from: 'top' // ('top', 'bottom')
        },

        // Alignment relative to the parent element
        align: 'right', // ('left', 'right', 'center')

        // With of the alert. The default is 250px, which is the same as Bootstrap's alerts
        width: 250, // (number, 'auto')

        // If true then a cross will be displayed in the top right hand corner of the alert
        allow_dismiss: true, // (true, false)

        // Type of dismissal when 'allow_dismiss' is set to true. If the type is 'hover' and 'draggable' is set to true,
        // then 'draggable' will be ignored
        allow_dismiss_type: 'click', // ('click', 'hover')

        // Options to pass to the .animate() function when displaying the an alert
        animate_show: {
            opacity: 'show', // See the animate() function in jQuery for more details
            duration: 'fast'
        },

        // Options to pass to the .animate() function when closing the an alert
        animate_hide: {
            opacity: 'hide', // See the animate() function in jQuery for more details
            duration: 'slow'
        },

        // Delay for 'on fade out' in milliseconds
        delay: 5000, // (number)

        // Pause the delay when hovering over the alert
        delay_pause: false, // (true, false)

        // Whether the alert should be draggable using the primary mouse button
        draggable: true, // (true, false)

        // Spacing between each new alert that is created
        stackup_spacing: 10 // (number)
    };
})(this, this.jQuery);
<commit_msg>Renamed variable<commit_after>/*
 * jQuery-Bootstrap-Purr
 * https://github.com/softwarespot/jquery-bootstrap-purr
 * Author: softwarespot
 * Licensed under the MIT license
 * Version: 1.1.1
 */
; // jshint ignore:line
(function (window, $) {

    // Plugin Logic

    $.bootstrapPurr = function (message, options) {
        // Set our options from the defaults, overriding with the
        // parameter we pass into this function
        options = $.extend({}, $.bootstrapPurr.options, options);

        // Create a temporary div element
        var $alert = $('<div/>')
            // Add the 'alert' and 'bootstrap-purr' classes for distinguishing
            // other Bootstrap alerts
            .addClass('alert bootstrap-purr')
            .attr('role', 'alert');

        // If the 'type' is set, then add the relevant alert-* class name
        if (isString(options.type) && /^DANGER|INFO|SUCCESS|WARNING$/i.test(options.type)) {
            $alert.addClass('alert-' + options.type.toLowerCase());
        }

        // Set the default value of 'allow_dismiss' if not a boolean datatype
        options.allow_dismiss = isBoolean(options.allow_dismiss) ? options.allow_dismiss : true;

        // Set the default value of 'allow_dismiss_type' if not defined
        options.allow_dismiss_type = isString(options.allow_dismiss_type) && /^CLICK|HOVER$/i.test(options.allow_dismiss_type) ?
            options.allow_dismiss_type.toUpperCase() :
            'CLICK';

        var isClick = options.allow_dismiss_type === 'CLICK';
        var isHover = options.allow_dismiss_type === 'HOVER';

        // If 'allow dismissal' is set to true, then add the relevant class and append a button element
        if (options.allow_dismiss && isClick) {
            // Close button
            var $button = $('<button/>')
                .attr('type', 'button')
                .addClass('close')
                .attr('data-dismiss', 'alert')
                .attr('aria-label', 'Close');

            // The small 'x' in the top right hand corner
            var $cross = $('<span/>')
                .attr('aria-hidden', 'true')
                .html('×');

            // Append the cross to the button element
            $button.append($cross);

            // Append the close button to the alert and add the class
            // that specifies it's dimissible
            $alert.append($button)
                .addClass('alert-dismissible');
        }

        // Append the message to the alert. This could be HTML or simple a TEXT node
        if (message) {
            $alert.append(message);
        }

        // Check if the options.offset is correctly formatted
        options.offset.amount = $.isNumeric(options.offset.amount) ? options.offset.amount : 20;
        options.offset.from = /^TOP|BOTTOM$/i.test(options.offset.from) ? options.offset.from.toLowerCase() : 'top';

        // If 'stack spacing' is not numeric, then set the default to 10
        if (!$.isNumeric(options.stackup_spacing)) {
            options.stackup_spacing = 10;
        }

        // Store the offset amount for use outside the forEach()
        var offsetTotal = options.offset.amount;

        // For each element with the class name of '.bootstrap-purr', calculate the total offset
        $('.bootstrap-purr').each(function () {
            // Cache the jQuery selector
            var $this = $(this);
            // ES2015 use Number.parseInt
            offsetTotal = Math.max(offsetTotal, parseInt($this.css(options.offset.from)) + $this.outerHeight() + options.stackup_spacing);
        });

        // Set the default 'element' to 'body', if it's an invalid string
        if (!isString(options.element)) {
            options.element = 'body';
        }

        // Create a css object literal
        var css = {
            display: 'none',
            margin: 0,
            position: (options.element === 'body' ? 'fixed' : 'absolute'),
            width: 'auto',
            'z-index': '9999'
        };

        // Set the css property i.e. top or bottom, with the offset total
        css[options.offset.from] = offsetTotal + 'px';

        // Convert to lowercase
        options.width = ('' + options.width).toLowerCase();

        if (options.width !== 'auto' && $.isNumeric(options.width)) {
            css.width = options.width + 'px';
        }

        // Apply the css styles from above
        $alert.css(css);

        // Get the parent jQuery selector
        var $parent = $(options.element);

        // If the jQuery selector is empty, then default to 'body'
        if ($parent.length === 0) {
            $parent = $('body');
        }

        // Append the alert to the parent element
        $parent.append($alert);

        // Convert to uppercase for case-insensitive matching
        if (isString(options.align)) {
            options.align = options.align.toUpperCase();
        }

        // Apply the css styles with regards to alignment in the parent element
        switch (options.align) {
            case 'CENTER':
                $alert.css({
                    'left': '50%',
                    'margin-left': '-' + ($alert.outerWidth() / 2) + 'px'
                });
                break;

            case 'LEFT':
                $alert.css('left', '20px');
                break;

            default:
                $alert.css('right', '20px');
        }

        // Set the default value of 'draggable' if not a boolean datatype
        options.draggable = isBoolean(options.draggable) ? options.draggable : true;

        // Create variables to store anonymous functions
        var mouseDown = null;
        var mouseMove = null;

        // If 'draggable' is set to true
        if (options.draggable && !isHover) {
            // Add a moving cursor to signify the alert can be moved
            $alert.css('cursor', 'move');

            // Object to store the mouse co-ordinates
            var mouse = {
                x: 0,
                y: 0,
                // Update function
                update: function (event) {
                    this.x = event.pageX;
                    this.y = event.pageY;
                }
            };

            // Create a function expression to reference at a later stage
            mouseDown = function (event) {
                event.preventDefault();

                // If not absolute, fixed or relative, then set the position to relative by default
                if (!/^(ABSOLUTE|FIXED|RELATIVE)$/i.test($alert.css('position'))) {
                    $alert.css('position', 'relative');
                }

                // Update the mouse coordinates
                mouse.update(event);

                // Create a function expression to reference at a later stage
                mouseMove = function (event) {
                    event.preventDefault();

                    // Get the offset object relative to the document
                    var offset = $alert.offset();

                    // Set the offset of the alert element
                    $alert.offset({
                        left: (offset.left + (event.pageX - mouse.x)),
                        top: (offset.top + (event.pageY - mouse.y))
                    });

                    // Update the mouse coordinates
                    mouse.update(event);
                };

                // Register an event for 'MOUSE_MOVE' on the parent element
                $parent.on(_events.MOUSE_MOVE, mouseMove);

                // Tidy up registered events (good housekeeping)

                // Register an event for 'MOUSE_UP' on the parent element
                $parent.one(_events.MOUSE_UP, function () {
                    // 'MOUSE_UP' will automatically be unregistered, due to using .one()

                    // Unregister the 'MOUSE_MOVE' event on the parent element
                    $parent.off(_events.MOUSE_MOVE, mouseMove);
                });
            };

            // Register an event for 'MOUSE_DOWN' on the alert element
            $alert.on(_events.MOUSE_DOWN, mouseDown);
        }

        // Create variable to store anonymous functions
        var mouseHover = null;
        var mouseLeave = null;

        // Create a function expression to reference at a later stage
        // This close the alert
        var alertClose = function () {
            $alert.alert('close');
        };

        // Create a function expression to reference at a later stage
        // Unregister events
        var unregisterEvents = function () {
            // Tidy up registered events (good housekeeping)
            if (options.draggable && !isHover) {
                // Tidy up registered events (good housekeeping)

                // Unregister the 'MOUSE_MOVE' event applied to the parent element
                $parent.off(_events.MOUSE_MOVE, mouseMove);

                // Unregister the 'MOUSE_DOWN' event applied to the alert element
                $alert.off(_events.MOUSE_DOWN, mouseDown);
            }

            // Unregister the 'MOUSE_HOVER' event applied to the alert element
            $alert.off(_events.MOUSE_HOVER, mouseHover);

            // Unregister the 'MOUSE_LEAVE' event applied to the alert element
            $alert.off(_events.MOUSE_LEAVE, mouseLeave);
        };

        // Add the complete function to the 'animate hide' options
        $.extend(options.animate_hide, {
            complete: alertClose
        });

        // Set the default value of 'delay' if not a number datatype or greater than zero
        options.delay = $.isNumeric(options.delay) && options.delay >= 0 ? options.delay : 5000;

        // Set the default value of 'draggable' if not a boolean datatype
        options.delay_pause = isBoolean(options.delay_pause) ? options.delay_pause : false;

        // Store if to dismiss the alert on hover
        var isDismissOnHover = options.allow_dismiss && isHover;

        // Store if to pause the dismissal on hover
        var isPauseOnHover = options.delay > 0 && options.delay_pause;

        // Store whether or not the pause alert has taken place or the timeout has been cancelled
        var isPaused = false;
        var hasTimedout = false;

        // If 'allow_dismiss' is true and the type is 'hover' OR
        // if delay on hover, then register the 'MOUSE_HOVER' event
        if (isDismissOnHover || isPauseOnHover) {
            // Create a function expression to reference at a later stage
            mouseHover = function () {
                if (isDismissOnHover) {
                    $alert.animate(options.animate_hide, options.animate_hide);
                } else if (isPauseOnHover) {
                    isPaused = true;

                    // Register a 'MOUSE_LEAVE' event only once
                    $alert.one(_events.MOUSE_LEAVE, function () {
                        isPaused = false;
                        if (hasTimedout) {
                            $alert.animate(options.animate_hide, options.animate_hide);
                        }
                    });
                }
            };

            // When the alert is hovered over. Register the 'MOUSE_HOVER' event
            $alert.on(_events.MOUSE_HOVER, mouseHover);
        }

        // When the alert is closed, unregister registered events
        $alert.one(_events.ALERT_CLOSED, unregisterEvents);

        // Display the alert
        $alert.animate(options.animate_show);

        // Create a delay on fade out if greater than zero,
        // otherwise the alert will stay there indefinitely
        if (options.delay > 0) {
            setTimeout(function () {
                hasTimedout = true;
                if (!isPaused) {
                    $alert.animate(options.animate_hide, options.animate_hide);
                }
            }, options.delay);
        }

        // Return the alert selector
        return $alert;
    };

    // Constants

    var _events = {
        // Fired by Bootstrap when the alert has finally closed
        ALERT_CLOSED: 'closed.bs.alert',

        // When the primary mouse button is pushed down on the alert
        MOUSE_DOWN: 'mousedown.bootstrap.purr',

        // When the mouse starts hovering over the alert
        MOUSE_HOVER: 'mouseenter.bootstrap.purr',

        // When the mouse stops hovering over the alert
        MOUSE_LEAVE: 'mouseleave.boostrap.purr',

        // When the mouse is moved whilst the primary mouse button is down. This is only created when 'MOUSE_DOWN' is invoked
        MOUSE_MOVE: 'mousemove.bootstrap.purr',

        // When the primary mouse button is released. This is only called once using .one()
        MOUSE_UP: 'mouseup.bootstrap.purr'
    };

    // Fields

    // Methods (Private)

    // Check if value is a boolean datatype
    function isBoolean(value) {
        return $.type(value) === 'boolean';
    }

    // Check if a value is a string datatype with a length greater than zero when whitespace is stripped
    function isString(value) {
        return $.type(value) === 'string' && value.trim().length > 0;
    }

    // Defaults

    $.bootstrapPurr.options = {
        // Default parent element to append the alert to
        element: 'body',

        // Type of alert. See Bootstrap documentation for any additional supported formats
        type: 'info', // (null|'default', 'info', 'danger', 'success')

        // Alert offset
        offset: {
            amount: 20, // (number)
            from: 'top' // ('top', 'bottom')
        },

        // Alignment relative to the parent element
        align: 'right', // ('left', 'right', 'center')

        // With of the alert. The default is 250px, which is the same as Bootstrap's alerts
        width: 250, // (number, 'auto')

        // If true then a cross will be displayed in the top right hand corner of the alert
        allow_dismiss: true, // (true, false)

        // Type of dismissal when 'allow_dismiss' is set to true. If the type is 'hover' and 'draggable' is set to true,
        // then 'draggable' will be ignored
        allow_dismiss_type: 'click', // ('click', 'hover')

        // Options to pass to the .animate() function when displaying the an alert
        animate_show: {
            opacity: 'show', // See the animate() function in jQuery for more details
            duration: 'fast'
        },

        // Options to pass to the .animate() function when closing the an alert
        animate_hide: {
            opacity: 'hide', // See the animate() function in jQuery for more details
            duration: 'slow'
        },

        // Delay for 'on fade out' in milliseconds
        delay: 5000, // (number)

        // Pause the delay when hovering over the alert
        delay_pause: false, // (true, false)

        // Whether the alert should be draggable using the primary mouse button
        draggable: true, // (true, false)

        // Spacing between each new alert that is created
        stackup_spacing: 10 // (number)
    };
})(this, this.jQuery);
<|endoftext|>