<commit_before>"use strict";

class gsuiLFO extends HTMLElement {
	constructor() {
		const children = GSUI.getTemplate( "gsui-lfo" ),
			elWave = children[ 1 ].firstChild,
			sliders = Object.freeze( {
				delay: [ children[ 2 ].lastChild.firstChild, children[ 2 ].firstChild.lastChild ],
				attack: [ children[ 3 ].lastChild.firstChild, children[ 3 ].firstChild.lastChild ],
				speed: [ children[ 4 ].lastChild.firstChild, children[ 4 ].firstChild.lastChild ],
				amp: [ children[ 5 ].lastChild.firstChild, children[ 5 ].firstChild.lastChild ],
			} );

		super();
		this._children = children;
		this._beatlines = elWave.firstChild;
		this._wave = elWave.lastChild;
		this._sliders = sliders;
		this._dur = 4;
		this._waveWidth = 300;
		this._dispatch = GSUI.dispatchEvent.bind( null, this, "gsuiLFO" );
		this._onresize = this._onresize.bind( this );
		Object.seal( this );

		this.onchange = this._onchangeForm.bind( this );
		this._initSlider( "delay" );
		this._initSlider( "attack" );
		this._initSlider( "speed" );
		this._initSlider( "amp" );
	}

	// .........................................................................
	connectedCallback() {
		if ( this._children ) {
			this.classList.add( "gsuiLFO" );
			this.append( ...this._children );
			this._children = null;
			GSUI.recallAttributes( this, {
				toggle: false,
				timesignature: "4,4",
				type: "sine",
				delay: 0,
				attack: 1,
				speed: 1,
				amp: 1,
			} );
			this.updateWave();
		}
		GSUI.observeSizeOf( this, this._onresize );
	}
	disconnectedCallback() {
		GSUI.unobserveSizeOf( this, this._onresize );
	}
	static get observedAttributes() {
		return [ "toggle", "timesignature", "type", "delay", "attack", "speed", "amp" ];
	}
	attributeChangedCallback( prop, prev, val ) {
		if ( this.firstChild && prev !== val ) {
			const num = +val;

			switch ( prop ) {
				case "timesignature": this._beatlines.setAttribute( "timesignature", val ); break;
				case "toggle": this._changeToggle( val !== null ); break;
				case "type": this._changeType( val ); break;
				case "delay":
				case "attack":
				case "speed":
					this._changeProp( prop, num );
					break;
				case "amp":
					if ( num > 0 !== prev > 0 ) {
						this._changeAmpSign( num );
					}
					this._changeProp( "amp", Math.abs( num ) );
					break;
			}
		}
	}

	// .........................................................................
	updateWave( prop, val ) {
		const w = this._wave,
			bPM = this.getAttribute( "timesignature" ).split( "," )[ 0 ];

		w.type = this.getAttribute( "type" );
		w.delay = prop === "delay" ? val : +this.getAttribute( "delay" );
		w.attack = prop === "attack" ? val : +this.getAttribute( "attack" );
		w.frequency = prop === "speed" ? val : +this.getAttribute( "speed" );
		w.amplitude = prop === "amp" ? val : +this.getAttribute( "amp" );
		w.duration =
		this._dur = Math.max( w.delay + w.attack + 2, bPM );
		w.draw();
		w.style.opacity = Math.min( 6 / w.frequency, 1 );
		this._updatePxPerBeat();
	}

	// .........................................................................
	_changeToggle( b ) {
		this.classList.toggle( "gsuiLFO-enable", b );
		this.querySelectorAll( ".gsuiLFO-typeRadio" )
			.forEach( b
				? el => el.removeAttribute( "disabled" )
				: el => el.setAttribute( "disabled", "" ) );
		this._sliders.delay[ 0 ].enable( b );
		this._sliders.attack[ 0 ].enable( b );
		this._sliders.speed[ 0 ].enable( b );
		this._sliders.amp[ 0 ].enable( b );
	}
	_changeType( type ) {
		this._wave.type = type;
		this.querySelector( `.gsuiLFO-typeRadio[value="${ type }"]` ).checked = true;
	}
	_changeAmpSign( amp ) {
		this.querySelector( `.gsuiLFO-ampSignRadio[value="${ Math.sign( amp ) || 1 }"]` ).checked = true;
	}
	_changeProp( prop, val ) {
		const [ sli, span ] = this._sliders[ prop ];

		sli.setValue( val );
		span.textContent = val.toFixed( 2 );
	}

	// .........................................................................
	_updatePxPerBeat() {
		this._beatlines.setAttribute( "pxPerBeat", this._waveWidth / this._dur );
	}
	_initSlider( prop ) {
		const slider = this._sliders[ prop ][ 0 ];

		slider.enable( false );
		slider.oninput = this._oninputSlider.bind( this, prop );
		slider.onchange = this._onchangeSlider.bind( this, prop );
	}

	// events:
	// .........................................................................
	_onresize() {
		this._waveWidth = this._beatlines.getBoundingClientRect().width;
		this._updatePxPerBeat();
		this._wave.resized();
	}
	_onchangeForm( e ) {
		switch ( e.target.name ) {
			case "gsuiLFO-toggle":
				GSUI.setAttribute( this, "toggle", !this.classList.contains( "gsuiLFO-enable" ) );
				this._dispatch( "toggle" );
				break;
			case "gsuiLFO-type":
				GSUI.setAttribute( this, "type", e.target.value );
				this.updateWave();
				this._dispatch( "change", "type", e.target.value );
				break;
			case "gsuiLFO-ampSign":
				GSUI.setAttribute( this, "amp", -this.getAttribute( "amp" ) );
				this.updateWave();
				this._dispatch( "change", "amp", +this.getAttribute( "amp" ) );
				break;
		}
	}
	_oninputSlider( prop, val ) {
		const realval = prop !== "amp"
				? val
				: val * Math.sign( this.getAttribute( "amp" ) );

		this._sliders[ prop ][ 1 ].textContent = val.toFixed( 2 );
		this.updateWave( prop, realval );
		this._dispatch( "liveChange", prop, realval );
	}
	_onchangeSlider( prop, val ) {
		const nval = prop === "amp"
				? val * Math.sign( this.getAttribute( "amp" ) )
				: val;

		GSUI.setAttribute( this, prop, nval );
		this._dispatch( "change", prop, nval );
	}
}

customElements.define( "gsui-lfo", gsuiLFO );

Object.freeze( gsuiLFO );
<commit_msg>gsuiLFO: attributeChangedCallback, typo<commit_after>"use strict";

class gsuiLFO extends HTMLElement {
	constructor() {
		const children = GSUI.getTemplate( "gsui-lfo" ),
			elWave = children[ 1 ].firstChild,
			sliders = Object.freeze( {
				delay: [ children[ 2 ].lastChild.firstChild, children[ 2 ].firstChild.lastChild ],
				attack: [ children[ 3 ].lastChild.firstChild, children[ 3 ].firstChild.lastChild ],
				speed: [ children[ 4 ].lastChild.firstChild, children[ 4 ].firstChild.lastChild ],
				amp: [ children[ 5 ].lastChild.firstChild, children[ 5 ].firstChild.lastChild ],
			} );

		super();
		this._children = children;
		this._beatlines = elWave.firstChild;
		this._wave = elWave.lastChild;
		this._sliders = sliders;
		this._dur = 4;
		this._waveWidth = 300;
		this._dispatch = GSUI.dispatchEvent.bind( null, this, "gsuiLFO" );
		this._onresize = this._onresize.bind( this );
		Object.seal( this );

		this.onchange = this._onchangeForm.bind( this );
		this._initSlider( "delay" );
		this._initSlider( "attack" );
		this._initSlider( "speed" );
		this._initSlider( "amp" );
	}

	// .........................................................................
	connectedCallback() {
		if ( this._children ) {
			this.classList.add( "gsuiLFO" );
			this.append( ...this._children );
			this._children = null;
			GSUI.recallAttributes( this, {
				toggle: false,
				timesignature: "4,4",
				type: "sine",
				delay: 0,
				attack: 1,
				speed: 1,
				amp: 1,
			} );
			this.updateWave();
		}
		GSUI.observeSizeOf( this, this._onresize );
	}
	disconnectedCallback() {
		GSUI.unobserveSizeOf( this, this._onresize );
	}
	static get observedAttributes() {
		return [ "toggle", "timesignature", "type", "delay", "attack", "speed", "amp" ];
	}
	attributeChangedCallback( prop, prev, val ) {
		if ( !this._children && prev !== val ) {
			const num = +val;

			switch ( prop ) {
				case "timesignature": this._beatlines.setAttribute( "timesignature", val ); break;
				case "toggle": this._changeToggle( val !== null ); break;
				case "type": this._changeType( val ); break;
				case "delay":
				case "attack":
				case "speed":
					this._changeProp( prop, num );
					break;
				case "amp":
					if ( num > 0 !== prev > 0 ) {
						this._changeAmpSign( num );
					}
					this._changeProp( "amp", Math.abs( num ) );
					break;
			}
		}
	}

	// .........................................................................
	updateWave( prop, val ) {
		const w = this._wave,
			bPM = this.getAttribute( "timesignature" ).split( "," )[ 0 ];

		w.type = this.getAttribute( "type" );
		w.delay = prop === "delay" ? val : +this.getAttribute( "delay" );
		w.attack = prop === "attack" ? val : +this.getAttribute( "attack" );
		w.frequency = prop === "speed" ? val : +this.getAttribute( "speed" );
		w.amplitude = prop === "amp" ? val : +this.getAttribute( "amp" );
		w.duration =
		this._dur = Math.max( w.delay + w.attack + 2, bPM );
		w.draw();
		w.style.opacity = Math.min( 6 / w.frequency, 1 );
		this._updatePxPerBeat();
	}

	// .........................................................................
	_changeToggle( b ) {
		this.classList.toggle( "gsuiLFO-enable", b );
		this.querySelectorAll( ".gsuiLFO-typeRadio" )
			.forEach( b
				? el => el.removeAttribute( "disabled" )
				: el => el.setAttribute( "disabled", "" ) );
		this._sliders.delay[ 0 ].enable( b );
		this._sliders.attack[ 0 ].enable( b );
		this._sliders.speed[ 0 ].enable( b );
		this._sliders.amp[ 0 ].enable( b );
	}
	_changeType( type ) {
		this._wave.type = type;
		this.querySelector( `.gsuiLFO-typeRadio[value="${ type }"]` ).checked = true;
	}
	_changeAmpSign( amp ) {
		this.querySelector( `.gsuiLFO-ampSignRadio[value="${ Math.sign( amp ) || 1 }"]` ).checked = true;
	}
	_changeProp( prop, val ) {
		const [ sli, span ] = this._sliders[ prop ];

		sli.setValue( val );
		span.textContent = val.toFixed( 2 );
	}

	// .........................................................................
	_updatePxPerBeat() {
		this._beatlines.setAttribute( "pxPerBeat", this._waveWidth / this._dur );
	}
	_initSlider( prop ) {
		const slider = this._sliders[ prop ][ 0 ];

		slider.enable( false );
		slider.oninput = this._oninputSlider.bind( this, prop );
		slider.onchange = this._onchangeSlider.bind( this, prop );
	}

	// events:
	// .........................................................................
	_onresize() {
		this._waveWidth = this._beatlines.getBoundingClientRect().width;
		this._updatePxPerBeat();
		this._wave.resized();
	}
	_onchangeForm( e ) {
		switch ( e.target.name ) {
			case "gsuiLFO-toggle":
				GSUI.setAttribute( this, "toggle", !this.classList.contains( "gsuiLFO-enable" ) );
				this._dispatch( "toggle" );
				break;
			case "gsuiLFO-type":
				GSUI.setAttribute( this, "type", e.target.value );
				this.updateWave();
				this._dispatch( "change", "type", e.target.value );
				break;
			case "gsuiLFO-ampSign":
				GSUI.setAttribute( this, "amp", -this.getAttribute( "amp" ) );
				this.updateWave();
				this._dispatch( "change", "amp", +this.getAttribute( "amp" ) );
				break;
		}
	}
	_oninputSlider( prop, val ) {
		const realval = prop !== "amp"
				? val
				: val * Math.sign( this.getAttribute( "amp" ) );

		this._sliders[ prop ][ 1 ].textContent = val.toFixed( 2 );
		this.updateWave( prop, realval );
		this._dispatch( "liveChange", prop, realval );
	}
	_onchangeSlider( prop, val ) {
		const nval = prop === "amp"
				? val * Math.sign( this.getAttribute( "amp" ) )
				: val;

		GSUI.setAttribute( this, prop, nval );
		this._dispatch( "change", prop, nval );
	}
}

