<commit_before>
function Mapa() {

	//t=tunel horizontal
	//p=pacman
	//.=bolita
	//f=fantasma
	//#=muro
	//c=cereza
this.mapa = [
"........#........",
"c##.###.#.###.##c",
".................",
".##.#.#####.#.##.",
"....#...#...#....",
"###.###   ###.###",
"###.#   f   #.###",
"t  .   fff   .  t",
"###.#       #.###",
"###.###   ###.###",
"....#...#...#....",
".##.#.#####.#.##.",
"........p........",
"c##.###.#.###.##c",
"........#........"
] ;

	this.tamanoTile=50 ;
	this.c = null ;
	this.alto = this.mapa.length ;
	this.ancho = this.mapa[0].length ;
	this.anchoPixels = this.tamanoTile * this.ancho ;
	this.altoPixels = this.tamanoTile * this.alto ;

	this.setContext = function(context) {
		this.c = context ;
	} ;

	this.esCamino = function(x,y) {
		var mapa = this.mapa ;
		if (x == mapa[0].length && mapa[y].charAt(x-1) == 't'
			|| x == -1 && mapa[y].charAt(0) == 't')
		{
			// Saliendo por un túnel
			return true ;
		}
		if (x < 0 || x > mapa[0].length-1) return false ;
		if (y < 0 || y > mapa.length-1) return false ;
		return mapa[y].charAt(x) != '#' ;
	} ;

	this.esPacman = function(x,y) {
		return this.mapa[y].charAt(x) == 'p' ;
	} ;

	this.esFantasma = function(x,y) {
		return this.mapa[y].charAt(x) == 'f' ;
	} ;

	this.getTile = function(x,y) {
		var mapa = this.mapa ;
		var arriba = "0" ;
		var abajo = "0" ;
		var derecha = "0" ;
		var izquierda = "0" ;

		//console.debug("("+x+ ","+y+") = " + mapa[y].charAt(x)) ;
		if (mapa[y].charAt(x) == '#') return null ;
		if (mapa[y].charAt(x) == 't') return "tiles/tile_0101.png" ;

		if (y>0) {
			if (this.esCamino(x,y-1)) arriba = "1" ;
		}
		if (y<this.alto-1) {
			if (this.esCamino(x,y+1)) abajo = "1" ;
		}
		if (x>0) {
			if (this.esCamino(x-1,y)) izquierda = "1" ;
		}
		if (x<this.ancho-1) {
			if (this.esCamino(x+1,y)) derecha = "1" ;
		}

		return "tiles/tile_"+arriba+derecha+abajo+izquierda+".png" ;
	} ;

	this.dibujarMapa = function() {
		var tamanoTile = this.tamanoTile ;
		for (var y=0 ; y<this.alto ; y++) {
			for (var x=0 ; x<this.ancho ; x++) {
				var fileTile = this.getTile(x,y) ;

				if (fileTile==null) continue ;

				var tile = new Image() ;
				tile.src= fileTile ;
				tile.mapa = this ;
				tile.tileX = x ;
				tile.tileY = y ;
				tile.onload = function() {
					var pixTileX = this.tileX * this.mapa.tamanoTile ;
					var pixTileY = this.tileY * this.mapa.tamanoTile ;
					// Dibujar el tile
					this.mapa.c.drawImage(this, pixTileX, pixTileY) ;
					// Dibujar las bolitas
					if (this.mapa.mapa[this.tileY].charAt(this.tileX) == '.') {
						this.mapa.c.fillStyle = "rgb(255,255,255)" ;
						this.mapa.c.beginPath() ;
						this.mapa.c.arc(pixTileX+25, pixTileY+25, 5, 0, 2*Math.PI) ;
						this.mapa.c.fill() ;
					}
				};
			}
		}
	} ;

	this.numeroDirecciones = function(x,y) {
		var direcciones=0 ;
		if (this.esCamino(x,y-1)) direcciones++ ;
		if (this.esCamino(x,y+1)) direcciones++ ;
		if (this.esCamino(x+1,y)) direcciones++ ;
		if (this.esCamino(x-1,y-1)) direcciones++ ;
		return direcciones ;
	} ;

	this.configurarPacman = function(pacman) {
		var tamanoTile = this.tamanoTile ;
		for (var y=0 ; y<this.alto ; y++) {
			for (var x=0 ; x<this.ancho ; x++) {
				if (this.esPacman(x,y)) {
					pacman.x = x*tamanoTile+25;
					pacman.y = y*tamanoTile+25;
				}
			}
		}
	} ;

	this.configurarFantasma = function(fantasma, numeroFantasma) {
		var tamanoTile = this.tamanoTile ;
		for (var y=0 ; y<this.alto ; y++) {
			for (var x=0 ; x<this.ancho ; x++) {
				if (this.esFantasma(x,y)) {
					numeroFantasma-- ;
					if (numeroFantasma == 0) {
						fantasma.x = x*tamanoTile+25;
						fantasma.y = y*tamanoTile+25;
					}
				}
			}
		}
	}
}
<commit_msg>Dibujo de las energías<commit_after>
function Mapa() {

	//t=tunel horizontal
	//p=pacman
	//.=bolita
	//f=fantasma
	//#=muro
	//e=energia
this.mapa = [
"........#........",
"e##.###.#.###.##e",
".................",
".##.#.#####.#.##.",
"....#...#...#....",
"###.###   ###.###",
"###.#   f   #.###",
"t  .   fff   .  t",
"###.#       #.###",
"###.###   ###.###",
"....#...#...#....",
".##.#.#####.#.##.",
"........p........",
"e##.###.#.###.##e",
"........#........"
] ;

	this.tamanoTile=50 ;
	this.c = null ;
	this.alto = this.mapa.length ;
	this.ancho = this.mapa[0].length ;
	this.anchoPixels = this.tamanoTile * this.ancho ;
	this.altoPixels = this.tamanoTile * this.alto ;

	this.setContext = function(context) {
		this.c = context ;
	} ;

	this.esCamino = function(x,y) {
		var mapa = this.mapa ;
		if (x == mapa[0].length && mapa[y].charAt(x-1) == 't'
			|| x == -1 && mapa[y].charAt(0) == 't')
		{
			// Saliendo por un túnel
			return true ;
		}
		if (x < 0 || x > mapa[0].length-1) return false ;
		if (y < 0 || y > mapa.length-1) return false ;
		return mapa[y].charAt(x) != '#' ;
	} ;

	this.esPacman = function(x,y) {
		return this.mapa[y].charAt(x) == 'p' ;
	} ;

	this.esFantasma = function(x,y) {
		return this.mapa[y].charAt(x) == 'f' ;
	} ;

	this.getTile = function(x,y) {
		var mapa = this.mapa ;
		var arriba = "0" ;
		var abajo = "0" ;
		var derecha = "0" ;
		var izquierda = "0" ;

		//console.debug("("+x+ ","+y+") = " + mapa[y].charAt(x)) ;
		if (mapa[y].charAt(x) == '#') return null ;
		if (mapa[y].charAt(x) == 't') return "tiles/tile_0101.png" ;

		if (y>0) {
			if (this.esCamino(x,y-1)) arriba = "1" ;
		}
		if (y<this.alto-1) {
			if (this.esCamino(x,y+1)) abajo = "1" ;
		}
		if (x>0) {
			if (this.esCamino(x-1,y)) izquierda = "1" ;
		}
		if (x<this.ancho-1) {
			if (this.esCamino(x+1,y)) derecha = "1" ;
		}

		return "tiles/tile_"+arriba+derecha+abajo+izquierda+".png" ;
	} ;

	this.dibujarMapa = function() {
		var tamanoTile = this.tamanoTile ;
		for (var y=0 ; y<this.alto ; y++) {
			for (var x=0 ; x<this.ancho ; x++) {
				var fileTile = this.getTile(x,y) ;

				if (fileTile==null) continue ;

				var tile = new Image() ;
				tile.src= fileTile ;
				tile.mapa = this ;
				tile.tileX = x ;
				tile.tileY = y ;
				tile.onload = function() {
					var pixTileX = this.tileX * this.mapa.tamanoTile ;
					var pixTileY = this.tileY * this.mapa.tamanoTile ;
					// Dibujar el tile
					this.mapa.c.drawImage(this, pixTileX, pixTileY) ;
					// Dibujar las bolitas
					if (this.mapa.mapa[this.tileY].charAt(this.tileX) == '.') {
						this.mapa.c.fillStyle = "rgb(255,255,255)" ;
						this.mapa.c.beginPath() ;
						this.mapa.c.arc(pixTileX+25, pixTileY+25, 5, 0, 2*Math.PI) ;
						this.mapa.c.fill() ;
					}
					// Dibujar las energías
					if (this.mapa.mapa[this.tileY].charAt(this.tileX) == 'e') {
						this.mapa.c.fillStyle = "rgb(255,185,175)" ;
						this.mapa.c.strokeStyle = "rgb(255,255,255)" ;
						this.mapa.c.beginPath() ;
						this.mapa.c.arc(pixTileX+25, pixTileY+25, 8, 0, 2*Math.PI) ;
						this.mapa.c.fill() ;
						this.mapa.c.stroke() ;
					}

				};
			}
		}
	} ;

	this.numeroDirecciones = function(x,y) {
		var direcciones=0 ;
		if (this.esCamino(x,y-1)) direcciones++ ;
		if (this.esCamino(x,y+1)) direcciones++ ;
		if (this.esCamino(x+1,y)) direcciones++ ;
		if (this.esCamino(x-1,y-1)) direcciones++ ;
		return direcciones ;
	} ;

	this.configurarPacman = function(pacman) {
		var tamanoTile = this.tamanoTile ;
		for (var y=0 ; y<this.alto ; y++) {
			for (var x=0 ; x<this.ancho ; x++) {
				if (this.esPacman(x,y)) {
					pacman.x = x*tamanoTile+25;
					pacman.y = y*tamanoTile+25;
				}
			}
		}
	} ;

	this.configurarFantasma = function(fantasma, numeroFantasma) {
		var tamanoTile = this.tamanoTile ;
		for (var y=0 ; y<this.alto ; y++) {
			for (var x=0 ; x<this.ancho ; x++) {
				if (this.esFantasma(x,y)) {
					numeroFantasma-- ;
					if (numeroFantasma == 0) {
						fantasma.x = x*tamanoTile+25;
						fantasma.y = y*tamanoTile+25;
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>/* eslint-disable no-console */
const axios = require('axios');
const crypto = require('crypto');
const {execSync} = require('child_process');
const axiosRetry = require('axios-retry');
const dateFNs = require('date-fns');
const {default: PQueue} = require('p-queue'); // NOTE - pinned at p-queue@6.6.2 because 7 requires whole project to be esm
const {parseStringPromise} = require('xml2js');
const categoryList = require('./categories.json');

const API_URL = process.env.JENKINS_IO_API_URL || 'https://plugins.jenkins.io/api';

axiosRetry(axios, {retries: 3});

const requestGET = ({url, reporter}) => {
    const activity = reporter.activityTimer(`Fetching '${url}'`);
    activity.start();

    return axios
        .get(url)
        .then((results) => {
            activity.end();

            if (results.status !== 200) {
                throw results.data;
            }
            return results.data;
        });
};

const shouldFetchPluginContent = (id) => {
    if (process.env.GET_CONTENT_SINGLE && process.env.GET_CONTENT_SINGLE === id) {
        return true;
    }
    if (!process.env.GET_CONTENT) {
        return false;
    }
    return true;
};

const getPluginContent = async ({wiki, pluginName, reporter}) => {
    if (!shouldFetchPluginContent(pluginName)) {
        wiki.content = '';
        return wiki;
    }
    return requestGET({reporter, url: `https://plugins.jenkins.io/api/plugin/${pluginName}`}).then(data => {
        wiki.content = data.wiki.content || '';
        wiki.url = data.wiki.url || '';
        return wiki;
    });
};

const processPlugin = ({createNode, names, stats, updateData, detachedPlugins, documentation, bomDependencies, pipelinePluginIds, firstReleases, createContentDigest, createNodeId, reporter, plugin}) => {
    return async function () {
        const pluginName = plugin.name.trim();
        names.push(pluginName);
        const developers = plugin.developers || [];
        developers.forEach(maint => {maint.id = maint.developerId, delete(maint.developerId);});
        plugin.scm = fixGitHubUrl(plugin.scm, plugin.defaultBranch || 'master');
        const pluginStats = stats[pluginName] || {installations: null};
        pluginStats.trend = computeTrend(plugin, stats, updateData.plugins);
        const allDependencies = getImpliedDependenciesAndTitles(plugin, detachedPlugins, updateData);
        const wiki = await getPluginContent({wiki: documentation[pluginName] || {}, pluginName, reporter});

        const pluginNode = {
            ...plugin,
            id: createNodeId(plugin.name.trim()),
            stats: pluginStats,
            wiki: wiki,
            securityWarnings: updateData.warnings.filter(p => p.name == pluginName)
                .map(w => checkActive(w, plugin)),
            dependencies: allDependencies,
            firstRelease: firstReleases[pluginName] && firstReleases[pluginName].toISOString(),
            hasPipelineSteps: pipelinePluginIds.includes(pluginName),
            hasBomEntry: !!bomDependencies.find(artifactId => plugin.gav.includes(`:${artifactId}:`)),
            parent: null,
            children: [],
            internal: {
                type: 'JenkinsPlugin',
            }
        };
        pluginNode.internal.contentDigest = createContentDigest(pluginNode);
        createNode(pluginNode);

        for (const maintainer of developers) {
            const developerNode = {
                ...maintainer,
                id: createNodeId(maintainer.id),
                internal: {
                    type: 'JenkinsDeveloper',
                    contentDigest: crypto.createHash('md5').update(maintainer.id).digest('hex')
                }
            };
            developerNode.internal.contentDigest = createContentDigest(developerNode);
            createNode(developerNode);
        }
        for (const dependency of allDependencies) {
            const dependencyNode = {
                ...dependency,
                dependentTitle: plugin.title,
                dependentName: plugin.name,
                id: createNodeId(`${pluginName}:${dependency.name.trim()}`),
                internal: {
                    type: 'JenkinsPluginDependency',
                }
            };
            dependencyNode.internal.contentDigest = createContentDigest(dependencyNode);
            createNode(dependencyNode);
        }
    };
};

const fetchPluginData = async ({createNode, createContentDigest, createNodeId, reporter, firstReleases, stats}) => {
    const sectionActivity = reporter.activityTimer('fetch plugins info');
    sectionActivity.start();
    const names = [];
    const pipelinePluginsUrl = 'https://www.jenkins.io/doc/pipeline/steps/contents.json';
    const pipelinePluginIds = await requestGET({url: pipelinePluginsUrl, reporter});
    const bomDependencies = await fetchBomDependencies(reporter);
    const updateUrl = 'https://updates.jenkins.io/update-center.actual.json';
    const updateData = await requestGET({url: updateUrl, reporter});
    const detachedUrl = 'https://raw.githubusercontent.com/jenkinsci/jenkins/master/core/src/main/resources/jenkins/split-plugins.txt';
    const detachedPluginsData = await requestGET({url: detachedUrl, reporter});
    const detachedPlugins = detachedPluginsData.split('\n')
        .filter(row => row.length && !row.startsWith('#')).map(row => row.split(' '));
    for (const deprecation of Object.keys(updateData.deprecations)) {
        const deprecatedPlugin = updateData.plugins[deprecation];
        if (deprecatedPlugin) {
            deprecatedPlugin.labels.push('deprecated');
            deprecatedPlugin.deprecationNotice = updateData.deprecations[deprecation].url;
        }
    }
    const documentationListUrl = 'https://updates.jenkins.io/current/plugin-documentation-urls.json';
    const documentation = await requestGET({url: documentationListUrl, reporter});

    const queue = new PQueue({concurrency: 100, autoStart: true});
    Object.values(updateData.plugins).forEach(plugin => queue.add(processPlugin({
        plugin,
        names,
        stats,
        updateData,
        detachedPlugins,
        documentation,
        bomDependencies,
        pipelinePluginIds,
        firstReleases,
        createNode,
        createContentDigest,
        createNodeId,
        reporter
    })));
    await queue.onIdle();

    await fetchSuspendedPlugins({updateData, names, createNode});
    sectionActivity.end();
};

const getImpliedDependenciesAndTitles = (plugin, detachedPlugins, updateData) => {
    for (const detached of detachedPlugins) {
        const [detachedPlugin, detachedCore, detachedVersion] = detached;
        if (versionToNumber(detachedCore) > versionToNumber(plugin.requiredCore)) {
            plugin.dependencies.push({name: detachedPlugin,
                version: detachedVersion,
                implied: true,
                optional: false});
        }
    }
    plugin.dependencies = plugin.dependencies || [];
    for (const dependency of plugin.dependencies) {
        if (!updateData.plugins[dependency.name]) {
            dependency.title = dependency.name; // optional dependency suspended
        } else {
            dependency.title = updateData.plugins[dependency.name].title;
        }
    }
    return plugin.dependencies;
};

const versionToNumber = (version) => {
    const [major, minor] = version.split('.').map(n => parseInt(n));
    return major * 1E5 + minor;
};

const checkActive = (warning, plugin) => {
    warning.active = !!warning.versions.find(version => plugin.version.match(`^(${version.pattern})$`));
    return warning;
};

const fixGitHubUrl = (url, defaultBranch) => {
    const match = url && url.match(/^(https?:\/\/github.com\/[^/]+\/[^/]+)\/(.+)$/);
    if (match && defaultBranch && !match[2].startsWith('tree/')) {
        return `${match[1]}/tree/${defaultBranch}/${match[2]}`;
    }
    return url;
};

const computeTrend = (plugin, stats, plugins) => {
    if (!stats[plugin.name]) {
        return 0;
    }
    const increase = getPercentage(plugin.name, stats, 1) - getPercentage(plugin.name, stats, 2);
    const maxDependent = Object.values(plugins)
        .filter(p => isMandatoryDependency(p, plugin))
        .map(p => [p.name, getPercentage(p.name, stats, 1)])
        .reduce((a, b) => a[1] > b[1] ? a : b, ['', 0])[1];
    const independence = 1 - maxDependent / getPercentage(plugin.name, stats, 1);
    return Math.round(1E6 * increase * Math.max(0, independence)) || 0;
};

const isMandatoryDependency = (p, plugin) => p.dependencies && !!p.dependencies
    .filter(d => !d.optional && d.name == plugin.name).length;

const getPercentage = (name, stats, monthsAgo) => {
    if (!stats[name]) {
        return 0;
    }
    const installations = stats[name].installations;
    const coreInstallations = stats.core.installations;
    if (!installations[installations.length - monthsAgo]) {
        return 0;
    }
    return installations[installations.length - monthsAgo].total
            / coreInstallations[coreInstallations.length - monthsAgo].total;
};

const fetchBomDependencies = async (reporter) => {
    try {
        const bomUrl = 'https://raw.githubusercontent.com/jenkinsci/bom/master/bom-latest/pom.xml';
        const bom = await requestGET({url: bomUrl, reporter});
        const xml = await parseStringPromise(bom);
        return xml.project.dependencyManagement[0].dependencies[0].dependency.map(dep => dep.artifactId);
    } catch(ex) {
        console.warn('Failed to fetch BOM data', ex);
    }
    return [];
};

const fetchSuspendedPlugins = async ({updateData, names, createNode}) => {
    const suspendedPromises = [];
    for (const deprecation of Object.keys(updateData.deprecations)) {
        if (!names.includes(deprecation)) {
            suspendedPromises.push(createNode({
                ...updateData.deprecations[deprecation],
                id: deprecation,
                name: deprecation,
                parent: null,
                children: [],
                internal: {
                    type: 'SuspendedPlugin',
                    contentDigest: crypto.createHash('md5').update(deprecation.trim()).digest('hex')
                }
            }));
        }
    }
    await Promise.all(suspendedPromises);
};

const processCategoryData = async ({createNode, reporter}) => {
    const sectionActivity = reporter.activityTimer('process categories');
    sectionActivity.start();
    for (const category of categoryList) {
        createNode({
            ...category,
            id: category.id.trim(),
            parent: null,
            children: [],
            internal: {
                type: 'JenkinsPluginCategory',
                contentDigest: crypto
                    .createHash('md5')
                    .update(`category${category.name}`)
                    .digest('hex')
            }
        });
    }
    sectionActivity.end();
};

const fetchLabelData = async ({createNode, reporter}) => {
    const sectionActivity = reporter.activityTimer('fetch labels info');
    sectionActivity.start();
    const url = 'https://raw.githubusercontent.com/jenkinsci/jenkins/master/core/src/main/resources/hudson/model/Messages.properties';
    const messages = await requestGET({url, reporter});
    const keyPrefix = 'UpdateCenter.PluginCategory';
    for (const line of messages.split('\n')) {
        const [key, value] = line.split('=', 2).map(s => s.trim());
        const labelId = key.substring(keyPrefix.length + 1);
        if (key.startsWith(keyPrefix)) {
            createNode({
                title: value,
                id: labelId,
                parent: null,
                children: [],
                internal: {
                    type: 'JenkinsPluginLabel',
                    contentDigest: crypto
                        .createHash('md5')
                        .update(`label${labelId}`)
                        .digest('hex')
                }
            });
        }
    }
    sectionActivity.end();
};

const fetchSiteInfo = async ({createNode, reporter}) => {
    const sectionActivity = reporter.activityTimer('fetch plugin api info');
    sectionActivity.start();
    const url = `${API_URL}/info`;
    const info = await requestGET({url, reporter});

    createNode({
        api: {
            ...info
        },
        website: {
            commit: execSync('git rev-parse HEAD').toString().trim(),
            version: require('find-package-json')().next().value.version,
        },
        id: 'pluginSiteInfo',
        parent: null,
        children: [],
        internal: {
            type: 'JenkinsPluginSiteInfo',
            contentDigest: crypto.createHash('md5').update('pluginSiteInfo').digest('hex')
        }
    });
    sectionActivity.end();
};

const fetchStats = async ({reporter, stats}) => {
    const timeSpan = 12;
    const totalUrl = 'https://stats.jenkins.io/jenkins-stats/svg/total-jenkins.csv';
    const totalInstalls = (await requestGET({url: totalUrl, reporter})).trim().split('\n');
    stats.core = {installations: []};
    const timestamps = [];
    for (let monthsAgo = 1; monthsAgo <= timeSpan; monthsAgo++) {
        const [month, coreInstalls] = csvParse(totalInstalls[totalInstalls.length - monthsAgo]);
        const pluginInstallsUrl = `https://stats.jenkins.io/jenkins-stats/svg/${month}-plugins.csv`;
        const timestamp = Date.parse(`${month}`.replace(/(..)$/, '-$1'));
        timestamps.push(timestamp);
        const pluginInstalls = (await requestGET({url: pluginInstallsUrl, reporter})).split('\n');
        stats.core.installations[timeSpan - monthsAgo] = {timestamp: timestamp, total: coreInstalls};
        for (const pluginRow of pluginInstalls) {
            const [pluginName, installs] = csvParse(pluginRow);
            stats[pluginName] = stats[pluginName] || {installations: []};
            stats[pluginName].installations[timeSpan - monthsAgo] = {timestamp: timestamp, total: installs};
        }
    }
    for (const pluginName of Object.keys(stats)) {
        for (let idx = 0; idx < timeSpan; idx++) {
            stats[pluginName].installations[idx] = stats[pluginName].installations[idx]
                || {total: 0, timestamp: timestamps[idx]};
        }
        stats[pluginName].currentInstalls = stats[pluginName].installations[timeSpan - 1].total || 0;
    }
};

const csvParse = (row) => {
    return row.split(',').map(s => s ? JSON.parse(s) : s).map(s => isNaN(parseInt(s)) ? s : parseInt(s));
};

const fetchPluginVersions = async ({createNode, reporter, firstReleases}) => {
    const sectionActivity = reporter.activityTimer('fetch plugin versions');
    sectionActivity.start();
    const url = 'https://updates.jenkins.io/plugin-versions.json';
    const json = await requestGET({url, reporter});
    for (const pluginVersions of Object.values(json.plugins)) {
        for (const data of Object.values(pluginVersions)) {
            /*
            buildDate    "May 13, 2020"
            dependencies    […]
            name    "42crunch-security-audit"
            requiredCore    "2.164.3"
            sha1    "nQl64SC4dvbFE0Kbwkdqe2C0hG8="
            sha256    "rMxlZQqnAofpD1/vNosqcPgghuhXKzRrpLuLHV8XUQ8="
            url    "https://updates.jenkins.io/download/plugins/42crunch-security-audit/2.1/42crunch-security-audit.hpi"
            version 2.1
            */
            const date = dateFNs.parse(data.buildDate, 'MMMM d, yyyy', new Date(0));
            if (!firstReleases[data.name] || firstReleases[data.name].getTime() > date.getTime()) {
                firstReleases[data.name] = date;
            }
            createNode({
                ...data,
                buildDate: date,
                id: `${data.name}_${data.version}`,
                parent: null,
                children: [],
                internal: {
                    type: 'JenkinsPluginVersion',
                    contentDigest: crypto
                        .createHash('md5')
                        .update(`pluginVersion_${data.name}_${data.version}`)
                        .digest('hex')
                }
            });
        }
    }
    sectionActivity.end();
};

module.exports = {
    fetchSiteInfo,
    fetchLabelData,
    processCategoryData,
    fetchPluginData,
    fetchPluginVersions,
    fixGitHubUrl,
    fetchStats,
    getPluginContent,
    requestGET
};
<commit_msg>Make api site failures more clear/non fatal<commit_after>/* eslint-disable no-console */
const axios = require('axios');
const crypto = require('crypto');
const {execSync} = require('child_process');
const axiosRetry = require('axios-retry');
const dateFNs = require('date-fns');
const {default: PQueue} = require('p-queue'); // NOTE - pinned at p-queue@6.6.2 because 7 requires whole project to be esm
const {parseStringPromise} = require('xml2js');
const categoryList = require('./categories.json');

const API_URL = process.env.JENKINS_IO_API_URL || 'https://plugins.jenkins.io/api';

axiosRetry(axios, {retries: 3});

const requestGET = ({url, reporter}) => {
    const activity = reporter.activityTimer(`Fetching '${url}'`);
    activity.start();

    return axios
        .get(url)
        .then((results) => {
            activity.end();

            if (results.status !== 200) {
                throw results.data;
            }
            return results.data;
        });
};

const shouldFetchPluginContent = (id) => {
    if (process.env.GET_CONTENT_SINGLE && process.env.GET_CONTENT_SINGLE === id) {
        return true;
    }
    if (!process.env.GET_CONTENT) {
        return false;
    }
    return true;
};

const getPluginContent = async ({wiki, pluginName, reporter}) => {
    if (!shouldFetchPluginContent(pluginName)) {
        wiki.content = '';
        return wiki;
    }
    return requestGET({reporter, url: `https://plugins.jenkins.io/api/plugin/${pluginName}`}).then(data => {
        if (!data.wiki) {
            console.warn('Results from plugin site did not include wiki', pluginName, data);
            return wiki;
        }
        wiki.content = data.wiki.content || wiki.content || '';
        wiki.url = data.wiki.url || wiki.url || '';
        return wiki;
    });
};

const processPlugin = ({createNode, names, stats, updateData, detachedPlugins, documentation, bomDependencies, pipelinePluginIds, firstReleases, createContentDigest, createNodeId, reporter, plugin}) => {
    return async function () {
        const pluginName = plugin.name.trim();
        names.push(pluginName);
        const developers = plugin.developers || [];
        developers.forEach(maint => {maint.id = maint.developerId, delete(maint.developerId);});
        plugin.scm = fixGitHubUrl(plugin.scm, plugin.defaultBranch || 'master');
        const pluginStats = stats[pluginName] || {installations: null};
        pluginStats.trend = computeTrend(plugin, stats, updateData.plugins);
        const allDependencies = getImpliedDependenciesAndTitles(plugin, detachedPlugins, updateData);
        const wiki = await getPluginContent({wiki: documentation[pluginName] || {}, pluginName, reporter});

        const pluginNode = {
            ...plugin,
            id: createNodeId(plugin.name.trim()),
            stats: pluginStats,
            wiki: wiki,
            securityWarnings: updateData.warnings.filter(p => p.name == pluginName)
                .map(w => checkActive(w, plugin)),
            dependencies: allDependencies,
            firstRelease: firstReleases[pluginName] && firstReleases[pluginName].toISOString(),
            hasPipelineSteps: pipelinePluginIds.includes(pluginName),
            hasBomEntry: !!bomDependencies.find(artifactId => plugin.gav.includes(`:${artifactId}:`)),
            parent: null,
            children: [],
            internal: {
                type: 'JenkinsPlugin',
            }
        };
        pluginNode.internal.contentDigest = createContentDigest(pluginNode);
        createNode(pluginNode);

        for (const maintainer of developers) {
            const developerNode = {
                ...maintainer,
                id: createNodeId(maintainer.id),
                internal: {
                    type: 'JenkinsDeveloper',
                    contentDigest: crypto.createHash('md5').update(maintainer.id).digest('hex')
                }
            };
            developerNode.internal.contentDigest = createContentDigest(developerNode);
            createNode(developerNode);
        }
        for (const dependency of allDependencies) {
            const dependencyNode = {
                ...dependency,
                dependentTitle: plugin.title,
                dependentName: plugin.name,
                id: createNodeId(`${pluginName}:${dependency.name.trim()}`),
                internal: {
                    type: 'JenkinsPluginDependency',
                }
            };
            dependencyNode.internal.contentDigest = createContentDigest(dependencyNode);
            createNode(dependencyNode);
        }
    };
};

const fetchPluginData = async ({createNode, createContentDigest, createNodeId, reporter, firstReleases, stats}) => {
    const sectionActivity = reporter.activityTimer('fetch plugins info');
    sectionActivity.start();
    const names = [];
    const pipelinePluginsUrl = 'https://www.jenkins.io/doc/pipeline/steps/contents.json';
    const pipelinePluginIds = await requestGET({url: pipelinePluginsUrl, reporter});
    const bomDependencies = await fetchBomDependencies(reporter);
    const updateUrl = 'https://updates.jenkins.io/update-center.actual.json';
    const updateData = await requestGET({url: updateUrl, reporter});
    const detachedUrl = 'https://raw.githubusercontent.com/jenkinsci/jenkins/master/core/src/main/resources/jenkins/split-plugins.txt';
    const detachedPluginsData = await requestGET({url: detachedUrl, reporter});
    const detachedPlugins = detachedPluginsData.split('\n')
        .filter(row => row.length && !row.startsWith('#')).map(row => row.split(' '));
    for (const deprecation of Object.keys(updateData.deprecations)) {
        const deprecatedPlugin = updateData.plugins[deprecation];
        if (deprecatedPlugin) {
            deprecatedPlugin.labels.push('deprecated');
            deprecatedPlugin.deprecationNotice = updateData.deprecations[deprecation].url;
        }
    }
    const documentationListUrl = 'https://updates.jenkins.io/current/plugin-documentation-urls.json';
    const documentation = await requestGET({url: documentationListUrl, reporter});

    const queue = new PQueue({concurrency: 100, autoStart: true});
    Object.values(updateData.plugins).forEach(plugin => queue.add(processPlugin({
        plugin,
        names,
        stats,
        updateData,
        detachedPlugins,
        documentation,
        bomDependencies,
        pipelinePluginIds,
        firstReleases,
        createNode,
        createContentDigest,
        createNodeId,
        reporter
    })));
    await queue.onIdle();

    await fetchSuspendedPlugins({updateData, names, createNode});
    sectionActivity.end();
};

const getImpliedDependenciesAndTitles = (plugin, detachedPlugins, updateData) => {
    for (const detached of detachedPlugins) {
        const [detachedPlugin, detachedCore, detachedVersion] = detached;
        if (versionToNumber(detachedCore) > versionToNumber(plugin.requiredCore)) {
            plugin.dependencies.push({name: detachedPlugin,
                version: detachedVersion,
                implied: true,
                optional: false});
        }
    }
    plugin.dependencies = plugin.dependencies || [];
    for (const dependency of plugin.dependencies) {
        if (!updateData.plugins[dependency.name]) {
            dependency.title = dependency.name; // optional dependency suspended
        } else {
            dependency.title = updateData.plugins[dependency.name].title;
        }
    }
    return plugin.dependencies;
};

const versionToNumber = (version) => {
    const [major, minor] = version.split('.').map(n => parseInt(n));
    return major * 1E5 + minor;
};

const checkActive = (warning, plugin) => {
    warning.active = !!warning.versions.find(version => plugin.version.match(`^(${version.pattern})$`));
    return warning;
};

const fixGitHubUrl = (url, defaultBranch) => {
    const match = url && url.match(/^(https?:\/\/github.com\/[^/]+\/[^/]+)\/(.+)$/);
    if (match && defaultBranch && !match[2].startsWith('tree/')) {
        return `${match[1]}/tree/${defaultBranch}/${match[2]}`;
    }
    return url;
};

const computeTrend = (plugin, stats, plugins) => {
    if (!stats[plugin.name]) {
        return 0;
    }
    const increase = getPercentage(plugin.name, stats, 1) - getPercentage(plugin.name, stats, 2);
    const maxDependent = Object.values(plugins)
        .filter(p => isMandatoryDependency(p, plugin))
        .map(p => [p.name, getPercentage(p.name, stats, 1)])
        .reduce((a, b) => a[1] > b[1] ? a : b, ['', 0])[1];
    const independence = 1 - maxDependent / getPercentage(plugin.name, stats, 1);
    return Math.round(1E6 * increase * Math.max(0, independence)) || 0;
};

const isMandatoryDependency = (p, plugin) => p.dependencies && !!p.dependencies
    .filter(d => !d.optional && d.name == plugin.name).length;

const getPercentage = (name, stats, monthsAgo) => {
    if (!stats[name]) {
        return 0;
    }
    const installations = stats[name].installations;
    const coreInstallations = stats.core.installations;
    if (!installations[installations.length - monthsAgo]) {
        return 0;
    }
    return installations[installations.length - monthsAgo].total
            / coreInstallations[coreInstallations.length - monthsAgo].total;
};

const fetchBomDependencies = async (reporter) => {
    try {
        const bomUrl = 'https://raw.githubusercontent.com/jenkinsci/bom/master/bom-latest/pom.xml';
        const bom = await requestGET({url: bomUrl, reporter});
        const xml = await parseStringPromise(bom);
        return xml.project.dependencyManagement[0].dependencies[0].dependency.map(dep => dep.artifactId);
    } catch(ex) {
        console.warn('Failed to fetch BOM data', ex);
    }
    return [];
};

const fetchSuspendedPlugins = async ({updateData, names, createNode}) => {
    const suspendedPromises = [];
    for (const deprecation of Object.keys(updateData.deprecations)) {
        if (!names.includes(deprecation)) {
            suspendedPromises.push(createNode({
                ...updateData.deprecations[deprecation],
                id: deprecation,
                name: deprecation,
                parent: null,
                children: [],
                internal: {
                    type: 'SuspendedPlugin',
                    contentDigest: crypto.createHash('md5').update(deprecation.trim()).digest('hex')
                }
            }));
        }
    }
    await Promise.all(suspendedPromises);
};

const processCategoryData = async ({createNode, reporter}) => {
    const sectionActivity = reporter.activityTimer('process categories');
    sectionActivity.start();
    for (const category of categoryList) {
        createNode({
            ...category,
            id: category.id.trim(),
            parent: null,
            children: [],
            internal: {
                type: 'JenkinsPluginCategory',
                contentDigest: crypto
                    .createHash('md5')
                    .update(`category${category.name}`)
                    .digest('hex')
            }
        });
    }
    sectionActivity.end();
};

const fetchLabelData = async ({createNode, reporter}) => {
    const sectionActivity = reporter.activityTimer('fetch labels info');
    sectionActivity.start();
    const url = 'https://raw.githubusercontent.com/jenkinsci/jenkins/master/core/src/main/resources/hudson/model/Messages.properties';
    const messages = await requestGET({url, reporter});
    const keyPrefix = 'UpdateCenter.PluginCategory';
    for (const line of messages.split('\n')) {
        const [key, value] = line.split('=', 2).map(s => s.trim());
        const labelId = key.substring(keyPrefix.length + 1);
        if (key.startsWith(keyPrefix)) {
            createNode({
                title: value,
                id: labelId,
                parent: null,
                children: [],
                internal: {
                    type: 'JenkinsPluginLabel',
                    contentDigest: crypto
                        .createHash('md5')
                        .update(`label${labelId}`)
                        .digest('hex')
                }
            });
        }
    }
    sectionActivity.end();
};

const fetchSiteInfo = async ({createNode, reporter}) => {
    const sectionActivity = reporter.activityTimer('fetch plugin api info');
    sectionActivity.start();
    const url = `${API_URL}/info`;
    const info = await requestGET({url, reporter});

    createNode({
        api: {
            ...info
        },
        website: {
            commit: execSync('git rev-parse HEAD').toString().trim(),
            version: require('find-package-json')().next().value.version,
        },
        id: 'pluginSiteInfo',
        parent: null,
        children: [],
        internal: {
            type: 'JenkinsPluginSiteInfo',
            contentDigest: crypto.createHash('md5').update('pluginSiteInfo').digest('hex')
        }
    });
    sectionActivity.end();
};

const fetchStats = async ({reporter, stats}) => {
    const timeSpan = 12;
    const totalUrl = 'https://stats.jenkins.io/jenkins-stats/svg/total-jenkins.csv';
    const totalInstalls = (await requestGET({url: totalUrl, reporter})).trim().split('\n');
    stats.core = {installations: []};
    const timestamps = [];
    for (let monthsAgo = 1; monthsAgo <= timeSpan; monthsAgo++) {
        const [month, coreInstalls] = csvParse(totalInstalls[totalInstalls.length - monthsAgo]);
        const pluginInstallsUrl = `https://stats.jenkins.io/jenkins-stats/svg/${month}-plugins.csv`;
        const timestamp = Date.parse(`${month}`.replace(/(..)$/, '-$1'));
        timestamps.push(timestamp);
        const pluginInstalls = (await requestGET({url: pluginInstallsUrl, reporter})).split('\n');
        stats.core.installations[timeSpan - monthsAgo] = {timestamp: timestamp, total: coreInstalls};
        for (const pluginRow of pluginInstalls) {
            const [pluginName, installs] = csvParse(pluginRow);
            stats[pluginName] = stats[pluginName] || {installations: []};
            stats[pluginName].installations[timeSpan - monthsAgo] = {timestamp: timestamp, total: installs};
        }
    }
    for (const pluginName of Object.keys(stats)) {
        for (let idx = 0; idx < timeSpan; idx++) {
            stats[pluginName].installations[idx] = stats[pluginName].installations[idx]
                || {total: 0, timestamp: timestamps[idx]};
        }
        stats[pluginName].currentInstalls = stats[pluginName].installations[timeSpan - 1].total || 0;
    }
};

const csvParse = (row) => {
    return row.split(',').map(s => s ? JSON.parse(s) : s).map(s => isNaN(parseInt(s)) ? s : parseInt(s));
};

const fetchPluginVersions = async ({createNode, reporter, firstReleases}) => {
    const sectionActivity = reporter.activityTimer('fetch plugin versions');
    sectionActivity.start();
    const url = 'https://updates.jenkins.io/plugin-versions.json';
    const json = await requestGET({url, reporter});
    for (const pluginVersions of Object.values(json.plugins)) {
        for (const data of Object.values(pluginVersions)) {
            /*
            buildDate    "May 13, 2020"
            dependencies    […]
            name    "42crunch-security-audit"
            requiredCore    "2.164.3"
            sha1    "nQl64SC4dvbFE0Kbwkdqe2C0hG8="
            sha256    "rMxlZQqnAofpD1/vNosqcPgghuhXKzRrpLuLHV8XUQ8="
            url    "https://updates.jenkins.io/download/plugins/42crunch-security-audit/2.1/42crunch-security-audit.hpi"
            version 2.1
            */
            const date = dateFNs.parse(data.buildDate, 'MMMM d, yyyy', new Date(0));
            if (!firstReleases[data.name] || firstReleases[data.name].getTime() > date.getTime()) {
                firstReleases[data.name] = date;
            }
            createNode({
                ...data,
                buildDate: date,
                id: `${data.name}_${data.version}`,
                parent: null,
                children: [],
                internal: {
                    type: 'JenkinsPluginVersion',
                    contentDigest: crypto
                        .createHash('md5')
                        .update(`pluginVersion_${data.name}_${data.version}`)
                        .digest('hex')
                }
            });
        }
    }
    sectionActivity.end();
};

module.exports = {
    fetchSiteInfo,
    fetchLabelData,
    processCategoryData,
    fetchPluginData,
    fetchPluginVersions,
    fixGitHubUrl,
    fetchStats,
    getPluginContent,
    requestGET
};
<|endoftext|>


<commit_before><commit_msg>Appeded svg<commit_after>var sizes = {
   height: 900,
   width: 900,
   paddin: 5
};

var diameter = 960,
    format = d3.format(",d"),
    color = d3.scale.category20c();

var bubble = d3.layout.pack()
    .sort(null)
    .size([diameter, diameter])
    .padding(1.5);

var svg = d3.select("#chart").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .attr("class", "bubble");<|endoftext|>


<commit_before>/**
 * A FreeDOM transport provider using WebRTC
 */
var Transport_unprivileged = function(channel) {
  this.nextId = 10;
  this.channel = channel;
  this.rtcConnections = {};
  this.rtcChannels = {};
  this.rtcStates = {};
  handleEvents(this);
};

Transport_unprivileged.prototype.onStateChange = function(id) {
  var sendChannel = this.rtcChannels[id];
  //console.log(id + " send channel's state:"+sendChannel.readyState);
  this.channel.postMessage({
    'action':'event',
    'type': 'onStateChange',
    'value': {'id': id, 'state': sendChannel.readyState}
  });
};

Transport_unprivileged.prototype.onMessage = function(id, evt) {
  var blobber = new Blob([evt.data]);
  console.log("message " + id + " had length " + evt.data.length + " and turned into blob length " + blobber.size);

  this.channel.postMessage({
    'action':'event',
    'type': 'onMessage',
    'value': {'header': id, 'data': blobber}
  });
};

Transport_unprivileged.prototype.onIceCandidate = function(id, evt) {
  //console.log(id + " icecandidate: "+JSON.stringify(evt));
  if (evt && evt.candidate) {
    this.channel.postMessage({
      'action': 'event',
      'type': 'onSignal',
      'value': {'id': id, 'message': JSON.stringify(evt)}
    });
  }
}

Transport_unprivileged.prototype['create'] = function (continuation) {
  var sockId = this.nextId++;
  var servers = null;
  var sendChannel;
  var pc = new webkitRTCPeerConnection(servers,
                {'optional': [{'RtpDataChannels': true}]});
  this.rtcConnections[sockId] = pc;
  try {
    sendChannel = pc['createDataChannel']("sendDataChannel", {'reliable': false});
    this.rtcChannels[sockId] = sendChannel;
    sendChannel.onopen = this.onStateChange.bind(this,sockId);
    sendChannel.onclose = this.onStateChange.bind(this,sockId);
    sendChannel.onmessage = this.onMessage.bind(this,sockId);
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);

    pc['createOffer'](function(desc) {
      pc['setLocalDescription'](desc);
      continuation({'id': sockId, 'offer': JSON.stringify(desc)});
    });
  } catch (e) {
    console.warn(e.message);
    console.warn('Failed to create data channel. You need Chrome M25' +
                  'or later with --enable-data-channels flag');
    delete this.rtcConnections[sockId];
  }
};

Transport_unprivileged.prototype['accept'] = function (id, strdesc, continuation) {
  var desc = JSON.parse(strdesc);
  if (desc.type == 'icecandidate') {
    /**
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: desc.candidate.sdpMLineIndex,
      sdpMid: desc.candidate.sdpMid,
      candidate: desc.candidate.candidate
    });
    **/
    var candidate = new RTCIceCandidate(desc.candidate);
    if (this.rtcConnections[id]) {
      this.rtcConnections[id]['addIceCandidate'](candidate);
      console.log("Successfully accepted ICE candidate");
    }
    continuation();
  //} else if (id == null || !(id in this.rtcConnections)) {
  } else if (desc.type == 'offer') {
    var desc = new RTCSessionDescription(desc);
    var sockId = this.nextId++;
    var servers = null;
    var pc = new webkitRTCPeerConnection(servers,
                  {'optional': [{'RtpDataChannels': true}]});
    this.rtcConnections[sockId] = pc;
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);
    pc['ondatachannel'] = function(evt) {
      var channel = evt['channel'];
      this.rtcChannels[sockId] = channel;
      channel.onopen = this.onStateChange.bind(this,sockId);
      channel.onclose = this.onStateChange.bind(this,sockId);
      channel.onmessage = this.onMessage.bind(this,sockId);
    }.bind(this);
    //desc, successCallback, failureCallback
    pc['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    pc['createAnswer'](function(answer) {
      pc['setLocalDescription'](answer);
      continuation({'id': sockId, 'offer': JSON.stringify(answer)});
    });
  } else if (desc.type == 'answer') {
    var desc = new RTCSessionDescription(desc);
    this.rtcConnections[id]['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    continuation();
  } else if (desc.type == 'bye') {
    this.close(id);
    continuation();
  }
};

Transport_unprivileged.prototype['send'] = function (msg, continuation) {
  var blob = msg.data;
  var reader = new FileReader();
  reader.onload = function(m, e) {
    console.log("Will Send " + msg.data.size + " which converted to " + e.target.result.length);
    this.rtcChannels[m.header].send(e.target.result);
    continuation();
  }.bind(this, msg);
  reader.onerror = function(e) {
    console.log("Binary translation error!");
    continuation();
  }
  reader.readAsBinaryString(blob);
};

Transport_unprivileged.prototype['close'] = function (id, continuation) {
  this.rtcChannels[id].close();
  this.rtcConnections[id].close();
  //delete this.rtcChannels[id];
  //delete this.rtcConnections[id];
  continuation();
};

/**
Transport_unprivileged.prototype.get = function(key, continuation) {
  try {
    var val = localTransport[this.channel.app.id + key];
    continuation(val);
  } catch(e) {
    continuation(null);
  }
};

Transport_unprivileged.prototype.set = function(key, value, continuation) {
  localTransport[this.channel.app.id + key] = value;
  continuation();
};
**/

fdom.apis.register("core.transport", Transport_unprivileged);
<commit_msg>update logs<commit_after>/**
 * A FreeDOM transport provider using WebRTC
 */
var Transport_unprivileged = function(channel) {
  this.nextId = 10;
  this.channel = channel;
  this.rtcConnections = {};
  this.rtcChannels = {};
  this.rtcStates = {};
  handleEvents(this);
};

Transport_unprivileged.prototype.onStateChange = function(id) {
  var sendChannel = this.rtcChannels[id];
  //console.log(id + " send channel's state:"+sendChannel.readyState);
  this.channel.postMessage({
    'action':'event',
    'type': 'onStateChange',
    'value': {'id': id, 'state': sendChannel.readyState}
  });
};

Transport_unprivileged.prototype.onMessage = function(id, evt) {
  var blobber = new Blob([evt.data]);
  console.log("message " + id + " had length " + evt.data.length + " and turned into blob length " + blobber.size);

  this.channel.postMessage({
    'action':'event',
    'type': 'onMessage',
    'value': {'header': id, 'data': blobber}
  });
};

Transport_unprivileged.prototype.onIceCandidate = function(id, evt) {
  //console.log(id + " icecandidate: "+JSON.stringify(evt));
  if (evt && evt.candidate) {
    this.channel.postMessage({
      'action': 'event',
      'type': 'onSignal',
      'value': {'id': id, 'message': JSON.stringify(evt)}
    });
  }
}

Transport_unprivileged.prototype['create'] = function (continuation) {
  var sockId = this.nextId++;
  var servers = null;
  var sendChannel;
  var pc = new webkitRTCPeerConnection(servers,
                {'optional': [{'RtpDataChannels': true}]});
  this.rtcConnections[sockId] = pc;
  try {
    sendChannel = pc['createDataChannel']("sendDataChannel", {'reliable': false});
    this.rtcChannels[sockId] = sendChannel;
    sendChannel.onopen = this.onStateChange.bind(this,sockId);
    sendChannel.onclose = this.onStateChange.bind(this,sockId);
    sendChannel.onmessage = this.onMessage.bind(this,sockId);
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);

    pc['createOffer'](function(desc) {
      pc['setLocalDescription'](desc);
      continuation({'id': sockId, 'offer': JSON.stringify(desc)});
    });
  } catch (e) {
    console.warn(e.message);
    console.warn('Failed to create data channel. You need Chrome M25' +
                  'or later with --enable-data-channels flag');
    delete this.rtcConnections[sockId];
  }
};

Transport_unprivileged.prototype['accept'] = function (id, strdesc, continuation) {
  var desc = JSON.parse(strdesc);
  if (desc.type == 'icecandidate') {
    /**
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: desc.candidate.sdpMLineIndex,
      sdpMid: desc.candidate.sdpMid,
      candidate: desc.candidate.candidate
    });
    **/
    var candidate = new RTCIceCandidate(desc.candidate);
    if (this.rtcConnections[id]) {
      this.rtcConnections[id]['addIceCandidate'](candidate);
      console.log("Successfully accepted ICE candidate");
    }
    continuation();
  //} else if (id == null || !(id in this.rtcConnections)) {
  } else if (desc.type == 'offer') {
    var desc = new RTCSessionDescription(desc);
    var sockId = this.nextId++;
    var servers = null;
    var pc = new webkitRTCPeerConnection(servers,
                  {'optional': [{'RtpDataChannels': true}]});
    this.rtcConnections[sockId] = pc;
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);
    pc['ondatachannel'] = function(evt) {
      var channel = evt['channel'];
      this.rtcChannels[sockId] = channel;
      channel.onopen = this.onStateChange.bind(this,sockId);
      channel.onclose = this.onStateChange.bind(this,sockId);
      channel.onmessage = this.onMessage.bind(this,sockId);
    }.bind(this);
    //desc, successCallback, failureCallback
    pc['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    pc['createAnswer'](function(answer) {
      pc['setLocalDescription'](answer);
      continuation({'id': sockId, 'offer': JSON.stringify(answer)});
    });
  } else if (desc.type == 'answer') {
    var desc = new RTCSessionDescription(desc);
    console.log("connection status is " + this.rtcConnections[id].readyState);
    this.rtcConnections[id]['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(e){console.warn(e);});
    continuation();
  } else if (desc.type == 'bye') {
    this.close(id);
    continuation();
  }
};

Transport_unprivileged.prototype['send'] = function (msg, continuation) {
  var blob = msg.data;
  var reader = new FileReader();
  reader.onload = function(m, e) {
    console.log("Will Send " + msg.data.size + " which converted to " + e.target.result.length);
    this.rtcChannels[m.header].send(e.target.result);
    continuation();
  }.bind(this, msg);
  reader.onerror = function(e) {
    console.log("Binary translation error!");
    continuation();
  }
  reader.readAsBinaryString(blob);
};

Transport_unprivileged.prototype['close'] = function (id, continuation) {
  this.rtcChannels[id].close();
  this.rtcConnections[id].close();
  //delete this.rtcChannels[id];
  //delete this.rtcConnections[id];
  continuation();
};

/**
Transport_unprivileged.prototype.get = function(key, continuation) {
  try {
    var val = localTransport[this.channel.app.id + key];
    continuation(val);
  } catch(e) {
    continuation(null);
  }
};

Transport_unprivileged.prototype.set = function(key, value, continuation) {
  localTransport[this.channel.app.id + key] = value;
  continuation();
};
**/

fdom.apis.register("core.transport", Transport_unprivileged);
<|endoftext|>


<commit_before>var currentDate = new Date();
// Committee list DOM
var $cteList = $('.cte-list');
var $cteListItems = $('.cte-list .tab-pane.active .committee');
var $cteListSearchInput = $('.cte-list-search input');
var $cteListSearchResults = $('.cte-list-search-results');
var $cteListNavItem = $('.cte-list-nav a, .cte-list-nav-mobile option');
// Committee details DOM
var $cteDtlNavItem = $('.cte-dtl-meetings-nav a, .cte-dtl-meetings-nav-mobile option');
var $cteDtlNavMobileSelect = $('.cte-dtl-meetings-nav-mobile select');
var $cteDtlFilterSelect = $('.cte-dtl-meetings-filter select');
var $cteDtlListTables = $('.cte-dtl-meetings-list .table');
var $cteDtlReadMore = $('.read-more a');
var $cteDtlMtngsList = $('.cte-dtl-meetings-list');
var $cteDtlMtngsSearchInput = $('.cte-dtl-meetings-search input');
var $cteDtlMtngsSearchResults = $('.cte-dtl-meetings-search-results');
var showingSearchResult = false;
// General elements
var $cteTabNav = $('.cte-tab-nav');
var $cteTabNavItem = $('.cte-tab-nav a');
var $cteSelectTabNav = $('.cte-select-tab-nav');
// Lunr elements
var $lunrDict = null;

// Set up lunr index for filtering
var index = null;

// Committee list page methods and handlers
var clearSearchResult = function($list,$res,params) {
  var $resultsList = $res.find('.results-list');

  $res.find('.no-results')
    .hide();

  if(!!params && params.twoCol) {
    $resultsList.find('.left, .right')
    .empty();
  } else {
    $resultsList.empty();
  }

  $resultsList.hide();
  $list.show();
}

// Underscore debounce
var debounce = function(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		};
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
};

var renderSearchResults = function(results,$list,$res,params) {
  var resultsCount = results.length;
  var $noResults = $res.find('.no-results');
  var $resultsList = $res.find('.results-list');

  //clearSearchResult($list,$res,params);

  showingSearchResult = true;

  $list.hide();
  $noResults.hide();

  if(!!resultsCount) {
    $resultsList.show();

    if(!!params && params.sort) {
      results.sort(function(a,b) {
        return new Date($(b).find('.date').text()) - new Date($(a).find('.date').text());
      });
    }

    if(!!params && params.twoCol) {
      var $resultsListLeft = $resultsList.find('.left');
      var $resultsListRight = $resultsList.find('.right');

      if(resultsCount > 20) {
        if(resultsCount < 40) {
          $resultsListLeft.append(results.slice(0,19));
          $resultsListRight.append(results.slice(20));
        } else {
          $resultsListLeft.append(results.slice(0,resultsCount / 2 - 1));
          $resultsListRight.append(results.slice(resultsCount / 2));
        }
      } else {
        $resultsListLeft.append(results);
      }
    } else {
      $resultsList.append(results);
    }
  } else {
    $resultsList.hide();
    $noResults.show();
  }
};

var filterMeetings = function($target) {
  var filter = $target.attr('data-filter');
  var $table = $('#m-' + filter);

  $cteDtlListTables.hide();
  $table.fadeIn({ duration: 250, easing: 'linear' });

  return filter;
}

var searchIndex = debounce(function(query,$list,$res,params) {
  var results = null;

  if(!query.length)  {
    clearSearchResult($list,$res,params);
    return;
  } else {
    results = index.search(query).map(function(result) {
      return $lunrDict.filter(function(i,item) {
        return $(item).attr('data-id') == result.ref;
      })[0];
    });

    renderSearchResults(results,$list,$res,params);
  }
}, 200);

var indexItems = function() {
  $lunrDict = $('.lunr-dict .active .item:not(.exclude)').clone();

  index = lunr(function() {
    var self = this;

    self.ref('id');

    Array.prototype.forEach.call($($lunrDict[0])[0].children, function(child) {
      var $child = $(child);

      if(!$child.hasClass('exclude')) self.field($child.attr('class').split(' ')[0]);
    });
  });

  $lunrDict.each(function(i,item) {
    var $item = $(item);
    var indexItem = {
      id: $item.attr('data-id')
    };

    index._fields.forEach(function(field) {
      var name = field.name;

      indexItem[name] = $item.find('.' + name).text();
    });

    index.add(indexItem);
  });
}

/**
 * Event handlers
 */

 // For pairing tab navs with selects
 $cteTabNavItem.on('click', function(e) {
   e.preventDefault();
   $(this).tab('show');

   $cteSelectTabNav.val($(e.target).attr('href'));
 });

 $cteSelectTabNav.on('change', function(e) {
   e.preventDefault();

   var $selected = $('option:selected',this);

   $selected.tab('show');
   $cteTabNav.find('li')
     .removeClass('active')
     .has('[href="' + $selected.attr('data-target') + '"]')
     .closest('li')
     .addClass('active');
 });

 // Committee list page
$cteListNavItem.on('click', function(e) {
  e.preventDefault();
  $cteListItems = $('.cte-list .tab-pane.active .committee');

  indexItems();
  $cteListSearchInput.val('');

  if(showingSearchResult) clearSearchResult($cteList,$cteListSearchResults,{ twoCol: true });
});

$cteListSearchInput.on('keyup', function(e) {
  searchIndex($(e.target).val(), $cteList, $cteListSearchResults, { twoCol: true });
});

// Committee detail page handlers
$cteDtlNavItem.on('click', indexItems);

$cteDtlFilterSelect.on('change', function(e) {
  filterMeetings($('option:selected',this));
  $cteDtlMtngsSearchInput.val('');

  clearSearchResult($cteDtlMtngsList,$cteDtlMtngsSearchResults);
  $cteDtlMtngsList.show();
});

$cteDtlFilterSelect.on('focus', function() {
  this.selectedIndex = -1;
});

$cteDtlFilterSelect.on('blur', function() {
  if(this.selectedIndex === -1) {
    this.selectedIndex = 0;
  }
});

// Committee detail meeting list filter
$cteDtlMtngsSearchInput.on('keyup', function(e) {
  searchIndex($(e.target).val(),$cteDtlMtngsList,$cteDtlMtngsSearchResults, { sort: true });
});

// Generate initial index
indexItems();
<commit_msg>Lunr dict<commit_after>var currentDate = new Date();
// Committee list DOM
var $cteList = $('.cte-list');
var $cteListItems = $('.cte-list .tab-pane.active .committee');
var $cteListSearchInput = $('.cte-list-search input');
var $cteListSearchResults = $('.cte-list-search-results');
var $cteListNavItem = $('.cte-list-nav a, .cte-list-nav-mobile option');
// Committee details DOM
var $cteDtlNavItem = $('.cte-dtl-meetings-nav a, .cte-dtl-meetings-nav-mobile option');
var $cteDtlNavMobileSelect = $('.cte-dtl-meetings-nav-mobile select');
var $cteDtlFilterSelect = $('.cte-dtl-meetings-filter select');
var $cteDtlListTables = $('.cte-dtl-meetings-list .table');
var $cteDtlReadMore = $('.read-more a');
var $cteDtlMtngsList = $('.cte-dtl-meetings-list');
var $cteDtlMtngsSearchInput = $('.cte-dtl-meetings-search input');
var $cteDtlMtngsSearchResults = $('.cte-dtl-meetings-search-results');
var showingSearchResult = false;
// General elements
var $cteTabNav = $('.cte-tab-nav');
var $cteTabNavItem = $('.cte-tab-nav a');
var $cteSelectTabNav = $('.cte-select-tab-nav');
// Lunr elements
var $lunrDict = $('.lunr-dict');
var $lunrDictItems = null;

// Set up lunr index for filtering
var index = null;

// Committee list page methods and handlers
var clearSearchResult = function($list,$res,params) {
  var $resultsList = $res.find('.results-list');

  $res.find('.no-results')
    .hide();

  if(!!params && params.twoCol) {
    $resultsList.find('.left, .right')
    .empty();
  } else {
    $resultsList.empty();
  }

  $resultsList.hide();
  $list.show();
}

// Underscore debounce
var debounce = function(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		};
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
};

