<commit_before>var fs=require("fs");
/*
    check if exists
    check if is directory
        if is directory
            check if default filename exists
                if exists respond
                else next
        else is file
            respond
*/

/* Static route */
module.exports=function(opt){
    opt=opt||{};
    opt.def=opt.def||'index.html';
    opt.debug=opt.debug||false;
    var error=opt.error||console.error;

    /* if no type can be inferred, say that it's utf-8 html */
    var defaultType=(typeof opt.defaultType === 'undefined'? 'text/html;charset=utf-8':opt.defaultType);
    // opt.path is required

    var BIN='otf ttf woff ico gif png jpeg jpg pdf exe ogv mp4 webm'.split(' ');

    var DICT={
        otf:'application/font-sfnt',
        html:'text/html',
        htm:'text/html',
        ttf:'application/x-font-ttf',
        woff:'application/x-font-woff',
        svg:'image/svg+xml',
        css:'text/css',
        json:'application/json',
        js:'text/javascript;charset=UTF-8',
        ico:'image/x-icon',
        gif:'image/gif',
        png:'image/png',
        jpeg:'image/jpeg',
        jpg:'image/jpeg',
        pdf:'application/pdf',
        exe:'application/x-msdownload',
        ogv:'video/ogg',
        mp4:'video/mp4',
        webm:'video/webm',
        xml:'application/rss+xml',
    };

    // override the dictionary with your own headers, if you want
    if(opt.dict){
        Object.keys(opt.dict)
            .map(function(k){
                DICT[k]=opt.dict[k];
            });
    }

    // backwards compatibility
    var statpath=opt.path;
    var def=opt.def;

    return function (req, res, next) {
        var path = statpath + decodeURIComponent(req.url);
        var bin;

        var response = function (path) {
            var ext;
            path.replace(/\.[^.]*$/, function (e) {
                ext = e.slice(1);
            });
            var type;

            type = DICT[ext]; //||'text/plain;charset=UTF-8';
            bin = BIN.indexOf(ext);

            fs.readFile(path, (!bin) ? "utf8" :null, function (err,data) {
                if(err) error(err), next();
                else
                    res.statusCode=200,
                    res.setHeader("Content-Type", type || defaultType),
                    // TODO don't buffer then send, pipe
                    res.end(data);
            });
        };

        fs.stat(path,function(err,stats){
            if(err) return error(err),next();

            // is it a directory?
            if(stats.isDirectory()){
                response(path+def);
            }else if (stats.isFile()){
                response(path);
            }else{
                next()
            }
        });
    };
};
<commit_msg>text/plain<commit_after>var fs=require("fs");
/*
    check if exists
    check if is directory
        if is directory
            check if default filename exists
                if exists respond
                else next
        else is file
            respond
*/

/* Static route */
module.exports=function(opt){
    opt=opt||{};
    opt.def=opt.def||'index.html';
    opt.debug=opt.debug||false;
    var error=opt.error||console.error;

    /* if no type can be inferred, say that it's utf-8 html */
    var defaultType=(typeof opt.defaultType === 'undefined'? 'text/html;charset=utf-8':opt.defaultType);
    // opt.path is required

    var BIN='otf ttf woff ico gif png jpeg jpg pdf exe ogv mp4 webm'.split(' ');

    var DICT={
        otf:'application/font-sfnt',
        html:'text/html',
        htm:'text/html',
        ttf:'application/x-font-ttf',
        woff:'application/x-font-woff',
        svg:'image/svg+xml',
        css:'text/css',
        json:'application/json',
        js:'text/javascript;charset=UTF-8',
        ico:'image/x-icon',
        gif:'image/gif',
        png:'image/png',
        jpeg:'image/jpeg',
        jpg:'image/jpeg',
        pdf:'application/pdf',
        exe:'application/x-msdownload',
        ogv:'video/ogg',
        mp4:'video/mp4',
        webm:'video/webm',
        xml:'application/rss+xml',
        txt:'text/plain;charset=UTF-8',
        text:'text/plain;charset=UTF-8',
    };

    // override the dictionary with your own headers, if you want
    if(opt.dict){
        Object.keys(opt.dict)
            .map(function(k){
                DICT[k]=opt.dict[k];
            });
    }

    // backwards compatibility
    var statpath=opt.path;
    var def=opt.def;

    return function (req, res, next) {
        var path = statpath + decodeURIComponent(req.url);
        var bin;

        var response = function (path) {
            var ext;
            path.replace(/\.[^.]*$/, function (e) {
                ext = e.slice(1);
            });
            var type;

            type = DICT[ext]; //||'text/plain;charset=UTF-8';
            bin = BIN.indexOf(ext);

            fs.readFile(path, (!bin) ? "utf8" :null, function (err,data) {
                if(err) error(err), next();
                else
                    res.statusCode=200,
                    res.setHeader("Content-Type", type || defaultType),
                    // TODO don't buffer then send, pipe
                    res.end(data);
            });
        };

        fs.stat(path,function(err,stats){
            if(err) return error(err),next();

            // is it a directory?
            if(stats.isDirectory()){
                response(path+def);
            }else if (stats.isFile()){
                response(path);
            }else{
                next()
            }
        });
    };
};
<|endoftext|>


<commit_before>// caniuse.com functionality for oftn-bot
// by ImBcmDth - jon.carlos.rivera@gmail.com
//
// Thanks to FireFly, sorella, gkatsev, Morthchek,
// and eboy for your testing and suggestions.
//
// All data provided by the good folks at <http://caniuse.com/>

var Utils = require("util");
var File = require("fs");
var Path = require('path');

var agentsToShow = {
	'ie': 'IE',
	'firefox': 'FF',
	'chrome': 'Chrome',
	'opera': 'Opera',
	'safari': 'Safari',
	'ios_saf': 'iOS',
	'android': 'Android'
};

var agentSupportToColors = {
	'n': '\x034',
	'y': '\x033',
	'p': '\x038',
	'a': '\x038',
	'u': '\x03E',
	'x': '\x03E'
};

var CanIUseServer = module.exports = function(filename) {
	if (filename) {
		this.filename = filename;
	} else {
		this.filename = Path.join(__dirname, "data/data.json");
	}

	File.readFile(this.filename, function (err, data) {
		try {
			if (err) {
				throw err;
			}
			Utils.puts("Loaded file: "+Path.basename(this.filename));
			var data = JSON.parse(data);
			this.db = data;
			this.buildIndex();
		} catch (e) {
			Utils.puts("JSON Parse Error: "+e);
		}
	}.bind(this));
};

CanIUseServer.prototype.find = function(key) {
	var db = this.db.data;

	if (typeof db[key] === "undefined") {
		throw new Error("Can I Use `"+key+"` was not found.");
	}

	var feature = db[key];
	var agentSupportInfo = this.getAgentSupportArray(feature);
	var agentSupportStrings = this.formatAgentSupportArray(agentSupportInfo.supported);
	var supportedString = agentSupportStrings.join(' | ');
	var unsupportedString = agentSupportInfo.unsupported.join(', ');
	var overallPercent = (feature.usage_perc_y + feature.usage_perc_a);

	return formatResponse(feature.title, supportedString, unsupportedString, overallPercent, key);
};

CanIUseServer.prototype.search = function(key) {
	key = key.toLowerCase();
	try {
		return this.find(key);
	} catch(e) {
		var i = this.index.length;

		while (i--) {
			if (this.index[i].search(key) > -1) {
				return this.find(this.keys[i]);
			}
		}

		throw e;
	}
}

CanIUseServer.prototype.formatAgentSupportArray = function(agentSupport) {
	return agentSupport.map(makeAgentSupportStrings, this.db.agents)

	function makeAgentSupportStrings(agentInfo) {
		var concatedName = agentInfo.name + " " + agentInfo.version;
		var supportFlags = agentInfo.support.split(' ');
		var agentColor = agentSupportToColors[supportFlags[0]];

		if (supportFlags.indexOf('p') > -1) {
			agentInfo.version += ' (w/polyfill)';
		}
		else if (supportFlags.indexOf('x') > -1) {
			agentInfo.version += ' (w/prefix)';
		}

		return agentInfo.name + agentColor + ' ' + agentInfo.version + '\x0F';
	}
}

CanIUseServer.prototype.getAgentSupportArray = function(feature) {
	var data = this.db;
	var agents = data.agents;

	var agentSupportObject = {supported:[], unsupported:[]};

	Object.keys(agentsToShow).forEach(getMinimalAgentSupport);

	return agentSupportObject;

	function getMinimalAgentSupport(agent) {
		var agentName = agentsToShow[agent];
		var agentSupport;

		var supportedVersions = agents[agent].versions.filter(onlyTotallySupported);

		if (supportedVersions.length === 0) {
			supportedVersions = agents[agent].versions.filter(onlySomewhatSupported);
		}

		if (supportedVersions.length !== 0) {
			agentSupport = feature.stats[agent][supportedVersions[0]];

			agentSupportObject.supported.push({
				name: agentName,
				version: supportedVersions[0] + '+',
				support: agentSupport
			});
		} else {
			agentSupportObject.unsupported.push(agentName);
		}

		function onlyTotallySupported(version) {
			return (version != null && !/[n|u|p|a|x]/g.test(feature.stats[agent][version]));
		}

		function onlySomewhatSupported(version) {
			return (version != null && !/[n|u|y]/g.test(feature.stats[agent][version]));
		}
	}
}

CanIUseServer.prototype.buildIndex = function() {
	var db = this.db.data;

	this.keys = Object.keys(db)
	this.index = this.keys.map(concatTitleKeywords);

	function concatTitleKeywords(key) {
		return db[key].title.toLowerCase() + ',' + db[key].keywords.toLowerCase();
	}
}

function formatResponse(title, supportedList, unsupportedList, overallPercent, keyword) {
	var response = "Can I Use \x02" + title + "\x0F?";
	response += " [" + supportedList + "]";
	if (unsupportedList) {
		response += " (unsupported: " + unsupportedList + ")";
	}
	response += " \x02Overall:\x0F " + overallPercent.toFixed(1) + "%";
	response += ' \x032<http://caniuse.com/' + keyword + '>\x0F';

	return response;
}<commit_msg>Cleaned and reorganized source<commit_after>// caniuse.com functionality for oftn-bot
// by ImBcmDth - jon.carlos.rivera@gmail.com
//
// Thanks to FireFly, sorella, gkatsev, Morthchek,
// and eboy for your testing and suggestions.
//
// All data provided by the good folks at <http://caniuse.com/>

var Utils = require("util");
var File = require("fs");
var Path = require('path');