customElements.define( "gsui-lfo", gsuiLFO );

Object.freeze( gsuiLFO );
<|endoftext|>


<commit_before>const chai = require('chai');
const expect = chai.expect;
const getMainActivityPatch = require('../src/android/getPrefix');
const newPrefix = 'patches/0.18';
const oldPrefix = 'patches/0.17';

describe('getMainActivityPatch', () => {
  it('require a specific patch for react-native < 0.18', () => {
    expect(getMainActivityPatch('0.17.0-rc')).to.equals(oldPrefix);
    expect(getMainActivityPatch('0.17.2')).to.equals(oldPrefix);
  });

  it('require a specific patch for react-native > 0.18', () => {
    expect(getMainActivityPatch('0.19.0')).to.equals(newPrefix);
    expect(getMainActivityPatch('0.19.0-rc')).to.equals(newPrefix);
    expect(getMainActivityPatch('0.18.0-rc')).to.equals(newPrefix);
    expect(getMainActivityPatch('0.18.2')).to.equals(newPrefix);
  });
});
<commit_msg>Rename test case<commit_after>const chai = require('chai');
const expect = chai.expect;
const getMainActivityPatch = require('../src/android/getPrefix');
const newPrefix = 'patches/0.18';
const oldPrefix = 'patches/0.17';

describe('getPrefix', () => {
  it('require a specific patch for react-native < 0.18', () => {
    expect(getMainActivityPatch('0.17.0-rc')).to.equals(oldPrefix);
    expect(getMainActivityPatch('0.17.2')).to.equals(oldPrefix);
  });

  it('require a specific patch for react-native > 0.18', () => {
    expect(getMainActivityPatch('0.19.0')).to.equals(newPrefix);
    expect(getMainActivityPatch('0.19.0-rc')).to.equals(newPrefix);
    expect(getMainActivityPatch('0.18.0-rc')).to.equals(newPrefix);
    expect(getMainActivityPatch('0.18.2')).to.equals(newPrefix);
  });
});
<|endoftext|>


<commit_before>function wait(con, fun, max) {
	var waiter = setInterval(function() {
		if(con()) {
			fun();
			clearInterval(waiter);
		}
	}, 100);
	setTimeout(function() {
		clearInterval(waiter);
	}, max || 5000);
};

var page = new WebPage(), options = phantom.args[0] || "",
    path = "/tests/run_tests.html?auto=on&log_to_json=1&"+options,
    lastFramework;

page.onConsoleMessage = function (msg) {
	var d = JSON.parse(msg);
	if (!d.failed) return;
	if (d.framework!=lastFramework) console.log(d.framework+":");
	console.log("\033[31mo    " + d.name + "\033[0m");
	lastFramework = d.framework;
};
page.open(require('fs').workingDirectory+path, function(status){
    if (status !== "success") {
        console.log("Unable to access network");
        phantom.exit();
    } else {
        wait(function(){
            return page.evaluate(function(){
                return window.Eve_results !== undefined;
            });
        }, function(){
            var r = page.evaluate(function(){
                return window.Eve_results;
            });
			console.log(
				( (r.failures>0) ? "Failed" : "Passed") + ": "+(r.passes+"/"+r.total)
			);
            phantom.exit(r.failures);
        });
    }
});
<commit_msg>Added a timeout to Phantom script so Travis doesn't get confused after a timeout on a broken build following a fixed one.<commit_after>function wait(con, fun, max) {
	max = max || 5000;
	var waiter = setInterval(function() {
		if(con()) {
			fun();
			clearInterval(waiter);
		}
	}, 100);
	setTimeout(function() {
		console.log("Failed: Stopped waiting for tests to complete after "+max+"ms.");
		phantom.exit(1);
		clearInterval(waiter);
	}, max);
};

var page = new WebPage(), options = phantom.args[0] || "",
    path = "/tests/run_tests.html?auto=on&log_to_json=1&"+options,
    lastFramework;

page.onConsoleMessage = function (msg) {
	var d = JSON.parse(msg);
	if (!d.failed) return;
	if (d.framework!=lastFramework) console.log(d.framework+":");
	console.log("\033[31mo    " + d.name + "\033[0m");
	lastFramework = d.framework;
};
page.open(require('fs').workingDirectory+path, function(status){
    if (status !== "success") {
        console.log("Unable to access network");
        phantom.exit();
    } else {
        wait(function(){
            return page.evaluate(function(){
                return window.Eve_results !== undefined;
            });
        }, function(){
            var r = page.evaluate(function(){
                return window.Eve_results;
            });
			console.log(
				( (r.failures>0) ? "Failed" : "Passed") + ": "+(r.passes+"/"+r.total)
			);
            phantom.exit(r.failures);
        });
    }
});
<|endoftext|>


<commit_before>const { BrowserPool, PlaywrightPlugin } = require('browser-pool');
const { chromium } = require('playwright-core');
const debug = require('debug')('express-dom');
const clone = require('clone');
const { randomUUID } = require('node:crypto');

const Phase = require('./phase');
const plugins = require('./plugins');
const routers = require('./routers');
const RequestTracker = require('./request-tracker');
const tracker = require('./client-tracker');
const asyncEmitter = require('./async-emitter');
const { ManualRequest, ManualResponse } = require('./manual');

module.exports = class Handler {
	static pageMax = 20;
	static pageUse = 200;
	static browser = 'chrome';
	static debug = process.env.PWDEBUG == 1;

	static header = 'Sec-Purpose';

	static #pool;
	static plugins = plugins;
	static routers = routers;
	static defaults = {
		cookies: new Set(),
		log: process.env.NODE_ENV != "production" ? "info" : "error",
		timeout: process.env.PWDEBUG == 1 ? 0 : 10000,
		scale: 1
	};
	static offline = {
		header: 'prepare',
		policies: {
			default: "'none'"
		},
		enabled: false,
		track: false,
		styles: [],
		scripts: [],
		plugins: new Set([
			'console',
			'hidden',
			'cookies',
			'html'
		])
	};
	static online = {
		header: 'prefetch; prerender',
		policies: {
			default: "'none'",
			script: "'self' 'unsafe-inline'",
			connect: "'self'"
		},
		enabled: !process.env.DEVELOP,
		track: true,
		styles: [],
		scripts: [],
		plugins: new Set([
			'console',
			'hidden',
			'cookies',
			'redirect',
			'referrer',
			'html'
		])
	};
	static visible = {
		policies: {}
	};

	#router;

	constructor(conf = {}) {
		this.plugins = clone(Handler.plugins);
		for (const phase of ['offline', 'online', 'visible']) {
			this[phase] = Object.assign(
				clone(Handler.defaults),
				clone(Handler[phase]),
				conf[phase]
			);
		}
		if (typeof conf == "function") {
			conf(this);
		}
		this.chain = (...args) => this.middleware(...args);
	}

	#init() {
		if (!Handler.#pool) Handler.#pool = new BrowserPool({
			browserPlugins: [new PlaywrightPlugin(chromium, {
				maxOpenPagesPerBrowser: Handler.pageMax,
				retireBrowserAfterPageCount: Handler.pageUse,
				useIncognitoPages: true, // each page can have its cookies
				launchOptions: {
					channel: Handler.browser,
					devtools: Handler.debug,
					timeout: Handler.timeout / 2
				}
			})],
		});
	}

	route(fn) {
		this.#router = fn;
		return this.chain;
	}

	static async destroy() {
		await Handler.#pool.destroy();
		Handler.#pool = null;
	}

	async middleware(req, res, next) {
		this.#init();
		if (typeof req == "string") {
			debug("Called in manual mode");
			req = new ManualRequest(req);
			res = req.res = new ManualResponse();
		}
		try {
			await this.runMiddleware(req, res, next);
			if (res instanceof ManualResponse) {
				return res;
			}
		} catch (err) {
			if (next) next(err);
			else throw err;
		}
	}

	async runMiddleware(req, res, next) {
		const phase = new Phase(this, req);

		if (this.#router && phase.settings) {
			await this.#router(phase, req, res);
		}

		res.set(phase.headers());
		res.vary(Handler.header);

		if (phase.settings) {
			if (Handler.debug) phase.settings.timeout = 0;
			await this.runMethod(phase, req, res);
		} else {
			next();
		}
	}

	async runMethod(phase, req, res) {
		const { location, settings } = phase;
		const page = await Handler.#pool.newPage({
			pageOptions: {
				ignoreHTTPSErrors: true,
				deviceScaleFactor: settings.scale ?? 1
			}
		});
		if (Array.isArray(settings.plugins)) {
			settings.plugins = new Set(settings.plugins);
		}

		const {
			scripts, styles, timeout, referer, track, plugins
		} = settings;

		page.location = location;

		for (const plugin of plugins) {
			const fn = this.plugins[plugin];
			if (!fn) {
				throw new Error(`plugin not found: ${plugin}`);
			}
			await fn(page, settings, req, res);
		}
		const url = page.location.toString();

		page.on('crash', err => console.error(err));
		page.on('pageerror', err => console.error(err));

		page.once('response', response => {
			const code = response.status();
			if (code != 200 && res.statusCode == 200) {
				res.status(code);
			}
		});

		await page.route(url, route => {
			const headers = {
				...route.request().headers(),
				[Handler.header]: settings.header
			};
			route.continue({ headers });
		}, { times: 1 });

		const reqTrack = new RequestTracker(page);

		const id = 'signal_' + randomUUID();
		await initScripts(page, [
			track ? [tracker, { id }] : null,
			styles.length > 0 ? [initStyles, styles.join('\n')] : null
		].concat(scripts));

		try {
			await page.goto(url, {
				waitUntil: 'domcontentloaded',
				timeout, referer
			});
			debug("page loaded");

			const event = await Promise.race([
				Promise.all([
					track ? page.evaluate(id => window[id], id) : null,
					reqTrack
				]).then(() => 'idle'),
				new Promise(resolve => {
					if (timeout) setTimeout(() => resolve('timeout'), timeout);
				}),
				new Promise(resolve => {
					page.on('close', () => resolve('close'));
				})
			]);
			if (event == "timeout") {
				debug("page timeout", url);
				throw new Error('Page timeout');
			} else if (event == "close") {
				debug("page closed", url);
			} else if (event == "idle") {
				debug("page idle", url);
				await asyncEmitter(page, 'idle');
			} else {
				debug("page stale", url);
				throw new Error('Page stale');
			}
		} finally {
			if (!page.isClosed()) await page.close();
		}
	}
};

function initScripts(page, list) {
	return Promise.all(list.map(args => {
		if (!args) return;
		if (typeof args == "function") args = [args];
		return page.addInitScript(...args);
	}));
}

function initStyles(css) {
	const sheet = new CSSStyleSheet();
	document.adoptedStyleSheets.push(sheet);
	return sheet.replace(css);
}
<commit_msg>Clone phase settings, policies, respect enabled<commit_after>const { BrowserPool, PlaywrightPlugin } = require('browser-pool');
const { chromium } = require('playwright-core');
const debug = require('debug')('express-dom');
const clone = require('clone');
const { randomUUID } = require('node:crypto');

const Phase = require('./phase');
const plugins = require('./plugins');
const routers = require('./routers');
const RequestTracker = require('./request-tracker');
const tracker = require('./client-tracker');
const asyncEmitter = require('./async-emitter');
const { ManualRequest, ManualResponse } = require('./manual');