var renderSearchResults = function(results,$list,$res,params) {
  var resultsCount = results.length;
  var $noResults = $res.find('.no-results');
  var $resultsList = $res.find('.results-list');

  //clearSearchResult($list,$res,params);

  showingSearchResult = true;

  $list.hide();
  $noResults.hide();

  if(!!resultsCount) {
    $resultsList.show();

    if(!!params && params.sort) {
      results.sort(function(a,b) {
        return new Date($(b).find('.date').text()) - new Date($(a).find('.date').text());
      });
    }

    if(!!params && params.twoCol) {
      var $resultsListLeft = $resultsList.find('.left');
      var $resultsListRight = $resultsList.find('.right');

      if(resultsCount > 20) {
        if(resultsCount < 40) {
          $resultsListLeft.append(results.slice(0,19));
          $resultsListRight.append(results.slice(20));
        } else {
          $resultsListLeft.append(results.slice(0,resultsCount / 2 - 1));
          $resultsListRight.append(results.slice(resultsCount / 2));
        }
      } else {
        $resultsListLeft.append(results);
      }
    } else {
      $resultsList.append(results);
    }
  } else {
    $resultsList.hide();
    $noResults.show();
  }
};

var filterMeetings = function($target) {
  var filter = $target.attr('data-filter');
  var $table = $('#m-' + filter);

  $cteDtlListTables.hide();
  $table.fadeIn({ duration: 250, easing: 'linear' });

  return filter;
}