var agentsToShow = {
	'ie': 'IE',
	'firefox': 'FF',
	'chrome': 'Chrome',
	'opera': 'Opera',
	'safari': 'Safari',
	'ios_saf': 'iOS',
	'android': 'Android'
};

var agentSupportToColors = {
	'n': '\x034',
	'y': '\x033',
	'p': '\x038',
	'a': '\x038',
	'u': '\x03E',
	'x': '\x03E'
};

var CanIUseServer = module.exports = function(filename) {
	if (filename) {
		this.filename = filename;
	} else {
		this.filename = Path.join(__dirname, "data/data.json");
	}

	File.readFile(this.filename, function (err, data) {
		try {
			if (err) {
				throw err;
			}
			Utils.puts("Loaded file: "+Path.basename(this.filename));
			var data = JSON.parse(data);
			this.db = data;

			this.buildIndex();
		} catch (e) {
			Utils.puts("JSON Parse Error: "+e);
		}
	}.bind(this));
};

CanIUseServer.prototype.buildIndex = function() {
	var db = this.db.data;

	this.keys = Object.keys(db)
	this.index = this.keys.map(concatTitleKeywords);

	function concatTitleKeywords(key) {
		return db[key].title.toLowerCase() + ',' + db[key].keywords.toLowerCase();
	}
};

CanIUseServer.prototype.search = function(key) {
	key = key.toLowerCase();

	try {
		return this.find(key);
	} catch(e) {
		var i = this.index.length;

		while (i--) {
			if (this.index[i].search(key) > -1) {
				return this.find(this.keys[i]);
			}
		}

		throw e;
	}
};

CanIUseServer.prototype.find = function(key) {
	var db = this.db.data;

	if (typeof db[key] === "undefined") {
		throw new Error("Can I Use `"+key+"` was not found.");
	}

	var feature = db[key];
	var agentSupportInfo = this.getAgentSupportArray(feature);
	var agentSupportStrings = this.formatAgentSupportArray(agentSupportInfo.supported);
	var supportedString = agentSupportStrings.join(' | ');
	var unsupportedString = agentSupportInfo.unsupported.join(', ');
	var overallPercent = (feature.usage_perc_y + feature.usage_perc_a);

	return formatResponse(feature.title, supportedString, unsupportedString, overallPercent, key);
};

CanIUseServer.prototype.getAgentSupportArray = function(feature) {
	var data = this.db;
	var agents = data.agents;

	var agentSupportObject = {supported:[], unsupported:[]};

	Object.keys(agentsToShow).forEach(getMinimalAgentSupport);

	return agentSupportObject;

	function getMinimalAgentSupport(agent) {
		var agentName = agentsToShow[agent];
		var agentSupport;

		var supportedVersions = agents[agent].versions.filter(onlyTotallySupported);

		if (supportedVersions.length === 0) {
			supportedVersions = agents[agent].versions.filter(onlySomewhatSupported);
		}

		if (supportedVersions.length !== 0) {
			agentSupport = feature.stats[agent][supportedVersions[0]];

			agentSupportObject.supported.push({
				name: agentName,
				version: supportedVersions[0] + '+',
				support: agentSupport
			});
		} else {
			agentSupportObject.unsupported.push(agentName);
		}

		function onlyTotallySupported(version) {
			return (version != null && !/[n|u|p|a|x]/g.test(feature.stats[agent][version]));
		}

		function onlySomewhatSupported(version) {
			return (version != null && !/[n|u]/g.test(feature.stats[agent][version]));
		}
	}
};

CanIUseServer.prototype.formatAgentSupportArray = function(agentSupport) {
	return agentSupport.map(makeAgentSupportStrings, this.db.agents)

	function makeAgentSupportStrings(agentInfo) {
		var concatedName = agentInfo.name + " " + agentInfo.version;
		var supportFlags = agentInfo.support.split(' ');
		var agentColor = agentSupportToColors[supportFlags[0]];

		if (supportFlags.indexOf('p') > -1) {
			agentInfo.version += ' (w/polyfill)';
		}
		else if (supportFlags.indexOf('x') > -1) {
			agentInfo.version += ' (w/prefix)';
		}

		return agentInfo.name + agentColor + ' ' + agentInfo.version + '\x0F';
	}
};

function formatResponse(title, supportedList, unsupportedList, overallPercent, keyword) {
	var response = "Can I Use \x02" + title + "\x0F?";
	response += " [" + supportedList + "]";
	if (unsupportedList) {
		response += " (Unsupported: " + unsupportedList + ")";
	}
	response += " \x02Overall:\x0F " + overallPercent.toFixed(1) + "%";
	response += ' \x032<http://caniuse.com/' + keyword + '>\x0F';

	return response;
}<|endoftext|>


<commit_before>const AUTH_URL = 'https://ssl.filmweb.pl/login';
const BASE_URL = 'http://www.filmweb.pl';

const USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36';

const webdriver = require('selenium-webdriver');
const phantomjsPath = require('phantomjs-prebuilt').path;
const phantomjsCapabilities = webdriver.Capabilities.phantomjs();

const log = require('./log');
const {simulateHumanDelay, throttleRequest} = require('./delayUtils');

const browsers = new WeakMap;
const logins = new WeakMap;

phantomjsCapabilities.set('phantomjs.binary.path', phantomjsPath);
phantomjsCapabilities.set('phantomjs.page.settings.userAgent', USER_AGENT);

function exitHandler ()
{
    let browser = browsers.get(this);
    if (browser)
    {
        log(2, 'Quit a browser');
        browser.quit();
        browsers.delete(this);
    }
}

function bootstrap (url)
{
    let browser = browsers.get(this);

    if (! browser)
    {
        return Promise.reject(new Error('Missing browser instance.'));
    }
    else if (url)
    {
        return browser.goToUrl(url);
    }
    else
    {
        return Promise.resolve();
    }
}

function createBrowser ()
{
    log(2, 'Create new browser');

    let browser = new webdriver.Builder()
        .withCapabilities(
            // Overwriteable with SELENIUM_BROWSER env variable
            phantomjsCapabilities
        )
        .build();

    browser.goToUrl =
        (url) => throttleRequest().then(() => browser.get(url));

    browser.querySelector =
        (selector) => browser.findElement(webdriver.By.css(selector));

    browser.querySelectorAll =
        (selector) => browser.findElements(webdriver.By.css(selector));

    browser.waitUntilElementLocated =
        (selector, timeout, failMessage) => browser.wait(webdriver.until.elementLocated(webdriver.By.css(selector)), timeout, failMessage);

    browsers.set(this, browser);

    // Clean up on exit
    process.on('exit', exitHandler.bind(this));
}

// This function will be serialized.
// Do not use any fancy syntax.
function searchDataCollector (context)
{
    var results = {items: []};
    var list;

    var query = function (context, selector) {
        var element = context.querySelector(selector);
        return element || {};
    };

    if (context.querySelector('.searchNoResults'))
    {
        results.total = 0;
        results.page = 0;
        results.totalPages = 0;
    }
    else
    {
        results.nav = context.querySelector('#searchPaginator .config').textContent;

        list = context.querySelector('.resultsList');
        Array.prototype.forEach.call(list.children, function (listItem) {
            var queryListItem = query.bind(null, listItem);
            var result = {};
            var subList;

            result.url = queryListItem('a.hitTitle').href;
            result.title = queryListItem('a.hitTitle').textContent;
            result.year = queryListItem('a.hitTitle ~ .hitTitle').textContent;
            result.posterUrl = queryListItem('.hitImage img').src;

            result.ratingValue = queryListItem('.rateInfo strong').textContent;
            result.ratingUser = queryListItem('.ribbonButton strong').textContent;

            result.titles = [];
            subList = queryListItem('.sep-line').children;
            Array.prototype.forEach.call(subList, function (subItem) {
                if (! subItem.querySelector('ul'))
                {
                    result.titles.push(subItem.textContent);
                }
            });

            result.directors = [];
            result.cast = [];
            subList = queryListItem('.filmInfo');
            Array.prototype.forEach.call(subList, function (subItem) {
                var peopleList;
                if (/reżyser/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.directors;
                }
                else if (/obsada/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.cast;
                }
                else if (subList.activeContainer)
                {
                    peopleList = query(subItem, 'a');
                    Array.prototype.forEach.call(peopleList, function (person) {
                        subList.activeContainer.push({
                            url: person.href,
                            name: person.textContent,
                        });
                    });
                }
            });

            results.items.push(result);
        });
    }

    return results;
}

function collectSearchResults (element)
{
    let browser = browsers.get(this);
    return browser.executeScript(searchDataCollector, element);
}

function formatRawSearchResults (rawResults)
{
    let results = {
        total: rawResults.total,
        page: rawResults.page,
        totalPages: rawResults.totalPages,
    };

    if (rawResults.nav)
    {
        let rawResultsNav = JSON.parse(rawResults.nav);

        results.total = rawResultsNav.countItems;
        results.page = rawResultsNav.activePage;
        results.totalPages = rawResultsNav.totalPages;
    }

    results.items = rawResults.items.map(formatRawSearchResultItem);

    return results;
}

function formatRawSearchResultItem (rawItem)
{
    let result = {};
    let value;

    result.url = rawItem.url.trim();
    result.title = rawItem.title.trim();
    result.year = rawItem.year.trim().replace(/[()]/g, '');
    result.posterUrl = rawItem.posterUrl.trim();

    result.ratings = {};
    value = parseFloat((rawItem.ratingValue || '').replace(',', '.'));
    result.ratings.average = value || undefined;
    value = parseInt(rawItem.ratingUser, 10);
    result.ratings.user = value || null;

    result.alternativeTitles = rawItem.titles.map(formatRawSearchResultsTitle);
    result.directors = rawItem.directors.map(formatRawSearchResultsPerson);
    result.cast = rawItem.cast.map(formatRawSearchResultsPerson);

    if (rawItem.hasOwnProperty('debug'))
    {
        result.debug = rawItem.debug;
    }

    return result;
}

function formatRawSearchResultsTitle (rawTitle)
{
    const parts = rawTitle.trim().split(' : ');
    const title = {
        value: parts[0],
        countries: [],
    };
    const types = [];

    for (let i = 1; i < parts.length; i += 1)
    {
        let value = parts[i].trim();
        let match = value.match(/^\((.*)\)$/);
        if (match)
        {
            types.push(match[1]);
        }
        else
        {
            title.countries.push(value.replace(/[\/ ]*$/, ''));
        }
    }

    // I haven’t seen any titles with multiple types specified, so we
    // return this as a string, but to support that case, we collect all
    // types and return them.
    if (types.length)
    {
        title.type = types.join(', ');
    }

    return title;
}

