<commit_before>// ==UserScript==
// @name        Baka-HTML
// @namespace   http://localhost
// @include     /^http[s]*?:\/\/(www\.|)baka-tsuki\.org\/project\/index\.php\?title=(.*)/
// @include     /^http[s]*?:\/\/web\.archive\.org\/web\/\d*\/http[s]*?:\/\/(www\.|)baka-tsuki\.org\/project\/index\.php\?title=(.*)/
// @version     1.2.1
// @grant       none
// ==/UserScript==

var clickMe = document.createElement("li");
clickMe.innerHTML = 'Baka-HTML
';
$('#p-views ul').append(clickMe);

var consolePrefix = '[Baka-HTML] ';
var title = document.title;
var imagesToProcess = 0;
var processedImages = 0;
var imageArray = [];

function dethumbnelize(img, success)
{
	imagesToProcess++;
	document.title = "Downloading images " + processedImages + "/" + imagesToProcess;
	console.log(consolePrefix + 'Image #' + imagesToProcess);

	// normally, you will be redirected to image page (/project/index.php?title=File:Imagename.jpg) if you click on the image
	// but it's entirely possible that the image (both inline and thumb) links to somewhere else
	// the filename in src is the same as that in image page url
	// make sure you use relative path to support web.archive.org
	var imgSrc = $(img).attr('src');
	console.log(consolePrefix + 'imgSrc = ' + imgSrc);
	if (imgSrc.charAt(imgSrc.length) == '/') { imgSrc = imgSrc.substring(0, imgSrc.length); } // kill the trailing slash if any

	var colonPos = imgSrc.indexOf(':');
	var thumbPos = imgSrc.indexOf('/project/thumb.php?f=');
	var imagePos = imgSrc.indexOf('/project/images/');
	var ampPos = imgSrc.indexOf('&');
	var imgName = "";
	var imgPageURL = "";
	if (thumbPos >= 0 && ampPos >= 0) { // resized images src="/project/thumb.php?f=Utsuro_no_Hako_vol1_pic1.jpg&width=1000"
		imgName = imgSrc.substring(thumbPos + '/project/thumb.php?f='.length , ampPos);
		imgPageURL = '/project/index.php?title=File:' + imgName;
	}
	else if (imagePos >= 0) { // full-size image src="/project/images/3/37/UtsuroNoHako_vol1.jpg"
		imgName = imgSrc.substring(imgSrc.lastIndexOf('/') + 1);
		imgPageURL = '/project/index.php?title=File:' + imgName;
	}
	else { // unknown. fallback to where the image point to.
		var parent = img.closest('a');
		if (parent !== null) {
			imgPageURL = parent.attr('href');
		}
	}
	console.log(consolePrefix + 'imgPageURL = ' + imgPageURL);
	if (imgPageURL.length > 0) {
		$.get(imgPageURL, function(data)
		{
			var newSrc = $(data).find('.fullImageLink > a').attr('href');
			console.log(consolePrefix + 'newSrc = ' + newSrc);
			success(newSrc);
			processedImages++;
			if (processedImages < imagesToProcess) {
				document.title = "Downloading images " + processedImages + "/" + imagesToProcess;
				if (imageArray.indexOf(newSrc) == -1) {
					imageArray.push(newSrc);
				}
			} else {
				document.title = title;
				console.log(consolePrefix + 'Processed ' + imageArray.length.toString() + ' images (duplicates excluded):');
				console.log(consolePrefix + imageArray.toString());
			}
		});
	} else { // can't find the image page url
		success(imgSrc);
		processedImages++;
		if (processedImages < imagesToProcess) {
			document.title = "Downloading images " + processedImages + "/" + imagesToProcess;
		} else {
			document.title = title;
		}
	}
}

function dethumbnelize2(img, targetID, success) {
	dethumbnelize(img, function(src) {
		success(src, targetID);
	});
}

$('#Baka-HTML').click(function(){
	// format the title a bit
	title = title.replace(':', ' - ').replace(' - Baka-Tsuki', '');
	// Pad volume number in title if existing. Note that number before the last hyphen can be a part of the title
	var hypPos = title.lastIndexOf('-');
	var titlesub = title.substring(hypPos);
	var digiPos = titlesub.search(/\d/);
	if (digiPos > 0) {
		var r = /\d+/;
		var n = titlesub.match(r);
		var digiLen = n.toString().length;
		var leadingStr = '0';
		if (titlesub.charAt(digiPos) == '0' || n >= 10) {
			leadingStr = '';
		}
		if (titlesub.charAt(digiPos-1) !== ' ') { // add a space before the number
			leadingStr = ' ' + leadingStr;
		}
		title = title.substring(0, hypPos) + titlesub.substring(0, digiPos) + leadingStr + n + titlesub.substring(digiPos + digiLen);
	}
	// remove "Editing " or "Creating " from title in preview tab mode
	if (document.URL.indexOf('&action=edit') >= 0) {
		if (title.indexOf('Editing ') >= 0) {
			title = title.substring('Editing '.length);
		} else if (title.indexOf('Creating ') >= 0) {
			title = title.substring('Creating '.length);
		}
	}
	// remove the layout, only keep the content text
	if ($('div.wikiEditor-preview-contents').length) {
		console.log(consolePrefix + 'Preview tab in editing page detected.');
		document.body.innerHTML = $('.wikiEditor-preview-contents').html(); // preview in edit page
	} else {
		document.body.innerHTML = $('#mw-content-text').html(); // normal
	}
	// remove a lot of needless page elements
	// .cite-accessibility-label is the "Jump Up" text that pops out of nowhere in notes when saving page
	// .wikitable.collapsible is big navigator at the end
	// .toc and .toctoggle are table of contents at the beginning. Yes, you need to remove both
	$('#navbar, #top, #jump-to-nav, #mw-js-message, #catlinks, .toc, .toctoggle, .printfooter, .mw-editsection, .wikitable.collapsible, .cite-accessibility-label').remove();
	// replace thumbnails in the gallery with the respective full images. Side note: traditional? Then what is the modern one?
	// each thumbnail is like this
	//

	//

	//

Cover

	//

	// there can be multiple galleries
	var galleryID = 0;
	$('.gallery.mw-gallery-traditional').each(function()
	{
		var oldGallery = $(this);
		var newGallery = document.createElement("div");
		// mark the new gallery to ease the later process
		galleryID++;
		newGallery.id = '__gallery__' + galleryID;
		// put full images to the new gallery
		var imageID = 0;
		oldGallery.find('.gallerybox').each(function()
		{
			var self = $(this);
			imageID++;
			var newGalleryBox = document.createElement("div");
			// mark the gallery box with an id to maintain image order. The 'dethumbnelize' process is asynchoronous,
			// so without this, the order can be wrong. In fact, the chance it's wrong is more than 50/50.
			var targetID = galleryID.toString() + '_' + imageID.toString();
			newGalleryBox.id = '__gallerybox__' + targetID;
			newGallery.appendChild(newGalleryBox);
			dethumbnelize2(self.find('img'), targetID, function(src, tgtID)
			{
				var newGalleryBoxImage = document.createElement("img");
				newGalleryBoxImage.src = src;
				newGalleryBoxImage.alt = '__galleryimage__'; // mark as gallery image
				$('#__gallerybox__' + tgtID).append(newGalleryBoxImage);
				console.log(consolePrefix + 'Inserted image to __gallerybox__' + tgtID);
			});
		});
		// Replace old gallery with the new one
		oldGallery.replaceWith(newGallery);
	});
	// do the same with illustration thumbnails.
	//

	$('div.thumb').each(function()
	{
		var self = $(this);
		var illustrationParagraph = document.createElement("p");
		var illustration = document.createElement("img");
		dethumbnelize(self.find('img'), function(src)
		{
			illustration.src = src;
			illustration.alt = '__thumbnail__';
			illustrationParagraph.appendChild(illustration);
			self.replaceWith(illustrationParagraph);
		});
	});
	// Process inline image like this one https://www.baka-tsuki.org/project/index.php?title=Utsuro_no_Hako:Volume1_1st_time
	// Pls also kill the anchor, if any
	//
	//
	//
	$('img').each(function()
	{
		var self = $(this);
		var imgSrc = self.attr('src');
		var imgAlt = self.attr('alt');
		var thumbPos = imgSrc.indexOf('/project/thumb.php?f=');
		var imagePos = imgSrc.indexOf('/project/images/');
		var ampPos = imgSrc.indexOf('&');
		var parent = self.closest('a');
		if ((imgAlt !== '__thumbnail__' && imgAlt !== '__galleryimage__') && ((thumbPos >= 0 && ampPos >= 0) || (thumbPos == -1 && imagePos >= 0))) {
			dethumbnelize(self, function(src)
			{
				var illustration = document.createElement("img");
				illustration.src = src;
				illustration.alt = src;
				if (parent !== null) {
					parent.replaceWith(illustration);
				} else {
					self.replaceWith(illustration);
				}
			});
		}
	});
	// erase all class info. it's no longer useful, except in table. Do not erase style.
	$('*').not('table').removeAttr("class");
	// Only keep the title in the header. Note: When saving the html, the browser might add some css junk. That can be handled later.
	document.head.innerHTML = '<title>' + title + '</title>';
	// remove contextmenu attribute from body
	document.body.removeAttribute("contextmenu");
});<commit_msg>[BKH] Stricter check. Yell at user if running in wrong page<commit_after>// ==UserScript==
// @name        Baka-HTML
// @namespace   http://localhost
// @include     /^http[s]*?:\/\/(www\.|)baka-tsuki\.org\/project\/index\.php\?title=(.*)/
// @include     /^http[s]*?:\/\/web\.archive\.org\/web\/\d*\/http[s]*?:\/\/(www\.|)baka-tsuki\.org\/project\/index\.php\?title=(.*)/
// @version     1.2.1
// @grant       none
// ==/UserScript==

var clickMe = document.createElement("li");
clickMe.innerHTML = 'Baka-HTML
';
$('#p-views ul').append(clickMe);

var consolePrefix = '[Baka-HTML] ';
var title = document.title;
var imagesToProcess = 0;
var processedImages = 0;
var imageArray = [];