var searchIndex = debounce(function(query,$list,$res,params) {
  var results = null;

  if(!query.length)  {
    clearSearchResult($list,$res,params);
    return;
  } else {
    results = index.search(query).map(function(result) {
      return $lunrDictItems.filter(function(i,item) {
        return $(item).attr('data-id') == result.ref;
      })[0];
    });

    renderSearchResults(results,$list,$res,params);
  }
}, 200);

var indexItems = function() {
  var isCteDtlMeetings = $lunrDict.hasClass('cte-dtl-meetings-list');

  $lunrDictItems = $('.lunr-dict .item:not(.exclude), .lunr-dict .active .item:not(.exclude)').clone();

  index = lunr(function() {
    var self = this;

    self.field('name');

    // On the committee detail page, we also have the option to search meetings
    // by date
    if(isCteDtlMeetings) {
      self.field('date');
    }

    self.ref('id');
  });

  $lunrDictItems.each(function(i,item) {
    var $item = $(item);
    var indexItem = {
      id: $item.attr('data-id')
    };

    // Here we take lunr's index fields and add them as properties to each item.
    // We expect an item to have child elements that have class names that
    // match these fields.
    index._fields.forEach(function(field) {
      var name = field.name;

      indexItem[name] = $item.find('.' + name).text();
    });

    index.add(indexItem);
  });
}