function formatRawSearchResultsPerson (rawPerson)
{
    let person = {};
    person.url = rawPerson.url.trim();
    person.name = rawPerson.name.trim();
    return person;
}

function normalizeRatingValue (value)
{
    if (! value)
    {
        value = null;
    }
    else
    {
        value = parseInt(value, 10);
    }
    return value;
}

function normalizeReviewValue (value)
{
    if (value === undefined || value === null)
    {
        value = '';
    }
    else
    {
        value = '' + value;
    }
    return value;
}

function rate (url, value, review)
{
    if (! this.isAuthenticated())
    {
        return Promise.reject(new Error('Rate method can only be used on authenticated object.'));
    }

    let browser = browsers.get(this);

    value = normalizeRatingValue(value);
    review = normalizeReviewValue(review);

    return bootstrap.call(this, url)
        .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons [data-index]'))
        .then(simulateHumanDelay)
        .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons').getAttribute('data-rate'))
        .then(normalizeRatingValue)
        .then(currentRating => {
            let promise = Promise.resolve(true);

            if (value === null && currentRating !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + currentRating + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons:not([data-rate])'));
            }
            else if (value !== currentRating)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + value + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons[data-rate="' + value + '"]'));
            }

            if (value !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').getText())
                    .then(normalizeReviewValue)
                    .then(currentReview => {
                        if (currentReview !== review)
                        {
                            return browser.querySelector('#filmVoteRatingPanel .commentArea').clear()
                                .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').sendKeys(review))
                                .then(() => browser.querySelector('#filmVoteRatingPanel .saveComment').click())
                                // Wait until comment is saved
                                .then(simulateHumanDelay)
                                .then(() => browser.waitUntilElementLocated('.filmVoteRatingPanelWrapper:not(.commonLoader)'));
                        }
                    });
            }

            return promise;
        });
}

class FilmWeb
{
    constructor ()
    {
        createBrowser.call(this);
    }

    isAuthenticated ()
    {
        return !! this.getLogin();
    }

    getLogin ()
    {
        return logins.get(this);
    }

    destroy ()
    {
        log(2, 'Destroy a browser');
        let browser = browsers.get(this);
        browser.quit();
        browsers.delete(this);
        logins.delete(this);
    }

    search (query, page=1)
    {
        log(1, 'search called', {query, page});

        let browser = browsers.get(this);
        let urlQuery = encodeURIComponent(query);
        page = parseInt(page, 10);
        let url = BASE_URL + '/search?q=' + urlQuery + '&page=' + page;

        return bootstrap.call(this, url)
            .then(() => browser.waitUntilElementLocated('.newPaginator, .searchNoResults'))
            .then(() => browser.querySelector('.mainCol'))
            .then(collectSearchResults.bind(this))
            .then(formatRawSearchResults)
            .catch(error => {
                log(2, 'search failed', error);
                throw error;
            })
            .then((results) => {
                log(2, 'search finished', {query, page});
                log(4, 'results for FilmWeb.search', {query, page}, ':', results);
                return results;
            });
    }

    authenticate (login, password)
    {
        log(1, 'authenticate called', {login, password: '*****'});

        let browser = browsers.get(this);

        logins.set(this, login);

        return bootstrap.call(this, AUTH_URL)
            .then(simulateHumanDelay)
            .then(() => {
                browser.querySelector('[name=j_username]').sendKeys(login);
                browser.querySelector('[name=j_password]').sendKeys(password);
                browser.querySelector('[type=submit]').click();
            })
            .then(() => browser.waitUntilElementLocated('#userHeader .user__name', 2000))
            .catch(error => {
                log(2, 'authenticate failed', error);
                throw error;
            })
            .then(() => log(2, 'authenticate finished', {login}));
    }

    rate (url, value, review)
    {
        log(1, 'rate called', {url, value, review});

        rate.apply(this, arguments)
            .catch(error => {
                log(2, 'rate failed', {error});
                throw error;
            })
            .then(() => log(2, 'rate finished', {url, value, review}));
    }

    removeRating (url)
    {
        log(1, 'removeRating called', {url});

        return rate.apply(this, arguments)
            .catch(error => {
                log(2, 'removeRating failed', error);
                throw error;
            })
            .then(() => log(2, 'removeRating finished', {url}));
    }
}

// Call process.exit when killed with signals to trigger cleanup
process.on('SIGINT', () => process.exit(1));
process.on('SIGHUP', () => process.exit(1));
process.on('SIGQUIT', () => process.exit(1));
process.on('SIGTERM', () => process.exit(1));

module.exports = FilmWeb;
<commit_msg>Use FilmWeb#destroy() in exit handler<commit_after>const AUTH_URL = 'https://ssl.filmweb.pl/login';
const BASE_URL = 'http://www.filmweb.pl';

const USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36';

const webdriver = require('selenium-webdriver');
const phantomjsPath = require('phantomjs-prebuilt').path;
const phantomjsCapabilities = webdriver.Capabilities.phantomjs();

const log = require('./log');
const {simulateHumanDelay, throttleRequest} = require('./delayUtils');

const browsers = new WeakMap;
const logins = new WeakMap;

phantomjsCapabilities.set('phantomjs.binary.path', phantomjsPath);
phantomjsCapabilities.set('phantomjs.page.settings.userAgent', USER_AGENT);

function exitHandler ()
{
    let browser = browsers.get(this);
    if (browser)
    {
        this.destroy();
    }
}

function bootstrap (url)
{
    let browser = browsers.get(this);

    if (! browser)
    {
        return Promise.reject(new Error('Missing browser instance.'));
    }
    else if (url)
    {
        return browser.goToUrl(url);
    }
    else
    {
        return Promise.resolve();
    }
}

function createBrowser ()
{
    log(2, 'Create new browser');

    let browser = new webdriver.Builder()
        .withCapabilities(
            // Overwriteable with SELENIUM_BROWSER env variable
            phantomjsCapabilities
        )
        .build();

    browser.goToUrl =
        (url) => throttleRequest().then(() => browser.get(url));

    browser.querySelector =
        (selector) => browser.findElement(webdriver.By.css(selector));

    browser.querySelectorAll =
        (selector) => browser.findElements(webdriver.By.css(selector));

    browser.waitUntilElementLocated =
        (selector, timeout, failMessage) => browser.wait(webdriver.until.elementLocated(webdriver.By.css(selector)), timeout, failMessage);

    browsers.set(this, browser);

    // Clean up on exit
    process.on('exit', exitHandler.bind(this));
}

// This function will be serialized.
// Do not use any fancy syntax.
function searchDataCollector (context)
{
    var results = {items: []};
    var list;

    var query = function (context, selector) {
        var element = context.querySelector(selector);
        return element || {};
    };

    if (context.querySelector('.searchNoResults'))
    {
        results.total = 0;
        results.page = 0;
        results.totalPages = 0;
    }
    else
    {
        results.nav = context.querySelector('#searchPaginator .config').textContent;

        list = context.querySelector('.resultsList');
        Array.prototype.forEach.call(list.children, function (listItem) {
            var queryListItem = query.bind(null, listItem);
            var result = {};
            var subList;

            result.url = queryListItem('a.hitTitle').href;
            result.title = queryListItem('a.hitTitle').textContent;
            result.year = queryListItem('a.hitTitle ~ .hitTitle').textContent;
            result.posterUrl = queryListItem('.hitImage img').src;

            result.ratingValue = queryListItem('.rateInfo strong').textContent;
            result.ratingUser = queryListItem('.ribbonButton strong').textContent;

            result.titles = [];
            subList = queryListItem('.sep-line').children;
            Array.prototype.forEach.call(subList, function (subItem) {
                if (! subItem.querySelector('ul'))
                {
                    result.titles.push(subItem.textContent);
                }
            });

            result.directors = [];
            result.cast = [];
            subList = queryListItem('.filmInfo');
            Array.prototype.forEach.call(subList, function (subItem) {
                var peopleList;
                if (/reżyser/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.directors;
                }
                else if (/obsada/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.cast;
                }
                else if (subList.activeContainer)
                {
                    peopleList = query(subItem, 'a');
                    Array.prototype.forEach.call(peopleList, function (person) {
                        subList.activeContainer.push({
                            url: person.href,
                            name: person.textContent,
                        });
                    });
                }
            });

            results.items.push(result);
        });
    }

    return results;
}

function collectSearchResults (element)
{
    let browser = browsers.get(this);
    return browser.executeScript(searchDataCollector, element);
}

function formatRawSearchResults (rawResults)
{
    let results = {
        total: rawResults.total,
        page: rawResults.page,
        totalPages: rawResults.totalPages,
    };

    if (rawResults.nav)
    {
        let rawResultsNav = JSON.parse(rawResults.nav);

        results.total = rawResultsNav.countItems;
        results.page = rawResultsNav.activePage;
        results.totalPages = rawResultsNav.totalPages;
    }

    results.items = rawResults.items.map(formatRawSearchResultItem);

    return results;
}

function formatRawSearchResultItem (rawItem)
{
    let result = {};
    let value;

    result.url = rawItem.url.trim();
    result.title = rawItem.title.trim();
    result.year = rawItem.year.trim().replace(/[()]/g, '');
    result.posterUrl = rawItem.posterUrl.trim();

    result.ratings = {};
    value = parseFloat((rawItem.ratingValue || '').replace(',', '.'));
    result.ratings.average = value || undefined;
    value = parseInt(rawItem.ratingUser, 10);
    result.ratings.user = value || null;

    result.alternativeTitles = rawItem.titles.map(formatRawSearchResultsTitle);
    result.directors = rawItem.directors.map(formatRawSearchResultsPerson);
    result.cast = rawItem.cast.map(formatRawSearchResultsPerson);

    if (rawItem.hasOwnProperty('debug'))
    {
        result.debug = rawItem.debug;
    }

    return result;
}

function formatRawSearchResultsTitle (rawTitle)
{
    const parts = rawTitle.trim().split(' : ');
    const title = {
        value: parts[0],
        countries: [],
    };
    const types = [];

    for (let i = 1; i < parts.length; i += 1)
    {
        let value = parts[i].trim();
        let match = value.match(/^\((.*)\)$/);
        if (match)
        {
            types.push(match[1]);
        }
        else
        {
            title.countries.push(value.replace(/[\/ ]*$/, ''));
        }
    }

    // I haven’t seen any titles with multiple types specified, so we
    // return this as a string, but to support that case, we collect all
    // types and return them.
    if (types.length)
    {
        title.type = types.join(', ');
    }

    return title;
}

function formatRawSearchResultsPerson (rawPerson)
{
    let person = {};
    person.url = rawPerson.url.trim();
    person.name = rawPerson.name.trim();
    return person;
}

