<commit_before>/**
 * CSSS javascript code
 * @author Lea Verou (http://leaverou.me)
 * @version 2.0
 */

/**
 * Make the environment a bit friendlier
 */
function $(expr, con) { return (con || document).querySelector(expr); }
function $$(expr, con) { return [].slice.call((con || document).querySelectorAll(expr)); }

(function(head, body){

// Check for classList support and include the polyfill if it's not supported
if(!('classList' in body)) {
	var thisScript = $('script[src$="slideshow.js"]'),
	    script = document.createElement('script');
	    script.src = thisScript.src.replace(/\bslideshow\.js/, 'classList.js');
	thisScript.parentNode.insertBefore(script, thisScript);
}

// Cache <title> element, we may need it for slides that don't have titles
var documentTitle = document.title + '';

var self = window.SlideShow = function(container, slide) {
	var me = this;

	// Set instance
	if(!window.slideshow) {
		window.slideshow = this;
	}

	container = container || body;

	// Current slide
	this.slide = slide || 0;

	// Current .delayed item in the slide
	this.item = 0;

	// Do we need to add a timer?
	this.duration = container.getAttribute('data-duration');

	if(this.duration > 0) {
		var timer = document.createElement('div'),
		    declaration = 'transition: ' + this.duration * 60 + 's linear; ';

		timer.id = 'timer';
		timer.setAttribute('style', '-moz-' + declaration + '-webkit-' + declaration + '-o-' + declaration + '-ms-' + declaration + declaration);
		container.appendChild(timer);

		setTimeout(function() {
			timer.className = 'end';
		}, 1);
	}

	// Get the slide elements into an array
	this.slides = Array.prototype.slice.apply($$('.slide', container));

	for(var i=0; i<this.slides.length; i++) {
		var slide = this.slides[i]; // to speed up references

		// Asign ids to slides that don't have one
		if(!slide.id) {
			slide.id = 'slide' + (i+1);
		}

		// Set data-title attribute to the title of the slide
		if(!slide.title) {
			// no title attribute, fetch title from heading(s)
			var heading = $('hgroup', screen) || $('h1,h2,h3,h4,h5,h6', slide);

			if(heading && heading.textContent.trim()) {
				slide.setAttribute('data-title', heading.textContent);
			}
		}
		else {
			// The title attribute is set, use that
			slide.setAttribute('data-title', slide.title);
			slide.removeAttribute('title');
		}
	}

	// If there's already a hash, update current slide number...
	this.goto(location.hash.substr(1) || 0);

	// ...and keep doing so every time the hash changes
	this.onhashchange = function() {
		me.goto(location.hash.substr(1) || 0);
	};
	window.addEventListener('hashchange', this.onhashchange, false);

	if(window.name === 'projector') {
		document.body.classList.add('projector');
	}

	// Adjust the font-size when the window is resized
	addEventListener('resize', function() {
		me.adjustFontSize();
	}, false);

	// In some browsers DOMContentLoaded is too early, so try again onload
	addEventListener('load', function() {
		me.adjustFontSize();
	}, false);

	/**
		Keyboard navigation
		Ctrl+G : Go to slide...
		Ctrl+H : Show thumbnails and go to slide
		Ctrl+P : Presenter view
		(Shift instead of Ctrl works too)
	*/
	document.addEventListener('keyup', function(evt) {
		if(evt.ctrlKey || evt.shiftKey) {
			switch(evt.keyCode) {
				case 71: // G
					var slide = prompt('Which slide?');
					me.goto(+slide? slide - 1 : slide);
					break;
				case 72: // H
					if(body.classList.contains('show-thumbnails')) {
						body.classList.remove('show-thumbnails');
						body.classList.remove('headers-only');
					}
					else {
						body.classList.add('show-thumbnails');

						if(!evt.shiftKey || !evt.ctrlKey) {
							body.classList.add('headers-only');
						}

						body.addEventListener('click', function(evt) {
							var slide = evt.target;

							while(slide && !slide.classList.contains('slide')) {
								slide = slide.parentNode;
							}

							if(slide) {
								me.goto(slide.id);
								setTimeout(function() { me.adjustFontSize(); }, 1000); // for Opera
							}

							body.classList.remove('show-thumbnails');
							body.classList.remove('headers-only');
						}, false);
					}
					break;
				case 74: // J
					if(body.classList.contains('hide-elements')) {
						body.classList.remove('hide-elements');
					}
					else {
						body.classList.add('hide-elements');
					}
					break;
				case 80: // P
					// Open new window for attendee view
					me.projector = window.open(location, 'projector', 'menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes');

					// Get the focus back
					window.focus();

					// Switch this one to presenter view
					body.classList.add('presenter');
			}
		}
	}, false);

	/**
		Keyboard navigation
		Home : First slide
		End : Last slide
		Space/Up/Right arrow : Next item/slide
		Ctrl + Space/Up/Right arrow : Next slide
		Down/Left arrow : Previous item/slide
		Ctrl + Down/Left arrow : Previous slide
		(Shift instead of Ctrl works too)
	*/
	document.addEventListener('keydown', function(evt) {
		if(evt.target === body || evt.target === body.parentNode || evt.altKey) {
			if(evt.keyCode >= 32 && evt.keyCode <= 40) {
				evt.preventDefault();
			}

			switch(evt.keyCode) {
				case 33: //page up
					me.previous();
					break;
				case 34: //page down
					me.next();
					break;
				case 35: // end
					me.end();
					break;
				case 36: // home
					me.start();
					break;
				case 37: // <-
				case 38: // up arrow
					me.previous(evt.ctrlKey || evt.shiftKey);
					break;
				case 32: // space
				case 39: // ->
				case 40: // down arrow
					me.next(evt.ctrlKey || evt.shiftKey);
					break;
			}
		}
	}, false);

	// Rudimentary style[scoped] polyfill
	var scoped = $$('style[scoped]', container);

	for(var i=scoped.length; i--;) {
		var style = scoped[i],
			rulez = style.sheet.cssRules,
			parentid = style.parentNode.id || self.getSlide(style).id;

		for(var j=rulez.length; j--;) {
			var cssText = rulez[j].cssText.replace(/^|,/g, function($0) { return '#' + parentid + ' ' + $0 });

			style.sheet.deleteRule(0);
			style.sheet.insertRule(cssText, 0);
		}
	}
}

self.prototype = {
	start: function() {
		this.goto(0);
	},

	end: function() {
		this.goto(this.slides.length - 1);
	},

	/**
		@param hard {Boolean} Whether to advance to the next slide (true) or
			just the next step (which could very well be showing a list item)
	 */
	next: function(hard) {
		if(!hard && this.items.length) {
			// If there's no current, then just mark the first one as such
			if(!this.item) {
				this.items[this.item++].classList.add('current');
			}
			// Add .current to current item if it exists, otherwise advance to next slide
			else if(this.item < this.items.length) {
				classes = this.items[this.item - 1].classList; // to speed up lookups

				classes.remove('current');
				classes.add('displayed');

				this.items[this.item++].classList.add('current');
			}
			else {
				this.item = 0;
				this.next(true);
			}
		}
		else {
			this.goto(this.slide + 1);

			this.item = 0;

			// Mark all items as not displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.remove('displayed');
						this.items[i].classList.remove('current');
					}
				}
			}
		}
	},

	previous: function(hard) {
		if(!hard && this.item > 0) {
			var classes = this.items[this.item - 1].classList; // to speed up lookups

			classes.remove('current');

			if(this.item > 1) {
				classes = this.items[--this.item - 1].classList;

				classes.remove('displayed');
				classes.add('current');
			}
			else {
				this.item = 0;
			}
		}
		else {

			this.goto(this.slide - 1);

			this.item = this.items.length;

			// Mark all items as displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.add('displayed');
					}
				}

				// Mark the last one as current
				var lastItem = this.items[this.items.length - 1];

				lastItem.classList.remove('displayed');
				lastItem.classList.add('current');
			}
		}
	},

	/**
		Go to an aribtary slide
		@param which {String|Integer} Which slide (identifier or slide number)
	*/
	goto: function(which) {
		var slide;

		// We have to remove it to prevent multiple calls to goto messing up
		// our current item (and there's no point either, so we save on performance)
		window.removeEventListener('hashchange', this.onhashchange, false);

		if(which + 0 === which && which in this.slides) { // Argument is a valid slide number
			this.slide = which;

			slide = this.slides[this.slide];
			location.hash = '#' + slide.id;
		}
		else if(which + '' === which) { // Argument is a slide id
			slide = document.getElementById(which);

			if(slide) {
				this.slide = this.slides.indexOf(slide);
				location.hash = '#' + which;
			}
		}

		if(slide) { // Slide actually changed, perform any other tasks needed
			document.title = slide.getAttribute('data-title') || documentTitle;

			this.adjustFontSize();

			// Update items collection
			this.items = $$('.delayed, .delayed-children > *', this.slides[this.slide]);
			this.item = 0;

			// Tell other windows
			if(this.projector && this.projector.slideshow && this.projector.slideshow.slide != this.slide) {
				this.projector.slideshow.goto(this.slide);
			}

			if(window.opener && opener.slideshow && opener.slideshow.slide != this.slide) {
				opener.slideshow.goto(this.slide);
			}
		}

		// If you attach the listener immediately again then it will catch the event
		// We have to do it asynchronously
		var me = this;
		setTimeout(function() {
			window.addEventListener('hashchange', me.onhashchange, false);
		}, 1000);
	},

	adjustFontSize: function() {
		// Cache long lookup chains, for performance
		var bodyStyle = body.style,
			scrollRoot = document[document.documentElement.scrollHeight? 'documentElement' : 'body'],
			innerHeight = window.innerHeight,
			innerWidth = window.innerWidth,
			slide = this.slides[this.slide];

		// Clear previous styles
		bodyStyle.fontSize = '';

		if(body.classList.contains('show-thumbnails')
			|| slide.classList.contains('dont-resize')) {
			return;
		}

		for(
			var percent = 100;
			(scrollRoot.scrollHeight > innerHeight || scrollRoot.scrollWidth > innerWidth) && percent >= 35;
			percent-=5
		) {
			bodyStyle.fontSize = percent + '%';
		}

		// Individual slide

		if(slide.clientHeight && slide.clientWidth) {
			// Strange FF bug: scrollHeight doesn't work properly with overflow:hidden
			var previousStyle = slide.getAttribute('style');
			slide.style.overflow = 'auto';

			for(
				;
				(slide.scrollHeight > slide.clientHeight || slide.scrollWidth > slide.clientWidth) && percent >= 35;
				percent--
			) {
				bodyStyle.fontSize = percent + '%';
			}

			slide.setAttribute('style', previousStyle);
		}
	},

	// Is the element on the current slide?
	onCurrent: function(element) {
		var slide = self.getSlide(element);

		if(slide) {
			return '#' + slide.id === location.hash;
		}

		return false;
	}
};