/**
 * Event handlers
 */

 // For pairing tab navs with selects
 $cteTabNavItem.on('click', function(e) {
   e.preventDefault();
   $(this).tab('show');

   $cteSelectTabNav.val($(e.target).attr('href'));
 });

 $cteSelectTabNav.on('change', function(e) {
   e.preventDefault();

   var $selected = $('option:selected',this);

   $selected.tab('show');
   $cteTabNav.find('li')
     .removeClass('active')
     .has('[href="' + $selected.attr('data-target') + '"]')
     .closest('li')
     .addClass('active');
 });

 // Committee list page
$cteListNavItem.on('click', function(e) {
  e.preventDefault();
  $cteListItems = $('.cte-list .tab-pane.active .committee');

  indexItems();
  $cteListSearchInput.val('');

  if(showingSearchResult) clearSearchResult($cteList,$cteListSearchResults,{ twoCol: true });
});

$cteListSearchInput.on('keyup', function(e) {
  searchIndex($(e.target).val(), $cteList, $cteListSearchResults, { twoCol: true });
});

// Committee detail page handlers
$cteDtlNavItem.on('click', indexItems);

$cteDtlFilterSelect.on('change', function(e) {
  filterMeetings($('option:selected',this));
  $cteDtlMtngsSearchInput.val('');

  clearSearchResult($cteDtlMtngsList,$cteDtlMtngsSearchResults);
  $cteDtlMtngsList.show();
});