module.exports = class Handler {
	static pageMax = 20;
	static pageUse = 200;
	static browser = 'chrome';
	static debug = process.env.PWDEBUG == 1;

	static header = 'Sec-Purpose';

	static #pool;
	static plugins = plugins;
	static routers = routers;
	static defaults = {
		cookies: new Set(),
		log: process.env.NODE_ENV != "production" ? "info" : "error",
		timeout: process.env.PWDEBUG == 1 ? 0 : 10000,
		scale: 1
	};
	static offline = {
		header: 'prepare',
		policies: {
			default: "'none'"
		},
		enabled: false,
		track: false,
		styles: [],
		scripts: [],
		plugins: new Set([
			'console',
			'hidden',
			'cookies',
			'html'
		])
	};
	static online = {
		header: 'prefetch; prerender',
		policies: {
			default: "'none'",
			script: "'self' 'unsafe-inline'",
			connect: "'self'"
		},
		enabled: !process.env.DEVELOP,
		track: true,
		styles: [],
		scripts: [],
		plugins: new Set([
			'console',
			'hidden',
			'cookies',
			'redirect',
			'referrer',
			'html'
		])
	};
	static visible = {
		policies: {}
	};

	#router;

	constructor(conf = {}) {
		this.plugins = clone(Handler.plugins);
		for (const phase of ['offline', 'online', 'visible']) {
			this[phase] = Object.assign(
				clone(Handler.defaults),
				clone(Handler[phase]),
				conf[phase]
			);
		}
		if (typeof conf == "function") {
			conf(this);
		}
		this.chain = (...args) => this.middleware(...args);
	}

	#init() {
		if (!Handler.#pool) Handler.#pool = new BrowserPool({
			browserPlugins: [new PlaywrightPlugin(chromium, {
				maxOpenPagesPerBrowser: Handler.pageMax,
				retireBrowserAfterPageCount: Handler.pageUse,
				useIncognitoPages: true, // each page can have its cookies
				launchOptions: {
					channel: Handler.browser,
					devtools: Handler.debug,
					timeout: Handler.timeout / 2
				}
			})],
		});
	}

	route(fn) {
		this.#router = fn;
		return this.chain;
	}

	static async destroy() {
		await Handler.#pool.destroy();
		Handler.#pool = null;
	}

	async middleware(req, res, next) {
		this.#init();
		if (typeof req == "string") {
			debug("Called in manual mode");
			req = new ManualRequest(req);
			res = req.res = new ManualResponse();
		}
		try {
			await this.runMiddleware(req, res, next);
			if (res instanceof ManualResponse) {
				return res;
			}
		} catch (err) {
			if (next) next(err);
			else throw err;
		}
	}

	async runMiddleware(req, res, next) {
		const phase = new Phase(this, req);

		phase.settings = clone(phase.settings);
		phase.policies = clone(phase.policies);

		if (this.#router && phase.settings) {
			await this.#router(phase, req, res);
		}

		res.set(phase.headers());
		res.vary(Handler.header);

		if (phase.settings?.enabled) {
			if (Handler.debug) phase.settings.timeout = 0;
			await this.runMethod(phase, req, res);
		} else {
			next();
		}
	}

	async runMethod(phase, req, res) {
		const { location, settings } = phase;
		const page = await Handler.#pool.newPage({
			pageOptions: {
				ignoreHTTPSErrors: true,
				deviceScaleFactor: settings.scale ?? 1
			}
		});
		if (Array.isArray(settings.plugins)) {
			settings.plugins = new Set(settings.plugins);
		}

		const {
			scripts, styles, timeout, referer, track, plugins
		} = settings;

		page.location = location;

		for (const plugin of plugins) {
			const fn = this.plugins[plugin];
			if (!fn) {
				throw new Error(`plugin not found: ${plugin}`);
			}
			await fn(page, settings, req, res);
		}
		const url = page.location.toString();

		page.on('crash', err => console.error(err));
		page.on('pageerror', err => console.error(err));

		page.once('response', response => {
			const code = response.status();
			if (code != 200 && res.statusCode == 200) {
				res.status(code);
			}
		});

		await page.route(url, route => {
			const headers = {
				...route.request().headers(),
				[Handler.header]: settings.header
			};
			route.continue({ headers });
		}, { times: 1 });

		const reqTrack = new RequestTracker(page);

		const id = 'signal_' + randomUUID();
		await initScripts(page, [
			track ? [tracker, { id }] : null,
			styles.length > 0 ? [initStyles, styles.join('\n')] : null
		].concat(scripts));

		try {
			await page.goto(url, {
				waitUntil: 'domcontentloaded',
				timeout, referer
			});
			debug("page loaded");

			const event = await Promise.race([
				Promise.all([
					track ? page.evaluate(id => window[id], id) : null,
					reqTrack
				]).then(() => 'idle'),
				new Promise(resolve => {
					if (timeout) setTimeout(() => resolve('timeout'), timeout);
				}),
				new Promise(resolve => {
					page.on('close', () => resolve('close'));
				})
			]);
			if (event == "timeout") {
				debug("page timeout", url);
				throw new Error('Page timeout');
			} else if (event == "close") {
				debug("page closed", url);
			} else if (event == "idle") {
				debug("page idle", url);
				await asyncEmitter(page, 'idle');
			} else {
				debug("page stale", url);
				throw new Error('Page stale');
			}
		} finally {
			if (!page.isClosed()) await page.close();
		}
	}
};

function initScripts(page, list) {
	return Promise.all(list.map(args => {
		if (!args) return;
		if (typeof args == "function") args = [args];
		return page.addInitScript(...args);
	}));
}

function initStyles(css) {
	const sheet = new CSSStyleSheet();
	document.adoptedStyleSheets.push(sheet);
	return sheet.replace(css);
}
<|endoftext|>


<commit_before>/**
 * @overview Base type for visible elements in the visualization.
 */
/* jshint
 funcscope: true,
 newcap: true,
 nonew: true,
 shadow: false,
 unused: true,

 maxlen: 90,
 maxstatements: 200
 */
'use strict';

var jQuerySvgElement = require('./NetSimUtils').jQuerySvgElement;
var tweens = require('./tweens');

/**
 * A VizElement is an object that  has a representation in the network
 * visualization.  Its role is to maintain that visual representation.
 * A VizElement has helpers for positioning, scaling and tweening.
 * Every VizElement has a root element which is a <g> tag, an SVG "group"
 * that contains the other components that will actually draw.
 *
 * @constructor
 */
var NetSimVizElement = module.exports =  function () {
  /**
   * @type {number}
   */
  this.posX = 0;

  /**
   * @type {number}
   */
  this.posY = 0;

  /**
   * @type {number}
   */
  this.scale = 1;

  /**
   * @type {boolean}
   */
  this.isForeground = false;

  /**
   * Root SVG <g> (group) element for this object.
   * @type {jQuery}
   * @private
   */
  this.rootGroup_ = jQuerySvgElement('g');

  /**
   * Set of tweens we should currently be running on this node.
   * Processed by tick()
   * @type {Array.<exports.TweenValueTo>}
   * @private
   */
  this.tweens_ = [];

  /**
   * @type {boolean}
   * @private
   */
  this.isDead_ = false;
};

/**
 * @returns {jQuery} wrapper around root <g> element
 */
NetSimVizElement.prototype.getRoot = function () {
  return this.rootGroup_;
};

/**
 * Begins the process of destroying this VizElement.  Once started, this
 * process cannot be stopped.
 * This method can be overridden to trigger an "on-death" animation.
 */
NetSimVizElement.prototype.kill = function () {
  this.isDead_ = true;
};

/**
 * @returns {boolean} whether this entity is done with its death animation
 *          and is ready to be cleaned up by the visualization manager.
 *          The default implementation here returns TRUE as soon as kill()
 *          is called and all animations are completed.
 */
NetSimVizElement.prototype.isDead = function () {
  return this.isDead_ && this.tweens_.length === 0;
};

/**
 * @returns {boolean} whether this entity is playing its final animation
 *          and will be ready to be cleaned up by the visualization manager
 *          soon.
 */
NetSimVizElement.prototype.isDying = function () {
  return this.isDead_ && this.tweens_.length > 0;
};

/**
 * Update all of the tweens currently running on this VizElement (which will
 * probably modify its properties) and then remove any tweens that are completed
 * from the list.
 */
NetSimVizElement.prototype.tick = function () {
};

/**
 * Update the root group's properties to reflect our current position
 * and scale.
 * @param {RunLoop.Clock} [clock] - sometimes omitted during setup
 */
NetSimVizElement.prototype.render = function (clock) {
  if (clock) {
    // Update tweens in the render loop so they are very smooth
    this.tweens_.forEach(function (tween) {
      tween.tick(clock);
    });
    this.tweens_ = this.tweens_.filter(function (tween) {
      return !tween.isFinished;
    });
  }

  // TODO (bbuchanan): Use a dirty flag to only update the DOM when it's
  //                   out of date.
  var transform = 'translate(' + this.posX + ' ' + this.posY + ')' +
      ' scale(' + this.scale + ')';
  this.rootGroup_.attr('transform', transform);
};

/**
 * @param {boolean} isForeground
 */
NetSimVizElement.prototype.onDepthChange = function (isForeground) {
  this.isForeground = isForeground;
};

/**
 * Throw away all existing tweens on this object.
 */
NetSimVizElement.prototype.stopAllAnimation = function () {
  this.tweens_.length = 0;
};

/**
 * Stops any existing motion animation and begins an animated motion to the
 * given coordinates.  Note: This animates the VizElement's root group.
 * @param {number} newX given in SVG points
 * @param {number} newY given in SVG points
 * @param {number} [duration=600] in milliseconds
 * @param {TweenFunction} [tweenFunction=linear]
 */
NetSimVizElement.prototype.tweenToPosition = function (newX, newY, duration,
    tweenFunction) {
  // Don't accept new animation commands if we've already been killed
  if (this.isDying() || this.isDead()) {
    return;
  }

  // Remove any existing tweens controlling posX or posY
  this.removeAllTweensOnProperties(['posX', 'posY']);

  // Add two new tweens, one for each axis
  if (duration > 0) {
    this.tweens_.push(new tweens.TweenValueTo(this, 'posX', newX, duration,
        tweenFunction));
    this.tweens_.push(new tweens.TweenValueTo(this, 'posY', newY, duration,
        tweenFunction));
  } else {
    this.posX = newX;
    this.posY = newY;
  }

};

/**
 * Alias for calling tweenToPosition with a zero duration
 * @param {number} newX given in SVG points
 * @param {number} newY given in SVG points
 */
NetSimVizElement.prototype.snapToPosition = function (newX, newY) {
  this.tweenToPosition(newX, newY, 0);
};

/**
 * Stops any existing animation of the entity's scale and begins an animated
 * change to the given target scale value.  Note: this animates the VizElement's
 * root group.
 * @param {number} newScale where 1.0 is 100% (unscaled)
 * @param {number} [duration=600] in milliseconds
 * @param {TweenFunction} [tweenFunction=linear]
 */
NetSimVizElement.prototype.tweenToScale = function (newScale, duration,
    tweenFunction) {
  // Remove existing scale tweens
  this.removeAllTweensOnProperty('scale');

  // On nonzero duration, add tween to target scale.  Otherwise just set it.
  if (duration > 0) {
    this.tweens_.push(new tweens.TweenValueTo(this, 'scale', newScale, duration,
        tweenFunction));
  } else {
    this.scale = newScale;
  }
};

NetSimVizElement.prototype.doAfterDelay = function (delay, callback) {
  if (delay > 0) {
    this.tweens_.push(new tweens.DoAfterDelay(this, delay, callback));
  } else {
    callback();
  }
};

/**
 * Remove (stop) all active tweens that control the given property on this
 * visualization entity.
 * @param {string} propertyName
 */
NetSimVizElement.prototype.removeAllTweensOnProperty = function (propertyName) {
  this.removeAllTweensOnProperties([propertyName]);
};

/**
 * Remove (stop) all active tweens that control any of the given properties
 * on this visualization entity.
 * @param {string[]} propertyNames
 */
NetSimVizElement.prototype.removeAllTweensOnProperties = function (propertyNames) {
  this.tweens_ = this.tweens_.filter(function (tween) {
    var targetsThisEntity = tween.target === this;
    var isRemovableProperty = propertyNames.some(function (name) {
      return tween.propertyName === name;
    });

    // Invert for filter() because we want to keep everything BUT the matched
    // properties
    return !(targetsThisEntity && isRemovableProperty);
  }, this);
};