function normalizeRatingValue (value)
{
    if (! value)
    {
        value = null;
    }
    else
    {
        value = parseInt(value, 10);
    }
    return value;
}

function normalizeReviewValue (value)
{
    if (value === undefined || value === null)
    {
        value = '';
    }
    else
    {
        value = '' + value;
    }
    return value;
}

function rate (url, value, review)
{
    if (! this.isAuthenticated())
    {
        return Promise.reject(new Error('Rate method can only be used on authenticated object.'));
    }

    let browser = browsers.get(this);

    value = normalizeRatingValue(value);
    review = normalizeReviewValue(review);

    return bootstrap.call(this, url)
        .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons [data-index]'))
        .then(simulateHumanDelay)
        .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons').getAttribute('data-rate'))
        .then(normalizeRatingValue)
        .then(currentRating => {
            let promise = Promise.resolve(true);

            if (value === null && currentRating !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + currentRating + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons:not([data-rate])'));
            }
            else if (value !== currentRating)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + value + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons[data-rate="' + value + '"]'));
            }

            if (value !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').getText())
                    .then(normalizeReviewValue)
                    .then(currentReview => {
                        if (currentReview !== review)
                        {
                            return browser.querySelector('#filmVoteRatingPanel .commentArea').clear()
                                .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').sendKeys(review))
                                .then(() => browser.querySelector('#filmVoteRatingPanel .saveComment').click())
                                // Wait until comment is saved
                                .then(simulateHumanDelay)
                                .then(() => browser.waitUntilElementLocated('.filmVoteRatingPanelWrapper:not(.commonLoader)'));
                        }
                    });
            }

            return promise;
        });
}

class FilmWeb
{
    constructor ()
    {
        createBrowser.call(this);
    }

    isAuthenticated ()
    {
        return !! this.getLogin();
    }

    getLogin ()
    {
        return logins.get(this);
    }

    destroy ()
    {
        log(2, 'Destroy a browser');
        let browser = browsers.get(this);
        browser.quit();
        browsers.delete(this);
        logins.delete(this);
    }

    search (query, page=1)
    {
        log(1, 'search called', {query, page});

        let browser = browsers.get(this);
        let urlQuery = encodeURIComponent(query);
        page = parseInt(page, 10);
        let url = BASE_URL + '/search?q=' + urlQuery + '&page=' + page;

        return bootstrap.call(this, url)
            .then(() => browser.waitUntilElementLocated('.newPaginator, .searchNoResults'))
            .then(() => browser.querySelector('.mainCol'))
            .then(collectSearchResults.bind(this))
            .then(formatRawSearchResults)
            .catch(error => {
                log(2, 'search failed', error);
                throw error;
            })
            .then((results) => {
                log(2, 'search finished', {query, page});
                log(4, 'results for FilmWeb.search', {query, page}, ':', results);
                return results;
            });
    }

    authenticate (login, password)
    {
        log(1, 'authenticate called', {login, password: '*****'});

        let browser = browsers.get(this);

        logins.set(this, login);

        return bootstrap.call(this, AUTH_URL)
            .then(simulateHumanDelay)
            .then(() => {
                browser.querySelector('[name=j_username]').sendKeys(login);
                browser.querySelector('[name=j_password]').sendKeys(password);
                browser.querySelector('[type=submit]').click();
            })
            .then(() => browser.waitUntilElementLocated('#userHeader .user__name', 2000))
            .catch(error => {
                log(2, 'authenticate failed', error);
                throw error;
            })
            .then(() => log(2, 'authenticate finished', {login}));
    }

    rate (url, value, review)
    {
        log(1, 'rate called', {url, value, review});

        rate.apply(this, arguments)
            .catch(error => {
                log(2, 'rate failed', {error});
                throw error;
            })
            .then(() => log(2, 'rate finished', {url, value, review}));
    }

    removeRating (url)
    {
        log(1, 'removeRating called', {url});

        return rate.apply(this, arguments)
            .catch(error => {
                log(2, 'removeRating failed', error);
                throw error;
            })
            .then(() => log(2, 'removeRating finished', {url}));
    }
}

// Call process.exit when killed with signals to trigger cleanup
process.on('SIGINT', () => process.exit(1));
process.on('SIGHUP', () => process.exit(1));
process.on('SIGQUIT', () => process.exit(1));
process.on('SIGTERM', () => process.exit(1));

module.exports = FilmWeb;
<|endoftext|>


<commit_before>const AUTH_URL = 'https://ssl.filmweb.pl/login';
const BASE_URL = 'http://www.filmweb.pl';

const USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36';

const webdriver = require('selenium-webdriver');
const phantomjsPath = require('phantomjs-prebuilt').path;
const phantomjsCapabilities = webdriver.Capabilities.phantomjs();

const log = require('./log');
const {simulateHumanDelay, throttleRequest} = require('./delayUtils');

const browsers = new WeakMap;
const logins = new WeakMap;

phantomjsCapabilities.set('phantomjs.binary.path', phantomjsPath);
phantomjsCapabilities.set('phantomjs.page.settings.userAgent', USER_AGENT);

function exitHandler ()
{
    let browser = browsers.get(this);
    if (browser)
    {
        log(2, 'Quit a browser');
        browser.quit();
        browsers.delete(this);
    }
}

function bootstrap (url)
{
    let browser = browsers.get(this);

    if (! browser)
    {
        return Promise.reject(new Error('Missing browser instance.'));
    }
    else if (url)
    {
        return browser.goToUrl(url);
    }
    else
    {
        return Promise.resolve();
    }
}

function createBrowser ()
{
    log(2, 'Create new browser');

    let browser = new webdriver.Builder()
        .withCapabilities(
            // Overwriteable with SELENIUM_BROWSER env variable
            phantomjsCapabilities
        )
        .build();

    browser.goToUrl =
        (url) => throttleRequest().then(() => browser.get(url));

    browser.querySelector =
        (selector) => browser.findElement(webdriver.By.css(selector));

    browser.querySelectorAll =
        (selector) => browser.findElements(webdriver.By.css(selector));

    browser.waitUntilElementLocated =
        (selector, timeout, failMessage) => browser.wait(webdriver.until.elementLocated(webdriver.By.css(selector)), timeout, failMessage);

    browsers.set(this, browser);

    // Clean up on exit
    process.on('exit', exitHandler.bind(this));
}

// This function will be serialized.
// Do not use any fancy syntax.
function searchDataCollector (context)
{
    var results = {items: []};
    var list;

    var query = function (context, selector) {
        var element = context.querySelector(selector);
        return element || {};
    };

    if (context.querySelector('.searchNoResults'))
    {
        results.total = 0;
        results.page = 0;
        results.totalPages = 0;
    }
    else
    {
        results.nav = context.querySelector('#searchPaginator .config').textContent;

        list = context.querySelector('.resultsList');
        Array.prototype.forEach.call(list.children, function (listItem) {
            var queryListItem = query.bind(null, listItem);
            var result = {};
            var subList;

            result.url = queryListItem('a.hitTitle').href;
            result.title = queryListItem('a.hitTitle').textContent;
            result.year = queryListItem('a.hitTitle ~ .hitTitle').textContent;
            result.posterUrl = queryListItem('.hitImage img').src;

            result.ratingValue = queryListItem('.rateInfo strong').textContent;
            result.ratingUser = queryListItem('.ribbonButton strong').textContent;

            result.titles = [];
            subList = queryListItem('.sep-line').children;
            Array.prototype.forEach.call(subList, function (subItem) {
                if (! subItem.querySelector('ul'))
                {
                    result.titles.push(subItem.textContent);
                }
            });

            result.directors = [];
            result.cast = [];
            subList = queryListItem('.filmInfo');
            Array.prototype.forEach.call(subList, function (subItem) {
                var peopleList;
                if (/reżyser/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.directors;
                }
                else if (/obsada/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.cast;
                }
                else if (subList.activeContainer)
                {
                    peopleList = query(subItem, 'a');
                    Array.prototype.forEach.call(peopleList, function (person) {
                        subList.activeContainer.push({
                            url: person.href,
                            name: person.textContent,
                        });
                    });
                }
            });

            results.items.push(result);
        });
    }

    return results;
}

function collectSearchResults (element)
{
    let browser = browsers.get(this);
    return browser.executeScript(searchDataCollector, element);
}

function formatRawSearchResults (rawResults)
{
    let results = {
        total: rawResults.total,
        page: rawResults.page,
        totalPages: rawResults.totalPages,
    };

    if (rawResults.nav)
    {
        let rawResultsNav = JSON.parse(rawResults.nav);

        results.total = rawResultsNav.countItems;
        results.page = rawResultsNav.activePage;
        results.totalPages = rawResultsNav.totalPages;
    }

    results.items = rawResults.items.map(formatRawSearchResultItem);

    return results;
}

function formatRawSearchResultItem (rawItem)
{
    let result = {};
    let value;

    result.url = rawItem.url.trim();
    result.title = rawItem.title.trim();
    result.year = rawItem.year.trim().replace(/[()]/g, '');
    result.posterUrl = rawItem.posterUrl.trim();

    result.ratings = {};
    value = parseFloat((rawItem.ratingValue || '').replace(',', '.'));
    result.ratings.average = value || undefined;
    value = parseInt(rawItem.ratingUser, 10);
    result.ratings.user = value || null;

    result.alternativeTitles = rawItem.titles.map(formatRawSearchResultsTitle);
    result.directors = rawItem.directors.map(formatRawSearchResultsPerson);
    result.cast = rawItem.cast.map(formatRawSearchResultsPerson);

    if (rawItem.hasOwnProperty('debug'))
    {
        result.debug = rawItem.debug;
    }

    return result;
}

function formatRawSearchResultsTitle (rawTitle)
{
    const parts = rawTitle.trim().split(' : ');
    const title = {
        value: parts[0],
        countries: [],
    };
    const types = [];

    for (let i = 1; i < parts.length; i += 1)
    {
        let value = parts[i].trim();
        let match = value.match(/^\((.*)\)$/);
        if (match)
        {
            types.push(match[1]);
        }
        else
        {
            title.countries.push(value.replace(/[\/ ]*$/, ''));
        }
    }

    // I haven’t seen any titles with multiple types specified, so we
    // return this as a string, but to support that case, we collect all
    // types and return them.
    if (types.length)
    {
        title.type = types.join(', ');
    }

    return title;
}

function formatRawSearchResultsPerson (rawPerson)
{
    let person = {};
    person.url = rawPerson.url.trim();
    person.name = rawPerson.name.trim();
    return person;
}