$cteDtlFilterSelect.on('focus', function() {
  this.selectedIndex = -1;
});

$cteDtlFilterSelect.on('blur', function() {
  if(this.selectedIndex === -1) {
    this.selectedIndex = 0;
  }
});

// Committee detail meeting list filter
$cteDtlMtngsSearchInput.on('keyup', function(e) {
  searchIndex($(e.target).val(),$cteDtlMtngsList,$cteDtlMtngsSearchResults, { sort: true });
});

// Generate initial index
indexItems();
<|endoftext|>


<commit_before>module("WebGL Rendering", {
    setup : function() {
        stop();
        var that = this;
        this.cb = function(e) {
            ok(true, "Scene loaded");
            that.doc = document.getElementById("xml3dframe").contentDocument;
            start();
        };
        loadDocument("scenes/webgl-rendering01.xhtml"+window.location.search, this.cb);
    },
    teardown : function() {
        var v = document.getElementById("xml3dframe");
        v.removeEventListener("load", this.cb, true);
    }
});

function getContextForXml3DElement(x) {
    return x._configured.adapters.XML3DRenderAdapterFactory.handler.gl;
};

test("Background and invisible mesh", 4, function() {
    var x = this.doc.getElementById("xml3DElem"), actual, win = this.doc.defaultView;
    var gl = getContextForXml3DElement(x);

    actual = win.getPixelValue(gl, 40, 40);
    deepEqual(actual, [0,0,0,0], "Transparent at 40,40");
    actual = win.getPixelValue(gl, 0, 0);
    deepEqual(actual, [0,0,0,0], "Transparent at 0,0");
});