/**
 * Alias for calling tweenToScale with a zero duration.
 * @param {number} newScale where 1.0 is 100% (unscaled)
 */
NetSimVizElement.prototype.snapToScale = function (newScale) {
  this.tweenToScale(newScale, 0);
};
<commit_msg>completing TODO; only update the DOM when it's out of date<commit_after>/**
 * @overview Base type for visible elements in the visualization.
 */
/* jshint
 funcscope: true,
 newcap: true,
 nonew: true,
 shadow: false,
 unused: true,

 maxlen: 90,
 maxstatements: 200
 */
'use strict';

var jQuerySvgElement = require('./NetSimUtils').jQuerySvgElement;
var tweens = require('./tweens');

/**
 * A VizElement is an object that  has a representation in the network
 * visualization.  Its role is to maintain that visual representation.
 * A VizElement has helpers for positioning, scaling and tweening.
 * Every VizElement has a root element which is a <g> tag, an SVG "group"
 * that contains the other components that will actually draw.
 *
 * @constructor
 */
var NetSimVizElement = module.exports =  function () {
  /**
   * @type {number}
   */
  this.posX = 0;

  /**
   * @type {number}
   */
  this.posY = 0;

  /**
   * @type {number}
   */
  this.scale = 1;

  /**
   * @type {boolean}
   */
  this.isForeground = false;

  /**
   * Root SVG <g> (group) element for this object.
   * @type {jQuery}
   * @private
   */
  this.rootGroup_ = jQuerySvgElement('g');

  /**
   * Set of tweens we should currently be running on this node.
   * Processed by tick()
   * @type {Array.<exports.TweenValueTo>}
   * @private
   */
  this.tweens_ = [];

  /**
   * @type {boolean}
   * @private
   */
  this.isDead_ = false;
};

/**
 * @returns {jQuery} wrapper around root <g> element
 */
NetSimVizElement.prototype.getRoot = function () {
  return this.rootGroup_;
};

/**
 * Begins the process of destroying this VizElement.  Once started, this
 * process cannot be stopped.
 * This method can be overridden to trigger an "on-death" animation.
 */
NetSimVizElement.prototype.kill = function () {
  this.isDead_ = true;
};

/**
 * @returns {boolean} whether this entity is done with its death animation
 *          and is ready to be cleaned up by the visualization manager.
 *          The default implementation here returns TRUE as soon as kill()
 *          is called and all animations are completed.
 */
NetSimVizElement.prototype.isDead = function () {
  return this.isDead_ && this.tweens_.length === 0;
};

/**
 * @returns {boolean} whether this entity is playing its final animation
 *          and will be ready to be cleaned up by the visualization manager
 *          soon.
 */
NetSimVizElement.prototype.isDying = function () {
  return this.isDead_ && this.tweens_.length > 0;
};

/**
 * Update all of the tweens currently running on this VizElement (which will
 * probably modify its properties) and then remove any tweens that are completed
 * from the list.
 */
NetSimVizElement.prototype.tick = function () {
};

/**
 * Update the root group's properties to reflect our current position
 * and scale.
 * @param {RunLoop.Clock} [clock] - sometimes omitted during setup
 */
NetSimVizElement.prototype.render = function (clock) {
  if (!clock) {
    return;
  }

  // cache initial settings here; we check them later to see if anything
  // has actually changed
  var posX = this.posX;
  var posY = this.posY;
  var scale = this.scale;

  // Update tweens in the render loop so they are very smooth
  this.tweens_.forEach(function (tween) {
    tween.tick(clock);
  });
  this.tweens_ = this.tweens_.filter(function (tween) {
    return !tween.isFinished;
  });

  // If nothing has changed, don't bother to update transform
  if (posX !== this.posX ||
      posY !== this.posY ||
      scale !== this.scale) {
    var transform = 'translate(' + this.posX + ' ' + this.posY + ')' +
        ' scale(' + this.scale + ')';
    this.rootGroup_.attr('transform', transform);
  }
};

/**
 * @param {boolean} isForeground
 */
NetSimVizElement.prototype.onDepthChange = function (isForeground) {
  this.isForeground = isForeground;
};

/**
 * Throw away all existing tweens on this object.
 */
NetSimVizElement.prototype.stopAllAnimation = function () {
  this.tweens_.length = 0;
};

/**
 * Stops any existing motion animation and begins an animated motion to the
 * given coordinates.  Note: This animates the VizElement's root group.
 * @param {number} newX given in SVG points
 * @param {number} newY given in SVG points
 * @param {number} [duration=600] in milliseconds
 * @param {TweenFunction} [tweenFunction=linear]
 */
NetSimVizElement.prototype.tweenToPosition = function (newX, newY, duration,
    tweenFunction) {
  // Don't accept new animation commands if we've already been killed
  if (this.isDying() || this.isDead()) {
    return;
  }

  // Remove any existing tweens controlling posX or posY
  this.removeAllTweensOnProperties(['posX', 'posY']);

  // Add two new tweens, one for each axis
  if (duration > 0) {
    this.tweens_.push(new tweens.TweenValueTo(this, 'posX', newX, duration,
        tweenFunction));
    this.tweens_.push(new tweens.TweenValueTo(this, 'posY', newY, duration,
        tweenFunction));
  } else {
    this.posX = newX;
    this.posY = newY;
  }

};

/**
 * Alias for calling tweenToPosition with a zero duration
 * @param {number} newX given in SVG points
 * @param {number} newY given in SVG points
 */
NetSimVizElement.prototype.snapToPosition = function (newX, newY) {
  this.tweenToPosition(newX, newY, 0);
};

/**
 * Stops any existing animation of the entity's scale and begins an animated
 * change to the given target scale value.  Note: this animates the VizElement's
 * root group.
 * @param {number} newScale where 1.0 is 100% (unscaled)
 * @param {number} [duration=600] in milliseconds
 * @param {TweenFunction} [tweenFunction=linear]
 */
NetSimVizElement.prototype.tweenToScale = function (newScale, duration,
    tweenFunction) {
  // Remove existing scale tweens
  this.removeAllTweensOnProperty('scale');

  // On nonzero duration, add tween to target scale.  Otherwise just set it.
  if (duration > 0) {
    this.tweens_.push(new tweens.TweenValueTo(this, 'scale', newScale, duration,
        tweenFunction));
  } else {
    this.scale = newScale;
  }
};

NetSimVizElement.prototype.doAfterDelay = function (delay, callback) {
  if (delay > 0) {
    this.tweens_.push(new tweens.DoAfterDelay(this, delay, callback));
  } else {
    callback();
  }
};

/**
 * Remove (stop) all active tweens that control the given property on this
 * visualization entity.
 * @param {string} propertyName
 */
NetSimVizElement.prototype.removeAllTweensOnProperty = function (propertyName) {
  this.removeAllTweensOnProperties([propertyName]);
};

/**
 * Remove (stop) all active tweens that control any of the given properties
 * on this visualization entity.
 * @param {string[]} propertyNames
 */
NetSimVizElement.prototype.removeAllTweensOnProperties = function (propertyNames) {
  this.tweens_ = this.tweens_.filter(function (tween) {
    var targetsThisEntity = tween.target === this;
    var isRemovableProperty = propertyNames.some(function (name) {
      return tween.propertyName === name;
    });

    // Invert for filter() because we want to keep everything BUT the matched
    // properties
    return !(targetsThisEntity && isRemovableProperty);
  }, this);
};

/**
 * Alias for calling tweenToScale with a zero duration.
 * @param {number} newScale where 1.0 is 100% (unscaled)
 */
NetSimVizElement.prototype.snapToScale = function (newScale) {
  this.tweenToScale(newScale, 0);
};
<|endoftext|>