/**********************************************
 * Static methods
 **********************************************/

// Helper method for plugins
self.getSlide = function(element) {
	var slide = element;

	while (slide && slide.classList && !slide.classList.contains('slide')) {
		slide = slide.parentNode;
	}

	return slide;
}

})(document.head || document.getElementsByTagName('head')[0], document.body);<commit_msg>Added convenience functions $ and $$<commit_after>/**
 * CSSS javascript code
 * @author Lea Verou (http://leaverou.me)
 * @version 2.0
 */

/**
 * Make the environment a bit friendlier
 */
function $(expr, con) { if(con && !con.querySelector) console.trace();
return (con || document).querySelector(expr); }
function $$(expr, con) { return [].slice.call((con || document).querySelectorAll(expr)); }

(function(head, body){

// Check for classList support and include the polyfill if it's not supported
if(!('classList' in body)) {
	var thisScript = $('script[src$="slideshow.js"]'),
	    script = document.createElement('script');
	    script.src = thisScript.src.replace(/\bslideshow\.js/, 'classList.js');
	thisScript.parentNode.insertBefore(script, thisScript);
}

// Cache <title> element, we may need it for slides that don't have titles
var documentTitle = document.title + '';

var self = window.SlideShow = function(container, slide) {
	var me = this;

	// Set instance
	if(!window.slideshow) {
		window.slideshow = this;
	}

	container = container || body;

	// Current slide
	this.slide = slide || 0;

	// Current .delayed item in the slide
	this.item = 0;

	// Do we need to add a timer?
	this.duration = container.getAttribute('data-duration');

	if(this.duration > 0) {
		var timer = document.createElement('div'),
		    declaration = 'transition: ' + this.duration * 60 + 's linear; ';

		timer.id = 'timer';
		timer.setAttribute('style', '-moz-' + declaration + '-webkit-' + declaration + '-o-' + declaration + '-ms-' + declaration + declaration);
		container.appendChild(timer);

		setTimeout(function() {
			timer.className = 'end';
		}, 1);
	}

	// Get the slide elements into an array
	this.slides = Array.prototype.slice.apply($$('.slide', container));

	for(var i=0; i<this.slides.length; i++) {
		var slide = this.slides[i]; // to speed up references

		// Asign ids to slides that don't have one
		if(!slide.id) {
			slide.id = 'slide' + (i+1);
		}

		// Set data-title attribute to the title of the slide
		if(!slide.title) {
			// no title attribute, fetch title from heading(s)
			var heading = $('hgroup', slide) || $('h1,h2,h3,h4,h5,h6', slide);

			if(heading && heading.textContent.trim()) {
				slide.setAttribute('data-title', heading.textContent);
			}
		}
		else {
			// The title attribute is set, use that
			slide.setAttribute('data-title', slide.title);
			slide.removeAttribute('title');
		}
	}

	// If there's already a hash, update current slide number...
	this.goto(location.hash.substr(1) || 0);

	// ...and keep doing so every time the hash changes
	this.onhashchange = function() {
		me.goto(location.hash.substr(1) || 0);
	};
	window.addEventListener('hashchange', this.onhashchange, false);

	if(window.name === 'projector') {
		document.body.classList.add('projector');
	}

	// Adjust the font-size when the window is resized
	addEventListener('resize', function() {
		me.adjustFontSize();
	}, false);

	// In some browsers DOMContentLoaded is too early, so try again onload
	addEventListener('load', function() {
		me.adjustFontSize();
	}, false);

	/**
		Keyboard navigation
		Ctrl+G : Go to slide...
		Ctrl+H : Show thumbnails and go to slide
		Ctrl+P : Presenter view
		(Shift instead of Ctrl works too)
	*/
	document.addEventListener('keyup', function(evt) {
		if(evt.ctrlKey || evt.shiftKey) {
			switch(evt.keyCode) {
				case 71: // G
					var slide = prompt('Which slide?');
					me.goto(+slide? slide - 1 : slide);
					break;
				case 72: // H
					if(body.classList.contains('show-thumbnails')) {
						body.classList.remove('show-thumbnails');
						body.classList.remove('headers-only');
					}
					else {
						body.classList.add('show-thumbnails');

						if(!evt.shiftKey || !evt.ctrlKey) {
							body.classList.add('headers-only');
						}

						body.addEventListener('click', function(evt) {
							var slide = evt.target;

							while(slide && !slide.classList.contains('slide')) {
								slide = slide.parentNode;
							}

							if(slide) {
								me.goto(slide.id);
								setTimeout(function() { me.adjustFontSize(); }, 1000); // for Opera
							}

							body.classList.remove('show-thumbnails');
							body.classList.remove('headers-only');
						}, false);
					}
					break;
				case 74: // J
					if(body.classList.contains('hide-elements')) {
						body.classList.remove('hide-elements');
					}
					else {
						body.classList.add('hide-elements');
					}
					break;
				case 80: // P
					// Open new window for attendee view
					me.projector = window.open(location, 'projector', 'menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes');

					// Get the focus back
					window.focus();

					// Switch this one to presenter view
					body.classList.add('presenter');
			}
		}
	}, false);

	/**
		Keyboard navigation
		Home : First slide
		End : Last slide
		Space/Up/Right arrow : Next item/slide
		Ctrl + Space/Up/Right arrow : Next slide
		Down/Left arrow : Previous item/slide
		Ctrl + Down/Left arrow : Previous slide
		(Shift instead of Ctrl works too)
	*/
	document.addEventListener('keydown', function(evt) {
		if(evt.target === body || evt.target === body.parentNode || evt.altKey) {
			if(evt.keyCode >= 32 && evt.keyCode <= 40) {
				evt.preventDefault();
			}

			switch(evt.keyCode) {
				case 33: //page up
					me.previous();
					break;
				case 34: //page down
					me.next();
					break;
				case 35: // end
					me.end();
					break;
				case 36: // home
					me.start();
					break;
				case 37: // <-
				case 38: // up arrow
					me.previous(evt.ctrlKey || evt.shiftKey);
					break;
				case 32: // space
				case 39: // ->
				case 40: // down arrow
					me.next(evt.ctrlKey || evt.shiftKey);
					break;
			}
		}
	}, false);

	// Rudimentary style[scoped] polyfill
	var scoped = $$('style[scoped]', container);

	for(var i=scoped.length; i--;) {
		var style = scoped[i],
			rulez = style.sheet.cssRules,
			parentid = style.parentNode.id || self.getSlide(style).id;

		for(var j=rulez.length; j--;) {
			var cssText = rulez[j].cssText.replace(/^|,/g, function($0) { return '#' + parentid + ' ' + $0 });

			style.sheet.deleteRule(0);
			style.sheet.insertRule(cssText, 0);
		}
	}
}

self.prototype = {
	start: function() {
		this.goto(0);
	},

	end: function() {
		this.goto(this.slides.length - 1);
	},

	/**
		@param hard {Boolean} Whether to advance to the next slide (true) or
			just the next step (which could very well be showing a list item)
	 */
	next: function(hard) {
		if(!hard && this.items.length) {
			// If there's no current, then just mark the first one as such
			if(!this.item) {
				this.items[this.item++].classList.add('current');
			}
			// Add .current to current item if it exists, otherwise advance to next slide
			else if(this.item < this.items.length) {
				classes = this.items[this.item - 1].classList; // to speed up lookups

				classes.remove('current');
				classes.add('displayed');

				this.items[this.item++].classList.add('current');
			}
			else {
				this.item = 0;
				this.next(true);
			}
		}
		else {
			this.goto(this.slide + 1);

			this.item = 0;

			// Mark all items as not displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.remove('displayed');
						this.items[i].classList.remove('current');
					}
				}
			}
		}
	},

	previous: function(hard) {
		if(!hard && this.item > 0) {
			var classes = this.items[this.item - 1].classList; // to speed up lookups

			classes.remove('current');

			if(this.item > 1) {
				classes = this.items[--this.item - 1].classList;

				classes.remove('displayed');
				classes.add('current');
			}
			else {
				this.item = 0;
			}
		}
		else {

			this.goto(this.slide - 1);

			this.item = this.items.length;

			// Mark all items as displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.add('displayed');
					}
				}

				// Mark the last one as current
				var lastItem = this.items[this.items.length - 1];

				lastItem.classList.remove('displayed');
				lastItem.classList.add('current');
			}
		}
	},

	/**
		Go to an aribtary slide
		@param which {String|Integer} Which slide (identifier or slide number)
	*/
	goto: function(which) {
		var slide;

		// We have to remove it to prevent multiple calls to goto messing up
		// our current item (and there's no point either, so we save on performance)
		window.removeEventListener('hashchange', this.onhashchange, false);

		if(which + 0 === which && which in this.slides) { // Argument is a valid slide number
			this.slide = which;

			slide = this.slides[this.slide];
			location.hash = '#' + slide.id;
		}
		else if(which + '' === which) { // Argument is a slide id
			slide = document.getElementById(which);

			if(slide) {
				this.slide = this.slides.indexOf(slide);
				location.hash = '#' + which;
			}
		}

		if(slide) { // Slide actually changed, perform any other tasks needed
			document.title = slide.getAttribute('data-title') || documentTitle;

			this.adjustFontSize();

			// Update items collection
			this.items = $$('.delayed, .delayed-children > *', this.slides[this.slide]);
			this.item = 0;

			// Tell other windows
			if(this.projector && this.projector.slideshow && this.projector.slideshow.slide != this.slide) {
				this.projector.slideshow.goto(this.slide);
			}

			if(window.opener && opener.slideshow && opener.slideshow.slide != this.slide) {
				opener.slideshow.goto(this.slide);
			}
		}

		// If you attach the listener immediately again then it will catch the event
		// We have to do it asynchronously
		var me = this;
		setTimeout(function() {
			window.addEventListener('hashchange', me.onhashchange, false);
		}, 1000);
	},

	adjustFontSize: function() {
		// Cache long lookup chains, for performance
		var bodyStyle = body.style,
			scrollRoot = document[document.documentElement.scrollHeight? 'documentElement' : 'body'],
			innerHeight = window.innerHeight,
			innerWidth = window.innerWidth,
			slide = this.slides[this.slide];

		// Clear previous styles
		bodyStyle.fontSize = '';

		if(body.classList.contains('show-thumbnails')
			|| slide.classList.contains('dont-resize')) {
			return;
		}

		for(
			var percent = 100;
			(scrollRoot.scrollHeight > innerHeight || scrollRoot.scrollWidth > innerWidth) && percent >= 35;
			percent-=5
		) {
			bodyStyle.fontSize = percent + '%';
		}

		// Individual slide

		if(slide.clientHeight && slide.clientWidth) {
			// Strange FF bug: scrollHeight doesn't work properly with overflow:hidden
			var previousStyle = slide.getAttribute('style');
			slide.style.overflow = 'auto';

			for(
				;
				(slide.scrollHeight > slide.clientHeight || slide.scrollWidth > slide.clientWidth) && percent >= 35;
				percent--
			) {
				bodyStyle.fontSize = percent + '%';
			}

			slide.setAttribute('style', previousStyle);
		}
	},

	// Is the element on the current slide?
	onCurrent: function(element) {
		var slide = self.getSlide(element);

		if(slide) {
			return '#' + slide.id === location.hash;
		}

		return false;
	}
};