test("Change visibility via script", 6, function() {
    var x = this.doc.getElementById("xml3DElem"), actual, win = this.doc.defaultView;
    var gl = getContextForXml3DElement(x);

    this.doc.getElementById("myGroup").visible = true;

    actual = win.getPixelValue(gl, 40, 40);
    deepEqual(actual, [0,0,255,255], "Blue at 40,40");
    actual = win.getPixelValue(gl, 0, 0);
    deepEqual(actual, [0,0,0,0], "Transparent at 0,0");

    this.doc.getElementById("myGroup").setAttribute("visible", "false");
    actual = win.getPixelValue(gl, 40, 40);
    deepEqual(actual, [0,0,0,0], "Transparent at 40,40");
    actual = win.getPixelValue(gl, 0, 0);
    deepEqual(actual, [0,0,0,0], "Transparent at 0,0");
});<commit_msg>Made test wait for rendering finished.<commit_after>module("WebGL Rendering", {
    setup : function() {
        stop();
        var that = this;
        this.cb = function(e) {
            ok(true, "Scene loaded");
            that.doc = document.getElementById("xml3dframe").contentDocument;
            start();
        };
        loadDocument("scenes/webgl-rendering01.xhtml"+window.location.search, this.cb);
    },
    teardown : function() {
        var v = document.getElementById("xml3dframe");
        v.removeEventListener("load", this.cb, true);
    }
});