function dethumbnelize(img, success)
{
	imagesToProcess++;
	document.title = "Downloading images " + processedImages + "/" + imagesToProcess;
	console.log(consolePrefix + 'Image #' + imagesToProcess);

	// normally, you will be redirected to image page (/project/index.php?title=File:Imagename.jpg) if you click on the image
	// but it's entirely possible that the image (both inline and thumb) links to somewhere else
	// the filename in src is the same as that in image page url
	// make sure you use relative path to support web.archive.org
	var imgSrc = $(img).attr('src');
	console.log(consolePrefix + 'imgSrc = ' + imgSrc);
	if (imgSrc.charAt(imgSrc.length) == '/') { imgSrc = imgSrc.substring(0, imgSrc.length); } // kill the trailing slash if any

	var colonPos = imgSrc.indexOf(':');
	var thumbPos = imgSrc.indexOf('/project/thumb.php?f=');
	var imagePos = imgSrc.indexOf('/project/images/');
	var ampPos = imgSrc.indexOf('&');
	var imgName = "";
	var imgPageURL = "";
	if (thumbPos >= 0 && ampPos >= 0) { // resized images src="/project/thumb.php?f=Utsuro_no_Hako_vol1_pic1.jpg&width=1000"
		imgName = imgSrc.substring(thumbPos + '/project/thumb.php?f='.length , ampPos);
		imgPageURL = '/project/index.php?title=File:' + imgName;
	}
	else if (imagePos >= 0) { // full-size image src="/project/images/3/37/UtsuroNoHako_vol1.jpg"
		imgName = imgSrc.substring(imgSrc.lastIndexOf('/') + 1);
		imgPageURL = '/project/index.php?title=File:' + imgName;
	}
	else { // unknown. fallback to where the image point to.
		var parent = img.closest('a');
		if (parent !== null) {
			imgPageURL = parent.attr('href');
		}
	}
	console.log(consolePrefix + 'imgPageURL = ' + imgPageURL);
	if (imgPageURL.length > 0) {
		$.get(imgPageURL, function(data)
		{
			var newSrc = $(data).find('.fullImageLink > a').attr('href');
			console.log(consolePrefix + 'newSrc = ' + newSrc);
			success(newSrc);
			processedImages++;
			if (processedImages < imagesToProcess) {
				document.title = "Downloading images " + processedImages + "/" + imagesToProcess;
				if (imageArray.indexOf(newSrc) == -1) {
					imageArray.push(newSrc);
				}
			} else {
				document.title = title;
				console.log(consolePrefix + 'Processed ' + imageArray.length.toString() + ' images (duplicates excluded):');
				console.log(consolePrefix + imageArray.toString());
			}
		});
	} else { // can't find the image page url
		success(imgSrc);
		processedImages++;
		if (processedImages < imagesToProcess) {
			document.title = "Downloading images " + processedImages + "/" + imagesToProcess;
		} else {
			document.title = title;
		}
	}
}

function dethumbnelize2(img, targetID, success) {
	dethumbnelize(img, function(src) {
		success(src, targetID);
	});
}

$('#Baka-HTML').click(function(){
	// format the title a bit
	title = title.replace(':', ' - ').replace(' - Baka-Tsuki', '');
	// Pad volume number in title if existing. Note that number before the last hyphen can be a part of the title
	var hypPos = title.lastIndexOf('-');
	var titlesub = title.substring(hypPos);
	var digiPos = titlesub.search(/\d/);
	if (digiPos > 0) {
		var r = /\d+/;
		var n = titlesub.match(r);
		var digiLen = n.toString().length;
		var leadingStr = '0';
		if (titlesub.charAt(digiPos) == '0' || n >= 10) {
			leadingStr = '';
		}
		if (titlesub.charAt(digiPos-1) !== ' ') { // add a space before the number
			leadingStr = ' ' + leadingStr;
		}
		title = title.substring(0, hypPos) + titlesub.substring(0, digiPos) + leadingStr + n + titlesub.substring(digiPos + digiLen);
	}
	// remove "Editing " or "Creating " from title in preview tab mode
	if (document.URL.indexOf('&action=edit') >= 0) {
		if (title.indexOf('Editing ') >= 0) {
			title = title.substring('Editing '.length);
		} else if (title.indexOf('Creating ') >= 0) {
			title = title.substring('Creating '.length);
		}
	}
	// remove the layout, only keep the content text
	if ($('div.wikiEditor-preview-contents').length > 0) {
		console.log(consolePrefix + 'Preview tab in editing page detected.');
		document.body.innerHTML = $('.wikiEditor-preview-contents').html(); // preview in edit page
	} else if ($('#mw-content-text').length > 0) {
		console.log(consolePrefix + 'Normal page detected.');
		document.body.innerHTML = $('#mw-content-text').html(); // normal
	} else {
		console.log(consolePrefix + "Contents not detected. You're running me on the wrong page.");
		alert(consolePrefix + "Contents not detected. You're running me on the wrong page.");
	}
	// remove a lot of needless page elements
	// .cite-accessibility-label is the "Jump Up" text that pops out of nowhere in notes when saving page
	// .wikitable.collapsible is big navigator at the end
	// .toc and .toctoggle are table of contents at the beginning. Yes, you need to remove both
	$('#navbar, #top, #jump-to-nav, #mw-js-message, #catlinks, .toc, .toctoggle, .printfooter, .mw-editsection, .wikitable.collapsible, .cite-accessibility-label').remove();
	// replace thumbnails in the gallery with the respective full images. Side note: traditional? Then what is the modern one?
	// each thumbnail is like this
	//

	//

	//

Cover

	//

	// there can be multiple galleries
	var galleryID = 0;
	$('.gallery.mw-gallery-traditional').each(function()
	{
		var oldGallery = $(this);
		var newGallery = document.createElement("div");
		// mark the new gallery to ease the later process
		galleryID++;
		newGallery.id = '__gallery__' + galleryID;
		// put full images to the new gallery
		var imageID = 0;
		oldGallery.find('.gallerybox').each(function()
		{
			var self = $(this);
			imageID++;
			var newGalleryBox = document.createElement("div");
			// mark the gallery box with an id to maintain image order. The 'dethumbnelize' process is asynchoronous,
			// so without this, the order can be wrong. In fact, the chance it's wrong is more than 50/50.
			var targetID = galleryID.toString() + '_' + imageID.toString();
			newGalleryBox.id = '__gallerybox__' + targetID;
			newGallery.appendChild(newGalleryBox);
			dethumbnelize2(self.find('img'), targetID, function(src, tgtID)
			{
				var newGalleryBoxImage = document.createElement("img");
				newGalleryBoxImage.src = src;
				newGalleryBoxImage.alt = '__galleryimage__'; // mark as gallery image
				$('#__gallerybox__' + tgtID).append(newGalleryBoxImage);
				console.log(consolePrefix + 'Inserted image to __gallerybox__' + tgtID);
			});
		});
		// Replace old gallery with the new one
		oldGallery.replaceWith(newGallery);
	});
	// do the same with illustration thumbnails.
	//

	$('div.thumb').each(function()
	{
		var self = $(this);
		var illustrationParagraph = document.createElement("p");
		var illustration = document.createElement("img");
		dethumbnelize(self.find('img'), function(src)
		{
			illustration.src = src;
			illustration.alt = '__thumbnail__';
			illustrationParagraph.appendChild(illustration);
			self.replaceWith(illustrationParagraph);
		});
	});
	// Process inline image like this one https://www.baka-tsuki.org/project/index.php?title=Utsuro_no_Hako:Volume1_1st_time
	// Pls also kill the anchor, if any
	//
	//
	//
	$('img').each(function()
	{
		var self = $(this);
		var imgSrc = self.attr('src');
		var imgAlt = self.attr('alt');
		var thumbPos = imgSrc.indexOf('/project/thumb.php?f=');
		var imagePos = imgSrc.indexOf('/project/images/');
		var ampPos = imgSrc.indexOf('&');
		var parent = self.closest('a');
		if ((imgAlt !== '__thumbnail__' && imgAlt !== '__galleryimage__') && ((thumbPos >= 0 && ampPos >= 0) || (thumbPos == -1 && imagePos >= 0))) {
			dethumbnelize(self, function(src)
			{
				var illustration = document.createElement("img");
				illustration.src = src;
				illustration.alt = src;
				if (parent !== null) {
					parent.replaceWith(illustration);
				} else {
					self.replaceWith(illustration);
				}
			});
		}
	});
	// erase all class info. it's no longer useful, except in table. Do not erase style.
	$('*').not('table').removeAttr("class");
	// Only keep the title in the header. Note: When saving the html, the browser might add some css junk. That can be handled later.
	document.head.innerHTML = '<title>' + title + '</title>';
	// remove contextmenu attribute from body
	document.body.removeAttribute("contextmenu");
});<|endoftext|>


<commit_before>import React from 'react';
import { cleanup, render, fireEvent } from '@testing-library/react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';

import { Grommet } from '../../Grommet';
import { CheckBox } from '..';