/**********************************************
 * Static methods
 **********************************************/

// Helper method for plugins
self.getSlide = function(element) {
	var slide = element;

	while (slide && slide.classList && !slide.classList.contains('slide')) {
		slide = slide.parentNode;
	}

	return slide;
}

})(document.head || document.getElementsByTagName('head')[0], document.body);<|endoftext|>


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

    var website = openerp.website;

    website.EditorBar.include({
        start: function () {
            this.registerTour(new website.EditorShopTour(this));
            return this._super();
        },
    });

    website.EditorShopTour = website.Tour.extend({
        id: 'shop-tutorial',
        name: "Create a product",
        init: function (editor) {
            var self = this;
            self.steps = [
                {
                    stepId: 'welcome-shop',
                    orphan: true,
                    backdrop: true,
                    title: "Welcome to your shop",
                    content: "You successfully installed the e-commerce. This guide will help you to create your product and promote your sales.",
                    template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
                },
                {
                    stepId: 'content-menu',
                    element: '#content-menu-button',
                    placement: 'left',
                    reflex: true,
                    title: "Create your first product",
                    content: "Click here to add a new product.",
                },
                {
                    stepId: 'edit-entry',
                    element: '#create-new-product',
                    placement: 'left',
                    title: "Create a new product",
                    content: "Select 'New Product' to create it and manage its properties to boost your sales.",
                    modal: {
                        stopOnClose: true,
                        afterSubmit: 'product-page',
                    },
                },
                {
                    stepId: 'enter-name',
                    element: '.modal input[type=text]',
                    placement: 'right',
                    title: "Choose name",
                    content: "Enter a name for your new product then click 'Continue'.",

                },
                {
                    stepId: 'product-page',
                    orphan: true,
                    backdrop: true,
                    title: "New product created",
                    content: "This page contains all the information related to the new product.",
                    template: self.popover({ next: "OK" }),
                },
                {
                    stepId: 'edit-price',
                    element: '.product_price',
                    placement: 'left',
                    title: "Change the public price",
                    content: "Edit the sale price of this product by clicking on the amount. The price is the sale price used in all sale order when selling this product.",
                    template: self.popover({ next: "OK" }),
                },
                {
                    stepId: 'update-image',
                    element: '#wrap img.img:first',
                    placement: 'top',
                    title: "Update image",
                    content: "Click here to set an image describing your product.",
                    triggers: function () {
                        function registerClick () {
                            $('button.image-edit-button').one('click', function () {
                                $('#wrap img.img:first').off('hover', registerClick);
                                self.moveToNextStep();
                            });
                        }
                        $('#wrap img.img:first').on('hover', registerClick);

                    },
                },
                {
                    stepId: 'upload-image',
                    element: 'button.filepicker',
                    placement: 'left',
                    title: "Upload image",
                    content: "Click on 'Upload an image from your computer' to pick an image describing your product.",
                    template: self.popover({ next: "OK" }),
                    triggers: function () {
                        $(document).on('hide.bs.modal', function () {
                            self.moveToStep('add-block');
                        });
                    }
                },
                {
                    stepId: 'save-image',
                    element: 'button.save',
                    placement: 'right',
                    title: "Save the image",
                    content: "Click 'Save Changes' to add the image to the product decsription.",
                },
                {
                    stepId: 'add-block',
                    element: 'button[data-action=snippet]',
                    placement: 'bottom',
                    title: "Describe the product for your audience",
                    content: "Insert blocks like text-image, or gallery to fully describe the product and make your visitors want to buy this product.",
                    triggers: function () {
                        $('button[data-action=snippet]').one('click', function () {
                            self.moveToNextStep();
                        });
                    },
                },
                {
                    stepId: 'drag-big-picture',
                    element: '#website-top-navbar [data-snippet-id=big-picture].ui-draggable',
                    placement: 'bottom',
                    title: "Drag & Drop a block",
                    content: "Drag the 'Big Picture' block and drop it in your page.",
                    triggers: function () {
                        self.onSnippetDraggedAdvance('big-picture');
                    },
                },
                {
                    stepId: 'save-changes',
                    element: 'button[data-action=save]',
                    placement: 'right',
                    reflex: true,
                    title: "Save your modifications",
                    content: "Once you click on save, your product is updated.",
                },
                {
                    stepId: 'publish-product',
                    element: 'button.js_publish_btn',
                    placement: 'top',
                    reflex: true,
                    title: "Publish your product",
                    content: "Click to publish your product so your customers can see it.",
                },
            ];
            return this._super();
        },
        resume: function () {
            return (this.isCurrentStep('product-page') || this.isCurrentStep('publish-product')) && this._super();
        },
        trigger: function () {
            return (this.resume() && this.testUrl(/^\/shop\/product\/[0-9]+\//)) || this._super();
        },
    });

}());
<commit_msg>[FIX] Shop tour was broken by change<commit_after>(function () {
    'use strict';

    var website = openerp.website;

    website.EditorBar.include({
        start: function () {
            this.registerTour(new website.EditorShopTour(this));
            return this._super();
        },
    });

    website.EditorShopTour = website.Tour.extend({
        id: 'shop-tutorial',
        name: "Create a product",
        init: function (editor) {
            var self = this;
            self.steps = [
                {
                    stepId: 'welcome-shop',
                    orphan: true,
                    backdrop: true,
                    title: "Welcome to your shop",
                    content: "You successfully installed the e-commerce. This guide will help you to create your product and promote your sales.",
                    template: self.popover({ next: "Start Tutorial", end: "Skip It" }),
                },
                {
                    stepId: 'content-menu',
                    element: '#content-menu-button',
                    placement: 'left',
                    reflex: true,
                    title: "Create your first product",
                    content: "Click here to add a new product.",
                },
                {
                    stepId: 'edit-entry',
                    element: '#create-new-product',
                    placement: 'left',
                    title: "Create a new product",
                    content: "Select 'New Product' to create it and manage its properties to boost your sales.",
                    modal: {
                        stopOnClose: true,
                        afterSubmit: 'product-page',
                    },
                },
                {
                    stepId: 'enter-name',
                    element: '.modal input[type=text]',
                    placement: 'right',
                    title: "Choose name",
                    content: "Enter a name for your new product then click 'Continue'.",

                },
                {
                    stepId: 'product-page',
                    orphan: true,
                    backdrop: true,
                    title: "New product created",
                    content: "This page contains all the information related to the new product.",
                    template: self.popover({ next: "OK" }),
                },
                {
                    stepId: 'edit-price',
                    element: '.product_price',
                    placement: 'left',
                    title: "Change the public price",
                    content: "Edit the sale price of this product by clicking on the amount. The price is the sale price used in all sale order when selling this product.",
                    template: self.popover({ next: "OK" }),
                },
                {
                    stepId: 'update-image',
                    element: '#wrap img.img:first',
                    placement: 'top',
                    title: "Update image",
                    content: "Click here to set an image describing your product.",
                    triggers: function () {
                        function registerClick () {
                            $('button.image-edit-button').one('click', function () {
                                $('#wrap img.img:first').off('hover', registerClick);
                                self.moveToNextStep();
                            });
                        }
                        $('#wrap img.img:first').on('hover', registerClick);

                    },
                },
                {
                    stepId: 'upload-image',
                    element: 'button.filepicker',
                    placement: 'left',
                    title: "Upload image",
                    content: "Click on 'Upload an image from your computer' to pick an image describing your product.",
                    template: self.popover({ next: "OK" }),
                    triggers: function () {
                        $(document).on('hide.bs.modal', function () {
                            self.moveToStep('add-block');
                        });
                    }
                },
                {
                    stepId: 'save-image',
                    element: 'button.save',
                    placement: 'right',
                    title: "Save the image",
                    content: "Click 'Save Changes' to add the image to the product decsription.",
                },
                {
                    stepId: 'add-block',
                    element: 'button[data-action=snippet]',
                    placement: 'bottom',
                    title: "Describe the product for your audience",
                    content: "Insert blocks like text-image, or gallery to fully describe the product and make your visitors want to buy this product.",
                    triggers: function () {
                        $('button[data-action=snippet]').one('click', function () {
                            self.moveToNextStep();
                        });
                    },
                },
                {
                    stepId: 'drag-big-picture',
                    element: '#website-top-navbar [data-snippet-id=big-picture].ui-draggable',
                    placement: 'bottom',
                    title: "Drag & Drop a block",
                    content: "Drag the 'Big Picture' block and drop it in your page.",
                    triggers: function () {
                        self.onSnippetDraggedAdvance('big-picture');
                    },
                },
                {
                    stepId: 'save-changes',
                    element: 'button[data-action=save]',
                    placement: 'right',
                    title: "Save your modifications",
                    content: "Once you click on save, your product is updated.",
                    triggers: function () {
                        var $publish = $('button.js_publish_btn');
                        if ($publish.length > 0 && $publish.is(":visible")) {
                            self.moveToNextStep();
                        }
                    },

                },
                {
                    stepId: 'publish-product',
                    element: 'button.js_publish_btn',
                    placement: 'top',
                    reflex: true,
                    title: "Publish your product",
                    content: "Click to publish your product so your customers can see it.",
                },
            ];
            return this._super();
        },
        resume: function () {
            return (this.isCurrentStep('product-page') || this.isCurrentStep('save-changes')) && this._super();
        },
        trigger: function () {
            return (this.resume() && this.testUrl(/^\/shop\/product\/[0-9]+\//)) || this._super();
        },
    });

}());
<|endoftext|>


<commit_before>
const expect = require('chai').expect;

const RG = require('../../../client/src/battles');
const Game = require('../../../client/src/game');
const RGTest = require('../../roguetest');

const FromJSON = Game.FromJSON;

describe('RG.Game.FromJSON', () => {
    let fromJSON = null;

    beforeEach(() => {
        fromJSON = new FromJSON();
    });

    afterEach(() => {
        fromJSON = null;
    });

    it('Converts item JSON back to RG.Items', () => {
        const item1 = new RG.Item.Weapon('knife');
        const json = item1.toJSON();
        const newItem = fromJSON.createItem(json);

        expect(newItem.getName()).to.equal(item1.getName());
        expect(newItem.getType()).to.equal(item1.getType());
    });

    it('Converts level JSON back to RG.Map.Level', () => {
        const level = RGTest.createLevel('arena', 20, 20);
        const json = level.toJSON();
        const newLevel = fromJSON.createLevel(json);
        expect(newLevel.getID()).to.equal(level.getID());
    });

    it('Converts level.map JSON back to RG.Map', () => {
        const level = RGTest.createLevel('arena', 20, 20);
        const json = level.toJSON();
        const newLevel = fromJSON.createLevel(json);
        const newMap = newLevel.getMap();
        for (let x = 0; x < 20; x++) {
            expect(newMap.getCell(x, 0).isPassable(),
                `Cell ${x},0 should have wall thus not passable`,
            ).to.equal(false);
        }
    });

    it('converts level and its objects into JSON and back to object', () => {
        const level = RGTest.createLevel('arena', 10, 10);
        const actor = new RG.Actor.Rogue('Urkh!');
        actor.setType('goblin');
        const item = new RG.Item.Weapon('sword');
        level.addActor(actor, 2, 2);
        level.addItem(item, 3, 3);
        const json = level.toJSON();
        const newLevel = fromJSON.createLevel(json);

        const actors = newLevel.getActors();
        const items = newLevel.getItems();
        expect(actors).to.have.length(1);
        expect(actors[0].getName()).to.equal('Urkh!');
        expect(items).to.have.length(1);
        expect(items[0].getName()).to.equal('sword');
    });

    it('connects levels after restoring game from JSON', () => {
        const game = new RG.Game.Main();
        const level1 = RGTest.createLevel('arena', 10, 10);
        const level2 = RGTest.createLevel('arena', 10, 10);
        const s1 = new RG.Element.Stairs(true, level1, level2);
        const s2 = new RG.Element.Stairs(false, level2, level1);
        s1.connect(s2);
        level1.addStairs(s1, 1, 1);
        level2.addStairs(s2, 2, 2);
        game.addLevel(level1);
        game.addLevel(level2);

        const json = game.toJSON();
        const newGame = fromJSON.createGame(json);
        const newLevels = newGame.getLevels();
        expect(newLevels).to.have.length(2);

        const newS1 = newLevels[0].getStairs()[0];
        const newS2 = newLevels[1].getStairs()[0];
        const id1 = newLevels[0].getID();
        const id2 = newLevels[1].getID();

        expect(newS1.getTargetLevel(), 'Target level must be set').to.exist;
        expect(newS2.getTargetLevel(), 'Target level must be set').to.exist;

        expect(newS1.getTargetLevel().getID()).to.equal(id2);
        expect(newS2.getTargetLevel().getID()).to.equal(id1);

    });

    it('converts full game into JSON and back to object', () => {
        const game = new RG.Game.Main();
        const level = RGTest.createLevel('arena', 10, 10);
        const player = new RG.Actor.Rogue('MyPlayer');
        player.setType('player');
        player.setIsPlayer(true);
        game.addLevel(level);
        game.addPlayer(player);
        const json = game.toJSON();
        const newGame = fromJSON.createGame(json);
        const newPlayer = newGame.getPlayer();
        expect(newPlayer.getName()).to.equal('MyPlayer');
    });

    /* TODO Decide on places serialization and add this test.
    it('converts a game containing world into JSON and back', () => {
        const fact = new RG.Factory.World();
        const game = new RG.Game.Main();
        const conf = {name: 'Ice Kingdom', nAreas: 1,
            area: [{ name: 'Area51', maxX: 1, maxY: 1}]
        };
        const world = fact.createWorld(conf);
        const player = new RG.Actor.Rogue('MyPlayer');
        player.setType('player');
        player.setIsPlayer(true);
        game.addPlace(world);
        game.addPlayer(player, {place: 'Ice Kingdom'});
        const json = game.toJSON();
        const newGame = fromJSON.createGame(json);
        expect(Object.keys(newGame.getPlaces())).to.have.length(1);
    });
    */

});
<commit_msg>Added tests to check that idCount is stored.<commit_after>
const expect = require('chai').expect;

const RG = require('../../../client/src/battles');
const Game = require('../../../client/src/game');
const RGTest = require('../../roguetest');

const FromJSON = Game.FromJSON;

describe('RG.Game.FromJSON', () => {
    let fromJSON = null;

    beforeEach(() => {
        fromJSON = new FromJSON();
    });

    afterEach(() => {
        fromJSON = null;
    });

    it('Converts item JSON back to RG.Items', () => {
        const item1 = new RG.Item.Weapon('knife');
        const json = item1.toJSON();
        const newItem = fromJSON.createItem(json);

        expect(newItem.getName()).to.equal(item1.getName());
        expect(newItem.getType()).to.equal(item1.getType());
    });

    it('Converts level JSON back to RG.Map.Level', () => {
        const level = RGTest.createLevel('arena', 20, 20);
        const json = level.toJSON();
        const newLevel = fromJSON.createLevel(json);
        expect(newLevel.getID()).to.equal(level.getID());
    });

    it('Converts level.map JSON back to RG.Map', () => {
        const level = RGTest.createLevel('arena', 20, 20);
        const json = level.toJSON();
        const newLevel = fromJSON.createLevel(json);
        const newMap = newLevel.getMap();
        for (let x = 0; x < 20; x++) {
            expect(newMap.getCell(x, 0).isPassable(),
                `Cell ${x},0 should have wall thus not passable`,
            ).to.equal(false);
        }
    });

    it('converts level and its objects into JSON and back to object', () => {
        const level = RGTest.createLevel('arena', 10, 10);
        const actor = new RG.Actor.Rogue('Urkh!');
        actor.setType('goblin');
        const item = new RG.Item.Weapon('sword');
        level.addActor(actor, 2, 2);
        level.addItem(item, 3, 3);
        const json = level.toJSON();
        const newLevel = fromJSON.createLevel(json);

        const actors = newLevel.getActors();
        const items = newLevel.getItems();
        expect(actors).to.have.length(1);
        expect(actors[0].getName()).to.equal('Urkh!');
        expect(items).to.have.length(1);
        expect(items[0].getName()).to.equal('sword');
    });

    it('connects levels after restoring game from JSON', () => {
        const game = new RG.Game.Main();
        const level1 = RGTest.createLevel('arena', 10, 10);
        const level2 = RGTest.createLevel('arena', 10, 10);
        const s1 = new RG.Element.Stairs(true, level1, level2);
        const s2 = new RG.Element.Stairs(false, level2, level1);
        s1.connect(s2);
        level1.addStairs(s1, 1, 1);
        level2.addStairs(s2, 2, 2);
        game.addLevel(level1);
        game.addLevel(level2);

        const json = game.toJSON();
        const newGame = fromJSON.createGame(json);
        const newLevels = newGame.getLevels();
        expect(newLevels).to.have.length(2);

        const newS1 = newLevels[0].getStairs()[0];
        const newS2 = newLevels[1].getStairs()[0];
        const id1 = newLevels[0].getID();
        const id2 = newLevels[1].getID();

        expect(newS1.getTargetLevel(), 'Target level must be set').to.exist;
        expect(newS2.getTargetLevel(), 'Target level must be set').to.exist;

        expect(newS1.getTargetLevel().getID()).to.equal(id2);
        expect(newS2.getTargetLevel().getID()).to.equal(id1);

    });

    it('converts full game into JSON and back to object', () => {
        const game = new RG.Game.Main();
        const level = RGTest.createLevel('arena', 10, 10);
        const player = new RG.Actor.Rogue('MyPlayer');
        player.setType('player');
        player.setIsPlayer(true);
        game.addLevel(level);
        game.addPlayer(player);
        const json = game.toJSON();
        const newGame = fromJSON.createGame(json);
        const newPlayer = newGame.getPlayer();
        expect(newPlayer.getName()).to.equal('MyPlayer');
    });

    it('converts a game containing world into JSON and back', () => {
        const fact = new RG.Factory.World();
        const game = new RG.Game.Main();
        const conf = {name: 'Ice Kingdom', nAreas: 1,
            area: [{ name: 'Area51', maxX: 1, maxY: 1}]
        };
        console.log('Started Factory.World');
        const world = fact.createWorld(conf);
        const player = new RG.Actor.Rogue('MyPlayer');
        player.setType('player');
        player.setIsPlayer(true);
        game.addPlace(world);
        game.addPlayer(player, {place: 'Ice Kingdom'});
        let numLevels = game.getLevels();
        expect(numLevels).to.have.length(1);
        const json = game.toJSON();
        expect(json.lastLevelID).to.equal(RG.Map.Level.prototype.idCount);
        expect(json.lastEntityID).to.equal(RG.Entity.prototype.idCount);

        console.log('Creating new game now');
        // Create new game from JSON
        const newGame = fromJSON.createGame(json);
        expect(Object.keys(newGame.getPlaces())).to.have.length(1);
        numLevels = newGame.getLevels();
        console.log(JSON.stringify(numLevels.map(l => l.getID())));
        expect(numLevels).to.have.length(1);
    });
});
<|endoftext|>


<commit_before>import Ember from "ember";

export default Ember.Mixin.create({
  activate: function(){
    this._super();
    window.scrollTo(0,0);
  }
});
<commit_msg>disable window.scroll during tests bc it is super annoying<commit_after>import Ember from "ember";

export default Ember.Mixin.create({
  activate: function(){
    this._super();
    if (!Ember.testing) {
      window.scrollTo(0,0);
    }
  }
});
<|endoftext|>


<commit_before>/**
 * Admin tracking opt in/out e2e tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * WordPress dependencies
 */
import { activatePlugin, visitAdminPage } from '@wordpress/e2e-test-utils';

/**
 * Internal dependencies
 */
import {
	deactivateUtilityPlugins,
	pageWait,
	resetSiteKit,
	setSearchConsoleProperty,
	setupSiteKit,
} from '../utils';

async function toggleOptIn() {
	await page.waitForSelector( '#googlesitekit-opt-in' );
	await expect( page ).toClick( '#googlesitekit-opt-in' );
	await page.waitForResponse( ( res ) => res.url().match( 'wp/v2/users/me' ) );
}

describe( 'management of tracking opt-in/out via settings page', () => {
	beforeEach( async () => {
		await activatePlugin( 'e2e-tests-proxy-auth-plugin' );
		await activatePlugin( 'e2e-tests-site-verification-plugin' );
		await setSearchConsoleProperty();

		await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );
		await page.waitForSelector( '.mdc-tab-bar button.mdc-tab' );
		await expect( page ).toMatchElement( 'button.mdc-tab', { text: 'Admin Settings' } );

		await pageWait(); // Delay the next steps.

		// Click on Admin Settings Tab.
		await Promise.all( [
			page.waitForSelector( '#googlesitekit-opt-in' ),
			expect( page ).toClick( 'button.mdc-tab', { text: 'Admin Settings' } ),
		] );
	} );

	afterEach( async () => {
		await resetSiteKit();
		await deactivateUtilityPlugins();
	} );

	it( 'should be opted-out by default', async () => {
		await expect( page ).not.toHaveTracking();
		expect( await page.$eval( '#googlesitekit-opt-in', ( el ) => el.checked ) ).toBe( false );
	} );

	it( 'should have tracking code when opted in', async () => {
		await expect( page ).not.toHaveTracking();

		// Make sure the script tags are not yet loaded on the page.
		await expect( page ).not.toMatchElement( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );

		// Opt-in to tracking to ensure the checkbox is selected.
		await toggleOptIn();

		expect( await page.$eval( '#googlesitekit-opt-in', ( el ) => el.checked ) ).toBe( true );

		await expect( page ).toHaveTracking();
		// Ensure the script tags are injected into the page if they weren't
		// loaded already.
		await page.waitForSelector( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );

		// Ensure tag manager script tag exists.
		await expect( page ).toMatchElement( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );

		// Opt-out again.
		await toggleOptIn();
	} );

	it( 'should check opt-in box when clicked', async () => {
		await toggleOptIn();

		await page.waitForSelector( '.mdc-checkbox.mdc-checkbox--selected #googlesitekit-opt-in' );

		// Ensure checked checkbox exists.
		await expect( page ).toMatchElement( '.mdc-checkbox.mdc-checkbox--selected #googlesitekit-opt-in' );
	} );

	it( 'should uncheck opt-in box when clicked', async () => {
		// Opt-in to tracking to ensure the checkbox is selected.
		await toggleOptIn();

		// Uncheck the checkbox.
		await toggleOptIn();
		await page.waitForSelector( '.mdc-checkbox:not(.mdc-checkbox--selected) #googlesitekit-opt-in' );

		// Ensure unchecked checkbox exists.
		await expect( page ).toMatchElement( '.mdc-checkbox:not(.mdc-checkbox--selected) #googlesitekit-opt-in' );
	} );

	it( 'should not have tracking code when not opted in', async () => {
		// Ensure unchecked checkbox exists.
		await expect( page ).toMatchElement( '.mdc-checkbox:not(.mdc-checkbox--selected) #googlesitekit-opt-in' );

		// Ensure no analytics script tag exists.
		await expect( page ).not.toMatchElement( 'script[src^="https://www.google-analytics.com/analytics.js"]' );

		await expect( page ).not.toHaveTracking();
		// Ensure no tag manager script exists.
		await expect( page ).not.toMatchElement( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );
	} );
} );

describe( 'initialization on load for Site Kit screens', () => {
	describe( 'splash page', () => {
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await page.reload();

			await expect( page ).toHaveTracking();
		} );
	} );

	describe( 'settings page', () => {
		beforeEach( async () => await setupSiteKit() );
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );

			await expect( page ).toHaveTracking();
		} );
	} );

	describe( 'Site Kit dashboard', () => {
		beforeEach( async () => await setupSiteKit() );

		afterEach( async () => {
			await resetSiteKit();
			await deactivateUtilityPlugins();
		} );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-dashboard' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'admin.php', 'page=googlesitekit-dashboard' );

			await expect( page ).toHaveTracking();
		} );
	} );

	describe( 'module pages', () => {
		beforeEach( async () => await setupSiteKit() );

		afterEach( async () => {
			await resetSiteKit();
			await deactivateUtilityPlugins();
		} );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-module-search-console' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'admin.php', 'page=googlesitekit-module-search-console' );

			await expect( page ).toHaveTracking();
		} );
	} );
} );