function getContextForXml3DElement(x) {
    return x._configured.adapters.XML3DRenderAdapterFactory.handler.gl;
};

test("Background and invisible mesh", 4, function() {
    var x = this.doc.getElementById("xml3DElem"), actual, win = this.doc.defaultView;
    var gl = getContextForXml3DElement(x);

    actual = win.getPixelValue(gl, 40, 40);
    deepEqual(actual, [0,0,0,0], "Transparent at 40,40");
    actual = win.getPixelValue(gl, 0, 0);
    deepEqual(actual, [0,0,0,0], "Transparent at 0,0");
});

test("Change visibility via script", 7, function() {
    var x = this.doc.getElementById("xml3DElem"),
        actual,
        win = this.doc.defaultView,
        gl = getContextForXml3DElement(x),
        testFunc = null;

    // Should be
    /*x.addEventListener("framedrawn", function() {
    console.log("Frame drawn");
    });*/

    x._configured.adapters.XML3DRenderAdapterFactory.handler.events.framedrawn.push({
        listener : function(n) {
            ok("Redraw was triggered");
            if(testFunc)
                testFunc(n);
            start();
        }
    });

    testFunc = function(n) {
        equal(n.numberOfObjectsDrawn, 1, "1 Object drawn");
        equal(n.numberOfTrianglesDrawn, 2, "2 Triangles drawn");
        actual = win.getPixelValue(gl, 40, 40);
        deepEqual(actual, [0,0,255,255], "Blue at 40,40");
        actual = win.getPixelValue(gl, 0, 0);
        deepEqual(actual, [0,0,0,0], "Transparent at 0,0");
    };
    stop();
    this.doc.getElementById("myGroup").visible = true;
});<|endoftext|>


<commit_before>/* global expect */

describe("jB.extend", function () {

    describe("Deep mode not enabled", function () {
        it("should be able to extend 2 (or more) Objects", function () {
            var Obj1 = {
                a1: 1,
                b1: 2
            };

            var Obj2 = {
                a2: 1,
                b2: 2
            };
            // base test, check if everything is ok
            var extObj = jB.extend({}, Obj1, Obj2);

            for (var index in Obj1) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj1[index]);
            }
            for (var index in Obj2) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj2[index]);
            }

            expect(jB.count(extObj)).toEqual(jB.count(Obj1) + jB.count(Obj2));

            var Obj3 = {
                a3: 1,
                b3: 2,
                c3: 4 //break redundancy!!
            };

            var Obj4 = {
                d6: [1, 2, 3, 4]
            };
            // test with more object and different attributes
            extObj = jB.extend({}, Obj1, Obj2, Obj3, Obj4);

            for (var index in Obj1) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj1[index]);
            }
            for (var index in Obj2) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj2[index]);
            }
            for (var index in Obj3) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj3[index]);
            }
            // ensure the deep mode has not been used
            expect(extObj.d6).toBeDefined();
            expect(extObj.d6).toEqual(Obj4.d6);
            expect(Object.is(extObj.d6, Obj4.d6)).toBe(true);

            expect(jB.count(extObj)).toEqual(jB.count(Obj1) + jB.count(Obj2) + jB.count(Obj3) + jB.count(Obj4));
        });

    });
});<commit_msg>Add tests for extend() using functions and objects with different structure, tests cover also deep mode<commit_after>/* global expect */