function normalizeRatingValue (value)
{
    if (! value)
    {
        value = null;
    }
    else
    {
        value = parseInt(value, 10);
    }
    return value;
}

function normalizeReviewValue (value)
{
    if (value === undefined || value === null)
    {
        value = '';
    }
    else
    {
        value = '' + value;
    }
    return value;
}

function rate (url, value, review)
{
    if (! this.isAuthenticated())
    {
        return Promise.reject(new Error('Rate method can only be used on authenticated object.'));
    }

    let browser = browsers.get(this);

    value = normalizeRatingValue(value);
    review = normalizeReviewValue(review);

    return bootstrap.call(this, url)
        .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons[data-rate] [data-index]'))
        .then(simulateHumanDelay)
        .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons').getAttribute('data-rate'))
        .then(normalizeRatingValue)
        .then(currentRating => {
            let promise = Promise.resolve(true);

            if (value === null && currentRating !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + currentRating + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons:not([data-rate])'));
            }
            else if (value !== currentRating)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + value + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons[data-rate="' + value + '"]'));
            }

            if (value !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').getText())
                    .then(normalizeReviewValue)
                    .then(currentReview => {
                        if (currentReview !== review)
                        {
                            return browser.querySelector('#filmVoteRatingPanel .commentArea').clear()
                                .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').sendKeys(review))
                                .then(() => browser.querySelector('#filmVoteRatingPanel .saveComment').click())
                                // Wait until comment is saved
                                .then(simulateHumanDelay)
                                .then(() => browser.waitUntilElementLocated('.filmVoteRatingPanelWrapper:not(.commonLoader)'));
                        }
                    });
            }

            return promise;
        });
}

class FilmWeb
{
    constructor ()
    {
        createBrowser.call(this);
    }

    isAuthenticated ()
    {
        return !! this.getLogin();
    }

    getLogin ()
    {
        return logins.get(this);
    }

    destroy ()
    {
        log(2, 'Destroy a browser');
        let browser = browsers.get(this);
        browser.quit();
        browsers.delete(this);
        logins.delete(this);
    }

    search (query, page=1)
    {
        log(1, 'search called', {query, page});

        let browser = browsers.get(this);
        let urlQuery = encodeURIComponent(query);
        page = parseInt(page, 10);
        let url = BASE_URL + '/search?q=' + urlQuery + '&page=' + page;

        return bootstrap.call(this, url)
            .then(() => browser.waitUntilElementLocated('.newPaginator, .searchNoResults'))
            .then(() => browser.querySelector('.mainCol'))
            .then(collectSearchResults.bind(this))
            .then(formatRawSearchResults)
            .catch(error => {
                log(2, 'search failed', error);
                throw error;
            })
            .then((results) => {
                log(2, 'search finished', {query, page});
                log(4, 'results for FilmWeb.search', {query, page}, ':', results);
                return results;
            });
    }

    authenticate (login, password)
    {
        log(1, 'authenticate called', {login, password: '*****'});

        let browser = browsers.get(this);

        logins.set(this, login);

        return bootstrap.call(this, AUTH_URL)
            .then(simulateHumanDelay)
            .then(() => {
                browser.querySelector('[name=j_username]').sendKeys(login);
                browser.querySelector('[name=j_password]').sendKeys(password);
                browser.querySelector('[type=submit]').click();
            })
            .then(() => browser.waitUntilElementLocated('.loggedUserInfo', 2000))
            .catch(error => {
                log(2, 'authenticate failed', error);
                throw error;
            })
            .then(() => log(2, 'authenticate finished', {login}));
    }

    rate (url, value, review)
    {
        log(1, 'rate called', {url, value, review});

        rate.apply(this, arguments)
            .catch(error => {
                log(2, 'rate failed', {error});
                throw error;
            })
            .then(() => log(2, 'rate finished', {url, value, review}));
    }

    removeRating (url)
    {
        log(1, 'removeRating called', {url});

        return rate.apply(this, arguments)
            .catch(error => {
                log(2, 'removeRating failed', error);
                throw error;
            })
            .then(() => log(2, 'removeRating finished', {url}));
    }
}

// Call process.exit when killed with signals to trigger cleanup
process.on('SIGINT', () => process.exit(1));
process.on('SIGHUP', () => process.exit(1));
process.on('SIGQUIT', () => process.exit(1));
process.on('SIGTERM', () => process.exit(1));

module.exports = FilmWeb;
<commit_msg>Update selector of rating widget<commit_after>const AUTH_URL = 'https://ssl.filmweb.pl/login';
const BASE_URL = 'http://www.filmweb.pl';

const USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36';

const webdriver = require('selenium-webdriver');
const phantomjsPath = require('phantomjs-prebuilt').path;
const phantomjsCapabilities = webdriver.Capabilities.phantomjs();

const log = require('./log');
const {simulateHumanDelay, throttleRequest} = require('./delayUtils');

const browsers = new WeakMap;
const logins = new WeakMap;

phantomjsCapabilities.set('phantomjs.binary.path', phantomjsPath);
phantomjsCapabilities.set('phantomjs.page.settings.userAgent', USER_AGENT);

function exitHandler ()
{
    let browser = browsers.get(this);
    if (browser)
    {
        log(2, 'Quit a browser');
        browser.quit();
        browsers.delete(this);
    }
}

function bootstrap (url)
{
    let browser = browsers.get(this);

    if (! browser)
    {
        return Promise.reject(new Error('Missing browser instance.'));
    }
    else if (url)
    {
        return browser.goToUrl(url);
    }
    else
    {
        return Promise.resolve();
    }
}

function createBrowser ()
{
    log(2, 'Create new browser');

    let browser = new webdriver.Builder()
        .withCapabilities(
            // Overwriteable with SELENIUM_BROWSER env variable
            phantomjsCapabilities
        )
        .build();

    browser.goToUrl =
        (url) => throttleRequest().then(() => browser.get(url));

    browser.querySelector =
        (selector) => browser.findElement(webdriver.By.css(selector));

    browser.querySelectorAll =
        (selector) => browser.findElements(webdriver.By.css(selector));

    browser.waitUntilElementLocated =
        (selector, timeout, failMessage) => browser.wait(webdriver.until.elementLocated(webdriver.By.css(selector)), timeout, failMessage);

    browsers.set(this, browser);

    // Clean up on exit
    process.on('exit', exitHandler.bind(this));
}

// This function will be serialized.
// Do not use any fancy syntax.
function searchDataCollector (context)
{
    var results = {items: []};
    var list;

    var query = function (context, selector) {
        var element = context.querySelector(selector);
        return element || {};
    };

    if (context.querySelector('.searchNoResults'))
    {
        results.total = 0;
        results.page = 0;
        results.totalPages = 0;
    }
    else
    {
        results.nav = context.querySelector('#searchPaginator .config').textContent;

        list = context.querySelector('.resultsList');
        Array.prototype.forEach.call(list.children, function (listItem) {
            var queryListItem = query.bind(null, listItem);
            var result = {};
            var subList;

            result.url = queryListItem('a.hitTitle').href;
            result.title = queryListItem('a.hitTitle').textContent;
            result.year = queryListItem('a.hitTitle ~ .hitTitle').textContent;
            result.posterUrl = queryListItem('.hitImage img').src;

            result.ratingValue = queryListItem('.rateInfo strong').textContent;
            result.ratingUser = queryListItem('.ribbonButton strong').textContent;

            result.titles = [];
            subList = queryListItem('.sep-line').children;
            Array.prototype.forEach.call(subList, function (subItem) {
                if (! subItem.querySelector('ul'))
                {
                    result.titles.push(subItem.textContent);
                }
            });

            result.directors = [];
            result.cast = [];
            subList = queryListItem('.filmInfo');
            Array.prototype.forEach.call(subList, function (subItem) {
                var peopleList;
                if (/reżyser/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.directors;
                }
                else if (/obsada/i.test(subItem.textContent))
                {
                    subList.activeContainer = results.cast;
                }
                else if (subList.activeContainer)
                {
                    peopleList = query(subItem, 'a');
                    Array.prototype.forEach.call(peopleList, function (person) {
                        subList.activeContainer.push({
                            url: person.href,
                            name: person.textContent,
                        });
                    });
                }
            });

            results.items.push(result);
        });
    }

    return results;
}

function collectSearchResults (element)
{
    let browser = browsers.get(this);
    return browser.executeScript(searchDataCollector, element);
}

function formatRawSearchResults (rawResults)
{
    let results = {
        total: rawResults.total,
        page: rawResults.page,
        totalPages: rawResults.totalPages,
    };

    if (rawResults.nav)
    {
        let rawResultsNav = JSON.parse(rawResults.nav);

        results.total = rawResultsNav.countItems;
        results.page = rawResultsNav.activePage;
        results.totalPages = rawResultsNav.totalPages;
    }

    results.items = rawResults.items.map(formatRawSearchResultItem);

    return results;
}

function formatRawSearchResultItem (rawItem)
{
    let result = {};
    let value;

    result.url = rawItem.url.trim();
    result.title = rawItem.title.trim();
    result.year = rawItem.year.trim().replace(/[()]/g, '');
    result.posterUrl = rawItem.posterUrl.trim();

    result.ratings = {};
    value = parseFloat((rawItem.ratingValue || '').replace(',', '.'));
    result.ratings.average = value || undefined;
    value = parseInt(rawItem.ratingUser, 10);
    result.ratings.user = value || null;

    result.alternativeTitles = rawItem.titles.map(formatRawSearchResultsTitle);
    result.directors = rawItem.directors.map(formatRawSearchResultsPerson);
    result.cast = rawItem.cast.map(formatRawSearchResultsPerson);

    if (rawItem.hasOwnProperty('debug'))
    {
        result.debug = rawItem.debug;
    }

    return result;
}

function formatRawSearchResultsTitle (rawTitle)
{
    const parts = rawTitle.trim().split(' : ');
    const title = {
        value: parts[0],
        countries: [],
    };
    const types = [];

    for (let i = 1; i < parts.length; i += 1)
    {
        let value = parts[i].trim();
        let match = value.match(/^\((.*)\)$/);
        if (match)
        {
            types.push(match[1]);
        }
        else
        {
            title.countries.push(value.replace(/[\/ ]*$/, ''));
        }
    }

    // I haven’t seen any titles with multiple types specified, so we
    // return this as a string, but to support that case, we collect all
    // types and return them.
    if (types.length)
    {
        title.type = types.join(', ');
    }

    return title;
}

function formatRawSearchResultsPerson (rawPerson)
{
    let person = {};
    person.url = rawPerson.url.trim();
    person.name = rawPerson.name.trim();
    return person;
}