<commit_before>(function(global) {
	"use strict";

	/***************************************************************************
	 *	swapper.js:
	 *	A JS class to help you toggle the visibility of parts of your web project.
	 *
	 *	By Dani Gámez Franco, http://gmzcodes.com
	 *	Licensed under MIT.
	 *
	 *	Version: 2.0.0
	 *	Last Update: 2014-09-13
	 *
	 *	NOTE about classes:
	 *	The classNames attribute can be changed to use custom class names, but
	 *  it will be changed in the prototype, so all instances of Swapper will be
	 *  affected.
	 *
	 **************************************************************************/

	// CONSTRUCTOR & (PUBLIC) VARIABLES ////////////////////////////////////////

	var Swapper = function(IDs, currentIndex, defaultIndex, options) {

		/* OPTIONS FORMAT:

		options = {

			anchorMode: boolean,

			classNames: {
				page: className (String),
				pageButton: className (String),
				currentPage: className (String),
				currentButton: className (String),
				loadingPage: className (String),
				deadPage: className (String)
			},

			buttonsIDs: DOM id (String), "autoMODE", or Array (of Strings),

			callbacks: {
				DOM id (String): callback (function),
				...
			},

			callbacksEnabled: boolean,

			masterProperty: String of a CSS property

		}

		*/

		// Initialize options:
		options = options || {};

		// VARIABLES:
		this.pagesIDs = [];
		this.pagesDOM = [];

		this.buttonsIDs = [];
		this.buttonsDOM = [];

		this.callbacks = {};
		this.callbacksEnabled = false;

		this.pagesCount=0;

		this.masterProperty = options.masterProperty || "opacity";

		// Initialize classNames (in prototype):
		if(options.hasOwnProperty("classNames")) for(key in this.classNames) this.classNames[key] = options.classNames[key] || this.classNames[key];

		// Load elements:
		IDs = this._normalizeIDs(IDs, this.classNames.page);
		this.addPages(IDs);

		// Load buttons, if required:
		if(options.hasOwnProperty("buttonsIDs")){
			IDs = this._normalizeIDs(options.buttonsIDs, this.classNames.pageButton);
			this.addButtons(IDs);

			if(this.pagesIDs.length != this.pagesCount || this.pagesIDs.length != this.buttonsIDs.length || this.pagesDOM.length != this.buttonsDOM.length) // Check.
				throw "Swapper.js - There is a different number of pages and buttons.";
		}

		// Validate and set callbacks:
		if(typeof options.callbacks == "object") for(key in options.callbacks) this.setCallbackByID(key, options.callbacks[key]);

		// Current element:
		if(typeof currentIndex === "number" && this.pagesCount > 0)
			this.currentIndex = Math.max( Math.min(currentIndex, this.pagesCount-1), 0);
		else
			this.currentIndex = null;

		// Default element:
		this.setDefaultByIndex(defaultIndex);

		// Anchor mode:
		this.anchorMode = options.anchorMode===true?true:false;

		// Create transitionEnd listener closure for this instance (delegation approach):
		(function(instance){
			var masterProperty = instance.masterProperty;
			var callbacks = instance.callbacks;

			instance.transitionListener = function(e){
				if(e.propertyName==masterProperty && callbacks.hasOwnProperty(e.target.id)) callbacks[e.target.id](e);
			};

		})(this);

		// Enable callbacks:
		if(options.callbacksEnabled===true) this.enableCallbacks();
	};

	// PROTOTYPE VARIABLES /////////////////////////////////////////////////////

	// Set transitionEvent property in the prototype. Code from Modernizr:
	Swapper.prototype.transitionEvent = (function(){
		"use strict";
		var t;
		var el = document.createElement('fakeelement');
		var transitions = {
		  'transition':'transitionend',
		  'OTransition':'oTransitionEnd',
		  'MozTransition':'transitionend',
		  'WebkitTransition':'webkitTransitionEnd'
		}

		for(t in transitions){
			if( el.style[t] !== undefined ){
				return transitions[t];
			}
		}
	})();

	// ******************

	Swapper.prototype.classNames = {
		"page":"page",
		"pageButton":"pageButton",
		"currentPage":"currentPage",
		"currentButton":"currentButton",
		"loadingPage":"loadingPage",
		"deadPage":"deadPage"
	};

	// AUX. METHODS ////////////////////////////////////////////////////////////

	Swapper.prototype._normalizeIDs=function(IDs, className) {

		if(typeof IDs === "string"){
			if(IDs==="autoMODE"){ // Automatically seach for .page elements.
				var elements=document.getElementsByClassName(className);

				IDs = [];

				for(var i=0, L=elements.length; i<L; ++i) {
					var id = elements[i].id;
					id.length>0 && (IDs[IDs.length] = id);
				}
			}
			else IDs = [IDs]; // Just one element added.
		}
		else if(IDs == null || !IDs) IDs = [];

		if(Object.prototype.toString.call( IDs ) === "[object Array]")
			return IDs;
		else
			throw "Swapper.js  - Invalid IDs.";

	};

	// (PUBLIC) METHODS ////////////////////////////////////////////////////////

	Swapper.prototype.addPage=function(ID) {

		if(this.pagesIDs.indexOf(ID)==-1){

			var element=document.getElementById(ID);

			if(element){
				this.pagesIDs[this.pagesCount] = ID;
				this.pagesDOM[this.pagesCount] = element;
				++this.pagesCount;
				return true;
			}
			else{
				console.error("Swapper.js - No element with ID '"+ID+"' found.");
			}
		}
		else{
			console.error("Swapper.js - Page with ID '"+ID+"' already exists.");
		}

		return false;
	};

	Swapper.prototype.addPages=function(IDs) {
		for (var i=0, L=IDs.length; i<L; ++i)
			this.addPage(IDs[i]);
	};

	// ******************

	Swapper.prototype.addButton=function(ID) {

		if(this.buttonsIDs.indexOf(ID)==-1){
			if(this.buttonsIDs.length < this.pagesCount){

				if(typeof ID == "string" && ID.length>0){
					var element=document.getElementById(ID);

					if(element){
						this.buttonsIDs[this.buttonsIDs.length] = ID;
						this.buttonsDOM[this.buttonsDOM.length] = element;
						return true;
					}
					else{
						console.error("Swapper.js - No element with ID '"+ID+"' found.");
					}
				}
				else{
					this.buttonsIDs[this.buttonsIDs.length] = null;
					this.buttonsDOM[this.buttonsDOM.length] = null;
				}
			}
			else{
				console.error("Swapper.js - You can't add more buttons than pages. Add a page first.");
			}
		}
		else{
			console.error("Swapper.js - Button with ID '"+ID+"' already exists.");
		}

		return false;
	};

	Swapper.prototype.addButtons=function(IDs) {
		for (var i=0, L=IDs.length; i<L; ++i)
			this.addButton(IDs[i]);
	};

	// ******************

	// Add callbacks:

	Swapper.prototype.setCallbackByID=function(ID, callback) {

		if(this.pagesIDs.indexOf(ID)>-1){
			if(callback == null) delete this.callbacks[ID];
			else if(typeof callback == "function") this.callbacks[ID] = callback;
			else throw "Swapper.js - Invalid callback.";
		}
		else throw "Swapper.js - Invalid ID.";

	};

	Swapper.prototype.setCallbackByIndex=function(index, callback) {
		if( typeof index == "number" && index >=0 & index < this.pagesCount)
			this.setCallbackByID(this.pagesIDs[index], callback);
		else console.error("Swapper.js - Invalid index.");
	};

	// Remove callbacks:

	Swapper.prototype.removeCallbackByID=function(ID) {
		if(this.pagesIDs.indexOf(ID)>-1) delete this.callbacks[ID];
		else throw "Swapper.js - Invalid ID.";
	};

	Swapper.prototype.removeCallbackByIndex=function(index) {
		if( typeof index == "number" && index >=0 & index < this.pagesCount)
			this.removeCallbackByID(this.pagesIDs[index]);
		else console.error("Swapper.js - Invalid index.");
	};

	// ******************

	Swapper.prototype.setDefaultByID=function(ID) {
		var index = this.pagesIDs.indexOf(ID);

		if(index>-1) this.setDefaultByIndex(index);
		else throw "Swapper.js - Invalid ID.";
	};

	Swapper.prototype.setDefaultByIndex=function(index) {
		if(typeof index == "number" && this.pagesCount > 0)
			this.defaultIndex = Math.max( Math.min(index, this.pagesCount-1), 0);
		else
			this.defaultIndex = null;
	};

	Swapper.prototype.clearDefault=function(){
		this.defaultIndex = null;
	};

	// ******************

	Swapper.prototype.selectByID=function(ID, loading) {
		this.selectByIndex(this.pagesIDs.indexOf(ID));
	};

	Swapper.prototype.selectByIndex=function(index, loading) {

		if( typeof index == "number" && index >=0 & index < this.pagesCount){

			// If there's a default element and we want to select the current one (already selected):
			if(this.currentIndex == index && this.defaultIndex != null) index = this.defaultIndex;

			// Hide current page:
			if(this.currentIndex >=0){
				this.pagesDOM[this.currentIndex].classList.remove(this.classNames.currentPage);
				if(this.buttonsDOM[this.currentIndex]) this.buttonsDOM[this.currentIndex].classList.remove(this.classNames.currentButton);
			}

			// Put loading cover if required:
			if(loading===true) this.pagesDOM[index].classList.add(this.classNames.loadingPage);
			else this.pagesDOM[index].classList.remove(this.classNames.loadingPage);

			// Show requested page:
			if(this.anchorMode) window.location.hash="#"+this.pagesIDs[index];

			this.pagesDOM[index].classList.add(this.classNames.currentPage);
			if(this.buttonsDOM[index]) this.buttonsDOM[index].classList.add(this.classNames.currentButton);

			this.currentIndex=index;
		}
		else console.error("Swapper.js - Invalid index.");
	};

	// ******************

	Swapper.prototype.finishLoadingByID=function(ID) {
		this.finishLoadingByIndex(this.pagesIDs.indexOf(ID));
	};

	Swapper.prototype.finishLoadingByIndex=function(index) {
		if( typeof index == "number" && index >=0 & index < this.pagesCount)
			this.pagesDOM[index].classList.remove(this.classNames.loadingPage);
		else console.error("Swapper.js - Invalid index.");
	};

	// ******************

	Swapper.prototype.selectNone=function() {
		if(this.anchorMode) window.location.hash = "";

		if(this.currentIndex >=0){
			this.pagesDOM[this.currentIndex].classList.remove(this.classNames.currentPage);
			if(this.buttonsDOM[this.currentIndex]) this.buttonsDOM[this.currentIndex].classList.remove(this.classNames.currentButton);
		}

		this.currentIndex=null;
	};

	// ******************

	Swapper.prototype.removeByID=function(ID) {
		this.removeByIndex(this.pagesIDs.indexOf(ID));
	};

	Swapper.prototype.removeByIndex=function(index) {

		if( typeof index == "number" && index >=0 & index < this.pagesCount){

			// If we want to remove the default element:
			if(this.defaultIndex == index) this.defaultIndex = null;

			// If we want to remove the current element:
			if(this.currentIndex == index && this.currentIndex >=0){

				// Deselect the current element:
				this.pagesDOM[index].classList.remove(this.classNames.currentPage);
				if(this.buttonsDOM[index]) this.buttonsDOM[index].classList.remove(this.classNames.currentButton);

				if(this.defaultIndex != null){ // If there's a default.
					this.currentIndex = this.defaultIndex;
					if(this.anchorMode) window.location.hash="#"+this.pagesIDs[this.defaultIndex];

					// Select the default element:
					this.pagesDOM[this.defaultIndex].classList.add(this.classNames.currentPage);
					if(this.buttonsDOM[this.defaultIndex]) this.buttonsDOM[this.defaultIndex].classList.add(this.classNames.currentButton);
				}
				else{ // If there's NOT a default:
					this.currentIndex = null;
					if(this.anchorMode) window.location.hash="";
				}
			}

			// REMOVE ALL RELATED ELEMENTS:

			delete this.callbacks[this.pagesIDs[index]];

			this.pagesIDs.splice(index, 1);
			this.pagesDOM.splice(index, 1);

			this.buttonsIDs.splice(index, 1);
			this.buttonsDOM.splice(index, 1);

			--this.pagesCount;
		}
		else console.error("Swapper.js - Invalid index.");
	};

	// ******************

	Swapper.prototype.kill=function(id){ // Page DOM id, not private one.
		var deadElement = document.getElementById(id);

		if(deadElement){
			var that = this.masterProperty;
			deadElement.addEventListener(this.transitionEvent, function(e) {
				if(e.propertyName==that) e.target.remove(); // Also remove the event listener.
			});

			deadElement.classList.add(this.classNames.deadPage);
		}
	};

	// ******************

	Swapper.prototype.enableCallbacks=function(){
		if(!this.callbacksEnabled) document.addEventListener(this.transitionEvent, this.transitionListener);
	};

	Swapper.prototype.disableCallbacks=function(){
		if(this.callbacksEnabled) document.removeEventListener(this.transitionEvent, this.transitionListener);
	};

	// ******************

	global.Swapper = Swapper;

}(window));
<commit_msg>Some minor changes on comments.<commit_after>(function(global) {
	"use strict";

	/***************************************************************************
	 *	swapper.js:
	 *	A JS class to help you toggle the visibility of parts of your web project.
	 *
	 *	By Dani Gámez Franco, http://gmzcodes.com
	 *	Licensed under MIT.
	 *
	 *	Version: 2.0.0
	 *	Last Update: 2014-09-13
	 *
	 *	NOTE about classes:
	 *	The classNames attribute can be changed to use custom class names, but
	 *  it will be changed in the prototype, so all instances of Swapper will be
	 *  affected.
	 *
	 **************************************************************************/

	// CONSTRUCTOR & (PUBLIC) VARIABLES ////////////////////////////////////////

	var Swapper = function(IDs, currentIndex, defaultIndex, options) {

		/* OPTIONS FORMAT:

		options = {

			anchorMode: boolean,

			classNames: {
				page: className (String),
				pageButton: className (String),
				currentPage: className (String),
				currentButton: className (String),
				loadingPage: className (String),
				deadPage: className (String)
			},

			buttonsIDs: DOM id (String), "autoMODE", or Array (of Strings),

			callbacks: {
				DOM id (String): callback (function),
				...
			},

			callbacksEnabled: boolean,

			masterProperty: String of a CSS property

		}

		*/

		// Initialize options:
		options = options || {};

		// VARIABLES:
		this.pagesIDs = [];
		this.pagesDOM = [];

		this.buttonsIDs = [];
		this.buttonsDOM = [];

		this.callbacks = {};
		this.callbacksEnabled = false;

		this.pagesCount=0;

		this.masterProperty = options.masterProperty || "opacity";

		// Initialize classNames (in prototype):
		if(options.hasOwnProperty("classNames")) for(key in this.classNames) this.classNames[key] = options.classNames[key] || this.classNames[key];

		// Load elements:
		IDs = this._normalizeIDs(IDs, this.classNames.page);
		this.addPages(IDs);

		// Load buttons, if required:
		if(options.hasOwnProperty("buttonsIDs")){
			IDs = this._normalizeIDs(options.buttonsIDs, this.classNames.pageButton);
			this.addButtons(IDs);

			if(this.pagesIDs.length != this.pagesCount || this.pagesIDs.length != this.buttonsIDs.length || this.pagesDOM.length != this.buttonsDOM.length) // Check.
				throw "Swapper.js - There is a different number of pages and buttons.";
		}

		// Validate and set callbacks:
		if(typeof options.callbacks == "object") for(key in options.callbacks) this.setCallbackByID(key, options.callbacks[key]);

		// Current element:
		if(typeof currentIndex === "number" && this.pagesCount > 0)
			this.currentIndex = Math.max( Math.min(currentIndex, this.pagesCount-1), 0);
		else
			this.currentIndex = null;

		// Default element:
		this.setDefaultByIndex(defaultIndex);

		// Anchor mode:
		this.anchorMode = options.anchorMode===true?true:false;

		// Create transitionEnd listener closure for this instance (delegation approach):
		(function(instance){
			var masterProperty = instance.masterProperty;
			var callbacks = instance.callbacks;

			instance.transitionListener = function(e){
				if(e.propertyName==masterProperty && callbacks.hasOwnProperty(e.target.id)) callbacks[e.target.id](e);
			};

		})(this);

		// Enable callbacks:
		if(options.callbacksEnabled===true) this.enableCallbacks();
	};

	// PROTOTYPE VARIABLES /////////////////////////////////////////////////////

	// Set transitionEvent property in the prototype. Code from Modernizr:
	Swapper.prototype.transitionEvent = (function(){
		"use strict";
		var t;
		var el = document.createElement('fakeelement');
		var transitions = {
		  'transition':'transitionend',
		  'OTransition':'oTransitionEnd',
		  'MozTransition':'transitionend',
		  'WebkitTransition':'webkitTransitionEnd'
		}

		for(t in transitions){
			if( el.style[t] !== undefined ){
				return transitions[t];
			}
		}
	})();

	// ******************

	Swapper.prototype.classNames = {
		"page":"page",
		"pageButton":"pageButton",
		"currentPage":"currentPage",
		"currentButton":"currentButton",
		"loadingPage":"loadingPage",
		"deadPage":"deadPage"
	};

	// AUX. METHODS (PRIVATE) //////////////////////////////////////////////////

	Swapper.prototype._normalizeIDs=function(IDs, className) {

		if(typeof IDs === "string"){
			if(IDs==="autoMODE"){ // Automatically seach for .page elements.
				var elements=document.getElementsByClassName(className);

				IDs = [];

				for(var i=0, L=elements.length; i<L; ++i) {
					var id = elements[i].id;
					id.length>0 && (IDs[IDs.length] = id);
				}
			}
			else IDs = [IDs]; // Just one element added.
		}
		else if(IDs == null || !IDs) IDs = [];

		if(Object.prototype.toString.call( IDs ) === "[object Array]")
			return IDs;
		else
			throw "Swapper.js  - Invalid IDs.";

	};

	// PAGES METHODS ///////////////////////////////////////////////////////////

	// Add pages: **************************************************************

	Swapper.prototype.addPage=function(ID) {

		if(this.pagesIDs.indexOf(ID)==-1){

			var element=document.getElementById(ID);

			if(element){
				this.pagesIDs[this.pagesCount] = ID;
				this.pagesDOM[this.pagesCount] = element;
				++this.pagesCount;
				return true;
			}
			else{
				console.error("Swapper.js - No element with ID '"+ID+"' found.");
			}
		}
		else{
			console.error("Swapper.js - Page with ID '"+ID+"' already exists.");
		}

		return false;
	};

	Swapper.prototype.addPages=function(IDs) {
		for (var i=0, L=IDs.length; i<L; ++i)
			this.addPage(IDs[i]);
	};

	// Remove pages: ***********************************************************

	Swapper.prototype.removeByID=function(ID) {
		this.removeByIndex(this.pagesIDs.indexOf(ID));
	};

	Swapper.prototype.removeByIndex=function(index) {

		if( typeof index == "number" && index >=0 & index < this.pagesCount){

			// If we want to remove the default element:
			if(this.defaultIndex == index) this.defaultIndex = null;

			// If we want to remove the current element:
			if(this.currentIndex == index && this.currentIndex >=0){

				// Deselect the current element:
				this.pagesDOM[index].classList.remove(this.classNames.currentPage);
				if(this.buttonsDOM[index]) this.buttonsDOM[index].classList.remove(this.classNames.currentButton);

				if(this.defaultIndex != null){ // If there's a default.
					this.currentIndex = this.defaultIndex;
					if(this.anchorMode) window.location.hash="#"+this.pagesIDs[this.defaultIndex];

					// Select the default element:
					this.pagesDOM[this.defaultIndex].classList.add(this.classNames.currentPage);
					if(this.buttonsDOM[this.defaultIndex]) this.buttonsDOM[this.defaultIndex].classList.add(this.classNames.currentButton);
				}
				else{ // If there's NOT a default:
					this.currentIndex = null;
					if(this.anchorMode) window.location.hash="";
				}
			}

			// REMOVE ALL RELATED ELEMENTS:

			delete this.callbacks[this.pagesIDs[index]];

			this.pagesIDs.splice(index, 1);
			this.pagesDOM.splice(index, 1);

			this.buttonsIDs.splice(index, 1);
			this.buttonsDOM.splice(index, 1);

			--this.pagesCount;
		}
		else console.error("Swapper.js - Invalid index.");
	};

	// Select pages: ***********************************************************

	Swapper.prototype.selectByID=function(ID, loading) {
		this.selectByIndex(this.pagesIDs.indexOf(ID));
	};

	Swapper.prototype.selectByIndex=function(index, loading) {

		if( typeof index == "number" && index >=0 & index < this.pagesCount){

			// If there's a default element and we want to select the current one (already selected):
			if(this.currentIndex == index && this.defaultIndex != null) index = this.defaultIndex;

			// Hide current page:
			if(this.currentIndex >=0){
				this.pagesDOM[this.currentIndex].classList.remove(this.classNames.currentPage);
				if(this.buttonsDOM[this.currentIndex]) this.buttonsDOM[this.currentIndex].classList.remove(this.classNames.currentButton);
			}

			// Put loading cover if required:
			if(loading===true) this.pagesDOM[index].classList.add(this.classNames.loadingPage);
			else this.pagesDOM[index].classList.remove(this.classNames.loadingPage);

			// Show requested page:
			if(this.anchorMode) window.location.hash="#"+this.pagesIDs[index];

			this.pagesDOM[index].classList.add(this.classNames.currentPage);
			if(this.buttonsDOM[index]) this.buttonsDOM[index].classList.add(this.classNames.currentButton);

			this.currentIndex=index;
		}
		else console.error("Swapper.js - Invalid index.");
	};

	Swapper.prototype.selectNone=function() {
		if(this.anchorMode) window.location.hash = "";

		if(this.currentIndex >=0){
			this.pagesDOM[this.currentIndex].classList.remove(this.classNames.currentPage);
			if(this.buttonsDOM[this.currentIndex]) this.buttonsDOM[this.currentIndex].classList.remove(this.classNames.currentButton);
		}

		this.currentIndex=null;
	};

	// Kill pages: *************************************************************

	Swapper.prototype.kill=function(id){ // Page DOM id, not private one.
		var deadElement = document.getElementById(id);

		if(deadElement){
			var that = this.masterProperty;
			deadElement.addEventListener(this.transitionEvent, function(e) {
				if(e.propertyName==that) e.target.remove(); // Also remove the event listener.
			});

			deadElement.classList.add(this.classNames.deadPage);
		}
	};

	// BUTTONS METHODS /////////////////////////////////////////////////////////

	// Add buttons: ************************************************************

	Swapper.prototype.addButton=function(ID) {

		if(this.buttonsIDs.indexOf(ID)==-1){
			if(this.buttonsIDs.length < this.pagesCount){

				if(typeof ID == "string" && ID.length>0){
					var element=document.getElementById(ID);

					if(element){
						this.buttonsIDs[this.buttonsIDs.length] = ID;
						this.buttonsDOM[this.buttonsDOM.length] = element;
						return true;
					}
					else{
						console.error("Swapper.js - No element with ID '"+ID+"' found.");
					}
				}
				else{
					this.buttonsIDs[this.buttonsIDs.length] = null;
					this.buttonsDOM[this.buttonsDOM.length] = null;
				}
			}
			else{
				console.error("Swapper.js - You can't add more buttons than pages. Add a page first.");
			}
		}
		else{
			console.error("Swapper.js - Button with ID '"+ID+"' already exists.");
		}

		return false;
	};

	Swapper.prototype.addButtons=function(IDs) {
		for (var i=0, L=IDs.length; i<L; ++i)
			this.addButton(IDs[i]);
	};

	// CALLBACKS METHODS ///////////////////////////////////////////////////////

	// Add callbacks: **********************************************************

	Swapper.prototype.setCallbackByID=function(ID, callback) {

		if(this.pagesIDs.indexOf(ID)>-1){
			if(callback == null) delete this.callbacks[ID];
			else if(typeof callback == "function") this.callbacks[ID] = callback;
			else throw "Swapper.js - Invalid callback.";
		}
		else throw "Swapper.js - Invalid ID.";

	};

	Swapper.prototype.setCallbackByIndex=function(index, callback) {
		if( typeof index == "number" && index >=0 & index < this.pagesCount)
			this.setCallbackByID(this.pagesIDs[index], callback);
		else console.error("Swapper.js - Invalid index.");
	};

	// Remove callbacks: *******************************************************

	Swapper.prototype.removeCallbackByID=function(ID) {
		if(this.pagesIDs.indexOf(ID)>-1) delete this.callbacks[ID];
		else throw "Swapper.js - Invalid ID.";
	};

	Swapper.prototype.removeCallbackByIndex=function(index) {
		if( typeof index == "number" && index >=0 & index < this.pagesCount)
			this.removeCallbackByID(this.pagesIDs[index]);
		else console.error("Swapper.js - Invalid index.");
	};

	// Enable / Disable callbacks: *********************************************

	Swapper.prototype.enableCallbacks=function(){
		if(!this.callbacksEnabled) document.addEventListener(this.transitionEvent, this.transitionListener);
	};

	Swapper.prototype.disableCallbacks=function(){
		if(this.callbacksEnabled) document.removeEventListener(this.transitionEvent, this.transitionListener);
	};

	// SET & CLEAR DEFAULT PAGE ////////////////////////////////////////////////

	Swapper.prototype.setDefaultByID=function(ID) {
		var index = this.pagesIDs.indexOf(ID);

		if(index>-1) this.setDefaultByIndex(index);
		else throw "Swapper.js - Invalid ID.";
	};

	Swapper.prototype.setDefaultByIndex=function(index) {
		if(typeof index == "number" && this.pagesCount > 0)
			this.defaultIndex = Math.max( Math.min(index, this.pagesCount-1), 0);
		else
			this.defaultIndex = null;
	};

	Swapper.prototype.clearDefault=function(){
		this.defaultIndex = null;
	};

	// LOADER METHODS //////////////////////////////////////////////////////////

	Swapper.prototype.removeLoaderByID=function(ID) {
		this.removeLoaderByIndex(this.pagesIDs.indexOf(ID));
	};

	Swapper.prototype.removeLoaderByIndex=function(index) {
		if( typeof index == "number" && index >=0 & index < this.pagesCount)
			this.pagesDOM[index].classList.remove(this.classNames.loadingPage);
		else console.error("Swapper.js - Invalid index.");
	};

	// *************************************************************************

	global.Swapper = Swapper;

}(window));
<|endoftext|>