describe( 'initialization on load for non-Site Kit screens', () => {
	describe( 'plugins page', () => {
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'plugins.php' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'does not load tracking if opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'plugins.php' );

			await expect( page ).not.toHaveTracking();
		} );
	} );

	describe( 'WordPress dashboard', () => {
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'index.php' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'does not load tracking if opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'index.php' );

			await expect( page ).not.toHaveTracking();
		} );
	} );
} );
<commit_msg>Use Promise.all again in toggleOptIn.<commit_after>/**
 * Admin tracking opt in/out e2e tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * WordPress dependencies
 */
import { activatePlugin, visitAdminPage } from '@wordpress/e2e-test-utils';

/**
 * Internal dependencies
 */
import {
	deactivateUtilityPlugins,
	pageWait,
	resetSiteKit,
	setSearchConsoleProperty,
	setupSiteKit,
} from '../utils';

async function toggleOptIn() {
	await page.waitForSelector( '#googlesitekit-opt-in' );
	await Promise.all( [
		page.waitForResponse( ( res ) => res.url().match( 'wp/v2/users/me' ) ),
		expect( page ).toClick( '#googlesitekit-opt-in' ),
	] );
}

describe( 'management of tracking opt-in/out via settings page', () => {
	beforeEach( async () => {
		await activatePlugin( 'e2e-tests-proxy-auth-plugin' );
		await activatePlugin( 'e2e-tests-site-verification-plugin' );
		await setSearchConsoleProperty();

		await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );
		await page.waitForSelector( '.mdc-tab-bar button.mdc-tab' );
		await expect( page ).toMatchElement( 'button.mdc-tab', { text: 'Admin Settings' } );

		await pageWait(); // Delay the next steps.

		// Click on Admin Settings Tab.
		await Promise.all( [
			page.waitForSelector( '#googlesitekit-opt-in' ),
			expect( page ).toClick( 'button.mdc-tab', { text: 'Admin Settings' } ),
		] );
	} );

	afterEach( async () => {
		await resetSiteKit();
		await deactivateUtilityPlugins();
	} );

	it( 'should be opted-out by default', async () => {
		await expect( page ).not.toHaveTracking();
		expect( await page.$eval( '#googlesitekit-opt-in', ( el ) => el.checked ) ).toBe( false );
	} );

	it( 'should have tracking code when opted in', async () => {
		await expect( page ).not.toHaveTracking();

		// Make sure the script tags are not yet loaded on the page.
		await expect( page ).not.toMatchElement( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );

		// Opt-in to tracking to ensure the checkbox is selected.
		await toggleOptIn();

		expect( await page.$eval( '#googlesitekit-opt-in', ( el ) => el.checked ) ).toBe( true );

		await expect( page ).toHaveTracking();
		// Ensure the script tags are injected into the page if they weren't
		// loaded already.
		await page.waitForSelector( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );

		// Ensure tag manager script tag exists.
		await expect( page ).toMatchElement( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );

		// Opt-out again.
		await toggleOptIn();
	} );

	it( 'should check opt-in box when clicked', async () => {
		await toggleOptIn();

		await page.waitForSelector( '.mdc-checkbox.mdc-checkbox--selected #googlesitekit-opt-in' );

		// Ensure checked checkbox exists.
		await expect( page ).toMatchElement( '.mdc-checkbox.mdc-checkbox--selected #googlesitekit-opt-in' );
	} );

	it( 'should uncheck opt-in box when clicked', async () => {
		// Opt-in to tracking to ensure the checkbox is selected.
		await toggleOptIn();

		// Uncheck the checkbox.
		await toggleOptIn();
		await page.waitForSelector( '.mdc-checkbox:not(.mdc-checkbox--selected) #googlesitekit-opt-in' );

		// Ensure unchecked checkbox exists.
		await expect( page ).toMatchElement( '.mdc-checkbox:not(.mdc-checkbox--selected) #googlesitekit-opt-in' );
	} );

	it( 'should not have tracking code when not opted in', async () => {
		// Ensure unchecked checkbox exists.
		await expect( page ).toMatchElement( '.mdc-checkbox:not(.mdc-checkbox--selected) #googlesitekit-opt-in' );

		// Ensure no analytics script tag exists.
		await expect( page ).not.toMatchElement( 'script[src^="https://www.google-analytics.com/analytics.js"]' );

		await expect( page ).not.toHaveTracking();
		// Ensure no tag manager script exists.
		await expect( page ).not.toMatchElement( 'script[src^="https://www.googletagmanager.com/gtag/js?id=UA-130569087-3"]' );
	} );
} );