function normalizeRatingValue (value)
{
    if (! value)
    {
        value = null;
    }
    else
    {
        value = parseInt(value, 10);
    }
    return value;
}

function normalizeReviewValue (value)
{
    if (value === undefined || value === null)
    {
        value = '';
    }
    else
    {
        value = '' + value;
    }
    return value;
}

function rate (url, value, review)
{
    if (! this.isAuthenticated())
    {
        return Promise.reject(new Error('Rate method can only be used on authenticated object.'));
    }

    let browser = browsers.get(this);

    value = normalizeRatingValue(value);
    review = normalizeReviewValue(review);

    return bootstrap.call(this, url)
        .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons [data-index]'))
        .then(simulateHumanDelay)
        .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons').getAttribute('data-rate'))
        .then(normalizeRatingValue)
        .then(currentRating => {
            let promise = Promise.resolve(true);

            if (value === null && currentRating !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + currentRating + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons:not([data-rate])'));
            }
            else if (value !== currentRating)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .rateButtons [data-index="' + value + '"]').click())
                    .then(() => browser.waitUntilElementLocated('#filmVoteRatingPanel .rateButtons[data-rate="' + value + '"]'));
            }

            if (value !== null)
            {
                promise = promise
                    .then(simulateHumanDelay)
                    .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').getText())
                    .then(normalizeReviewValue)
                    .then(currentReview => {
                        if (currentReview !== review)
                        {
                            return browser.querySelector('#filmVoteRatingPanel .commentArea').clear()
                                .then(() => browser.querySelector('#filmVoteRatingPanel .commentArea').sendKeys(review))
                                .then(() => browser.querySelector('#filmVoteRatingPanel .saveComment').click())
                                // Wait until comment is saved
                                .then(simulateHumanDelay)
                                .then(() => browser.waitUntilElementLocated('.filmVoteRatingPanelWrapper:not(.commonLoader)'));
                        }
                    });
            }

            return promise;
        });
}

class FilmWeb
{
    constructor ()
    {
        createBrowser.call(this);
    }

    isAuthenticated ()
    {
        return !! this.getLogin();
    }

    getLogin ()
    {
        return logins.get(this);
    }

    destroy ()
    {
        log(2, 'Destroy a browser');
        let browser = browsers.get(this);
        browser.quit();
        browsers.delete(this);
        logins.delete(this);
    }

    search (query, page=1)
    {
        log(1, 'search called', {query, page});

        let browser = browsers.get(this);
        let urlQuery = encodeURIComponent(query);
        page = parseInt(page, 10);
        let url = BASE_URL + '/search?q=' + urlQuery + '&page=' + page;

        return bootstrap.call(this, url)
            .then(() => browser.waitUntilElementLocated('.newPaginator, .searchNoResults'))
            .then(() => browser.querySelector('.mainCol'))
            .then(collectSearchResults.bind(this))
            .then(formatRawSearchResults)
            .catch(error => {
                log(2, 'search failed', error);
                throw error;
            })
            .then((results) => {
                log(2, 'search finished', {query, page});
                log(4, 'results for FilmWeb.search', {query, page}, ':', results);
                return results;
            });
    }

    authenticate (login, password)
    {
        log(1, 'authenticate called', {login, password: '*****'});

        let browser = browsers.get(this);

        logins.set(this, login);

        return bootstrap.call(this, AUTH_URL)
            .then(simulateHumanDelay)
            .then(() => {
                browser.querySelector('[name=j_username]').sendKeys(login);
                browser.querySelector('[name=j_password]').sendKeys(password);
                browser.querySelector('[type=submit]').click();
            })
            .then(() => browser.waitUntilElementLocated('.loggedUserInfo', 2000))
            .catch(error => {
                log(2, 'authenticate failed', error);
                throw error;
            })
            .then(() => log(2, 'authenticate finished', {login}));
    }

    rate (url, value, review)
    {
        log(1, 'rate called', {url, value, review});

        rate.apply(this, arguments)
            .catch(error => {
                log(2, 'rate failed', {error});
                throw error;
            })
            .then(() => log(2, 'rate finished', {url, value, review}));
    }

    removeRating (url)
    {
        log(1, 'removeRating called', {url});

        return rate.apply(this, arguments)
            .catch(error => {
                log(2, 'removeRating failed', error);
                throw error;
            })
            .then(() => log(2, 'removeRating finished', {url}));
    }
}

// Call process.exit when killed with signals to trigger cleanup
process.on('SIGINT', () => process.exit(1));
process.on('SIGHUP', () => process.exit(1));
process.on('SIGQUIT', () => process.exit(1));
process.on('SIGTERM', () => process.exit(1));

module.exports = FilmWeb;
<|endoftext|>


<commit_before>"use strict";

const hash = require("../utils/hash");
const path = require("path");
const standalone = require("../standalone");
const fixturesPath = path.join(__dirname, "fixtures");
const cpFile = require("cp-file");
const fileExists = require("file-exists-promise");
const fs = require("fs");
const replaceBackslashes = require("./replaceBackslashes");
const { promisify } = require("util");
const unlink = promisify(fs.unlink);
const fCache = require("file-entry-cache");

const cwd = process.cwd();
const invalidFile = path.join(fixturesPath, "empty-block.css");
const syntaxErrorFile = path.join(fixturesPath, "syntax_error.css");
const validFile = path.join(fixturesPath, "cache", "valid.css");
const newFileDest = path.join(fixturesPath, "cache", "newFile.css");

// Config object is getting mutated internally.
// Return new object of the same structure to
// make sure config doesn't change between runs.
function getConfig() {
  return {
    files: replaceBackslashes(path.join(fixturesPath, "cache", "*.css")),
    config: {
      rules: { "block-no-empty": true, "color-no-invalid-hex": true }
    },
    cache: true
  };
}

describe("standalone cache", () => {
  const expectedCacheFilePath = path.join(cwd, ".stylelintcache");

  beforeEach(() => {
    // Initial run to warm up the cache
    return standalone(getConfig());
  });

  afterEach(() => {
    // Clean up after each test case
    return Promise.all([
      unlink(expectedCacheFilePath).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      }),
      unlink(newFileDest).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      })
    ]);
  });

  it("cache file is created at $CWD/.stylelintcache", () => {
    // Ensure cache file exists
    return fileExists(expectedCacheFilePath).then(isFileExist => {
      expect(!!isFileExist).toBe(true);

      const fileCache = fCache.createFromFile(expectedCacheFilePath);
      const { cache } = fileCache;

      // Ensure cache file contains only linted css file
      expect(typeof cache.getKey(validFile) === "object").toBe(true);
      expect(typeof cache.getKey(newFileDest) === "undefined").toBe(true);
    });
  });

  it("only changed files are linted", () => {
    // Add "changed" file
    return cpFile(validFile, newFileDest)
      .then(() => {
        // Next run should lint only changed files
        return standalone(getConfig());
      })
      .then(output => {
        // Ensure only changed files are linted
        const isValidFileLinted = !!output.results.find(
          file => file.source === validFile
        );
        const isNewFileLinted = !!output.results.find(
          file => file.source === newFileDest
        );

        expect(isValidFileLinted).toBe(false);
        expect(isNewFileLinted).toBe(true);

        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
        expect(typeof cache.getKey(newFileDest) === "object").toBe(true);
      });
  });

  it("all files are linted on config change", () => {
    const changedConfig = getConfig();

    changedConfig.config.rules["block-no-empty"] = false;

    return cpFile(validFile, newFileDest)
      .then(() => {
        // All file should be re-linted as config has changed
        return standalone(changedConfig);
      })
      .then(output => {
        // Ensure all files are re-linted
        const isValidFileLinted = !!output.results.find(
          file => file.source === validFile
        );
        const isNewFileLinted = !!output.results.find(
          file => file.source === newFileDest
        );

        expect(isValidFileLinted).toBe(true);
        expect(isNewFileLinted).toBe(true);
      });
  });

  it("invalid files are not cached", () => {
    return cpFile(invalidFile, newFileDest)
      .then(() => {
        // Should lint only changed files
        return standalone(getConfig());
      })
      .then(output => {
        expect(output.errored).toBe(true);
        // Ensure only changed files are linted
        const isValidFileLinted = !!output.results.find(
          file => file.source === validFile
        );
        const isInvalidFileLinted = !!output.results.find(
          file => file.source === newFileDest
        );

        expect(isValidFileLinted).toBe(false);
        expect(isInvalidFileLinted).toBe(true);

        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
        expect(typeof cache.getKey(newFileDest) === "undefined").toBe(true);
      });
  });
  it("files with syntax errors are not cached", () => {
    return cpFile(syntaxErrorFile, newFileDest)
      .then(() => {
        // Should lint only changed files
        return standalone(getConfig());
      })
      .then(() => {
        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
        expect(typeof cache.getKey(newFileDest) === "undefined").toBe(true);
      });
  });
  it("cache file is removed when cache is disabled", () => {
    const noCacheConfig = getConfig();

    noCacheConfig.cache = false;
    let cacheFileExists = true;

    return standalone(noCacheConfig).then(() => {
      return fileExists(expectedCacheFilePath)
        .then(() => {
          throw new Error(
            `Cache file is supposed to be removed, ${expectedCacheFilePath} is found instead`
          );
        })
        .catch(() => {
          cacheFileExists = false;
          expect(cacheFileExists).toBe(false);
        });
    });
  });
  it("cache doesn't do anything if string is passed", () => {
    const config = {
      code: ".foo {}",
      codeFilename: "foo.css",
      cache: true,
      config: {
        rules: {
          "color-no-invalid-hex": true
        }
      }
    };
    let cacheFileExists = true;

    return standalone(config).then(lintResults => {
      expect(lintResults.errored).toBe(false);

      return fileExists(expectedCacheFilePath)
        .then(() => {
          throw new Error(
            `Cache file should not be created if string is passed, ${expectedCacheFilePath} is found instead`
          );
        })
        .catch(() => {
          cacheFileExists = false;
          expect(cacheFileExists).toBe(false);
        });
    });
  });
});
describe("standalone cache uses cacheLocation", () => {
  const cacheLocationFile = path.join(fixturesPath, "cache", ".cachefile");
  const cacheLocationDir = path.join(fixturesPath, "cache");
  const expectedCacheFilePath = path.join(
    cacheLocationDir,
    `.stylelintcache_${hash(cwd)}`
  );

  afterEach(() => {
    // clean up after each test
    return Promise.all([
      unlink(expectedCacheFilePath).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      }),
      unlink(cacheLocationFile).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      })
    ]);
  });
  it("cacheLocation is a file", () => {
    const config = getConfig();

    config.cacheLocation = cacheLocationFile;

    return standalone(config)
      .then(() => {
        // Ensure cache file is created
        return fileExists(cacheLocationFile);
      })
      .then(fileStats => {
        expect(!!fileStats).toBe(true);

        const fileCache = fCache.createFromFile(cacheLocationFile);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
      });
  });
  it("cacheLocation is a directory", () => {
    const config = getConfig();

    config.cacheLocation = cacheLocationDir;

    return standalone(config)
      .then(() => {
        return fileExists(expectedCacheFilePath);
      })
      .then(cacheFileStats => {
        // Ensure cache file is created
        expect(!!cacheFileStats).toBe(true);

        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
      });
  });
});
<commit_msg>Revert "fix standalone-cache tests"<commit_after>"use strict";