<commit_before>function define(name, value) {
    Object.defineProperty(exports, name, {
        value:      value,
        enumerable: true
    });
}

define("KEYWORD_LEVEL1_WHERE", "where");
define("KEYWORD_LEVEL1_FIELDS", "fields");
define("KEYWORD_LEVEL1_INCLUDE", "include");
define("KEYWORD_LEVEL1_LIMIT", "limit");
define("KEYWORD_LEVEL1_ORDER", "order");
define("KEYWORD_LEVEL1_SKIP", "skip");
define("KEYWORD_LEVEL1_OFFSET", "offset");

define("KEYWORD_LEVEL2_AND", "and");
define("KEYWORD_LEVEL2_OR", "or");

define("KEYWORD_LEVEL3_GREAT_THAN", "gt");
define("KEYWORD_LEVEL3_LESS_THAN", "lt");
define("KEYWORD_LEVEL3_BETWEEN", "between");
define("KEYWORD_LEVEL3_LIKE", "like");

define("KEYWORD_CONVENTION_DOCID", "id");
define("KEYWORD_CONVENTION_DOCTYPE", "docType");

define("KEYWORD_PRESERVED_COUCHBASE", "couchbase");
define("KEYWORD_PRESERVED_KEYNAME", "preserve");

<commit_msg>Removed not needed constants (was needed in part for N1QL)<commit_after>function define(name, value) {
    Object.defineProperty(exports, name, {
        value:      value,
        enumerable: true
    });
}

define("KEYWORD_CONVENTION_DOCID", "id");
define("KEYWORD_CONVENTION_DOCTYPE", "docType");

define("KEYWORD_PRESERVED_COUCHBASE", "couchbase");
define("KEYWORD_PRESERVED_KEYNAME", "preserve");