describe( 'initialization on load for Site Kit screens', () => {
	describe( 'splash page', () => {
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await page.reload();

			await expect( page ).toHaveTracking();
		} );
	} );

	describe( 'settings page', () => {
		beforeEach( async () => await setupSiteKit() );
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );

			await expect( page ).toHaveTracking();
		} );
	} );

	describe( 'Site Kit dashboard', () => {
		beforeEach( async () => await setupSiteKit() );

		afterEach( async () => {
			await resetSiteKit();
			await deactivateUtilityPlugins();
		} );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-dashboard' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'admin.php', 'page=googlesitekit-dashboard' );

			await expect( page ).toHaveTracking();
		} );
	} );

	describe( 'module pages', () => {
		beforeEach( async () => await setupSiteKit() );

		afterEach( async () => {
			await resetSiteKit();
			await deactivateUtilityPlugins();
		} );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-module-search-console' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'loads tracking when opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'admin.php', 'page=googlesitekit-module-search-console' );

			await expect( page ).toHaveTracking();
		} );
	} );
} );

describe( 'initialization on load for non-Site Kit screens', () => {
	describe( 'plugins page', () => {
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'plugins.php' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'does not load tracking if opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'plugins.php' );

			await expect( page ).not.toHaveTracking();
		} );
	} );

	describe( 'WordPress dashboard', () => {
		afterEach( async () => await resetSiteKit() );

		it( 'does not load tracking if not opted-in', async () => {
			await visitAdminPage( 'index.php' );

			await expect( page ).not.toHaveTracking();
		} );

		it( 'does not load tracking if opted-in', async () => {
			await visitAdminPage( 'admin.php', 'page=googlesitekit-splash' );
			await toggleOptIn();
			await visitAdminPage( 'index.php' );

			await expect( page ).not.toHaveTracking();
		} );
	} );
} );
<|endoftext|>