const hash = require("../utils/hash");
const path = require("path");
const standalone = require("../standalone");
const fixturesPath = path.join(__dirname, "fixtures");
const cpFile = require("cp-file");
const fileExists = require("file-exists-promise");
const fs = require("fs");
const { promisify } = require("util");
const unlink = promisify(fs.unlink);
const fCache = require("file-entry-cache");

const cwd = process.cwd();
const invalidFile = path.join(fixturesPath, "empty-block.css");
const syntaxErrorFile = path.join(fixturesPath, "syntax_error.css");
const validFile = path.join(fixturesPath, "cache", "valid.css");
const newFileDest = path.join(fixturesPath, "cache", "newFile.css");

// Config object is getting mutated internally.
// Return new object of the same structure to
// make sure config doesn't change between runs.
function getConfig() {
  return {
    files: path.join(fixturesPath, "cache", "*.css"),
    config: {
      rules: { "block-no-empty": true, "color-no-invalid-hex": true }
    },
    cache: true
  };
}

describe("standalone cache", () => {
  const expectedCacheFilePath = path.join(cwd, ".stylelintcache");

  beforeEach(() => {
    // Initial run to warm up the cache
    return standalone(getConfig());
  });

  afterEach(() => {
    // Clean up after each test case
    return Promise.all([
      unlink(expectedCacheFilePath).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      }),
      unlink(newFileDest).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      })
    ]);
  });

  it("cache file is created at $CWD/.stylelintcache", () => {
    // Ensure cache file exists
    return fileExists(expectedCacheFilePath).then(isFileExist => {
      expect(!!isFileExist).toBe(true);

      const fileCache = fCache.createFromFile(expectedCacheFilePath);
      const { cache } = fileCache;

      // Ensure cache file contains only linted css file
      expect(typeof cache.getKey(validFile) === "object").toBe(true);
      expect(typeof cache.getKey(newFileDest) === "undefined").toBe(true);
    });
  });

  it("only changed files are linted", () => {
    // Add "changed" file
    return cpFile(validFile, newFileDest)
      .then(() => {
        // Next run should lint only changed files
        return standalone(getConfig());
      })
      .then(output => {
        // Ensure only changed files are linted
        const isValidFileLinted = !!output.results.find(
          file => file.source === validFile
        );
        const isNewFileLinted = !!output.results.find(
          file => file.source === newFileDest
        );

        expect(isValidFileLinted).toBe(false);
        expect(isNewFileLinted).toBe(true);

        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
        expect(typeof cache.getKey(newFileDest) === "object").toBe(true);
      });
  });

  it("all files are linted on config change", () => {
    const changedConfig = getConfig();

    changedConfig.config.rules["block-no-empty"] = false;

    return cpFile(validFile, newFileDest)
      .then(() => {
        // All file should be re-linted as config has changed
        return standalone(changedConfig);
      })
      .then(output => {
        // Ensure all files are re-linted
        const isValidFileLinted = !!output.results.find(
          file => file.source === validFile
        );
        const isNewFileLinted = !!output.results.find(
          file => file.source === newFileDest
        );

        expect(isValidFileLinted).toBe(true);
        expect(isNewFileLinted).toBe(true);
      });
  });

  it("invalid files are not cached", () => {
    return cpFile(invalidFile, newFileDest)
      .then(() => {
        // Should lint only changed files
        return standalone(getConfig());
      })
      .then(output => {
        expect(output.errored).toBe(true);
        // Ensure only changed files are linted
        const isValidFileLinted = !!output.results.find(
          file => file.source === validFile
        );
        const isInvalidFileLinted = !!output.results.find(
          file => file.source === newFileDest
        );

        expect(isValidFileLinted).toBe(false);
        expect(isInvalidFileLinted).toBe(true);

        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
        expect(typeof cache.getKey(newFileDest) === "undefined").toBe(true);
      });
  });
  it("files with syntax errors are not cached", () => {
    return cpFile(syntaxErrorFile, newFileDest)
      .then(() => {
        // Should lint only changed files
        return standalone(getConfig());
      })
      .then(() => {
        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
        expect(typeof cache.getKey(newFileDest) === "undefined").toBe(true);
      });
  });
  it("cache file is removed when cache is disabled", () => {
    const noCacheConfig = getConfig();

    noCacheConfig.cache = false;
    let cacheFileExists = true;

    return standalone(noCacheConfig).then(() => {
      return fileExists(expectedCacheFilePath)
        .then(() => {
          throw new Error(
            `Cache file is supposed to be removed, ${expectedCacheFilePath} is found instead`
          );
        })
        .catch(() => {
          cacheFileExists = false;
          expect(cacheFileExists).toBe(false);
        });
    });
  });
  it("cache doesn't do anything if string is passed", () => {
    const config = {
      code: ".foo {}",
      codeFilename: "foo.css",
      cache: true,
      config: {
        rules: {
          "color-no-invalid-hex": true
        }
      }
    };
    let cacheFileExists = true;

    return standalone(config).then(lintResults => {
      expect(lintResults.errored).toBe(false);

      return fileExists(expectedCacheFilePath)
        .then(() => {
          throw new Error(
            `Cache file should not be created if string is passed, ${expectedCacheFilePath} is found instead`
          );
        })
        .catch(() => {
          cacheFileExists = false;
          expect(cacheFileExists).toBe(false);
        });
    });
  });
});
describe("standalone cache uses cacheLocation", () => {
  const cacheLocationFile = path.join(fixturesPath, "cache", ".cachefile");
  const cacheLocationDir = path.join(fixturesPath, "cache");
  const expectedCacheFilePath = path.join(
    cacheLocationDir,
    `.stylelintcache_${hash(cwd)}`
  );

  afterEach(() => {
    // clean up after each test
    return Promise.all([
      unlink(expectedCacheFilePath).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      }),
      unlink(cacheLocationFile).catch(() => {
        // fs.unlink() throws an error if file doesn't exist and it's ok. We just
        // want to make sure it's not there for next test.
      })
    ]);
  });
  it("cacheLocation is a file", () => {
    const config = getConfig();

    config.cacheLocation = cacheLocationFile;

    return standalone(config)
      .then(() => {
        // Ensure cache file is created
        return fileExists(cacheLocationFile);
      })
      .then(fileStats => {
        expect(!!fileStats).toBe(true);

        const fileCache = fCache.createFromFile(cacheLocationFile);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
      });
  });
  it("cacheLocation is a directory", () => {
    const config = getConfig();

    config.cacheLocation = cacheLocationDir;

    return standalone(config)
      .then(() => {
        return fileExists(expectedCacheFilePath);
      })
      .then(cacheFileStats => {
        // Ensure cache file is created
        expect(!!cacheFileStats).toBe(true);

        const fileCache = fCache.createFromFile(expectedCacheFilePath);
        const { cache } = fileCache;

        expect(typeof cache.getKey(validFile) === "object").toBe(true);
      });
  });
});
<|endoftext|>


<commit_before>let pagination = require('./pagination');

let spec = {
  "title": "Graph",
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "title": "Item",
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "type": { "type": "integer" }
        }
      },
      "required": ["id", "name", "type"]
    },
    "relationships": {
      "type": "array",
      "items": {
        "title": "Relationship",
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "fromItem": { "type": "integer" },
          "toItem": { "type": "integer" },
          "type": { "type": "integer" }
        }
      },
      "required": ["id", "fromItem", "toItem", "type"]
    }
  },
  "required": ["items", "relationships"]
};

let mock = {
  "items": [
    {
      "id": 10,
      "name": "User Login Home Page",
      "type": 99
    },
    {
      "id": 11,
      "name": "Input a Username",
      "type": 24
    },
    {
      "id": 12,
      "name": "Input a Password",
      "type": 24
    },
    {
      "id": 13,
      "name": "Input a TeamName",
      "type": 24
    }
  ],
  "relationships": [
    {
      "id": 1,
      "fromItem": 11,
      "toItem": 10,
      "type": 6 // derived from
    },
    {
      "id": 2,
      "fromItem": 12,
      "toItem": 10,
      "type": 6 // derived from
    },
    {
      "id": 3,
      "fromItem": 13,
      "toItem": 10,
      "type": 6 // derived from
    }
  ]
};

let Graph = {
  name: 'Graph',
  nodes: [],
  edges: [],

  Graph: function () {
    let url = 'http://dummy:password@sevensource.jamacloud.com/rest/latest/items?project=33';
    let promise = pagination(url, 0, Number.MAX_SAFE_INTEGER);
    promise.then(function (items) {
      // console.log(item);
      items.forEach(function(item){
        console.log(item.documentKey);
      });
    }, function() {
      console.log('THIS FAILED KATHLEEN');
    });

  },
  Reorganize: function (rootNode) {

  },
  fromJson: function (graph) {
  },
  toJson: function () {
  }
};

module.exports = Graph;
<commit_msg>changed fromJson to fromGraphJson, implemented fromGraphJson<commit_after>let pagination = require('./pagination');

let spec = {
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Graph",
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "title": "Item",
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "type": { "type": "integer" }
        }
      },
      "required": ["id", "name", "type"]
    },
    "relationships": {
      "type": "array",
      "items": {
        "title": "Relationship",
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "fromItem": { "type": "integer" },
          "toItem": { "type": "integer" },
          "type": { "type": "integer" }
        }
      },
      "required": ["id", "fromItem", "toItem", "type"]
    }
  },
  "required": ["items", "relationships"]
};

let mock = {
  "items": [
    {
      "id": 10,
      "name": "User Login Home Page",
      "type": 99
    },
    {
      "id": 11,
      "name": "Input a Username",
      "type": 24
    },
    {
      "id": 12,
      "name": "Input a Password",
      "type": 24
    },
    {
      "id": 13,
      "name": "Input a TeamName",
      "type": 24
    }
  ],
  "relationships": [
    {
      "id": 1,
      "fromItem": 11,
      "toItem": 10,
      "type": 6 // derived from
    },
    {
      "id": 2,
      "fromItem": 12,
      "toItem": 10,
      "type": 6 // derived from
    },
    {
      "id": 3,
      "fromItem": 13,
      "toItem": 10,
      "type": 6 // derived from
    }
  ]
};