<|endoftext|>


<commit_before>
var _       = require('underscore'),
    util    = require('util'),
    yaml    = require('js-yaml'); // register .yaml require handler (mind = blown)

var helpers = {

    defaults: {
        assetsFile: process.cwd() + '/config/assets.yml',
        debug:      false,
        express3:   false,
        minify:     false
    },

    assets: function (file) {
        var result = {};

        try {
            // todo: support json or yml here
            result = require(file); // readFileSync under the hood

            // todo: don't need this for dev, just production
            if (!result.hasOwnProperty('s3')) {
                throw new Error('missing s3 section in assets file');
            }

            if (!result.s3.hasOwnProperty('bucket')) {
                throw new Error('missing bucket section in assets file');
            }
        }
        catch (err) {
            // todo: don't need this for dev, just production
        }

        return result;
    },

    baseUrl: function (assets) {
        return '//' + assets.s3.bucket + '.s3.amazonaws.com/';
    },

    endsWith: function (str, suffix) {
        // ref: http://stackoverflow.com/a/2548133/54612
        return str.indexOf(suffix, str.length - suffix.length) !== -1;
    },

    settings: function (options) {
        var result = _.clone(this.defaults);

        // merge in any override from consuming app
        for (var key in options) {
            if (options.hasOwnProperty(key)) {
                result[key] = options[key];
            }
        }

        return result;
    },

    validSectionAndItem: function (assets, sectionName, itemName) {
        if (!assets.hasOwnProperty(sectionName)) {
            log(util.format('could not find section "%s"', sectionName));
            return false;
        }

        if (!assets[sectionName].hasOwnProperty(itemName)) {
            log(util.format('could not find item "%s" in section "%s"', itemName, sectionName));
            return false;
        }

        return true;
    }
};

module.exports = helpers;
<commit_msg>defaulting to express 3<commit_after>
var _       = require('underscore'),
    util    = require('util'),
    yaml    = require('js-yaml'); // register .yaml require handler (mind = blown)

var helpers = {

    defaults: {
        assetsFile: process.cwd() + '/config/assets.yml',
        debug:      false,
        express3:   true,
        minify:     false
    },

    assets: function (file) {
        var result = {};

        try {
            // todo: support json or yml here
            result = require(file); // readFileSync under the hood

            // todo: don't need this for dev, just production
            if (!result.hasOwnProperty('s3')) {
                throw new Error('missing s3 section in assets file');
            }

            if (!result.s3.hasOwnProperty('bucket')) {
                throw new Error('missing bucket section in assets file');
            }
        }
        catch (err) {
            // todo: don't need this for dev, just production
        }

        return result;
    },

    baseUrl: function (assets) {
        return '//' + assets.s3.bucket + '.s3.amazonaws.com/';
    },

    endsWith: function (str, suffix) {
        // ref: http://stackoverflow.com/a/2548133/54612
        return str.indexOf(suffix, str.length - suffix.length) !== -1;
    },

    settings: function (options) {
        var result = _.clone(this.defaults);

        // merge in any override from consuming app
        for (var key in options) {
            if (options.hasOwnProperty(key)) {
                result[key] = options[key];
            }
        }

        return result;
    },

    validSectionAndItem: function (assets, sectionName, itemName) {
        if (!assets.hasOwnProperty(sectionName)) {
            log(util.format('could not find section "%s"', sectionName));
            return false;
        }

        if (!assets[sectionName].hasOwnProperty(itemName)) {
            log(util.format('could not find item "%s" in section "%s"', itemName, sectionName));
            return false;
        }

        return true;
    }
};

module.exports = helpers;
<|endoftext|>