describe('CheckBox', () => {
  afterEach(cleanup);

  test('renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox />
        <CheckBox id="test id" name="test name" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('label renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox label="test label" />
        <CheckBox label={
test label
} />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('checked renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox checked />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('disabled renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox disabled />
        <CheckBox disabled checked />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('reverse renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox reverse label="test label" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('toggle renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox toggle />
        <CheckBox toggle checked />
        <CheckBox toggle label="test label" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('indeterminate renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox indeterminate />
        <CheckBox indeterminate label="test label" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('indeterminate checked warns', () => {
    const spy = jest.spyOn(global.console, 'warn');
    renderer.create(
      <Grommet>
        <CheckBox indeterminate checked />
      </Grommet>,
    );
    expect(spy).toBeCalledWith(
      'Checkbox cannot be "checked" and "indeterminate" at the same time.',
    );
  });

  test('indeterminate toggle warns', () => {
    const spy = jest.spyOn(global.console, 'warn');
    renderer.create(
      <Grommet>
        <CheckBox indeterminate toggle />
      </Grommet>,
    );
    expect(spy).toBeCalledWith(
      'Checkbox of type toggle does not have "indeterminate" state.',
    );
  });

  test('controlled', () => {
    const { container, getByText } = render(
      <Grommet>
        <CheckBox label="test-label" checked />
      </Grommet>,
    );
    expect(container.firstChild).toMatchSnapshot();
    fireEvent.click(getByText('test-label'));
    expect(container.firstChild).toMatchSnapshot();
  });
});
<commit_msg>CheckBox test warnings (#4202)<commit_after>import React from 'react';
import { cleanup, render, fireEvent } from '@testing-library/react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';

import { Grommet } from '../../Grommet';
import { CheckBox } from '..';

describe('CheckBox', () => {
  afterEach(cleanup);

  test('renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox />
        <CheckBox id="test id" name="test name" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('label renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox label="test label" />
        <CheckBox label={
test label
} />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('checked renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox checked />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('disabled renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox disabled />
        <CheckBox disabled checked />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('reverse renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox reverse label="test label" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('toggle renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox toggle />
        <CheckBox toggle checked />
        <CheckBox toggle label="test label" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('indeterminate renders', () => {
    const component = renderer.create(
      <Grommet>
        <CheckBox indeterminate />
        <CheckBox indeterminate label="test label" />
      </Grommet>,
    );
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  test('indeterminate checked warns', () => {
    console.warn = jest.fn();
    const warnSpy = jest.spyOn(console, 'warn');
    render(
      <Grommet>
        <CheckBox indeterminate checked />
      </Grommet>,
    );
    expect(warnSpy).toBeCalledWith(
      'Checkbox cannot be "checked" and "indeterminate" at the same time.',
    );

    warnSpy.mockReset();
    warnSpy.mockRestore();
    console.warn.mockReset();
  });

  test('indeterminate toggle warns', () => {
    console.warn = jest.fn();
    const warnSpy = jest.spyOn(console, 'warn');
    render(
      <Grommet>
        <CheckBox indeterminate toggle />
      </Grommet>,
    );
    expect(warnSpy).toBeCalledWith(
      'Checkbox of type toggle does not have "indeterminate" state.',
    );

    warnSpy.mockReset();
    warnSpy.mockRestore();
    console.warn.mockReset();
  });

  test('controlled', () => {
    const { container, getByText } = render(
      <Grommet>
        <CheckBox label="test-label" checked />
      </Grommet>,
    );
    expect(container.firstChild).toMatchSnapshot();
    fireEvent.click(getByText('test-label'));
    expect(container.firstChild).toMatchSnapshot();
  });
});
<|endoftext|>


<commit_before>import { browserHistory } from 'react-router'
import { selectTerm, selectTermAsync } from './search'
import { CHANGE_ROUTE } from './_constants'
import { getPageAsync } from './pages';

export function setStartLocation(location) {
  return (dispatch, getState) => {
    if(location.query.term) {
      dispatch(selectTermAsync(location.query.term))
    }
    else if (location.pathname.indexOf('/pages/') === 0) {
      dispatch(getPageAsync(location.pathname.replace('/pages/', '')))
    }
  }
}

export function goBack(isEdit) {
  return (dispatch, getState) => {
    let location = getState().route.prevLocation || '/'
    dispatch({
      type: 'CHANGE_ROUTE_BACK'
    })
    browserHistory.push(location) // will force CHANGE_ROUTE action

    // go back from Edit if no history
    if(location === '/' && isEdit) {
      dispatch(selectTermAsync(getState().edit.termId))
    }
  }
}

export function changeRoute(location) {
  return (dispatch, getState) => {
    const state = getState()
    const prevLocation = state.route.location
    // /?term=chos
    if (location.pathname.length <= 1 && location.query.term) {
      if (prevLocation && prevLocation.query.term !== location.query.term) {
        if (state.selected.term && state.selected.term.id !== location.query.term) {
          let term = state.search.result.find(term => term.id === location.query.term)
          if (term) {
            dispatch(selectTerm(term, true))
          } else {
            dispatch(selectTermAsync(location.query.term))
          }
        }
      }
    }
    // pages/about
    if (location.pathname.indexOf('/pages/') === 0) {
      if(!prevLocation || prevLocation.pathname !== location.pathname) {
        dispatch(getPageAsync(location.pathname.replace('/pages/', '')))
      }
    }
    return dispatch({
      type: CHANGE_ROUTE,
      location: location
    })
  }
}<commit_msg>Fixed problem with double request on server<commit_after>import { browserHistory } from 'react-router'
import { selectTerm, selectTermAsync } from './search'
import { CHANGE_ROUTE } from './_constants'
import { getPageAsync } from './pages';

const excludeAdminRoutes = (_pathname) =>
  _pathname.indexOf('/edit') === -1 && _pathname.indexOf('/new') === -1

export function setStartLocation(location) {
  return (dispatch, getState) => {
    if(location.query.term) {
      dispatch(selectTermAsync(location.query.term))
    }
    else if (location.pathname.indexOf('/pages/') === 0 && excludeAdminRoutes(location.pathname)) {
      dispatch(getPageAsync(location.pathname.replace('/pages/', '')))
    }
  }
}

export function goBack(isEdit) {
  return (dispatch, getState) => {
    let location = getState().route.prevLocation || '/'
    dispatch({
      type: 'CHANGE_ROUTE_BACK'
    })
    browserHistory.push(location) // will force CHANGE_ROUTE action

    // go back from Edit if no history
    if(location === '/' && isEdit) {
      dispatch(selectTermAsync(getState().edit.termId))
    }
  }
}

export function changeRoute(location) {
  return (dispatch, getState) => {
    const state = getState()
    const prevLocation = state.route.location
    // /?term=chos
    if (location.pathname.length <= 1 && location.query.term) {
      if (prevLocation && prevLocation.query.term !== location.query.term) {
        if (state.selected.term && state.selected.term.id !== location.query.term) {
          let term = state.search.result.find(term => term.id === location.query.term)
          if (term) {
            dispatch(selectTerm(term, true))
          } else {
            dispatch(selectTermAsync(location.query.term))
          }
        }
      }
    }
    // pages/about
    if (location.pathname.indexOf('/pages/') === 0 && excludeAdminRoutes(location.pathname)) {
      if(!prevLocation || prevLocation.pathname !== location.pathname) {
        dispatch(getPageAsync(location.pathname.replace('/pages/', '')))
      }
    }
    return dispatch({
      type: CHANGE_ROUTE,
      location: location
    })
  }
}
<|endoftext|>


<commit_before>import execSync from "../services/exec-sync";
import log from "../services/logger";
import lambdaExists from "../utils/lambda-exists";

export default function deploy (options) {
    const {
        awsAccessKeyId,
        awsSecretAccessKey,
        awsRegion,
        lambdaName,
        lambdaRole,
        sourceDir
    } = options;
    // Create/update the lambda function
    const awsCliEnv = {
        AWS_ACCESS_KEY_ID: awsAccessKeyId,
        AWS_SECRET_ACCESS_KEY: awsSecretAccessKey,
        AWS_DEFAULT_REGION: awsRegion
    };
    switch (lambdaExists(awsCliEnv, lambdaName)) {
    case false:
        // Create the function
        log.info(`Lambda ${lambdaName} doesn't exist.`);
        log.info(`Creating lambda ${lambdaName}`);
        execSync([
            "aws lambda create-function",
            `--function-name ${lambdaName}`,
            "--runtime nodejs",
            `--role ${lambdaRole}`,
            "--handler index.handler",
            `--zip-file ${sourceDir}/bundle.zip`
        ].join(" "), {env: awsCliEnv});
        break;
    case true:
        // Update function code
        log.info(`Lambda ${lambdaName} already exists`);
        log.info(`Updating function code for lambda ${lambdaName}`);
        execSync([
            "aws lambda update-function-code",
            `--function-name ${lambdaName}`,
            `--zip-file ${sourceDir}/bundle.zip`
        ].join(" "), {env: awsCliEnv});
        // Update function configuration (just the role for now)
        log.info(`Updating function configuration for lambda ${lambdaName}`);
        execSync([
            "aws lambda update-function-configuration",
            `--function-name ${lambdaName}`,
            `--role ${lambdaRole}`
        ].join(" "), {env: awsCliEnv});
        break;
    }
}
<commit_msg>Fixed lambda deploy commands invocation<commit_after>import execSync from "../services/exec-sync";
import log from "../services/logger";
import lambdaExists from "../utils/lambda-exists";

export default function deploy (options) {
    const {
        awsAccessKeyId,
        awsSecretAccessKey,
        awsRegion,
        lambdaName,
        lambdaRole,
        sourceDir
    } = options;
    // Create/update the lambda function
    const awsCliEnv = {
        AWS_ACCESS_KEY_ID: awsAccessKeyId,
        AWS_SECRET_ACCESS_KEY: awsSecretAccessKey,
        AWS_DEFAULT_REGION: awsRegion
    };
    switch (lambdaExists(awsCliEnv, lambdaName)) {
    case false:
        // Create the function
        log.info(`Lambda ${lambdaName} doesn't exist.`);
        log.info(`Creating lambda ${lambdaName}`);
        execSync([
            "aws lambda create-function",
            `--function-name ${lambdaName}`,
            "--runtime nodejs",
            `--role ${lambdaRole}`,
            "--handler index.handler",
            `--zip-file fileb://${sourceDir}/bundle.zip`
        ].join(" "), {env: awsCliEnv});
        break;
    case true:
        // Update function code
        log.info(`Lambda ${lambdaName} already exists`);
        log.info(`Updating function code for lambda ${lambdaName}`);
        execSync([
            "aws lambda update-function-code",
            `--function-name ${lambdaName}`,
            `--zip-file fileb://${sourceDir}/bundle.zip`
        ].join(" "), {env: awsCliEnv});
        // Update function configuration (just the role for now)
        log.info(`Updating function configuration for lambda ${lambdaName}`);
        execSync([
            "aws lambda update-function-configuration",
            `--function-name ${lambdaName}`,
            `--role ${lambdaRole}`
        ].join(" "), {env: awsCliEnv});
        break;
    }
}
<|endoftext|>


<commit_before>/**
 * tag.js
 *
 * Copyright (C) 2013 by Florian Holzapfel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
**/
var utils = require('./utils');
var util = require('util');

var tag = function (client, xml) {
	this.client = client;
	if (xml) {
		utils.parse_xml(this, xml.tag || xml);
	}
};
tag.prototype.create = function (options, callback) {
	var path = '/' + options.subject_type + '/' +
		options.subject_id + '/tags.xml';
	this.client.create_item(path, util.format('<name>%s</name>', options.name),
		callback, tag);
};
tag.prototype.delete = function (options, callback) {
	var path = '/' + options.subject_type + '/' + options.subject_id +
		'/tags' + this.id + '.xml';
	this.client.send('DELETE', path, function (err, res) {
		callback(err);
	});
};

module.exports = tag;
<commit_msg>Added a missing slash to tag deletion path<commit_after>/**
 * tag.js
 *
 * Copyright (C) 2013 by Florian Holzapfel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
**/
var utils = require('./utils');
var util = require('util');

var tag = function (client, xml) {
	this.client = client;
	if (xml) {
		utils.parse_xml(this, xml.tag || xml);
	}
};
tag.prototype.create = function (options, callback) {
	var path = '/' + options.subject_type + '/' +
		options.subject_id + '/tags.xml';
	this.client.create_item(path, util.format('<name>%s</name>', options.name),
		callback, tag);
};
tag.prototype.delete = function (options, callback) {
	var path = '/' + options.subject_type + '/' + options.subject_id +
		'/tags/' + this.id + '.xml';
	this.client.send('DELETE', path, function (err, res) {
		callback(err);
	});
};

module.exports = tag;
<|endoftext|>


<commit_before>var HelloPhaser = {
    preload: function () {
        game.load.image('background', 'assets/bg_960-600.png');
        game.load.image('bloco', 'assets/bloco_100-50.png');
        game.load.image('estrela', 'assets/estrela_30-30.png');
        game.load.image('porta', 'assets/porta_60-100.png');
        // animacao do jogador
        game.load.spritesheet('jogador', 'assets/spritejogador_357-77-7.png', 51, 77);
    },

    create: function() {

        this.bg = game.add.sprite(0, 0, 'background');
        this.blocos = [[27, 336], [127, 336], [227, 286], [327, 236], [479, 236], [579, 286], [679, 336]];
        this.estrela = game.add.sprite(362, 189, 'estrela');
        this.porta = game.add.sprite(49,236,'porta');
        this.jogador = game.add.sprite(450, 423, 'jogador');
        this.jogador.animations.add('walk', [1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1], 10, true);
        for(var i = 0; i < this.blocos.length; i++){
            this.blocos[i] = game.add.sprite(this.blocos[i][0], this.blocos[i][1], 'bloco');
        }

        game.physics.enable(this.jogador, Phaser.Physics.ARCADE);
        this.jogador.body.velocity.x = -200;
        this.jogador.animations.play('walk');
        this.jogador.anchor.setTo(.5, 0);
    },

    update: function(){
        if(this.jogador.x <= 0) {
            this.jogador.body.velocity.x = 200;
            this.jogador.scale.x = -1;
        }
        else if(this.jogador.x - this.jogador.width >= 960 - this.jogador.width) {
            this.jogador.body.velocity.x = -200;
            this.jogador.scale.x = 1;
        }
    }
};
<commit_msg>Atualizando hello phaser<commit_after>var HelloPhaser = {
    preload: function () {
        game.load.image('background', 'assets/bg_960-600.png');
        game.load.image('bloco', 'assets/bloco_100-50.png');
        game.load.image('estrela', 'assets/estrela_30-30.png');
        game.load.image('porta', 'assets/porta_60-100.png');
        // animacao do jogador
        game.load.spritesheet('jogador', 'assets/jogador_306-77-6.png', 51, 77);
    },

    create: function() {
        this.bg = game.add.sprite(0, 0, 'background');
        this.blocos = [[27, 336], [127, 336], [227, 286], [327, 236], [479, 236], [579, 286], [679, 336]];
        this.estrela = game.add.sprite(362, 189, 'estrela');
        this.porta = game.add.sprite(49,236,'porta');
        this.jogador = game.add.sprite(450, 423, 'jogador');

        this.jogador.animations.add('jump', [0, 1, 2, 1, 0], 10, true);
        this.jogador.animations.add('walk', [3, 4, 5, 4, 3], 10, true);
        this.blockGroup = game.add.group();
        game.physics.enable(this.blockGroup);
        this.blockGroup.enableBody = true;
        for(var i = 0; i < this.blocos.length; i++){
            this.blocos[i] = this.blockGroup.create(this.blocos[i][0], this.blocos[i][1], 'bloco');
            this.blocos[i].body.immovable = true;
        }

        game.physics.enable(this.jogador, Phaser.Physics.ARCADE);
        this.jogador.body.gravity.y = 1000;
        this.jogador.body.collideWorldBounds = true;
        this.cursors = game.input.keyboard.createCursorKeys();
    },

    update: function(){
        game.physics.arcade.collide(this.jogador, this.blockGroup);

            if(this.cursors.left.isDown){
                //mover para a esquerda
                this.jogador.body.velocity.x = -150;
                this.jogador.animations.play('walk');
            }
            else if(this.cursors.right.isDown){
                // mover para a direira
                this.jogador.body.velocity.x = 150;
                this.jogador.animations.play('walk');
            }
            else {
                this.jogador.animations.stop();
                this.jogador.body.velocity.x = 0;
                this.jogador.frame = 2;
            }

        // se nenhuma das teclas for pressionada, parar sprite
        //else this.jogador.body.velocity.x = 0;

        if(game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR)){
            //this.jogador.body.velocity.y = -250;
            this.jogador.frame = 1;
        }
        else if(game.input.keyboard.justReleased(Phaser.Keyboard.SPACEBAR)) {
            this.jogador.body.velocity.y = -250;
            this.jogador.frame = 2;
        }
    },

    moonwalk: function() {
        if(this.jogador.x <= 0) {
            this.jogador.body.velocity.x = 200;
            this.jogador.scale.x = -1;
        }
        else if(this.jogador.x - this.jogador.width >= 960 - this.jogador.width) {
            this.jogador.body.velocity.x = -200;
            this.jogador.scale.x = 1;
        }
    }
};
<|endoftext|>


<commit_before>// @flow
import { describe, it } from 'mocha';
import React from 'react'; // eslint-disable-line no-unused-vars
import Button from 'material-ui/Button';
import { shallow } from 'enzyme';
import { expect } from 'chai';
import { Link } from '../../components/Link';

describe('Todo Manager', () => {
  describe('Components', () => {
    describe('Link', () => {
      it('given selected link, should be not a clickable button', () => {
        const wrapper = shallow(
          <Link active filter="Show" onSetVisibilityFilter={f => f}>Hello</Link>,
        );

        const button = wrapper.find(Button);

        expect(button).to.have.length(1);
        expect(button.prop('disabled')).to.equal(true);
        expect(button.prop('onClick')).to.equal(undefined);
      });

      it('given unselected link, should be clickable button', () => {
        const wrapper = shallow(
          <Link active={false} filter="Show" onSetVisibilityFilter={f => f}>Hello</Link>,
        );

        const button = wrapper.find(Button);

        expect(button).to.have.length(1);
        expect(button.prop('disabled')).to.equal(undefined);
        expect(button.prop('onClick')).to.be.a('Function');
      });
    });
  });
});
<commit_msg>Fixed tests<commit_after>// @flow
import { describe, it } from 'mocha';
import React from 'react'; // eslint-disable-line no-unused-vars
import Button from 'material-ui/Button';
import { shallow } from 'enzyme';
import { expect } from 'chai';
import { Link } from '../../components/Link';

describe('Todo Manager', () => {
  describe('Components', () => {
    describe('Link', () => {
      it('given selected link, should be not a clickable button', () => {
        const wrapper = shallow(
          <Link
            active
            filter="Show"
            onSetVisibilityFilter={f => f}
            classes={{ link: 'link' }}
          >Hello
          </Link>,
        );

        const button = wrapper.find(Button);

        expect(button).to.have.length(1);
        expect(button.prop('disabled')).to.equal(true);
        expect(button.prop('onClick')).to.equal(undefined);
      });

      it('given unselected link, should be clickable button', () => {
        const wrapper = shallow(
          <Link
            active={false}
            filter="Show"
            onSetVisibilityFilter={f => f}
            classes={{ link: 'link' }}
          >
            Hello
          </Link>,
        );

        const button = wrapper.find(Button);

        expect(button).to.have.length(1);
        expect(button.prop('disabled')).to.equal(undefined);
        expect(button.prop('onClick')).to.be.a('Function');
      });
    });
  });
});
<|endoftext|>


<commit_before>var semver = require('semver')
var fs = require('fs')
var path = require('path')
var pull = require('pull-stream')

function readJson(file, cb) {
  fs.readFile(file, 'utf-8', function (err, json) {
    if(err) return cb(err)
    try { json = JSON.parse(json) } catch (err) { return cb(err) }
    cb(null, json)
  })
}

module.exports = function (module, vrange, opts, cb) {
  if(!semver.validRange(vrange))
    return cb()

  var cache = opts.cache || path.join(process.env.HOME, '.npm')

  readJson(path.join(cache, module, '.cache.json'), function (err, doc) {
    if(err) return cb()

    pull(
      pull.values(
        Object.keys(doc.versions)
        .sort(semver.compareLoose)
        .reverse()
      ),
      pull.asyncMap(function (version, cb) {
        var hash = doc.versions[version].dist.shasum

        var filename = path.join(
                        opts.dbPath, 'blobs',
                        hash.substring(0, 2),
                        hash.substring(2)
                      )

        fs.stat(filename, function (err, stat) {
          if(err) cb()
          else    cb(null, doc.versions[version])
        })

      }),
      pull.filter(),
      pull.take(function (item) {
        cb(null, item)
        return false
      }),
      pull.drain()
    )
  })
}

<commit_msg>offline resolve must have hash, too<commit_after>var semver = require('semver')
var fs = require('fs')
var path = require('path')
var pull = require('pull-stream')

function readJson(file, cb) {
  fs.readFile(file, 'utf-8', function (err, json) {
    if(err) return cb(err)
    try { json = JSON.parse(json) } catch (err) { return cb(err) }
    cb(null, json)
  })
}

module.exports = function (module, vrange, opts, cb) {
  if(!semver.validRange(vrange))
    return cb()

  var cache = opts.cache || path.join(process.env.HOME, '.npm')

  readJson(path.join(cache, module, '.cache.json'), function (err, doc) {
    if(err) return cb()

    pull(
      pull.values(
        Object.keys(doc.versions)
        .sort(semver.compareLoose)
        .reverse()
      ),
      pull.asyncMap(function (version, cb) {
        var hash = doc.versions[version].dist.shasum

        var pkg = doc.versions[version]
        pkg.shasum = pkg.dist.shasum

        var filename = path.join(
                        opts.dbPath, 'blobs',
                        hash.substring(0, 2),
                        hash.substring(2)
                      )

        fs.stat(filename, function (err, stat) {
          if(err) cb()
          else    cb(null, pkg)
        })

      }),
      pull.filter(),
      pull.take(function (item) {
        cb(null, item)
        return false
      }),
      pull.drain()
    )
  })
}

<|endoftext|>


<commit_before>module.exports = {
  activate: function(state) {
    return atom.workspaceView.command("tag:closeTag", (function(_this) {
      return function() {
        return _this.closeTag();
      };
    })(this));
  },
  closeTag: function() {
    var editor = atom.workspace.activePaneItem;
    var cursorPos = editor.getCursor().getBufferPosition();
    var buffer = editor.getTextInBufferRange([[0, 0], cursorPos]);
    var voidElements = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];

    var getClosingTag = function (str) {
      var tokens = str.split('<');
      if (tokens.length == 0) {
        return '';
      }
      tokens.reverse();

      var getTag = function (token) {
        return token.substring(0, token.search(' |>'));
      }

      var isVoidElement = function(tag) {
        tag = tag.trim();
        if (tag.substr(tag.length - 1) == '/') {
          return true;
        }

        tag = tag.toLowerCase();
        if (voidElements.indexOf(tag) >= 0) {
          return true;
        }

        return false;
      }

      var skip = null;
      for (var i in tokens) {
        var token = tokens[i];
        var tag = getTag(token);
        if (tag !== undefined && tag.length > 0 && !isVoidElement(tag)) {
          if (tag.substring(0, 1) != '/') {
            if (skip == null) {
              return '</' + tag + '>';
            } else if (skip == tag) {
              skip = null;
            }
          } else {
            if (skip === null) {
              skip = tag.substring(1);
            }
          }
        }
      }
      return '';
    }

    editor.insertText(getClosingTag(buffer));
  }
};
<commit_msg>Stack implementation for back tracing of tags.<commit_after>module.exports = {
  activate: function(state) {
    return atom.workspaceView.command("tag:closeTag", (function(_this) {
      return function() {
        return _this.closeTag();
      };
    })(this));
  },
  closeTag: function() {
    var editor = atom.workspace.activePaneItem;
    var cursorPos = editor.getCursor().getBufferPosition();
    var buffer = editor.getTextInBufferRange([[0, 0], cursorPos]);
    var voidElements = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];

    var getClosingTag = function (str) {
      var tokens = str.split('<');
      if (tokens.length == 0) {
        return '';
      }
      tokens.reverse();

      var getTag = function (token) {
        return token.substring(0, token.search(' |>'));
      }

      var isVoidElement = function(tag) {
        tag = tag.trim();
        if (tag.substr(tag.length - 1) == '/') {
          return true;
        }

        tag = tag.toLowerCase();
        if (voidElements.indexOf(tag) >= 0) {
          return true;
        }

        return false;
      }

      var skip = [];
      for (var i in tokens) {
        var token = tokens[i];
        var tag = getTag(token);
        if (tag !== undefined && tag.length > 0 && !isVoidElement(tag)) {
          if (tag.substring(0, 1) != '/') {
            if (skip.length === 0) {
              return '</' + tag + '>';
            } else if (skip[skip.length - 1] == tag) {
              skip.pop();
            }
          } else {
            skip.push(tag.substring(1));
          }
        }
      }
      return '';
    }

    editor.insertText(getClosingTag(buffer));
  }
};
<|endoftext|>


<commit_before>var _ = require('lodash');

/**
 * @description Storage driver that uses entirely in-memory data structures.
 *
 * This is useful for unit testing and for local builds.
 */
function MemoryStorage () {}

MemoryStorage.prototype.setup = function (callback) {
  this.clear(callback);
};

MemoryStorage.prototype.clear = function (callback) {
  this.envelopes = {};
  this.indexedEnvelopes = [];
  this.assets = {};
  this.namedAssets = {};
  this.keys = {};
  this.sha = null;

  callback();
};

/**
 * @description Return prefix of the URL that assets are served under.
 */
MemoryStorage.prototype.assetURLPrefix = function () {
  return '/__local_asset__/';
};

MemoryStorage.prototype.storeAsset = function (asset, callback) {
  var assetBody = Buffer.concat(asset.chunks);

  this.assets[asset.filename] = {
    contentType: asset.type,
    body: assetBody
  };

  asset.publicURL = this.assetURLPrefix() + encodeURIComponent(asset.filename);

  callback(null, asset);
};

MemoryStorage.prototype.nameAsset = function (asset, callback) {
  this.namedAssets[asset.key] = {
    key: asset.key,
    publicURL: asset.publicURL
  };

  callback(null, asset);
};

MemoryStorage.prototype.findNamedAssets = function (callback) {
  callback(null, _.values(this.namedAssets));
};

MemoryStorage.prototype.getAsset = function (filename, callback) {
  var asset = this.assets[filename];

  if (asset === undefined) {
    var err = new Error('Memory storage error');

    err.statusCode = 404;
    err.responseBody = 'Oh snap';

    return callback(err);
  }

  callback(null, asset);
};

MemoryStorage.prototype.storeKey = function (key, callback) {
  this.keys[key.apikey] = key.name;
  callback();
};

MemoryStorage.prototype.deleteKey = function (apikey, callback) {
  delete this.keys[apikey];
  callback();
};

MemoryStorage.prototype.findKeys = function (apikey, callback) {
  var name = this.keys[apikey];
  if (name) {
    callback(null, [{
      apikey: apikey,
      name: name
    }]);
  } else {
    callback(null, []);
  }
};

MemoryStorage.prototype._storeContent = function (contentID, content, callback) {
  this.envelopes[contentID] = content;
  callback();
};

MemoryStorage.prototype._getContent = function (contentID, callback) {
  var envelope = this.envelopes[contentID];

  if (envelope === undefined) {
    var err = new Error('Memory storage error');

    err.statusCode = 404;
    err.responseBody = 'Oh snap';

    return callback(err);
  }

  callback(null, envelope);
};

MemoryStorage.prototype.deleteContent = function (contentID, callback) {
  delete this.envelopes[contentID];

  callback();
};

MemoryStorage.prototype.listContent = function (callback) {
  var ids = Object.keys(this.envelopes);

  callback(null, ids, function () {
    if (ids.length > 0) {
      callback(null, [], function () {});
    }
  });
};

MemoryStorage.prototype.createNewIndex = function (indexName, callback) {
  callback();
};

MemoryStorage.prototype._indexContent = function (contentID, envelope, indexName, callback) {
  this.indexedEnvelopes.push({
    _id: contentID,
    _source: envelope
  });

  callback();
};

MemoryStorage.prototype.makeIndexActive = function (indexName, callback) {
  callback();
};

MemoryStorage.prototype.queryContent = function (query, categories, pageNumber, perPage, callback) {
  var rx = new RegExp(query, 'i');

  var hits = this.indexedEnvelopes.filter(function (entry) {
    if (categories) {
      if (_.intersection(categories, entry._source.categories).length === 0) {
        return false;
      }
    }

    return rx.test([entry._source.title, entry._source.body, entry._source.keywords].join(' '));
  }).map(function (entry) {
    // Populate "highlights" as just the regexp matches, surrounded by <em> tags.
    var m = rx.exec(entry._source.body);
    var highlight = [];

    if (m !== null) {
      highlight.push('...<em>' + entry._source.body.substr(m.index, m[0].length) + '</em>...');
    }

    entry.highlight = {body: highlight};
    return entry;
  });

  // Mimic the important parts of the Elasticsearch response.
  callback(null, {
    hits: {
      total: hits.length,
      hits: hits
    }
  });
};

MemoryStorage.prototype.unindexContent = function (contentID, callback) {
  this.indexedEnvelopes = this.indexedEnvelopes.filter(function (each) {
    return each._id !== contentID;
  });

  callback(null);
};

MemoryStorage.prototype.storeSHA = function (sha, callback) {
  this.sha = sha;

  callback(null);
};

MemoryStorage.prototype.getSHA = function (callback) {
  callback(null, this.sha);
};

module.exports = {
  MemoryStorage: MemoryStorage
};
<commit_msg>Replicate the problem in MemoryStorage.<commit_after>var _ = require('lodash');

/**
 * @description Storage driver that uses entirely in-memory data structures.
 *
 * This is useful for unit testing and for local builds.
 */
function MemoryStorage () {}

MemoryStorage.prototype.setup = function (callback) {
  this.clear(callback);
};

MemoryStorage.prototype.clear = function (callback) {
  this.envelopes = {};
  this.indexedEnvelopes = [];
  this.assets = {};
  this.namedAssets = {};
  this.keys = {};
  this.sha = null;

  callback();
};

/**
 * @description Return prefix of the URL that assets are served under.
 */
MemoryStorage.prototype.assetURLPrefix = function () {
  return '/__local_asset__/';
};

MemoryStorage.prototype.storeAsset = function (asset, callback) {
  var assetBody = Buffer.concat(asset.chunks);

  this.assets[asset.filename] = {
    contentType: asset.type,
    body: assetBody
  };

  asset.publicURL = this.assetURLPrefix() + encodeURIComponent(asset.filename);

  callback(null, asset);
};

MemoryStorage.prototype.nameAsset = function (asset, callback) {
  this.namedAssets[asset.key] = {
    key: asset.key,
    publicURL: asset.publicURL
  };

  callback(null, asset);
};

MemoryStorage.prototype.findNamedAssets = function (callback) {
  callback(null, _.values(this.namedAssets));
};

MemoryStorage.prototype.getAsset = function (filename, callback) {
  var asset = this.assets[filename];

  if (asset === undefined) {
    var err = new Error('Memory storage error');

    err.statusCode = 404;
    err.responseBody = 'Oh snap';

    return callback(err);
  }

  callback(null, asset);
};

MemoryStorage.prototype.storeKey = function (key, callback) {
  this.keys[key.apikey] = key.name;
  callback();
};

MemoryStorage.prototype.deleteKey = function (apikey, callback) {
  delete this.keys[apikey];
  callback();
};

MemoryStorage.prototype.findKeys = function (apikey, callback) {
  var name = this.keys[apikey];
  if (name) {
    callback(null, [{
      apikey: apikey,
      name: name
    }]);
  } else {
    callback(null, []);
  }
};

MemoryStorage.prototype._storeContent = function (contentID, content, callback) {
  this.envelopes[contentID] = content;
  callback();
};

MemoryStorage.prototype._getContent = function (contentID, callback) {
  var envelope = this.envelopes[contentID];

  if (envelope === undefined) {
    var err = new Error('Memory storage error');

    err.statusCode = 404;
    err.responseBody = 'Oh snap';

    return callback(err);
  }

  callback(null, envelope);
};

MemoryStorage.prototype.deleteContent = function (contentID, callback) {
  delete this.envelopes[contentID];

  callback();
};

MemoryStorage.prototype.listContent = function (callback) {
  var ids = Object.keys(this.envelopes);

  callback(null, ids, function () {
    if (ids.length > 0) {
      callback(null, [], function () {});
    }
  });
};

MemoryStorage.prototype.createNewIndex = function (indexName, callback) {
  callback();
};

MemoryStorage.prototype._indexContent = function (contentID, envelope, indexName, callback) {
  this.indexedEnvelopes.push({
    _id: contentID,
    _source: envelope
  });

  callback();
};

MemoryStorage.prototype.makeIndexActive = function (indexName, callback) {
  callback();
};

MemoryStorage.prototype.queryContent = function (query, categories, pageNumber, perPage, callback) {
  var rx = new RegExp(query, 'i');

  var hits = this.indexedEnvelopes.filter(function (entry) {
    if (categories) {
      if (_.intersection(categories, entry._source.categories).length === 0) {
        return false;
      }
    }

    return rx.test([entry._source.title, entry._source.body, entry._source.keywords].join(' '));
  }).map(function (entry) {
    // Populate "highlights" as just the regexp matches, surrounded by <em> tags.
    var m = rx.exec(entry._source.body);

    if (m !== null) {
      var highlight = [`...<em>${m[0]}</em>...`];
      entry.highlight = {body: highlight};
    }

    return entry;
  });

  // Mimic the important parts of the Elasticsearch response.
  callback(null, {
    hits: {
      total: hits.length,
      hits: hits
    }
  });
};

MemoryStorage.prototype.unindexContent = function (contentID, callback) {
  this.indexedEnvelopes = this.indexedEnvelopes.filter(function (each) {
    return each._id !== contentID;
  });

  callback(null);
};

MemoryStorage.prototype.storeSHA = function (sha, callback) {
  this.sha = sha;

  callback(null);
};

MemoryStorage.prototype.getSHA = function (callback) {
  callback(null, this.sha);
};

module.exports = {
  MemoryStorage: MemoryStorage
};
<|endoftext|>


<commit_before>/* eslint-env browser */
const Docker = require('dockerode');
const assert = require('assert');
const concatStream = require('concat-stream');
const Promise = require('bluebird');
const path = require('path');
const tmp = require('tmp');
const shellescape = require('shell-escape');
const {getCodeLimit} = require('../controllers/utils.js');
const fs = Promise.promisifyAll(require('fs'));

const docker = new Docker();

const memoryLimit = 512 * 1024 * 1024;

class MemoryLimitExceededError extends Error {
	constructor(...args) {
		super(...args);
		this.name = 'MemoryLimitExceededError';
	}
}

module.exports = async ({id, code, stdin, trace}) => {
	assert(typeof id === 'string');
	assert(Buffer.isBuffer(code));
	assert(typeof stdin === 'string');
	assert(code.length <= getCodeLimit(id));
	assert(stdin.length < 10000);

	const {tmpPath, cleanup} = await new Promise((resolve, reject) => {
		tmp.dir({unsafeCleanup: true}, (error, dTmpPath, dCleanup) => {
			if (error) {
				reject(error);
			} else {
				resolve({tmpPath: dTmpPath, cleanup: dCleanup});
			}
		});
	});

	const stdinPath = path.join(tmpPath, 'INPUT');

	let filename = 'CODE';
	if (id === 'd-dmd') {
		filename = 'CODE.d';
	} else if (id === 'c-gcc') {
		filename = 'CODE.c';
	} else if (id === 'concise-folders' || id === 'pure-folders') {
		filename = 'CODE.tar';
	} else if (id === 'cmd') {
		filename = 'CODE.bat';
	} else if (id === 'nadesiko') {
		filename = 'CODE.nako3';
	}

	const codePath = path.join(tmpPath, filename);

	await Promise.all([
		fs.writeFileAsync(stdinPath, stdin),
		fs.writeFileAsync(codePath, code),
	]);

	let container = null;

	try {
		// eslint-disable-next-line init-declarations
		let stderrWriter, stdoutWriter;

		const stdoutPromise = new Promise((resolve) => {
			stdoutWriter = concatStream((stdout) => {
				resolve(stdout);
			});
		});

		const stderrPromise = new Promise((resolve) => {
			stderrWriter = concatStream((stderr) => {
				resolve(stderr);
			});
		});

		const dockerVolumePath = (() => {
			if (path.sep === '\\') {
				return tmpPath.replace('C:\\', '/c/').replace(/\\/g, '/');
			}

			return tmpPath;
		})();

		const containerPromise = (async () => {
			container = await docker.createContainer({
				Hostname: '',
				User: '',
				AttachStdin: false,
				AttachStdout: true,
				AttachStderr: true,
				Tty: false,
				OpenStdin: false,
				StdinOnce: false,
				Env: trace === true ? ['STRACE_OUTPUT_PATH=/volume/strace.log'] : null,
				Cmd: [
					'sh',
					'-c',
					`${shellescape(['script', `/volume/${filename}`])} < /volume/INPUT`,
				],
				Image: `esolang/${id}`,
				Volumes: {
					'/volume': {},
				},
				VolumesFrom: [],
				HostConfig: {
					Binds: [`${dockerVolumePath}:/volume:${trace === true ? 'rw' : 'ro'}`],
					Memory: memoryLimit,
					...(trace === true ? {CapAdd: ['SYS_PTRACE']} : {}),
				},
			});

			const stream = await container.attach({
				stream: true,
				stdout: true,
				stderr: true,
			});

			container.modem.demuxStream(stream, stdoutWriter, stderrWriter);
			stream.on('end', () => {
				stdoutWriter.end();
				stderrWriter.end();
			});

			await container.start();
			await container.wait();
			const data = await container.inspect();
			await container.remove();
			return data;
		})();

		const runner = Promise.all([
			stdoutPromise,
			stderrPromise,
			containerPromise,
		]);

		const executionStart = Date.now();
		const [stdout, stderr, containerData] = await runner.timeout(10000);
		const executionEnd = Date.now();

		const tracePath = path.join(tmpPath, 'strace.log');
		const traceLog = trace && (await fs.readFileAsync(tracePath));

		cleanup();

		return {
			stdout: Buffer.isBuffer(stdout) ? stdout : Buffer.alloc(0),
			stderr: Buffer.isBuffer(stderr) ? stderr : Buffer.alloc(0),
			duration: executionEnd - executionStart,
			...(containerData.State.OOMKilled
				? {
					error: new MemoryLimitExceededError(
						`Memory limit of ${memoryLimit} bytes exceeded`
					),
				  }
				: {}),
			trace: trace ? traceLog : null,
		};
	} catch (error) {
		if (container) {
			await container.kill().catch((e) => {
				console.error('error:', e);
			});
			await container.remove().catch((e) => {
				console.error('error:', e);
			});
		}
		throw error;
	}
};
<commit_msg>Disable tracing<commit_after>/* eslint-env browser */
const Docker = require('dockerode');
const assert = require('assert');
const concatStream = require('concat-stream');
const Promise = require('bluebird');
const path = require('path');
const tmp = require('tmp');
const shellescape = require('shell-escape');
const {getCodeLimit} = require('../controllers/utils.js');
const fs = Promise.promisifyAll(require('fs'));

const docker = new Docker();

const memoryLimit = 512 * 1024 * 1024;

class MemoryLimitExceededError extends Error {
	constructor(...args) {
		super(...args);
		this.name = 'MemoryLimitExceededError';
	}
}

module.exports = async ({id, code, stdin, trace}) => {
	assert(typeof id === 'string');
	assert(Buffer.isBuffer(code));
	assert(typeof stdin === 'string');
	assert(code.length <= getCodeLimit(id));
	assert(stdin.length < 10000);

	const {tmpPath, cleanup} = await new Promise((resolve, reject) => {
		tmp.dir({unsafeCleanup: true}, (error, dTmpPath, dCleanup) => {
			if (error) {
				reject(error);
			} else {
				resolve({tmpPath: dTmpPath, cleanup: dCleanup});
			}
		});
	});

	const stdinPath = path.join(tmpPath, 'INPUT');

	let filename = 'CODE';
	if (id === 'd-dmd') {
		filename = 'CODE.d';
	} else if (id === 'c-gcc') {
		filename = 'CODE.c';
	} else if (id === 'concise-folders' || id === 'pure-folders') {
		filename = 'CODE.tar';
	} else if (id === 'cmd') {
		filename = 'CODE.bat';
	} else if (id === 'nadesiko') {
		filename = 'CODE.nako3';
	}

	const codePath = path.join(tmpPath, filename);

	await Promise.all([
		fs.writeFileAsync(stdinPath, stdin),
		fs.writeFileAsync(codePath, code),
	]);

	let container = null;

	try {
		// eslint-disable-next-line init-declarations
		let stderrWriter, stdoutWriter;

		const stdoutPromise = new Promise((resolve) => {
			stdoutWriter = concatStream((stdout) => {
				resolve(stdout);
			});
		});

		const stderrPromise = new Promise((resolve) => {
			stderrWriter = concatStream((stderr) => {
				resolve(stderr);
			});
		});

		const dockerVolumePath = (() => {
			if (path.sep === '\\') {
				return tmpPath.replace('C:\\', '/c/').replace(/\\/g, '/');
			}

			return tmpPath;
		})();

		const containerPromise = (async () => {
			container = await docker.createContainer({
				Hostname: '',
				User: '',
				AttachStdin: false,
				AttachStdout: true,
				AttachStderr: true,
				Tty: false,
				OpenStdin: false,
				StdinOnce: false,
				Cmd: [
					'sh',
					'-c',
					`${shellescape(['script', `/volume/${filename}`])} < /volume/INPUT`,
				],
				Image: `esolang/${id}`,
				Volumes: {
					'/volume': {},
				},
				VolumesFrom: [],
				HostConfig: {
					Binds: [`${dockerVolumePath}:/volume:${trace === true ? 'rw' : 'ro'}`],
					Memory: memoryLimit,
					...(trace === true ? {CapAdd: ['SYS_PTRACE']} : {}),
				},
			});

			const stream = await container.attach({
				stream: true,
				stdout: true,
				stderr: true,
			});

			container.modem.demuxStream(stream, stdoutWriter, stderrWriter);
			stream.on('end', () => {
				stdoutWriter.end();
				stderrWriter.end();
			});

			await container.start();
			await container.wait();
			const data = await container.inspect();
			await container.remove();
			return data;
		})();

		const runner = Promise.all([
			stdoutPromise,
			stderrPromise,
			containerPromise,
		]);

		const executionStart = Date.now();
		const [stdout, stderr, containerData] = await runner.timeout(10000);
		const executionEnd = Date.now();

		const tracePath = path.join(tmpPath, 'strace.log');
		const traceLog = trace && (await fs.readFileAsync(tracePath));

		cleanup();

		return {
			stdout: Buffer.isBuffer(stdout) ? stdout : Buffer.alloc(0),
			stderr: Buffer.isBuffer(stderr) ? stderr : Buffer.alloc(0),
			duration: executionEnd - executionStart,
			...(containerData.State.OOMKilled
				? {
					error: new MemoryLimitExceededError(
						`Memory limit of ${memoryLimit} bytes exceeded`
					),
				  }
				: {}),
			trace: trace ? traceLog : null,
		};
	} catch (error) {
		if (container) {
			await container.kill().catch((e) => {
				console.error('error:', e);
			});
			await container.remove().catch((e) => {
				console.error('error:', e);
			});
		}
		throw error;
	}
};
<|endoftext|>


<commit_before>/*
 * This file is part of the hyyan/woo-poly-integration plugin.
 * (c) Hyyan Abo Fakher <tiribthea4hyyan@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

(function ($) {

    /**
     * Constrcut the class
     *
     * @param {jQuery} $ jquery object
     * @returns {FieldsLocker_L1.FieldsLocker}
     */
    var FieldsLocker = function ($) {
        this.$ = $;
    };

    FieldsLocker.prototype = {
        /**
         * Constrcutor
         */
        constructor: FieldsLocker,
        /**
         * Do lock all fields
         */
        init: function () {

            // Enable fields again before saving the post
            $('#post').bind('submit', function () {
                $(this).find(':input').removeAttr('disabled');
            });

            // handle disabled items
            var disabled = this.getDisabledItems();
            for (var i = 0; i < disabled.length; i++) {
                $(disabled[i]).attr('disabled', true);
            }

            // handle deleted items
            var deleted = this.getDeletedItems();
            for (var i = 0; i < deleted.length; i++) {
                $(deleted[i]).css({visibility: 'hidden'});
            }
        }

        /**
         * Get array of selectors to disable
         *
         * @returns Array
         */
        , getDisabledItems: function () {
            // default ids
            var items = [
                // quick edit
                '#woocommerce-fields :input',
                // product types
                '.type_box :input',
                // inputs : general
                '#general_product_data :input:not(#_button_text):not(#_product_url)',
                // inputs : stock
                '#inventory_product_data :input',
                // inputs : shipping
                '#shipping_product_data :input',
                // inputs : related
                '#parent_id',
                // inputs : attributes
                '#product_attributes :input',
                // inputs : advanced setting
                '#comment_status',
                '#menu_order'
            ];

            // fire event to allow other plugins to extend the list
            this.$('body').trigger('getDisabledItems.Hyyan_WPI', [items]);

            return items;
        }

        /**
         * Get array of selectors to delete
         *
         * @returns {Array}
         */
        , getDeletedItems: function () {
            // default items
            var items = [
                '#general_product_data .delete'
            ];

            // fire event to allow other plugins to extend the list
            this.$('body').trigger('getDeletedItems.Hyyan_WPI', [items]);

            return items;
        }
    };

    // bootstrap
    $(document).ready(function ($) {
        new FieldsLocker($).init();
    });

})(window.jQuery);
<commit_msg>Emulated disable in FieldsLocker<commit_after>/*
 * This file is part of the hyyan/woo-poly-integration plugin.
 * (c) Hyyan Abo Fakher <tiribthea4hyyan@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

(function ($, document) {

    /**
     * Constrcut the class
     *
     * @param {jQuery} $ jquery object
     * @param {document} document
     * @returns {FieldsLocker_L9.FieldsLocker}
     */
    var FieldsLocker = function ($, document) {
        this.$ = $;
        this.document = document;
    };

    FieldsLocker.prototype = {
        /**
         * Constrcutor
         */
        constructor: FieldsLocker,
        /**
         * Do lock all fields
         */
        init: function () {

            // handle disabled items
            var disabled = this.getDisabledItems();
            for (var i = 0; i < disabled.length; i++) {
                $(disabled[i])
                        .attr('onclick', 'event.preventDefault();') // for checkboxes;
                        .css({
                            'opacity': .5,
                            'pointer-events': 'none',
                            'cursor': 'not-allowed'
                        });
            }

            // handle deleted items
            var deleted = this.getDeletedItems();
            for (var i = 0; i < deleted.length; i++) {
                $(deleted[i]).css({visibility: 'hidden'});
            }

        }

        /**
         * Get array of selectors to disable
         *
         * @returns Array
         */
        , getDisabledItems: function () {
            // default ids
            var items = [
                // quick edit
                '#woocommerce-fields :input',
                // product types
                '.type_box :input',
                // inputs : general
                '#general_product_data :input:not(#_button_text):not(#_product_url)',
                // inputs : stock
                '#inventory_product_data :input',
                // inputs : shipping
                '#shipping_product_data :input',
                // inputs : related
                '#parent_id',
                // inputs : attributes
                '#product_attributes :input',
                // inputs : advanced setting
                '#comment_status',
                '#menu_order'
            ];

            // fire event to allow other plugins to extend the list
            this.$('body').trigger('getDisabledItems.Hyyan_WPI', [items]);

            return items;
        }

        /**
         * Get array of selectors to delete
         *
         * @returns {Array}
         */
        , getDeletedItems: function () {
            // default items
            var items = [
                '#general_product_data .delete'
            ];

            // fire event to allow other plugins to extend the list
            this.$('body').trigger('getDeletedItems.Hyyan_WPI', [items]);

            return items;
        }
    };

    // bootstrap
    $(document).ready(function ($) {
        new FieldsLocker($).init();
    });

})(window.jQuery, document);
<|endoftext|>


<commit_before>"use strict";

/*jshint node:true strict: true */

var Rule = require(__dirname + '/rule.js');

/**
 * tld library
 *
 * Useable methods are those documented with an @api in JSDoc
 * See README.md for more explainations on how to use this stuff.
 */
function tld () {
  /*jshint validthis: true */
  this.rules = [];
}

tld.init = function init () {
  return new tld();
};

/**
 * Returns the best rule for a given host based on candidates
 *
 * @static
 * @param host {String} Hostname to check rules against
 * @param rules {Array} List of rules used to work on
 * @return {Object} Candidate object, with a normal and exception state
 */
tld.getCandidateRule = function getCandidateRule (host, rules) {
  var rule = {'normal': null, 'exception': null};

  rules.some(function (r) {
    var pattern;

    //sld matching? escape the loop immediately (except if it's an exception)
    if ('.' + host === r.getNormalXld()) {
      if (r.exception === true) {
        rule.normal = r;
      }

      return true;
    }

    //otherwise check as a complete host
    //if it's an exception, we want to loop a bit more to a normal rule
    pattern = '.+' + r.getNormalPattern() + '$';

    if ((new RegExp(pattern)).test(host)) {
      rule[r.exception ? 'exception' : 'normal'] = r;
      return !r.exception;
    }

    return false;
  });

  //if there is an exception, we challenge its rules without changing pattern
  if (rule.normal && rule.exception) {
    rule.normal.wildcard = rule.exception.wildcard;
  }

  return rule.normal;
};

/**
 * Retrieve a subset of rules for a Top-Level-Domain string
 *
 * @param tld {String} Top-Level-Domain string
 * @return {Array} Rules subset
 */
tld.prototype.getRulesForTld = function getRulesForTld (tld, default_rule) {
  var exception = '!';
  var wildcard = '*';
  var append_tld_rule = true;
  var rules = this.rules[tld];

  // Already parsed
  if (Array.isArray(rules)) {
    return rules;
  }

  // Nothing found, apply some default value
  if (!rules) {
    return default_rule ? [ default_rule ] : [];
  }

  // Parsing needed
  rules = rules.split('|').map(function transformAsRule (sld) {
    var first_bit = sld[0];

    if (first_bit === exception || first_bit === wildcard) {
      sld = sld.slice(1);

      if (!sld) {
        append_tld_rule = false;
      }
    }

    return new Rule({
      "firstLevel":  tld,
      "secondLevel": sld,
      "exception":   first_bit === exception,
      "wildcard":    first_bit === wildcard
    });
  });

  // Always prepend to make it the latest rule to be applied
  if (append_tld_rule) {
    rules.unshift(new Rule({
      "firstLevel": tld
    }));
  }

  this.rules[tld] = rules.reverse();

  return rules;
};

/**
 * Checks if the TLD exists for a given host
 *
 * @api
 * @param {string} host
 * @return {boolean}
 */
tld.prototype.tldExists = function tldExists(host){
  var hostTld;

  host = tld.cleanHostValue(host);

  // Easy case, it's a TLD
  if (this.rules[host]){
    return true;
  }

  // Popping only the TLD of the hostname
  hostTld = tld.extractTldFromHost(host);

  return this.rules[hostTld] !== undefined;
};

/**
 * Returns the public suffix (including exact matches)
 *
 * @api
 * @param {string} host
 * @return {String}
 */
tld.prototype.getPublicSuffix = function getPublicSuffix(host) {
  if (this.tldExists(host)){
	  return host;
  }

  return this.getDomain(host);
};

/**
 * Detects the domain based on rules and upon and a host string
 *
 * @api
 * @param {string} host
 * @return {String}
 */
tld.prototype.getDomain = function getDomain (host) {
  var domain = null, hostTld, rules, rule;

  if (this.isValid(host) === false) {
    return null;
  }

  host = tld.cleanHostValue(host);
  hostTld = tld.extractTldFromHost(host);
  rules = this.getRulesForTld(hostTld, new Rule({"firstLevel": hostTld}));
  rule = tld.getCandidateRule(host, rules);

  if (rule === null) {
    return null;
  }

  host.replace(new RegExp(rule.getPattern()), function (m, d) {
    domain = d;
  });

  return domain;
};

/**
 * Returns the subdomain of a host string
 *
 * @api
 * @param {string} host
 * @return {string|null} a subdomain string if any, blank string if subdomain is empty, otherwise null
 */
tld.prototype.getSubdomain = function getSubdomain(host){
  var domain, r, subdomain = null;

  host = tld.cleanHostValue(host);
  domain = this.getDomain(host);

  // No domain found? Just abort, abort!
  if (domain === null){
    return subdomain;
  }

  r = '\\.?'+(domain.replace(/\./g, '\\.'))+'$';
  subdomain = host.replace(new RegExp(r, 'i'), '');

  return subdomain;
};

/**
 * Checking if a host string is valid
 * It's usually a preliminary check before trying to use getDomain or anything else
 *
 * Beware: it does not check if the TLD exists.
 *
 * @api
 * @todo handle localhost, local etc.
 * @param host {String}
 * @return {Boolean}
 */
tld.prototype.isValid = function isValid (host) {
  return !(typeof host !== 'string' || host.indexOf('.') === -1 || host[0] === '.');
};

/**
 * Utility to cleanup the base host value
 *
 * @param {string} host
 * @return {String}
 */
tld.cleanHostValue = function cleanHostValue(host){
  return (host+'').trim().toLowerCase();
};

/**
 * Utility to extract the TLD from a host string
 *
 * @param {string} host
 * @return {String}
 */
tld.extractTldFromHost = function extractTldFromHost(host){
  return host.split('.').pop();
};

module.exports = tld;
<commit_msg>Misunderstood 'tldExists', fixed getPublicSuffix to compensate<commit_after>"use strict";

/*jshint node:true strict: true */

var Rule = require(__dirname + '/rule.js');

/**
 * tld library
 *
 * Useable methods are those documented with an @api in JSDoc
 * See README.md for more explainations on how to use this stuff.
 */
function tld () {
  /*jshint validthis: true */
  this.rules = [];
}

tld.init = function init () {
  return new tld();
};

/**
 * Returns the best rule for a given host based on candidates
 *
 * @static
 * @param host {String} Hostname to check rules against
 * @param rules {Array} List of rules used to work on
 * @return {Object} Candidate object, with a normal and exception state
 */
tld.getCandidateRule = function getCandidateRule (host, rules) {
  var rule = {'normal': null, 'exception': null};

  rules.some(function (r) {
    var pattern;

    //sld matching? escape the loop immediately (except if it's an exception)
    if ('.' + host === r.getNormalXld()) {
      if (r.exception === true) {
        rule.normal = r;
      }

      return true;
    }

    //otherwise check as a complete host
    //if it's an exception, we want to loop a bit more to a normal rule
    pattern = '.+' + r.getNormalPattern() + '$';

    if ((new RegExp(pattern)).test(host)) {
      rule[r.exception ? 'exception' : 'normal'] = r;
      return !r.exception;
    }

    return false;
  });

  //if there is an exception, we challenge its rules without changing pattern
  if (rule.normal && rule.exception) {
    rule.normal.wildcard = rule.exception.wildcard;
  }

  return rule.normal;
};

/**
 * Retrieve a subset of rules for a Top-Level-Domain string
 *
 * @param tld {String} Top-Level-Domain string
 * @return {Array} Rules subset
 */
tld.prototype.getRulesForTld = function getRulesForTld (tld, default_rule) {
  var exception = '!';
  var wildcard = '*';
  var append_tld_rule = true;
  var rules = this.rules[tld];

  // Already parsed
  if (Array.isArray(rules)) {
    return rules;
  }

  // Nothing found, apply some default value
  if (!rules) {
    return default_rule ? [ default_rule ] : [];
  }

  // Parsing needed
  rules = rules.split('|').map(function transformAsRule (sld) {
    var first_bit = sld[0];

    if (first_bit === exception || first_bit === wildcard) {
      sld = sld.slice(1);

      if (!sld) {
        append_tld_rule = false;
      }
    }

    return new Rule({
      "firstLevel":  tld,
      "secondLevel": sld,
      "exception":   first_bit === exception,
      "wildcard":    first_bit === wildcard
    });
  });

  // Always prepend to make it the latest rule to be applied
  if (append_tld_rule) {
    rules.unshift(new Rule({
      "firstLevel": tld
    }));
  }

  this.rules[tld] = rules.reverse();

  return rules;
};

/**
 * Checks if the TLD exists for a given host
 *
 * @api
 * @param {string} host
 * @return {boolean}
 */
tld.prototype.tldExists = function tldExists(host){
  var hostTld;

  host = tld.cleanHostValue(host);

  // Easy case, it's a TLD
  if (this.rules[host]){
    return true;
  }

  // Popping only the TLD of the hostname
  hostTld = tld.extractTldFromHost(host);

  return this.rules[hostTld] !== undefined;
};

/**
 * Returns the public suffix (including exact matches)
 *
 * @api
 * @param {string} host
 * @return {String}
 */
tld.prototype.getPublicSuffix = function getPublicSuffix(host) {
  if (this.rules[host](host)){
	  return host;
  }

  return this.getDomain(host);
};

/**
 * Detects the domain based on rules and upon and a host string
 *
 * @api
 * @param {string} host
 * @return {String}
 */
tld.prototype.getDomain = function getDomain (host) {
  var domain = null, hostTld, rules, rule;

  if (this.isValid(host) === false) {
    return null;
  }

  host = tld.cleanHostValue(host);
  hostTld = tld.extractTldFromHost(host);
  rules = this.getRulesForTld(hostTld, new Rule({"firstLevel": hostTld}));
  rule = tld.getCandidateRule(host, rules);

  if (rule === null) {
    return null;
  }

  host.replace(new RegExp(rule.getPattern()), function (m, d) {
    domain = d;
  });

  return domain;
};

/**
 * Returns the subdomain of a host string
 *
 * @api
 * @param {string} host
 * @return {string|null} a subdomain string if any, blank string if subdomain is empty, otherwise null
 */
tld.prototype.getSubdomain = function getSubdomain(host){
  var domain, r, subdomain = null;

  host = tld.cleanHostValue(host);
  domain = this.getDomain(host);

  // No domain found? Just abort, abort!
  if (domain === null){
    return subdomain;
  }

  r = '\\.?'+(domain.replace(/\./g, '\\.'))+'$';
  subdomain = host.replace(new RegExp(r, 'i'), '');

  return subdomain;
};

/**
 * Checking if a host string is valid
 * It's usually a preliminary check before trying to use getDomain or anything else
 *
 * Beware: it does not check if the TLD exists.
 *
 * @api
 * @todo handle localhost, local etc.
 * @param host {String}
 * @return {Boolean}
 */
tld.prototype.isValid = function isValid (host) {
  return !(typeof host !== 'string' || host.indexOf('.') === -1 || host[0] === '.');
};

/**
 * Utility to cleanup the base host value
 *
 * @param {string} host
 * @return {String}
 */
tld.cleanHostValue = function cleanHostValue(host){
  return (host+'').trim().toLowerCase();
};

/**
 * Utility to extract the TLD from a host string
 *
 * @param {string} host
 * @return {String}
 */
tld.extractTldFromHost = function extractTldFromHost(host){
  return host.split('.').pop();
};

module.exports = tld;
<|endoftext|>


<commit_before>(function(){
var kbNav = window.kbNav;

var POPUP_ID = 'kbNavSelectBox';

function actionSelect($obj, autonum, nums) {
    return function() {
        var hasNums = false;
        $obj.focus();
        var $copy = $obj.clone();
        var pos = $obj.position();
        $('body').append('

');
        var top = getDropDownTop($obj, $copy, ($obj.attr('size') > 1));
        var $box = $('#' + POPUP_ID);

        copySelected($obj.get(0).options, $copy.get(0).options);

        kbNav.push();
        var $options = $('option', $copy);
        var i = 0;
        $options.each(function() {
            var opt = this,
            $o = $(opt),
            num = autonum ? i+1 :
                    nums !== undefined ? nums[i] : undefined;

            if(num !== undefined) {
                hasNums = true;
                kbNav.register(num, function() {
                    $copy.focus();
                    opt.selected = !opt.selected;
                });
                $o.prepend(num + ') ');
            }
            i++;
        });

        $box.css({
            position: 'absolute',
            top: top + 'px',
            left: pos.left + 'px'
        }).append($copy);

        var openingCommandPrompt = false;

        $copy.blur(function() {
            if(!openingCommandPrompt) {
                $box.remove();
                kbNav.pop();
            }
        }).keypress(function(e) {
            var keycode = e.which;
            if(keycode === 27) // esc
                $copy.blur();
            else if(keycode === 13) { // enter
                copySelected(this.options, $obj.get(0).options);
                $copy.blur();
            }
        }).focus();

        if(hasNums) {
            $copy.keydown(function(e) {
                if(e.ctrlKey || e.altKey)
                    return;

                var keycode = e.which;
                var input = String.fromCharCode(keycode);
                if(input.match(/\w/)) {
                    openingCommandPrompt = true;
                    kbNav.showPrompt($copy.get(0)).keyup(function() {
                        openingCommandPrompt = false;
                    });
                }
            });
        }
    };
}

function getDropDownTop($target, $copy, isML) {
    var pos = $target.offset();
    var height = $target.outerHeight();
    var scrollTop = $(window).scrollTop();

    var select = $copy.get(0);
    select.size = select.options.length;
    var $elem = $copy.appendTo('body');
    var targetHeight = $copy.outerHeight();
    $elem.remove();

    // TODO: find better way of doing this :(
    // safari doesn't return correct values for window.height()
    var windowHeight = $.browser.safari ? document.documentElement.clientHeight : $(window).height();
    var spaceAbove = pos.top - scrollTop;
    var spaceBelow = windowHeight - spaceAbove - height;

    var setTop;
    var setHeight;
    console.log('isml', isML);
    if(isML) {
        // if multiline, place popup box over the select
        var center = pos.top + height/2;
        var radius = targetHeight/2;

        if(targetHeight <= height) {
            // popup box fits within the select
            setHeight = height;
            setTop = pos.top;
        }
        else if(center - radius >= scrollTop && center + radius <= scrollTop + windowHeight) {
            // popup box is centered on the middle of the select
            setHeight = targetHeight;
            setTop = center - radius;
        }
        else {
            // popup box is centered on the middle of the screen
            setHeight = Math.min(targetHeight, windowHeight);
            setTop = scrollTop + (windowHeight - setHeight) / 2;
        }
    }
    else {
        // if not, place popup box under the select
        if(targetHeight <= spaceBelow) {
            // popup box fits below the select
            setHeight = targetHeight;
            setTop = pos.top + height;
        }
        else if(targetHeight <= spaceAbove) {
            // popup box fits above the select
            setHeight = targetHeight;
            setTop = pos.top - setHeight;
        }
        else {
            // popup box is centered on the middle of the screen
            setHeight = Math.min(targetHeight, windowHeight);
            setTop = scrollTop + (windowHeight - setHeight) / 2;
        }
    }

    $copy.height(setHeight);
    return setTop;
}

function copySelected(from, to) {
    for(var i=0, l=from.length; i<l; i++) {
        to[i].selected = from[i].selected;
    }
}

kbNav.processAction.select = function($o, data) {
    return actionSelect($o, data.autoNum, data.nums);
};

})();
<commit_msg>remove console.log, oops<commit_after>(function(){
var kbNav = window.kbNav;

var POPUP_ID = 'kbNavSelectBox';

function actionSelect($obj, autonum, nums) {
    return function() {
        var hasNums = false;
        $obj.focus();
        var $copy = $obj.clone();
        var pos = $obj.position();
        $('body').append('

');
        var top = getDropDownTop($obj, $copy, ($obj.attr('size') > 1));
        var $box = $('#' + POPUP_ID);

        copySelected($obj.get(0).options, $copy.get(0).options);

        kbNav.push();
        var $options = $('option', $copy);
        var i = 0;
        $options.each(function() {
            var opt = this,
            $o = $(opt),
            num = autonum ? i+1 :
                    nums !== undefined ? nums[i] : undefined;

            if(num !== undefined) {
                hasNums = true;
                kbNav.register(num, function() {
                    $copy.focus();
                    opt.selected = !opt.selected;
                });
                $o.prepend(num + ') ');
            }
            i++;
        });

        $box.css({
            position: 'absolute',
            top: top + 'px',
            left: pos.left + 'px'
        }).append($copy);

        var openingCommandPrompt = false;

        $copy.blur(function() {
            if(!openingCommandPrompt) {
                $box.remove();
                kbNav.pop();
            }
        }).keypress(function(e) {
            var keycode = e.which;
            if(keycode === 27) // esc
                $copy.blur();
            else if(keycode === 13) { // enter
                copySelected(this.options, $obj.get(0).options);
                $copy.blur();
            }
        }).focus();

        if(hasNums) {
            $copy.keydown(function(e) {
                if(e.ctrlKey || e.altKey)
                    return;

                var keycode = e.which;
                var input = String.fromCharCode(keycode);
                if(input.match(/\w/)) {
                    openingCommandPrompt = true;
                    kbNav.showPrompt($copy.get(0)).keyup(function() {
                        openingCommandPrompt = false;
                    });
                }
            });
        }
    };
}

function getDropDownTop($target, $copy, isML) {
    var pos = $target.offset();
    var height = $target.outerHeight();
    var scrollTop = $(window).scrollTop();

    var select = $copy.get(0);
    select.size = select.options.length;
    var $elem = $copy.appendTo('body');
    var targetHeight = $copy.outerHeight();
    $elem.remove();

    // TODO: find better way of doing this :(
    // safari doesn't return correct values for window.height()
    var windowHeight = $.browser.safari ? document.documentElement.clientHeight : $(window).height();
    var spaceAbove = pos.top - scrollTop;
    var spaceBelow = windowHeight - spaceAbove - height;

    var setTop;
    var setHeight;
    if(isML) {
        // if multiline, place popup box over the select
        var center = pos.top + height/2;
        var radius = targetHeight/2;

        if(targetHeight <= height) {
            // popup box fits within the select
            setHeight = height;
            setTop = pos.top;
        }
        else if(center - radius >= scrollTop && center + radius <= scrollTop + windowHeight) {
            // popup box is centered on the middle of the select
            setHeight = targetHeight;
            setTop = center - radius;
        }
        else {
            // popup box is centered on the middle of the screen
            setHeight = Math.min(targetHeight, windowHeight);
            setTop = scrollTop + (windowHeight - setHeight) / 2;
        }
    }
    else {
        // if not, place popup box under the select
        if(targetHeight <= spaceBelow) {
            // popup box fits below the select
            setHeight = targetHeight;
            setTop = pos.top + height;
        }
        else if(targetHeight <= spaceAbove) {
            // popup box fits above the select
            setHeight = targetHeight;
            setTop = pos.top - setHeight;
        }
        else {
            // popup box is centered on the middle of the screen
            setHeight = Math.min(targetHeight, windowHeight);
            setTop = scrollTop + (windowHeight - setHeight) / 2;
        }
    }

    $copy.height(setHeight);
    return setTop;
}

function copySelected(from, to) {
    for(var i=0, l=from.length; i<l; i++) {
        to[i].selected = from[i].selected;
    }
}

kbNav.processAction.select = function($o, data) {
    return actionSelect($o, data.autoNum, data.nums);
};

})();
<|endoftext|>