<commit_before>import QUnit from 'qunit';
import MockServer from './src/mock-server';
import MockSocket from './src/mock-socket';
import socketEventMessage from './src/helpers/message-event';

var socketUrl = 'ws://localhost';
var mockSocket;
var mockServer;
var done;

QUnit.module('MockSocket inherits EventTarget', {
  setup: function() {
    mockServer = new MockServer(socketUrl);
    mockServer.on('close', function() {
      done();
    });
    mockSocket = new MockSocket(socketUrl);
  }
});

QUnit.test('has all the required methods', function(assert) {
  assert.expect(3);
  done = assert.async();

  assert.ok(mockSocket.addEventListener);
  assert.ok(mockSocket.removeEventListener);
  assert.ok(mockSocket.dispatchEvent);
  mockServer.close();
});

QUnit.test('adding/removing "message" event listeners works', function(assert) {
  var eventMessage = socketEventMessage('message', 'testing', socketUrl);
  var fired = 0;
  var handler1 = function() {
    fired++;
  };
  var handler2 = function() {
    fired++;
  };

  assert.expect(3);
  done = assert.async();

  mockSocket.addEventListener('message', handler1);
  mockSocket.addEventListener('message', handler2);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 2, '2 handlers should be executed');

  fired = 0;
  mockSocket.removeEventListener('message', handler1);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 1, 'only 1 handler should be executed');

  fired = 0;
  mockSocket.removeEventListener('message', handler2);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 0, 'no handlers should be executed');

  mockServer.close();
});
<commit_msg>Add test to demonstrate that event handlers are shared between different sockets<commit_after>import QUnit from 'qunit';
import MockServer from './src/mock-server';
import MockSocket from './src/mock-socket';
import socketEventMessage from './src/helpers/message-event';