let Graph = {
  name: 'Graph',
  nodes: [],
  edges: [],

  Graph: function () {
    let url = 'http://dummy:password@sevensource.jamacloud.com/rest/latest/items?project=33';
    let promise = pagination(url, 0, Number.MAX_SAFE_INTEGER);
    promise.then(function (items) {
      // console.log(item);
      items.forEach(function(item){
        console.log(item.documentKey);
      });
    }, function() {
      console.log('THIS FAILED KATHLEEN');
    });

  },
  Reorganize: function (rootNode) {

  },
  fromGraphJson: function (graph) {
    mock.items.forEach(function (item) {
      this.nodes.push(item)
    });
    mock.relationships.forEach(function(relationship) {
      this.edges.push(relationship);
    })

  },
  toJson: function () {
  }
};

module.exports = Graph;
<|endoftext|>


<commit_before>/*
 * grunt
 * http://gruntjs.com/
 *
 * Copyright (c) 2015 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
 */

'use strict';

// Nodejs libs.
var path = require('path');

// This allows grunt to require() .coffee files.
require('coffee-script');

// The module to be exported.
var grunt = module.exports = {};

// Expose internal grunt libs.
function gRequire(name) {
  return grunt[name] = require('./grunt/' + name);
}

var util = require('grunt-legacy-util');
grunt.util = util;
grunt.util.task = require('./util/task');

var Log = require('grunt-legacy-log').Log;
var log = new Log({grunt: grunt});
grunt.log = log;

gRequire('template');
gRequire('event');
var fail = gRequire('fail');
gRequire('file');
var option = gRequire('option');
var config = gRequire('config');
var task = gRequire('task');
var help = gRequire('help');
gRequire('cli');
var verbose = grunt.verbose = log.verbose;

// Expose some grunt metadata.
grunt.package = require('../package.json');
grunt.version = grunt.package.version;

// Expose specific grunt lib methods on grunt.
function gExpose(obj, methodName, newMethodName) {
  grunt[newMethodName || methodName] = obj[methodName].bind(obj);
}
gExpose(task, 'registerTask');
gExpose(task, 'registerMultiTask');
gExpose(task, 'registerInitTask');
gExpose(task, 'renameTask');
gExpose(task, 'loadTasks');
gExpose(task, 'loadNpmTasks');
gExpose(config, 'init', 'initConfig');
gExpose(fail, 'warn');
gExpose(fail, 'fatal');

// Expose the task interface. I've never called this manually, and have no idea
// how it will work. But it might.
grunt.tasks = function(tasks, options, done) {
  // Update options with passed-in options.
  option.init(options);

  // Display the grunt version and quit if the user did --version.
  var _tasks, _options;
  if (option('version')) {
    // Not --verbose.
    log.writeln('grunt v' + grunt.version);

    if (option('verbose')) {
      // --verbose
      verbose.writeln('Install path: ' + path.resolve(__dirname, '..'));
      // Yes, this is a total hack, but we don't want to log all that verbose
      // task initialization stuff here.
      grunt.log.muted = true;
      // Initialize task system so that available tasks can be listed.
      grunt.task.init([], {help: true});
      // Re-enable logging.
      grunt.log.muted = false;

      // Display available tasks (for shell completion, etc).
      _tasks = Object.keys(grunt.task._tasks).sort();
      verbose.writeln('Available tasks: ' + _tasks.join(' '));

      // Display available options (for shell completion, etc).
      _options = [];
      Object.keys(grunt.cli.optlist).forEach(function(long) {
        var o = grunt.cli.optlist[long];
        _options.push('--' + (o.negate ? 'no-' : '') + long);
        if (o.short) { _options.push('-' + o.short); }
      });
      verbose.writeln('Available options: ' + _options.join(' '));
    }

    return;
  }

  // Init colors.
  log.initColors();

  // Display help and quit if the user did --help.
  if (option('help')) {
    help.display();
    return;
  }

  // A little header stuff.
  verbose.header('Initializing').writeflags(option.flags(), 'Command-line options');

  // Determine and output which tasks will be run.
  var tasksSpecified = tasks && tasks.length > 0;
  tasks = task.parseArgs([tasksSpecified ? tasks : 'default']);

  // Initialize tasks.
  task.init(tasks, options);

  verbose.writeln();
  if (!tasksSpecified) {
    verbose.writeln('No tasks specified, running default tasks.');
  }
  verbose.writeflags(tasks, 'Running tasks');

  // Handle otherwise unhandleable (probably asynchronous) exceptions.
  var uncaughtHandler = function(e) {
    fail.fatal(e, fail.code.TASK_FAILURE);
  };
  process.on('uncaughtException', uncaughtHandler);

  // Report, etc when all tasks have completed.
  task.options({
    error: function(e) {
      fail.warn(e, fail.code.TASK_FAILURE);
    },
    done: function() {
      // Stop handling uncaught exceptions so that we don't leave any
      // unwanted process-level side effects behind. There is no need to do
      // this in the error callback, because fail.warn() will either kill
      // the process, or with --force keep on going all the way here.
      process.removeListener('uncaughtException', uncaughtHandler);

      // Output a final fail / success report.
      fail.report();

      if (done) {
        // Execute "done" function when done (only if passed, of course).
        done();
      } else {
        // Otherwise, explicitly exit.
        util.exit(0);
      }
    }
  });

  // Execute all tasks, in order. Passing each task individually in a forEach
  // allows the error callback to execute multiple times.
  tasks.forEach(function(name) { task.run(name); });
  // Run tasks async internally to reduce call-stack, per:
  // https://github.com/gruntjs/grunt/pull/1026
  task.start({asyncDone:true});
};
<commit_msg>Using coffee-script/register<commit_after>/*
 * grunt
 * http://gruntjs.com/
 *
 * Copyright (c) 2015 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
 */

'use strict';

// Nodejs libs.
var path = require('path');

// This allows grunt to require() .coffee files.
require('coffee-script/register');

// The module to be exported.
var grunt = module.exports = {};

// Expose internal grunt libs.
function gRequire(name) {
  return grunt[name] = require('./grunt/' + name);
}

var util = require('grunt-legacy-util');
grunt.util = util;
grunt.util.task = require('./util/task');

var Log = require('grunt-legacy-log').Log;
var log = new Log({grunt: grunt});
grunt.log = log;

gRequire('template');
gRequire('event');
var fail = gRequire('fail');
gRequire('file');
var option = gRequire('option');
var config = gRequire('config');
var task = gRequire('task');
var help = gRequire('help');
gRequire('cli');
var verbose = grunt.verbose = log.verbose;

// Expose some grunt metadata.
grunt.package = require('../package.json');
grunt.version = grunt.package.version;

// Expose specific grunt lib methods on grunt.
function gExpose(obj, methodName, newMethodName) {
  grunt[newMethodName || methodName] = obj[methodName].bind(obj);
}
gExpose(task, 'registerTask');
gExpose(task, 'registerMultiTask');
gExpose(task, 'registerInitTask');
gExpose(task, 'renameTask');
gExpose(task, 'loadTasks');
gExpose(task, 'loadNpmTasks');
gExpose(config, 'init', 'initConfig');
gExpose(fail, 'warn');
gExpose(fail, 'fatal');

// Expose the task interface. I've never called this manually, and have no idea
// how it will work. But it might.
grunt.tasks = function(tasks, options, done) {
  // Update options with passed-in options.
  option.init(options);

  // Display the grunt version and quit if the user did --version.
  var _tasks, _options;
  if (option('version')) {
    // Not --verbose.
    log.writeln('grunt v' + grunt.version);

    if (option('verbose')) {
      // --verbose
      verbose.writeln('Install path: ' + path.resolve(__dirname, '..'));
      // Yes, this is a total hack, but we don't want to log all that verbose
      // task initialization stuff here.
      grunt.log.muted = true;
      // Initialize task system so that available tasks can be listed.
      grunt.task.init([], {help: true});
      // Re-enable logging.
      grunt.log.muted = false;

      // Display available tasks (for shell completion, etc).
      _tasks = Object.keys(grunt.task._tasks).sort();
      verbose.writeln('Available tasks: ' + _tasks.join(' '));

      // Display available options (for shell completion, etc).
      _options = [];
      Object.keys(grunt.cli.optlist).forEach(function(long) {
        var o = grunt.cli.optlist[long];
        _options.push('--' + (o.negate ? 'no-' : '') + long);
        if (o.short) { _options.push('-' + o.short); }
      });
      verbose.writeln('Available options: ' + _options.join(' '));
    }

    return;
  }

  // Init colors.
  log.initColors();

  // Display help and quit if the user did --help.
  if (option('help')) {
    help.display();
    return;
  }

  // A little header stuff.
  verbose.header('Initializing').writeflags(option.flags(), 'Command-line options');

  // Determine and output which tasks will be run.
  var tasksSpecified = tasks && tasks.length > 0;
  tasks = task.parseArgs([tasksSpecified ? tasks : 'default']);

  // Initialize tasks.
  task.init(tasks, options);

  verbose.writeln();
  if (!tasksSpecified) {
    verbose.writeln('No tasks specified, running default tasks.');
  }
  verbose.writeflags(tasks, 'Running tasks');

  // Handle otherwise unhandleable (probably asynchronous) exceptions.
  var uncaughtHandler = function(e) {
    fail.fatal(e, fail.code.TASK_FAILURE);
  };
  process.on('uncaughtException', uncaughtHandler);

  // Report, etc when all tasks have completed.
  task.options({
    error: function(e) {
      fail.warn(e, fail.code.TASK_FAILURE);
    },
    done: function() {
      // Stop handling uncaught exceptions so that we don't leave any
      // unwanted process-level side effects behind. There is no need to do
      // this in the error callback, because fail.warn() will either kill
      // the process, or with --force keep on going all the way here.
      process.removeListener('uncaughtException', uncaughtHandler);

      // Output a final fail / success report.
      fail.report();

      if (done) {
        // Execute "done" function when done (only if passed, of course).
        done();
      } else {
        // Otherwise, explicitly exit.
        util.exit(0);
      }
    }
  });

  // Execute all tasks, in order. Passing each task individually in a forEach
  // allows the error callback to execute multiple times.
  tasks.forEach(function(name) { task.run(name); });
  // Run tasks async internally to reduce call-stack, per:
  // https://github.com/gruntjs/grunt/pull/1026
  task.start({asyncDone:true});
};
<|endoftext|>