<commit_before>(function() {

var images = [
   "3d-abstract_hdwallpaper_steam-engine_50474.jpg                   ",
   "52_8134_Hoentrop_2012-09-16.jpg                   ",
   "steam-engine-4.png                          ",
   "steam-engine-wallpaper-9.jpg                            ",
   "LARGE-3-175 UP3618 mallet 1929.tif.jpg                   ",
   "LARGE-3-420 oiling 9000.Tif.jpg                          ",
   "LARGE-6 sideview 9000.Tif.jpg                            ",
   "LARGE-7-40 depot Omaha Stimson.Tif.jpg                   ",
   "LARGE-7-67 coach shop.tif.jpg                            ",
   "LARGE-7-160 Durant FD 1910.tif.jpg                       ",
   "LARGE-68-13 UP3703 mallet 1918.tif.jpg                   ",
   "LARGE-68-80 Council Bluffs no date.jpg.jpg               ",
   "LARGE-001347H tour buses-1.psd.jpg                       ",
   "LARGE-1791A M10000 overhead.Tif.jpg                      ",
   "LARGE-1802 poster BW M10000 progress.Tif.jpg             ",
   "LARGE-5127A LA Ltd SPA semaphores.Tif.jpg                ",
   "LARGE-10233 through the pipe.tif.jpg                     ",
   "LARGE-14618 UP9026 Archer.tif.jpg                        ",
   "LARGE-73069 Lane cutoff.tif.jpg                          ",
   "LARGE-73209 4-4-2 SFA 97.tif.jpg                         ",
   "LARGE-74688  Children's Book of Yellowstone Bears.jpg.jpg",
   "LARGE-74690 poster Zion.tif.jpg                          ",
   "LARGE-74694 ad Grand Canyon.Tif.jpg                      ",
   "LARGE-504590 Joseph ag train.jpg.jpg                     ",
   "LARGE-ag train Pilot Rock OWRN.jpg.jpg                   ",
   "LARGE-CoP Rochester NY .tif.jpg                          ",
   "LARGE-EHHarriman Sale of UP.tif.jpg                      ",
   "LARGE-H7-74 Omaha Shop emp 1908.tif.jpg                  ",
   "LARGE-LA Ltd SPA crew 3413.Tif.jpg                       ",
   "LARGE-Lane cutoff fill .tif.jpg                          ",
   "LARGE-lane cutoff.jpg.jpg                                ",
   "LARGE-Mountain type 4-8-2.tif.jpg                        ",
   "LARGE-Omaha Shop ext air 1907.tif.jpg                    ",
   "LARGE-Omaha stores dept 1912.tif.jpg                     ",
   "LARGE-Papio trestle construction.tif.jpg                 ",
   "LARGE-PFE cars ice dock.tif.jpg                          ",
   "LARGE-PFE Las Vegas 1931.tif.jpg                         ",
   "LARGE-SPLAandSL num504.tif.jpg                             ",
   "LARGE-Streamliner 3 locos.Tif.jpg                        ",
   "LARGE-UP SP office KC.tif.jpg                            ",
   "LARGE-X1729 No 4038 near Truckee.tif.jpg                 ",
   "LARGE-X2313 Excursion Midlake.tif.jpg                    ",
   "LARGE-Zoin Lodge opening 04-15-25.tif.jpg                "
];

function createText(text) {

  var material = new THREE.MeshFaceMaterial( [
    new THREE.MeshPhongMaterial( { color: 0xaaaaaa, shading: THREE.FlatShading } ), // front
    new THREE.MeshPhongMaterial( { color: 0xaaaaaa, shading: THREE.SmoothShading } ) // side
  ] );

  var textGeo = new THREE.TextGeometry( text, {
    size: 20,
    height: 2,
    curveSegments: 4,

    bevelThickness: 1.5,
    bevelSize: 1.5,
    bevelEnabled: true
  });

  textGeo.computeBoundingBox();
  textGeo.computeVertexNormals();

  var mesh = new THREE.Mesh( textGeo, material );
  mesh.name = "text";

  mesh.position.x = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );

  return mesh;
}

angular.module('directives', [])
  .directive('scene', function(vrControls) {
    return {
      restrict: 'E',
      template: '<canvas></canvas>',
      link: function(scope, element, attrs){

        window.plotter = new LeapDataPlotter();

        var scene = new THREE.Scene();
        Arrows.scene = scene;

        var camera = new THREE.PerspectiveCamera(
          75,
          window.innerWidth / window.innerHeight,
          0.1,
          10000
        );
        scene.add( camera ); // so that we can add things to it later.

        //we don't actually want the leap hand as child of the camera, as we want the data itself properly transformed.
        // on every frame, we combine the base transformation with the camera transformation to the leap data
        // this is used in grab, proximity, etc.
        Leap.loopController.plugins.boneHand.scene = scene;
        Leap.loopController.plugins.transform.effectiveParent = camera;

        var canvas = element.find('canvas')[0];
        canvas.style.position = 'absolute';
        canvas.style.top = 0;
        canvas.style.left = 0;

        var renderer = new THREE.WebGLRenderer({
          antialias: true,
          canvas: canvas
        });
        renderer.shadowMapEnabled = false;
        renderer.setClearColor(0x000000, 0);

        renderer.setSize(window.innerWidth, window.innerHeight);

        var transformPlugin = Leap.loopController.plugins.transform;
        // these would be better off directed as services.  But for now, we use window for message passing.
        window.vrEffect = new THREE.VREffect(renderer, null, {
          onWindowed: function(){
            Leap.loopController.setOptimizeHMD(false);
             transformPlugin.quaternion = window.DesktopTransformation.quaternion;
             transformPlugin.position   = window.DesktopTransformation.position;
             transformPlugin.scale      = window.DesktopTransformation.scale;
          },
          onFullscreen: function(){
            Leap.loopController.setOptimizeHMD(true);

            transformPlugin.quaternion = window.HMDTransformation.quaternion;
            transformPlugin.position   = window.HMDTransformation.position;
            transformPlugin.scale      = window.HMDTransformation.scale;
          }
        });

        vrControls._camera = camera;

        onResize = function() {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
          renderer.setSize(window.innerWidth, window.innerHeight);
        };

        window.addEventListener('resize', onResize, false);

        // idea - it would be ideal to have a 3d infinite grid - that would be much cooler
        // for now we suffice to a boring old floor.

        var light = new THREE.PointLight(0xffffff, 1, 1000);
        scene.add(light);

        var dockWidth = 1000;
        var dockHeight = 150;

        var dockMesh = new THREE.Mesh(
          new THREE.PlaneGeometry(dockWidth, dockHeight),
          new THREE.MeshPhongMaterial({
            wireframe: false,
            color: 0xffffff,
            map: THREE.ImageUtils.loadTexture("images/foto-viewer.jpg")
          })
        );
        dockMesh.name = "dock";

//        dockMesh.position.set(-90, 130 - dockHeight / 2, -200);
        dockMesh.position.set(dockWidth / 2 - 170, -90, -200);

        // for now, we don't create a scrollable object, but just let it be moved in the view
        var dock = new Dock(scene, dockMesh, Leap.loopController, {
          resize: false,
          moveZ: false,
          moveY: false
        });

//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
        dock.pushImage("images/trains/" + images[0]);
        dock.pushImage("images/trains/" + images[1]);
        dock.pushImage("images/trains/" + images[2]);
        dock.pushImage("images/trains/" + images[3]);

// leap proximity does not at all do well with angled objects:
//        dockMesh.position.set(-90, 130 - dockHeight / 2, -300);
//        dockMesh.rotation.set(0, Math.PI / 4, 0, 0);
//        camera.add(dockMesh);

        scene.add(dockMesh);

        scene.add(dockMesh);

        var text = createText('place images..');
        text.position.setZ(-250);
        text.position.setY(120);
        scene.add(text);

        Leap.loopController.on('hand', function(hand){
          return;
          if (hand.data('cursor')) return;

          var boneMeshes = hand.indexFinger.data('boneMeshes');

          console.log(boneMeshes);

          if (!boneMeshes) return;

          var cursorTarget = new THREE.Vector3(-5,-39,25);
          var cursorDirection = new THREE.Vector3(0,-1,0);
          var cursorLength = 40;

          var cursor = new THREE.ArrowHelper(
            cursorDirection,
            cursorTarget.clone().sub(cursorDirection.normalize().multiplyScalar(cursorLength)),
            cursorLength,
            0x5daa00,
            20,
            10
          );

          boneMeshes[0].add(cursor);

          hand.data('cursor', cursor);
        });

        var render = function() {
          Arrows.update();
          vrControls.update();
          vrEffect.render(scene, camera);

//          plotter.update();
          requestAnimationFrame(render);
        };

        render();

      }
    };
  });

}).call(this);<commit_msg>Add an sortedLayoutContainter to the scene<commit_after>(function() {

var images = [
   "3d-abstract_hdwallpaper_steam-engine_50474.jpg                   ",
   "52_8134_Hoentrop_2012-09-16.jpg                   ",
   "steam-engine-4.png                          ",
   "steam-engine-wallpaper-9.jpg                            ",
   "LARGE-3-175 UP3618 mallet 1929.tif.jpg                   ",
   "LARGE-3-420 oiling 9000.Tif.jpg                          ",
   "LARGE-6 sideview 9000.Tif.jpg                            ",
   "LARGE-7-40 depot Omaha Stimson.Tif.jpg                   ",
   "LARGE-7-67 coach shop.tif.jpg                            ",
   "LARGE-7-160 Durant FD 1910.tif.jpg                       ",
   "LARGE-68-13 UP3703 mallet 1918.tif.jpg                   ",
   "LARGE-68-80 Council Bluffs no date.jpg.jpg               ",
   "LARGE-001347H tour buses-1.psd.jpg                       ",
   "LARGE-1791A M10000 overhead.Tif.jpg                      ",
   "LARGE-1802 poster BW M10000 progress.Tif.jpg             ",
   "LARGE-5127A LA Ltd SPA semaphores.Tif.jpg                ",
   "LARGE-10233 through the pipe.tif.jpg                     ",
   "LARGE-14618 UP9026 Archer.tif.jpg                        ",
   "LARGE-73069 Lane cutoff.tif.jpg                          ",
   "LARGE-73209 4-4-2 SFA 97.tif.jpg                         ",
   "LARGE-74688  Children's Book of Yellowstone Bears.jpg.jpg",
   "LARGE-74690 poster Zion.tif.jpg                          ",
   "LARGE-74694 ad Grand Canyon.Tif.jpg                      ",
   "LARGE-504590 Joseph ag train.jpg.jpg                     ",
   "LARGE-ag train Pilot Rock OWRN.jpg.jpg                   ",
   "LARGE-CoP Rochester NY .tif.jpg                          ",
   "LARGE-EHHarriman Sale of UP.tif.jpg                      ",
   "LARGE-H7-74 Omaha Shop emp 1908.tif.jpg                  ",
   "LARGE-LA Ltd SPA crew 3413.Tif.jpg                       ",
   "LARGE-Lane cutoff fill .tif.jpg                          ",
   "LARGE-lane cutoff.jpg.jpg                                ",
   "LARGE-Mountain type 4-8-2.tif.jpg                        ",
   "LARGE-Omaha Shop ext air 1907.tif.jpg                    ",
   "LARGE-Omaha stores dept 1912.tif.jpg                     ",
   "LARGE-Papio trestle construction.tif.jpg                 ",
   "LARGE-PFE cars ice dock.tif.jpg                          ",
   "LARGE-PFE Las Vegas 1931.tif.jpg                         ",
   "LARGE-SPLAandSL num504.tif.jpg                             ",
   "LARGE-Streamliner 3 locos.Tif.jpg                        ",
   "LARGE-UP SP office KC.tif.jpg                            ",
   "LARGE-X1729 No 4038 near Truckee.tif.jpg                 ",
   "LARGE-X2313 Excursion Midlake.tif.jpg                    ",
   "LARGE-Zoin Lodge opening 04-15-25.tif.jpg                "
];

function createText(text) {

  var material = new THREE.MeshFaceMaterial( [
    new THREE.MeshPhongMaterial( { color: 0xaaaaaa, shading: THREE.FlatShading } ), // front
    new THREE.MeshPhongMaterial( { color: 0xaaaaaa, shading: THREE.SmoothShading } ) // side
  ] );

  var textGeo = new THREE.TextGeometry( text, {
    size: 20,
    height: 2,
    curveSegments: 4,

    bevelThickness: 1.5,
    bevelSize: 1.5,
    bevelEnabled: true
  });

  textGeo.computeBoundingBox();
  textGeo.computeVertexNormals();

  var mesh = new THREE.Mesh( textGeo, material );
  mesh.name = "text";

  mesh.position.x = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );

  return mesh;
}

angular.module('directives', [])
  .directive('scene', function(vrControls) {
    return {
      restrict: 'E',
      template: '<canvas></canvas>',
      link: function(scope, element, attrs){

        window.plotter = new LeapDataPlotter();

        var scene = new THREE.Scene();
        Arrows.scene = scene;

        var camera = new THREE.PerspectiveCamera(
          75,
          window.innerWidth / window.innerHeight,
          0.1,
          10000
        );
        scene.add( camera ); // so that we can add things to it later.

        //we don't actually want the leap hand as child of the camera, as we want the data itself properly transformed.
        // on every frame, we combine the base transformation with the camera transformation to the leap data
        // this is used in grab, proximity, etc.
        Leap.loopController.plugins.boneHand.scene = scene;
        Leap.loopController.plugins.transform.effectiveParent = camera;

        var canvas = element.find('canvas')[0];
        canvas.style.position = 'absolute';
        canvas.style.top = 0;
        canvas.style.left = 0;

        var renderer = new THREE.WebGLRenderer({
          antialias: true,
          canvas: canvas
        });
        renderer.shadowMapEnabled = false;
        renderer.setClearColor(0x000000, 0);

        renderer.setSize(window.innerWidth, window.innerHeight);

        var transformPlugin = Leap.loopController.plugins.transform;
        // these would be better off directed as services.  But for now, we use window for message passing.
        window.vrEffect = new THREE.VREffect(renderer, null, {
          onWindowed: function(){
            Leap.loopController.setOptimizeHMD(false);
             transformPlugin.quaternion = window.DesktopTransformation.quaternion;
             transformPlugin.position   = window.DesktopTransformation.position;
             transformPlugin.scale      = window.DesktopTransformation.scale;
          },
          onFullscreen: function(){
            Leap.loopController.setOptimizeHMD(true);

            transformPlugin.quaternion = window.HMDTransformation.quaternion;
            transformPlugin.position   = window.HMDTransformation.position;
            transformPlugin.scale      = window.HMDTransformation.scale;
          }
        });

        vrControls._camera = camera;

        onResize = function() {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
          renderer.setSize(window.innerWidth, window.innerHeight);
        };

        window.addEventListener('resize', onResize, false);

        // idea - it would be ideal to have a 3d infinite grid - that would be much cooler
        // for now we suffice to a boring old floor.

        var light = new THREE.PointLight(0xffffff, 1, 1000);
        scene.add(light);

        var dockWidth = 1000;
        var dockHeight = 150;

        var dockMesh = new THREE.Mesh(
          new THREE.PlaneGeometry(dockWidth, dockHeight),
          new THREE.MeshPhongMaterial({
            wireframe: false,
            color: 0xffffff,
            map: THREE.ImageUtils.loadTexture("images/foto-viewer.jpg")
          })
        );
        dockMesh.name = "dock";

//        dockMesh.position.set(-90, 130 - dockHeight / 2, -200);
        dockMesh.position.set(dockWidth / 2 - 170, -90, -200);

        // for now, we don't create a scrollable object, but just let it be moved in the view
        var dock = new Dock(scene, dockMesh, Leap.loopController, {
          resize: false,
          moveZ: false,
          moveY: false
        });

//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
//        dock.pushImage("images/UP/" + images[Math.floor(Math.random()*images.length)]);
        dock.pushImage("images/trains/" + images[0]);
        dock.pushImage("images/trains/" + images[1]);
        dock.pushImage("images/trains/" + images[2]);
        dock.pushImage("images/trains/" + images[3]);

        var sortedLayoutContainer = new SortedLayoutContainer();

        // When an image is removed from the doc, add it to the container.
        dock.imageRemoveCallbacks.push(function(data) {
          sortedLayoutContainer.addPlane(data[0]);
        });

        Leap.loopController.on('handBracket.update', function(points){
          sortedLayoutContainer.update(points[0], points[1]);
        });

// leap proximity does not at all do well with angled objects:
//        dockMesh.position.set(-90, 130 - dockHeight / 2, -300);
//        dockMesh.rotation.set(0, Math.PI / 4, 0, 0);
//        camera.add(dockMesh);

        scene.add(dockMesh);

        scene.add(dockMesh);

        var text = createText('place images..');
        text.position.setZ(-250);
        text.position.setY(120);
        scene.add(text);

        Leap.loopController.on('hand', function(hand){
          return;
          if (hand.data('cursor')) return;

          var boneMeshes = hand.indexFinger.data('boneMeshes');

          console.log(boneMeshes);

          if (!boneMeshes) return;

          var cursorTarget = new THREE.Vector3(-5,-39,25);
          var cursorDirection = new THREE.Vector3(0,-1,0);
          var cursorLength = 40;

          var cursor = new THREE.ArrowHelper(
            cursorDirection,
            cursorTarget.clone().sub(cursorDirection.normalize().multiplyScalar(cursorLength)),
            cursorLength,
            0x5daa00,
            20,
            10
          );

          boneMeshes[0].add(cursor);

          hand.data('cursor', cursor);
        });

        var render = function() {
          Arrows.update();
          vrControls.update();
          vrEffect.render(scene, camera);

//          plotter.update();
          requestAnimationFrame(render);
        };

        render();

      }
    };
  });

}).call(this);<|endoftext|>


<commit_before>var path = require('path');
var tester = require('gitbook-tester');
var assert = require('assert');

describe('PlantUML', function() {
    it('should correctly replace by img html tag in book root', function() {
        return tester.builder()
            .withContent('This is a diagram:\n\n{% plantuml %}\nBob->Alice : hello\n{% endplantuml %}')
            .withBookJson({
                plugins: ['local-plantuml']
            })
            .withLocalPlugin(path.join(__dirname, '..'))
            .create()
            .then(function(result) {
                assert.equal(result[0].content, '
This is a diagram:

')
            });
    });
    it('should correctly replace nested page by img html tag in book root', function() {
        return tester.builder()
            .withBookJson({
                plugins: ['local-plantuml']
            })
            .withLocalPlugin(path.join(__dirname, '..'))
            .withContent("Linking to [nested page](nesting/nested.md)")
            .withPage(
                "nesting/nested",
                "This is a diagram:\n\n{% plantuml %}\nBob->Alice : hello\n{% endplantuml %}",
                1
            )
            .withPage(
                "notnested",
                "This is a diagram:\n\n{% plantuml %}\nBob->Alice : hello\n{% endplantuml %}",
                0
            )
            .create()
            .then(function(result) {
                assert.equal(result.get("nesting/nested.html").content, '
This is a diagram:

')
            });
    });
});
<commit_msg>fix unit testing 2<commit_after>var path = require('path');
var tester = require('gitbook-tester');
var assert = require('assert');

describe('PlantUML', function() {
    it('should correctly replace by img html tag in book root', function() {
        return tester.builder()
            .withContent('This is a diagram:\n\n{% plantuml %}\nBob->Alice : hello\n{% endplantuml %}')
            .withBookJson({
                plugins: ['local-plantuml']
            })
            .withLocalPlugin(path.join(__dirname, '..'))
            .create()
            .then(function(result) {
                assert.equal(result[0].content, '
This is a diagram:
\n

')
            });
    });
    it('should correctly replace nested page by img html tag in book root', function() {
        return tester.builder()
            .withBookJson({
                plugins: ['local-plantuml']
            })
            .withLocalPlugin(path.join(__dirname, '..'))
            .withContent("Linking to [nested page](nesting/nested.md)")
            .withPage(
                "nesting/nested",
                "This is a diagram:\n\n{% plantuml %}\nBob->Alice : hello\n{% endplantuml %}",
                1
            )
            .withPage(
                "notnested",
                "This is a diagram:\n\n{% plantuml %}\nBob->Alice : hello\n{% endplantuml %}",
                0
            )
            .create()
            .then(function(result) {
                assert.equal(result.get("nesting/nested.html").content, '
This is a diagram:
\n

')
            });
    });
});
<|endoftext|>