var socketUrl = 'ws://localhost';
var mockSocket;
var mockServer;
var done;

QUnit.module('MockSocket inherits EventTarget', {
  setup: function() {
    mockServer = new MockServer(socketUrl);
    mockServer.on('close', function() {
      done();
    });
    mockSocket = new MockSocket(socketUrl);
  }
});

QUnit.test('has all the required methods', function(assert) {
  assert.expect(3);
  done = assert.async();

  assert.ok(mockSocket.addEventListener);
  assert.ok(mockSocket.removeEventListener);
  assert.ok(mockSocket.dispatchEvent);
  mockServer.close();
});

QUnit.test('adding/removing "message" event listeners works', function(assert) {
  var eventMessage = socketEventMessage('message', 'testing', socketUrl);
  var fired = 0;
  var handler1 = function() {
    fired++;
  };
  var handler2 = function() {
    fired++;
  };

  assert.expect(3);
  done = assert.async();

  mockSocket.addEventListener('message', handler1);
  mockSocket.addEventListener('message', handler2);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 2, '2 handlers should be executed');

  fired = 0;
  mockSocket.removeEventListener('message', handler1);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 1, 'only 1 handler should be executed');

  fired = 0;
  mockSocket.removeEventListener('message', handler2);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 0, 'no handlers should be executed');

  mockServer.close();
});

QUnit.test('sockets to different URLs should not share events', assert => {
  let socketUrl2 = 'ws://localhost/2';
  let mockServer2 = new MockServer(socketUrl2);
  mockServer2.on('close', () => {
    mockServer.close();
  });
  let mockSocket2 = new MockSocket(socketUrl2);
  let eventMessage = socketEventMessage('message', 'testing', socketUrl);
  let fired = 0;
  let handler = () => fired++;

  assert.expect(1);
  done = assert.async();

  mockSocket.addEventListener('message', handler);
  mockSocket2.addEventListener('message', handler);
  mockSocket.dispatchEvent(eventMessage);
  assert.equal(fired, 1, 'only 1 handler should be executed');
  mockServer2.close();
});
<|endoftext|>