describe("jB.extend", function () {

    describe("Deep mode not enabled", function () {
        it("should be able to extend 2 (or more) Objects", function () {
            var Obj1 = {
                a1: 1,
                b1: 2
            };

            var Obj2 = {
                a2: 1,
                b2: 2
            };
            // base test, check if everything is ok
            var extObj = jB.extend({}, Obj1, Obj2);

            for (var index in Obj1) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj1[index]);
            }
            for (var index in Obj2) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj2[index]);
            }

            expect(jB.count(extObj)).toEqual(jB.count(Obj1) + jB.count(Obj2));

            var Obj3 = {
                a3: 1,
                b3: 2,
                c3: 4 //break redundancy!!
            };

            var Obj4 = {
                d6: [1, 2, 3, 4]
            };
            // test with more object and different attributes
            extObj = jB.extend({}, Obj1, Obj2, Obj3, Obj4);

            for (var index in Obj1) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj1[index]);
            }
            for (var index in Obj2) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj2[index]);
            }
            for (var index in Obj3) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj3[index]);
            }
            // ensure the deep mode has not been used
            expect(extObj.d6).toBeDefined();
            expect(extObj.d6).toEqual(Obj4.d6);
            expect(Object.is(extObj.d6, Obj4.d6)).toBe(true);

            expect(jB.count(extObj)).toEqual(jB.count(Obj1) + jB.count(Obj2) + jB.count(Obj3) + jB.count(Obj4));
        });

        it("should be able to manage function", function () {
            var Obj1 = {
                a2: function () {
                    return;
                }
            };
            // base test, check if everything is ok
            var extObj = jB.extend( {a1: 1}, Obj1);
            expect(extObj.a2).toBeDefined();
            expect(typeof extObj.a2).toEqual('function');

        });

        it("should overwrite attribute in the right order", function () {
            var Obj1 = {
                a1: function () {
                    return;
                }
            };
            var Obj2 = {
                a2: true
            };
            // base test, check if everything is ok
            var extObj = jB.extend({a1: 1, a2 : 0}, Obj1, Obj2);
            expect(extObj.a1).toBeDefined();
            expect(typeof extObj.a1).toEqual('function');
            expect(extObj.a2).toBeDefined();
            expect(extObj.a2).toBe(true);

        });
    });

    describe("Deep mode enabled", function () {
        it("should be able to extend 2 (or more) Objects", function () {
            var Obj1 = {
                a1: 1,
                b1: 2
            };

            var Obj2 = {
                a2: 1,
                b2: 2
            };
            // base test, check if everything is ok
            var extObj = jB.extend(true, {}, Obj1, Obj2);

            for (var index in Obj1) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj1[index]);
            }
            for (var index in Obj2) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj2[index]);
            }

            expect(jB.count(extObj)).toEqual(jB.count(Obj1) + jB.count(Obj2));

            var Obj3 = {
                a3: 1,
                b3: 2,
                c3: 4 //break redundancy!!

            };

            var Obj4 = {
                d6: [1, 2, 3, 4]
            };
            // test with more object and different attributes
            extObj = jB.extend(true, {}, Obj1, Obj2, Obj3, Obj4);

            for (var index in Obj1) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj1[index]);
            }
            for (var index in Obj2) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj2[index]);
            }
            for (var index in Obj3) {
                expect(extObj[index]).toBeDefined();
                expect(extObj[index]).toEqual(Obj3[index]);
            }

            // ensure the deep mode has not been used
            expect(extObj.d6).toBeDefined();
            expect(extObj.d6).toEqual(Obj4.d6);
            expect(Object.is(extObj.d6, Obj4.d6)).toBe(false);

            expect(jB.count(extObj)).toEqual(jB.count(Obj1) + jB.count(Obj2) + jB.count(Obj3) + jB.count(Obj4));
        });

        it("should be able to manage function", function () {
            var Obj1 = {
                a2: function () {
                    return;
                }
            };
            var extObj = jB.extend(true, {a1: 1}, Obj1);
            expect(extObj.a2).toBeDefined();
            expect(typeof extObj.a2).toEqual('function');

        });

        it("should overwrite attribute in the right order", function () {
            var Obj1 = {
                a1: function () {
                    return;
                }
            };
            var Obj2 = {
                a2: true
            };
            var extObj = jB.extend(true, {a1: 1, a2 : 0}, Obj1, Obj2);
            expect(extObj.a1).toBeDefined();
            expect(typeof extObj.a1).toEqual('function');
            expect(extObj.a2).toBeDefined();
            expect(extObj.a2).toBe(true);

        });

    });
});<|endoftext|>


<commit_before>module('lively.ide.DirectoryWatcher').requires('lively.Network').toRun(function() {

// depends on the DirectoryWatcherServer

Object.extend(lively.ide.DirectoryWatcher, {
    watchServerURL: new URL(Config.nodeJSURL+'/DirectoryWatchServer/'),

    dirs: {},

    reset: function() {
        // lively.ide.DirectoryWatcher.reset()
        this.dirs = {};
        this.watchServerURL.withFilename('reset').asWebResource().post();
    },

    request: function(url, thenDo) {
        return url.asWebResource().beAsync().withJSONWhenDone(function(json, status) {
            thenDo(!json || json.error, json); }).get();
    },

    getFiles: function(dir, thenDo) {
        this.request(this.watchServerURL.withFilename('files').withQuery({dir: dir}), thenDo);
    },

    getChanges: function(dir, since, startWatchTime, thenDo) {
        this.request(this.watchServerURL.withFilename('changes').withQuery({
            startWatchTime: startWatchTime, since: since, dir: dir}), thenDo);
    },

    withFilesOfDir: function(dir, doFunc) {
        // Retrieves efficiently the files of dir. Uses a server side watcher that
        // sends infos about file changes, deletions, creations.
        // This methods synchs those with the cached state held in this object
        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        // dir = lively.shell.exec('pwd', {sync:true}).resultString()
        // lively.ide.DirectoryWatcher.dirs[dir]
        // lively.ide.DirectoryWatcher.withFilesOfDir(dir, function(files) { show(Object.keys(files).length); })
        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        var watchState = this.dirs[dir] || (this.dirs[dir] = {updateInProgress: false, callbacks: []});
        doFunc && watchState.callbacks.push(doFunc);
        if (watchState.updateInProgress) { return; }
        watchState.updateInProgress = true;
        function whenDone() {
            watchState.updateInProgress = false;
            var cb;
            while ((cb = watchState.callbacks.shift())) cb(watchState.files);
        }
        function extend(statObj) { // convert date string into a date object
            if (!statObj) statObj = {};
            statObj.isDirectory = !!(statObj.mode & 0x4000);
            ['atime', 'mtime', 'ctime'].forEach(function(field) {
                if (statObj[field]) statObj[field] = new Date(statObj[field]); });
            return statObj;
        }
        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        if (!watchState.files) { // first time called
            this.getFiles(dir, function(err, result) {
                result.files && Properties.forEachOwn(result.files, function(path, stat) { extend(stat); })
                Object.extend(watchState, {
                    files: result.files,
                    lastUpdated: result.startTime,
                    startTime: result.startTime
                });
                whenDone();
            });
            return;
        }
        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        var timeSinceLastUpdate = Date.now() - (watchState.lastUpdated || 0);
        if (timeSinceLastUpdate < 10 * 1000) { whenDone(); } // recently updated
        // get updates
        this.getChanges(dir, watchState.lastUpdated, watchState.startTime, function(err, result) {
            if (!result.changes || result.changes.length === 0) { whenDone(); return; }
            watchState.lastUpdated = result.changes[0].time;
            console.log('%s files changed in %s: %s', result.changes.length, dir, result.changes.pluck('path').join('\n'));
            result.changes.forEach(function(change) {
                switch (change.type) {
                    case 'removal': delete watchState.files[change.path]; break;
                    case 'creation': case 'change': watchState.files[change.path] = extend(change.stat); break;
                }
            });
            whenDone();
        });
    }

});

}) // end of module
<commit_msg>dirwatcher: catch error<commit_after>module('lively.ide.DirectoryWatcher').requires('lively.Network').toRun(function() {

// depends on the DirectoryWatcherServer

Object.extend(lively.ide.DirectoryWatcher, {
    watchServerURL: new URL(Config.nodeJSURL+'/DirectoryWatchServer/'),

    dirs: {},

    reset: function() {
        // lively.ide.DirectoryWatcher.reset()
        this.dirs = {};
        this.watchServerURL.withFilename('reset').asWebResource().post();
    },

    request: function(url, thenDo) {
        return url.asWebResource().beAsync().withJSONWhenDone(function(json, status) {
            thenDo(!json || json.error, json); }).get();
    },

    getFiles: function(dir, thenDo) {
        this.request(this.watchServerURL.withFilename('files').withQuery({dir: dir}), thenDo);
    },

    getChanges: function(dir, since, startWatchTime, thenDo) {
        this.request(this.watchServerURL.withFilename('changes').withQuery({
            startWatchTime: startWatchTime, since: since, dir: dir}), thenDo);
    },

    withFilesOfDir: function(dir, doFunc) {
        // Retrieves efficiently the files of dir. Uses a server side watcher that
        // sends infos about file changes, deletions, creations.
        // This methods synchs those with the cached state held in this object
        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        // dir = lively.shell.exec('pwd', {sync:true}).resultString()
        // lively.ide.DirectoryWatcher.dirs
        // lively.ide.DirectoryWatcher.withFilesOfDir(dir, function(files) { show(Object.keys(files).length); })
        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        var watchState = this.dirs[dir] || (this.dirs[dir] = {updateInProgress: false, callbacks: []});
        doFunc && watchState.callbacks.push(doFunc);
        if (watchState.updateInProgress) { return; }
        watchState.updateInProgress = true;

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        if (!watchState.files) { // first time called
            this.getFiles(dir, function(err, result) {
                if (err) show("dir watch error: %s", err);
                result.files && Properties.forEachOwn(result.files, function(path, stat) { extend(stat); })
                Object.extend(watchState, {
                    files: result.files,
                    lastUpdated: result.startTime,
                    startTime: result.startTime
                });
                whenDone();
            });
            return;
        }

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        var timeSinceLastUpdate = Date.now() - (watchState.lastUpdated || 0);
        if (timeSinceLastUpdate < 10 * 1000) { whenDone(); } // recently updated
        // get updates
        this.getChanges(dir, watchState.lastUpdated, watchState.startTime, function(err, result) {
            if (!result.changes || result.changes.length === 0) { whenDone(); return; }
            watchState.lastUpdated = result.changes[0].time;
            console.log('%s files changed in %s: %s', result.changes.length, dir, result.changes.pluck('path').join('\n'));
            result.changes.forEach(function(change) {
                switch (change.type) {
                    case 'removal': delete watchState.files[change.path]; break;
                    case 'creation': case 'change': watchState.files[change.path] = extend(change.stat); break;
                }
            });
            whenDone();
        });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        function whenDone() {
            watchState.updateInProgress = false;
            var cb;
            while ((cb = watchState.callbacks.shift())) cb(watchState.files);
        }

        function extend(statObj) { // convert date string into a date object
            if (!statObj) statObj = {};
            statObj.isDirectory = !!(statObj.mode & 0x4000);
            ['atime', 'mtime', 'ctime'].forEach(function(field) {
                if (statObj[field]) statObj[field] = new Date(statObj[field]); });
            return statObj;
        }

    }

});

}) // end of module
<|endoftext|>