<commit_before>var Slowparse = (function() {
  function ParseError(parseInfo) {
    this.name = "ParseError";
    this.message = parseInfo.type;
    this.parseInfo = parseInfo;
  }

  ParseError.prototype = Error.prototype;

  function Stream(text) {
    this.text = text;
    this.pos = 0;
    this.tokenStart = 0;
  }

  Stream.prototype = {
    peek: function() {
      return this.text[this.pos];
    },
    next: function() {
      if (!this.end())
        return this.text[this.pos++];
    },
    end: function() {
      return (this.pos == this.text.length);
    },
    eat: function(matcher) {
      if (this.peek().match(matcher))
        return this.next();
    },
    eatWhile: function(matcher) {
      var wereAnyEaten = false;
      while (!this.end()) {
        if (this.eat(matcher))
          wereAnyEaten = true;
        else
          return wereAnyEaten;
      }
    },
    eatSpace: function() {
      return this.eatWhile(/[\s\n]/);
    },
    makeToken: function() {
      if (this.pos == this.tokenStart)
        return null;
      var token = {
        value: this.text.slice(this.tokenStart, this.pos),
        interval: {
          start: this.tokenStart,
          end: this.pos
        }
      };
      this.tokenStart = this.pos;
      return token;
    },
  };

  function parseHTML(html, domBuilder) {
    var stream = new Stream(html);
    var error = null;

    var helpers = {
      buildTextNode: function() {
        var token = stream.makeToken();
        if (token) {
          domBuilder.text(token.value, token.interval);
        }
      },
    };

    var modes = {
      text: function() {
        while (!stream.end()) {
          if (stream.peek() == '<') {
            helpers.buildTextNode();
            modes.startTag();
          } else
            stream.next();
        }

        helpers.buildTextNode();

        if (domBuilder.currentNode != domBuilder.fragment)
          throw new ParseError({
            type: "UNCLOSED_TAG",
            node: domBuilder.currentNode,
            position: stream.pos
          });
      },
      startTag: function() {
        if (stream.next() != '<')
          throw new Error('assertion failed, expected to be on "<"');

        stream.eatWhile(/[A-Za-z\/]/);
        var token = stream.makeToken();
        var tagName = token.value.slice(1);
        if (tagName[0] == '/') {
          domBuilder.currentNode.parseInfo.closeTag = {
            start: token.interval.start
          };
          if (tagName.slice(1).toLowerCase() !=
              domBuilder.currentNode.nodeName.toLowerCase())
            throw new Error("TODO: parse err for unmatching close tag");
          modes.endCloseTag();
        } else {
          if (!(tagName && tagName.match(/^[A-Za-z]+$/)))
            throw new ParseError({
              type: "INVALID_TAG_NAME",
              value: tagName,
              position: token.interval.start + 1
            });
          domBuilder.pushElement(tagName, {
            openTag: {
              start: token.interval.start
            }
          });

          if (!stream.end())
            modes.endOpenTag();
        }
      },
      quotedAttributeValue: function() {
        stream.eatSpace();
        stream.makeToken();
        if (stream.next() != '"')
          throw new Error("TODO: unquoted attributes are unimplemented");
        stream.eatWhile(/[^"]/);
        if (stream.next() != '"')
          throw new Error("TODO: parse error for unterminated attr value");
      },
      endCloseTag: function() {
        stream.eatSpace();
        if (stream.next() != '>') {
          throw new Error("TODO: parse error for garbage in close tag");
        }
        var end = stream.makeToken().interval.end;
        domBuilder.currentNode.parseInfo.closeTag.end = end;
        domBuilder.popElement();
      },
      attribute: function() {
        var nameTok = stream.makeToken();
        stream.eatSpace();
        if (stream.peek() == '=') {
          stream.next();
          modes.quotedAttributeValue();
          var valueTok = stream.makeToken();
          domBuilder.attribute(nameTok.value, valueTok.value.slice(1, -1), {
            name: nameTok.interval,
            value: valueTok.interval
          });
        } else
          throw new Error("TODO: boolean attributes are unimplemented");
      },
      endOpenTag: function() {
        while (!stream.end()) {
          if (stream.eatWhile(/[A-Za-z]/)) {
            modes.attribute();
          } else if (stream.eatSpace()) {
            stream.makeToken();
          } else if (stream.peek() == '>') {
            stream.next();
            var end = stream.makeToken().interval.end;
            domBuilder.currentNode.parseInfo.openTag.end = end;
            return;
          } else
            throw new Error("TODO: don't know what to do with " +
                            stream.peek());
        }
      }
    };

    try {
      modes.text();
    } catch (e) {
      if (e.parseInfo) {
        error = e.parseInfo;
      } else
        throw e;
    }

    return {
      document: domBuilder.fragment,
      error: error
    };
  }

  function DOMBuilder(document) {
    this.document = document;
    this.fragment = document.createDocumentFragment();
    this.currentNode = this.fragment;
  }

  DOMBuilder.prototype = {
    pushElement: function(tagName, parseInfo) {
      var node = this.document.createElement(tagName);
      node.parseInfo = parseInfo;
      this.currentNode.appendChild(node);
      this.currentNode = node;
    },
    popElement: function() {
      this.currentNode = this.currentNode.parentNode;
    },
    attribute: function(name, value, parseInfo) {
      var attrNode = this.document.createAttribute(name);
      attrNode.parseInfo = parseInfo;
      attrNode.nodeValue = value;
      this.currentNode.attributes.setNamedItem(attrNode);
    },
    text: function(text, parseInfo) {
      var textNode = this.document.createTextNode(text);
      textNode.parseInfo = parseInfo;
      this.currentNode.appendChild(textNode);
    }
  };

  var Slowparse = {
    HTML: function(document, html) {
      return parseHTML(html, new DOMBuilder(document));
    }
  };

  return Slowparse;
})();
<commit_msg>more TODOs<commit_after>var Slowparse = (function() {
  function ParseError(parseInfo) {
    this.name = "ParseError";
    this.message = parseInfo.type;
    this.parseInfo = parseInfo;
  }

  ParseError.prototype = Error.prototype;

  function Stream(text) {
    this.text = text;
    this.pos = 0;
    this.tokenStart = 0;
  }

  Stream.prototype = {
    peek: function() {
      return this.text[this.pos];
    },
    next: function() {
      if (!this.end())
        return this.text[this.pos++];
    },
    end: function() {
      return (this.pos == this.text.length);
    },
    eat: function(matcher) {
      if (this.peek().match(matcher))
        return this.next();
    },
    eatWhile: function(matcher) {
      var wereAnyEaten = false;
      while (!this.end()) {
        if (this.eat(matcher))
          wereAnyEaten = true;
        else
          return wereAnyEaten;
      }
    },
    eatSpace: function() {
      return this.eatWhile(/[\s\n]/);
    },
    makeToken: function() {
      if (this.pos == this.tokenStart)
        return null;
      var token = {
        value: this.text.slice(this.tokenStart, this.pos),
        interval: {
          start: this.tokenStart,
          end: this.pos
        }
      };
      this.tokenStart = this.pos;
      return token;
    },
  };

  function parseHTML(html, domBuilder) {
    var stream = new Stream(html);
    var error = null;

    var helpers = {
      buildTextNode: function() {
        var token = stream.makeToken();
        if (token) {
          domBuilder.text(token.value, token.interval);
        }
      },
    };

    var modes = {
      text: function() {
        while (!stream.end()) {
          if (stream.peek() == '<') {
            helpers.buildTextNode();
            modes.startTag();
          } else
            stream.next();
        }

        helpers.buildTextNode();

        if (domBuilder.currentNode != domBuilder.fragment)
          throw new ParseError({
            type: "UNCLOSED_TAG",
            node: domBuilder.currentNode,
            position: stream.pos
          });
      },
      startTag: function() {
        if (stream.next() != '<')
          throw new Error('assertion failed, expected to be on "<"');

        stream.eatWhile(/[A-Za-z\/]/);
        var token = stream.makeToken();
        var tagName = token.value.slice(1);
        if (tagName[0] == '/') {
          domBuilder.currentNode.parseInfo.closeTag = {
            start: token.interval.start
          };
          if (tagName.slice(1).toLowerCase() !=
              domBuilder.currentNode.nodeName.toLowerCase())
            throw new Error("TODO: parse err for unmatching close tag");
          modes.endCloseTag();
        } else {
          if (!(tagName && tagName.match(/^[A-Za-z]+$/)))
            throw new ParseError({
              type: "INVALID_TAG_NAME",
              value: tagName,
              position: token.interval.start + 1
            });
          domBuilder.pushElement(tagName, {
            openTag: {
              start: token.interval.start
            }
          });

          if (!stream.end())
            modes.endOpenTag();
        }
      },
      quotedAttributeValue: function() {
        stream.eatSpace();
        stream.makeToken();
        if (stream.next() != '"')
          throw new Error("TODO: unquoted attributes are unimplemented");
        stream.eatWhile(/[^"]/);
        if (stream.next() != '"')
          throw new Error("TODO: parse error for unterminated attr value");
      },
      endCloseTag: function() {
        stream.eatSpace();
        if (stream.next() != '>') {
          if (stream.end())
            throw new Error("TODO: parse error for unterminated close tag");
          else
            throw new Error("TODO: parse error for garbage in close tag");
        }
        var end = stream.makeToken().interval.end;
        domBuilder.currentNode.parseInfo.closeTag.end = end;
        domBuilder.popElement();
      },
      attribute: function() {
        var nameTok = stream.makeToken();
        stream.eatSpace();
        if (stream.peek() == '=') {
          stream.next();
          modes.quotedAttributeValue();
          var valueTok = stream.makeToken();
          domBuilder.attribute(nameTok.value, valueTok.value.slice(1, -1), {
            name: nameTok.interval,
            value: valueTok.interval
          });
        } else
          throw new Error("TODO: boolean attributes are unimplemented");
      },
      endOpenTag: function() {
        while (!stream.end()) {
          if (stream.eatWhile(/[A-Za-z]/)) {
            modes.attribute();
          } else if (stream.eatSpace()) {
            stream.makeToken();
          } else if (stream.peek() == '>') {
            stream.next();
            var end = stream.makeToken().interval.end;
            domBuilder.currentNode.parseInfo.openTag.end = end;
            return;
          } else
            throw new Error("TODO: don't know what to do with " +
                            stream.peek());
        }
      }
    };

    try {
      modes.text();
    } catch (e) {
      if (e.parseInfo) {
        error = e.parseInfo;
      } else
        throw e;
    }

    return {
      document: domBuilder.fragment,
      error: error
    };
  }

  function DOMBuilder(document) {
    this.document = document;
    this.fragment = document.createDocumentFragment();
    this.currentNode = this.fragment;
  }

  DOMBuilder.prototype = {
    pushElement: function(tagName, parseInfo) {
      var node = this.document.createElement(tagName);
      node.parseInfo = parseInfo;
      this.currentNode.appendChild(node);
      this.currentNode = node;
    },
    popElement: function() {
      this.currentNode = this.currentNode.parentNode;
    },
    attribute: function(name, value, parseInfo) {
      var attrNode = this.document.createAttribute(name);
      attrNode.parseInfo = parseInfo;
      attrNode.nodeValue = value;
      this.currentNode.attributes.setNamedItem(attrNode);
    },
    text: function(text, parseInfo) {
      var textNode = this.document.createTextNode(text);
      textNode.parseInfo = parseInfo;
      this.currentNode.appendChild(textNode);
    }
  };

  var Slowparse = {
    HTML: function(document, html) {
      return parseHTML(html, new DOMBuilder(document));
    }
  };

  return Slowparse;
})();
<|endoftext|>


<commit_before>/*
 * slush-atom-shell
 * https://github.com/paulohp/slush-atom-shell
 *
 * Copyright (c) 2014, Paulo Pires
 * Licensed under the MIT license.
 */

'use strict';

var gulp = require('gulp'),
    install = require('gulp-install'),
    conflict = require('gulp-conflict'),
    template = require('gulp-template'),
    rename = require('gulp-rename'),
    downloadatomshell = require('gulp-download-atom-shell'),
    _ = require('underscore.string'),
    inquirer = require('inquirer');

gulp.task('default', function (done) {
    var prompts = [{
        type: 'input',
        name: 'appName',
        message: 'What is the name of your generator?',
        default: gulp.args.join(' ')
    }, {
        type: 'input',
        name: 'appDescription',
        message: 'What is the description for your generator?'
    }, {
        type: 'confirm',
        name: 'moveon',
        message: 'Continue?'
    }];
    //Ask
    inquirer.prompt(prompts,
        function (answers) {
            if (!answers.moveon) {
                return done();
            }
            answers.appNameSlug = _.slugify(answers.appName);
            gulp.src(__dirname + '/templates/**')
                .pipe(template(answers))
                .pipe(rename(function (file) {
                    if (file.basename[0] === '_') {
                        file.basename = '.' + file.basename.slice(1);
                    }
                }))
                .pipe(conflict('./'))
                .pipe(gulp.dest('./'))
                .pipe(install())
                .on('end', function () {
                    done();
                });
        });
});

gulp.task('downloadatomshell', function(){
    downloadatomshell({
      version: '0.12.5',
      outputDir: __dirname + 'templates/binaries'
    });
});

gulp.task('default', ['downloadatomshell']);
<commit_msg>fix path to directory<commit_after>/*
 * slush-atom-shell
 * https://github.com/paulohp/slush-atom-shell
 *
 * Copyright (c) 2014, Paulo Pires
 * Licensed under the MIT license.
 */

'use strict';

var gulp = require('gulp'),
    install = require('gulp-install'),
    conflict = require('gulp-conflict'),
    template = require('gulp-template'),
    rename = require('gulp-rename'),
    downloadatomshell = require('gulp-download-atom-shell'),
    _ = require('underscore.string'),
    inquirer = require('inquirer');

gulp.task('default', function (done) {
    var prompts = [{
        type: 'input',
        name: 'appName',
        message: 'What is the name of your generator?',
        default: gulp.args.join(' ')
    }, {
        type: 'input',
        name: 'appDescription',
        message: 'What is the description for your generator?'
    }, {
        type: 'confirm',
        name: 'moveon',
        message: 'Continue?'
    }];
    //Ask
    inquirer.prompt(prompts,
        function (answers) {
            if (!answers.moveon) {
                return done();
            }
            answers.appNameSlug = _.slugify(answers.appName);
            gulp.src(__dirname + '/templates/**')
                .pipe(template(answers))
                .pipe(rename(function (file) {
                    if (file.basename[0] === '_') {
                        file.basename = '.' + file.basename.slice(1);
                    }
                }))
                .pipe(conflict('./'))
                .pipe(gulp.dest('./'))
                .pipe(install())
                .on('end', function () {
                    done();
                });
        });
});

gulp.task('downloadatomshell', function(){
    downloadatomshell({
      version: '0.12.5',
      outputDir: __dirname + '/templates/binaries'
    });
});

gulp.task('default', ['downloadatomshell']);
<|endoftext|>