<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const fetch = require('node-fetch');
const HttpsProxyAgent = require('https-proxy-agent');
const ini = require('ini');
const decompress = require('decompress');
const crypto = require('crypto');

const LOCKFILE_NAME = 'download-realm.lock';

const TEMP_DIR_SUFFIX = generateRandomString();

function generateRandomString() {
    return crypto.randomBytes(20).toString('hex');
}

function ensureDirExists(dir) {
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
    }
}

function getTempDir() {
    /*
     * Each run of this script will have it's own temp directory.
     * That way, parallel runs of this script on a same computer won't
     * conflict.
     *
     * It is also possible to overrride this temp directory by setting
     * REALM_DOWNLOAD_CORE_TEMP_DIR so, for instance, CI systems will
     * be able to clean up files.
     */
    return process.env.REALM_DOWNLOAD_CORE_TEMP_DIR ||
           path.join(os.tmpdir(), TEMP_DIR_SUFFIX);
}

function printProgress(input, totalBytes, archive) {
    const ProgressBar = require('progress');
    const StreamCounter = require('stream-counter');

    const message = `Downloading ${archive} [:bar] (:ratek)`;
    const bar = new ProgressBar(message, {
       complete: '=',
       incomplete: ' ',
       width: process.stdout.columns - message.length,
       total: totalBytes / 1024
     });

     input.pipe(new StreamCounter()).on('progress', function() {
         bar.tick((this.bytes / 1024) - bar.curr);
     });
}

function download(serverFolder, archive, destination) {
    const url = `https://static.realm.io/downloads/${serverFolder}/${archive}`;
    console.log(`Download url: ${url}`);
    const proxyUrl = process.env.HTTP_PROXY || process.env.http_proxy || process.env.HTTPS_PROXY || process.env.https_proxy;
    let agent;
    if (proxyUrl) {
        const agentOpts = require('url').parse(proxyUrl);
        agent = new HttpsProxyAgent(agentOpts);
    }
    return fetch(url, { agent }).then((response) => {
        if (response.status !== 200) {
            throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
        }

        const lastModified = new Date(response.headers.get('Last-Modified'));
        return fs.exists(destination)
                 .then(exists => {
                     if (!exists) {
                         return saveFile();
                     } else {
                         return fs.stat(destination)
                                  .then(stat => {
                                      if (stat.mtime.getTime() !== lastModified.getTime()) {
                                          return saveFile();
                                      }
                         })
                     }
        });

        function saveFile() {
            if (process.stdout.isTTY) {
                printProgress(response.body, parseInt(response.headers.get('Content-Length')), archive);
            } else {
                console.log(`Downloading ${archive}`);
            }
            return new Promise((resolve) => {
                const file = fs.createWriteStream(destination);
                response.body.pipe(file).once('finish', () => file.close(resolve));
            }).then(() => fs.utimes(destination, lastModified, lastModified));
        }
    });
}

function extract(downloadedArchive, targetFolder, archiveRootFolder) {
    console.log(`Extracting ${path.basename(downloadedArchive)} => ${targetFolder}`);
    if (!archiveRootFolder) {
        return decompress(downloadedArchive, targetFolder);
    } else {
        const base = path.basename(downloadedArchive).split('.');
        const tempExtractLocation = path.resolve(getTempDir(), base.slice(0, base.length - 2).join('.'));
        return decompress(downloadedArchive, tempExtractLocation)
               .then(() => fs.readdir(path.resolve(tempExtractLocation, archiveRootFolder)))
               .then(items => Promise.all(items.map(item => {
                   const source = path.resolve(tempExtractLocation, archiveRootFolder, item);
                   const target = path.resolve(targetFolder, item);
                   return fs.copy(source, target, { filter: n => path.extname(n) !== '.so' });
               })))
               .then(() => fs.remove(tempExtractLocation))
    }
}

function acquire(desired, target) {
    const corePath = desired.CORE_ARCHIVE && path.resolve(getTempDir(), desired.CORE_ARCHIVE);
    const syncPath = desired.SYNC_ARCHIVE && path.resolve(getTempDir(), desired.SYNC_ARCHIVE);

    return fs.emptyDir(target)
        .then(() => corePath && download(desired.CORE_SERVER_FOLDER, desired.CORE_ARCHIVE, corePath))
        .then(() => corePath && extract(corePath, target, desired.CORE_ARCHIVE_ROOT))
        .then(() => syncPath && download(desired.SYNC_SERVER_FOLDER, desired.SYNC_ARCHIVE, syncPath))
        .then(() => syncPath && extract(syncPath, target, desired.SYNC_ARCHIVE_ROOT))
        .then(() => writeLockfile(target, desired))
}

function getCoreRequirements(dependencies, options, required = {}) {
    required.CORE_SERVER_FOLDER = `core/v${dependencies.REALM_CORE_VERSION}`;
    let flavor = options.debug ? 'Debug' : 'Release';

    switch (options.platform) {
        case 'mac':
            required.CORE_SERVER_FOLDER += `/macos/${flavor}`;
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Darwin-devel.tar.gz`;
            return Promise.resolve(required);
        case 'ios':
            flavor = flavor === 'Debug' ? 'MinSizeDebug' : flavor;
            required.CORE_SERVER_FOLDER += `/ios/${flavor}`;
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-iphoneos.tar.gz`;
            return Promise.resolve(required);
        case 'win': {
            if (!options.arch) throw new Error(`Specifying '--arch' is required for platform 'win'`);
            const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
            required.CORE_SERVER_FOLDER += `/windows/${arch}/nouwp/${flavor}`;
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Windows-${arch}-devel.tar.gz`;
            return Promise.resolve(required);
        }
        case 'linux':
            required.CORE_SERVER_FOLDER = 'core';
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Linux-devel.tar.gz`;
            return Promise.resolve(required);
        default:
            return Promise.reject(new Error(`Unsupported core platform '${options.platform}'`));
    }
}

function getSyncRequirements(dependencies, options, required = {}) {
    required.SYNC_SERVER_FOLDER = 'sync';
    let flavor = options.debug ? 'Debug' : 'Release';

    switch (options.platform) {
        case 'mac':
            required.SYNC_ARCHIVE = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
            required.SYNC_ARCHIVE_ROOT = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}`;
            return Promise.resolve(required);
        case 'ios':
            required.SYNC_ARCHIVE = `realm-sync-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
            required.SYNC_ARCHIVE_ROOT = `core`;
            return Promise.resolve(required);
        case 'win': {
            const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
            required.SYNC_ARCHIVE = `realm-sync-${flavor}-v${dependencies.REALM_SYNC_VERSION}-Windows-${arch}-devel.tar.gz`;
            return getCoreRequirements(dependencies, options, required);
        }
        case 'linux':
            // flavor is ignored since we only publish Release mode
            required.SYNC_ARCHIVE = `realm-sync-Release-v${dependencies.REALM_SYNC_VERSION}-Linux-devel.tar.gz`;
            return getCoreRequirements(dependencies, options, required);
        default:
            return Promise.reject(new Error(`Unsupported sync platform '${options.platform}'`));
    }
}

function writeLockfile(target, contents) {
    return fs.writeFile(path.resolve(target, LOCKFILE_NAME), ini.encode(contents));
}

function readLockfile(target) {
    try {
        return ini.parse(fs.readFileSync(path.resolve(target, LOCKFILE_NAME), 'utf8'));
    } catch (e) {
        return null;
    }
}

function shouldSkipAcquire(target, requirements, force) {
    if (force) {
        console.log('Skipping lockfile check as --force is enabled');
        return false;
    }

    const existingLockfile = readLockfile(target);

    if (!existingLockfile) {
        console.log('No lockfile found at the target, proceeding.');
        return false;
    }

    if (!Object.keys(requirements).every(key => existingLockfile[key] === requirements[key])) {
        console.log('Target directory has a differing lockfile, overwriting.');
        return false;
    }

    console.log('Matching lockfile already exists at target - nothing to do (use --force to override)');
    return true;
}

const optionDefinitions = [
    { name: 'platform', type: String, defaultOption: true },
    { name: 'arch', type: String },
    { name: 'sync', type: Boolean },
    { name: 'debug', type: Boolean },
    { name: 'force', type: Boolean },
];
const options = require('command-line-args')(optionDefinitions);
if (options.platform === '..\\win') {
    options.platform = 'win'; // handle gyp idiocy
}

const vendorDir = path.resolve(__dirname, '../vendor');

let realmDir = path.resolve(vendorDir, `realm-${options.platform}`);
if (options.arch) {
    realmDir += `-${options.arch}`;
}
if (options.debug) {
    realmDir += '-dbg'
}

ensureDirExists(getTempDir());

const dependencies = ini.parse(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));

(options.sync ? getSyncRequirements : getCoreRequirements)(dependencies, options)
    .then(requirements => {
        console.log('Resolved requirements:', requirements);
        if (!shouldSkipAcquire(realmDir, requirements, options.force)) {
            return acquire(requirements, realmDir)
        }
    })
    .then(() => console.log('Success'))
    .catch(error => {
        console.error(error);
        process.exit(1);
    });
<commit_msg>Fix the check for if the archive actually needs to be downloaded in download-realm.js<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const fetch = require('node-fetch');
const HttpsProxyAgent = require('https-proxy-agent');
const ini = require('ini');
const decompress = require('decompress');
const crypto = require('crypto');

const LOCKFILE_NAME = 'download-realm.lock';

const TEMP_DIR_SUFFIX = generateRandomString();

function generateRandomString() {
    return crypto.randomBytes(20).toString('hex');
}

function ensureDirExists(dir) {
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
    }
}

function getTempDir() {
    /*
     * Each run of this script will have it's own temp directory.
     * That way, parallel runs of this script on a same computer won't
     * conflict.
     *
     * It is also possible to overrride this temp directory by setting
     * REALM_DOWNLOAD_CORE_TEMP_DIR so, for instance, CI systems will
     * be able to clean up files.
     */
    return process.env.REALM_DOWNLOAD_CORE_TEMP_DIR ||
           path.join(os.tmpdir(), TEMP_DIR_SUFFIX);
}

function printProgress(input, totalBytes, archive) {
    const ProgressBar = require('progress');
    const StreamCounter = require('stream-counter');

    const message = `Downloading ${archive} [:bar] (:ratek)`;
    const bar = new ProgressBar(message, {
       complete: '=',
       incomplete: ' ',
       width: process.stdout.columns - message.length,
       total: totalBytes / 1024
     });

     input.pipe(new StreamCounter()).on('progress', function() {
         bar.tick((this.bytes / 1024) - bar.curr);
     });
}

function download(serverFolder, archive, destination) {
    const url = `https://static.realm.io/downloads/${serverFolder}/${archive}`;
    console.log(`Download url: ${url}`);
    const proxyUrl = process.env.HTTP_PROXY || process.env.http_proxy || process.env.HTTPS_PROXY || process.env.https_proxy;
    let agent;
    if (proxyUrl) {
        const agentOpts = require('url').parse(proxyUrl);
        agent = new HttpsProxyAgent(agentOpts);
    }
    return fetch(url, {agent}).then(response => {
        if (response.status !== 200) {
            throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
        }

        const lastModified = new Date(response.headers.get('Last-Modified'));
        const contentLength = response.headers.get('Content-Length');
        return fs.stat(destination).then(stat => {
            if (stat.mtime.getTime() <= lastModified.getTime() || stat.size !== +contentLength) {
                return saveFile(response);
            }
        }).catch(() => saveFile(response));
    });

    function saveFile(response) {
        if (process.stdout.isTTY) {
            printProgress(response.body, parseInt(response.headers.get('Content-Length')), archive);
        } else {
            console.log(`Downloading ${archive}`);
        }
        return new Promise((resolve) => {
            const file = fs.createWriteStream(destination);
            response.body.pipe(file).once('finish', () => file.close(resolve));
        });
    }
}

function extract(downloadedArchive, targetFolder, archiveRootFolder) {
    console.log(`Extracting ${path.basename(downloadedArchive)} => ${targetFolder}`);
    if (!archiveRootFolder) {
        return decompress(downloadedArchive, targetFolder);
    } else {
        const base = path.basename(downloadedArchive).split('.');
        const tempExtractLocation = path.resolve(getTempDir(), base.slice(0, base.length - 2).join('.'));
        return decompress(downloadedArchive, tempExtractLocation)
               .then(() => fs.readdir(path.resolve(tempExtractLocation, archiveRootFolder)))
               .then(items => Promise.all(items.map(item => {
                   const source = path.resolve(tempExtractLocation, archiveRootFolder, item);
                   const target = path.resolve(targetFolder, item);
                   return fs.copy(source, target, { filter: n => path.extname(n) !== '.so' });
               })))
               .then(() => fs.remove(tempExtractLocation))
    }
}

function acquire(desired, target) {
    const corePath = desired.CORE_ARCHIVE && path.resolve(getTempDir(), desired.CORE_ARCHIVE);
    const syncPath = desired.SYNC_ARCHIVE && path.resolve(getTempDir(), desired.SYNC_ARCHIVE);

    return fs.emptyDir(target)
        .then(() => corePath && download(desired.CORE_SERVER_FOLDER, desired.CORE_ARCHIVE, corePath))
        .then(() => corePath && extract(corePath, target, desired.CORE_ARCHIVE_ROOT))
        .then(() => syncPath && download(desired.SYNC_SERVER_FOLDER, desired.SYNC_ARCHIVE, syncPath))
        .then(() => syncPath && extract(syncPath, target, desired.SYNC_ARCHIVE_ROOT))
        .then(() => writeLockfile(target, desired))
}

function getCoreRequirements(dependencies, options, required = {}) {
    required.CORE_SERVER_FOLDER = `core/v${dependencies.REALM_CORE_VERSION}`;
    let flavor = options.debug ? 'Debug' : 'Release';

    switch (options.platform) {
        case 'mac':
            required.CORE_SERVER_FOLDER += `/macos/${flavor}`;
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Darwin-devel.tar.gz`;
            return required;
        case 'ios':
            flavor = flavor === 'Debug' ? 'MinSizeDebug' : flavor;
            required.CORE_SERVER_FOLDER += `/ios/${flavor}`;
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-iphoneos.tar.gz`;
            return required;
        case 'win': {
            if (!options.arch) throw new Error(`Specifying '--arch' is required for platform 'win'`);
            const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
            required.CORE_SERVER_FOLDER += `/windows/${arch}/nouwp/${flavor}`;
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Windows-${arch}-devel.tar.gz`;
            return required;
        }
        case 'linux':
            required.CORE_SERVER_FOLDER = 'core';
            required.CORE_ARCHIVE = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Linux-devel.tar.gz`;
            return required;
        default:
            throw new Error(`Unsupported core platform '${options.platform}'`);
    }
}

function getSyncRequirements(dependencies, options, required = {}) {
    required.SYNC_SERVER_FOLDER = 'sync';
    let flavor = options.debug ? 'Debug' : 'Release';

    switch (options.platform) {
        case 'mac':
            required.SYNC_ARCHIVE = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
            required.SYNC_ARCHIVE_ROOT = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}`;
            return required;
        case 'ios':
            required.SYNC_ARCHIVE = `realm-sync-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
            required.SYNC_ARCHIVE_ROOT = `core`;
            return required;
        case 'win': {
            const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
            required.SYNC_ARCHIVE = `realm-sync-${flavor}-v${dependencies.REALM_SYNC_VERSION}-Windows-${arch}-devel.tar.gz`;
            return getCoreRequirements(dependencies, options, required);
        }
        case 'linux':
            // flavor is ignored since we only publish Release mode
            required.SYNC_ARCHIVE = `realm-sync-Release-v${dependencies.REALM_SYNC_VERSION}-Linux-devel.tar.gz`;
            return getCoreRequirements(dependencies, options, required);
        default:
            throw new Error(`Unsupported sync platform '${options.platform}'`);
    }
}

function writeLockfile(target, contents) {
    return fs.writeFile(path.resolve(target, LOCKFILE_NAME), ini.encode(contents));
}

function readLockfile(target) {
    try {
        return ini.parse(fs.readFileSync(path.resolve(target, LOCKFILE_NAME), 'utf8'));
    } catch (e) {
        return null;
    }
}

function shouldSkipAcquire(target, requirements, force) {
    if (force) {
        console.log('Skipping lockfile check as --force is enabled');
        return false;
    }

    const existingLockfile = readLockfile(target);

    if (!existingLockfile) {
        console.log('No lockfile found at the target, proceeding.');
        return false;
    }

    if (!Object.keys(requirements).every(key => existingLockfile[key] === requirements[key])) {
        console.log('Target directory has a differing lockfile, overwriting.');
        return false;
    }

    console.log('Matching lockfile already exists at target - nothing to do (use --force to override)');
    return true;
}

const optionDefinitions = [
    { name: 'platform', type: String, defaultOption: true },
    { name: 'arch', type: String },
    { name: 'sync', type: Boolean },
    { name: 'debug', type: Boolean },
    { name: 'force', type: Boolean },
];
const options = require('command-line-args')(optionDefinitions);
if (options.platform === '..\\win') {
    options.platform = 'win'; // handle gyp idiocy
}

const vendorDir = path.resolve(__dirname, '../vendor');

let realmDir = path.resolve(vendorDir, `realm-${options.platform}`);
if (options.arch) {
    realmDir += `-${options.arch}`;
}
if (options.debug) {
    realmDir += '-dbg'
}

ensureDirExists(getTempDir());

const dependencies = ini.parse(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));

const requirements = (options.sync ? getSyncRequirements : getCoreRequirements)(dependencies, options);
console.log('Resolved requirements:', requirements);
if (!shouldSkipAcquire(realmDir, requirements, options.force)) {
    acquire(requirements, realmDir)
        .then(() => console.log('Success'))
        .catch(error => {
            console.error(error);
            process.exit(1);
        });
}
<|endoftext|>


<commit_before>/*
* tablesaw: A set of plugins for responsive tables
* Swipe Toggle: swipe gesture (or buttons) to navigate which columns are shown.
* Copyright (c) 2013 Filament Group, Inc.
* MIT License
*/

;(function( win, $, undefined ){

	$.extend( Tablesaw.config, {
		swipe: {
			horizontalThreshold: 15,
			verticalThreshold: 30
		}
	});

	var config = Tablesaw.config.swipe;

	function createSwipeTable( $table ){

		var $btns = $( "

" ),
			$prevBtn = $( "" ).appendTo( $btns ),
			$nextBtn = $( "" ).appendTo( $btns ),
			hideBtn = 'disabled',
			persistWidths = 'tablesaw-fix-persist',
			$headerCells = $table.find( "thead th" ),
			$headerCellsNoPersist = $headerCells.not( '[data-tablesaw-priority="persist"]' ),
			headerWidths = [],
			$head = $( document.head || 'head' ),
			tableId = $table.attr( 'id' ),
			// TODO switch this to an nth-child feature test
			isIE8 = $( 'html' ).is( '.ie-lte8' );

		if( !$headerCells.length ) {
			throw new Error( "tablesaw swipe: no header cells found. Are you using  inside of
?" );
		}

		// Calculate initial widths
		$table.css('width', 'auto');
		$headerCells.each(function() {
			headerWidths.push( $( this ).outerWidth() );
		});
		$table.css( 'width', '' );

		$btns.appendTo( $table.prev( '.tablesaw-bar' ) );

		$table.addClass( "tablesaw-swipe" );

		if( !tableId ) {
			tableId = 'tableswipe-' + Math.round( Math.random() * 10000 );
			$table.attr( 'id', tableId );
		}

		function $getCells( headerCell ) {
			return $( headerCell.cells ).add( headerCell );
		}

		function showColumn( headerCell ) {
			$getCells( headerCell ).removeClass( 'tablesaw-cell-hidden' );
		}

		function hideColumn( headerCell ) {
			$getCells( headerCell ).addClass( 'tablesaw-cell-hidden' );
		}

		function persistColumn( headerCell ) {
			$getCells( headerCell ).addClass( 'tablesaw-cell-persist' );
		}

		function isPersistent( headerCell ) {
			return $( headerCell ).is( '[data-tablesaw-priority="persist"]' );
		}

		function unmaintainWidths() {
			$table.removeClass( persistWidths );
			$( '#' + tableId + '-persist' ).remove();
		}

		function maintainWidths() {
			var prefix = '#' + tableId + ' ',
				styles = [],
				tableWidth = $table.width();

			$headerCells.each(function( index ) {
				var width;
				if( isPersistent( this ) ) {
					width = $( this ).outerWidth();

					// Only save width on non-greedy columns (take up less than 75% of table width)
					if( width < tableWidth * 0.75 ) {
						styles.push( prefix + ' .tablesaw-cell-persist:nth-child(' + ( index + 1 ) + ') { width: ' + width + 'px; }' );
					}
				}
			});

			unmaintainWidths();
			$table.addClass( persistWidths );
			$head.append( $( '' ).attr( 'id', tableId + '-persist' ) );
		}

		function getNext(){
			var next = [],
				checkFound;

			$headerCellsNoPersist.each(function( i ) {
				var $t = $( this ),
					isHidden = $t.css( "display" ) === "none" || $t.is( ".tablesaw-cell-hidden" );

				if( !isHidden && !checkFound ) {
					checkFound = true;
					next[ 0 ] = i;
				} else if( isHidden && checkFound ) {
					next[ 1 ] = i;

					return false;
				}
			});

			return next;
		}

		function getPrev(){
			var next = getNext();
			return [ next[ 1 ] - 1 , next[ 0 ] - 1 ];
		}

		function nextpair( fwd ){
			return fwd ? getNext() : getPrev();
		}

		function canAdvance( pair ){
			return pair[ 1 ] > -1 && pair[ 1 ] < $headerCellsNoPersist.length;
		}

		function fakeBreakpoints() {
			var extraPaddingPixels = 20,
				containerWidth = $table.parent().width(),
				persist = [],
				sum = 0,
				sums = [],
				visibleNonPersistantCount = $headerCells.length;

			$headerCells.each(function( index ) {
				var $t = $( this ),
					isPersist = $t.is( '[data-tablesaw-priority="persist"]' );

				persist.push( isPersist );

				sum += headerWidths[ index ] + ( isPersist ? 0 : extraPaddingPixels );
				sums.push( sum );

				// is persistent or is hidden
				if( isPersist || sum > containerWidth ) {
					visibleNonPersistantCount--;
				}
			});

			var needsNonPersistentColumn = visibleNonPersistantCount === 0;

			$headerCells.each(function( index ) {
				if( persist[ index ] ) {

					// for visual box-shadow
					persistColumn( this );
					return;
				}

				if( sums[ index ] <= containerWidth || needsNonPersistentColumn ) {
					needsNonPersistentColumn = false;
					showColumn( this );
				} else {
					hideColumn( this );
				}
			});

			if( !isIE8 ) {
				unmaintainWidths();
			}
			$table.trigger( 'tablesawcolumns' );
		}

		function advance( fwd ){
			var pair = nextpair( fwd );
			if( canAdvance( pair ) ){
				if( isNaN( pair[ 0 ] ) ){
					if( fwd ){
						pair[0] = 0;
					}
					else {
						pair[0] = $headerCellsNoPersist.length - 1;
					}
				}

				if( !isIE8 ) {
					maintainWidths();
				}

				hideColumn( $headerCellsNoPersist.get( pair[ 0 ] ) );
				showColumn( $headerCellsNoPersist.get( pair[ 1 ] ) );

				$table.trigger( 'tablesawcolumns' );
			}
		}

		$prevBtn.add( $nextBtn ).click(function( e ){
			advance( !!$( e.target ).closest( $nextBtn ).length );
			e.preventDefault();
		});

		function getCoord( event, key ) {
			return ( event.touches || event.originalEvent.touches )[ 0 ][ key ];
		}

		$table
			.bind( "touchstart.swipetoggle", function( e ){
				var originX = getCoord( e, 'pageX' ),
					originY = getCoord( e, 'pageY' ),
					x,
					y;

				$( this )
					.bind( "touchmove", function( e ){
						x = getCoord( e, 'pageX' );
						y = getCoord( e, 'pageY' );

						if( Math.abs( x - originX ) > config.horizontalThreshold && Math.abs( y - originY ) < config.verticalThreshold ) {
							e.preventDefault();
						}
					})
					.bind( "touchend.swipetoggle", function(){
						if( x - originX < -1 * config.horizontalThreshold ){
							advance( true );
						}
						if( x - originX > config.horizontalThreshold ){
							advance( false );
						}

						$( this ).unbind( "touchmove touchend" );
					});

			})
			.bind( "tablesawcolumns.swipetoggle", function(){
				$prevBtn[ canAdvance( getPrev() ) ? "removeClass" : "addClass" ]( hideBtn );
				$nextBtn[ canAdvance( getNext() ) ? "removeClass" : "addClass" ]( hideBtn );
			})
			.bind( "tablesawnext.swipetoggle", function(){
				advance( true );
			} )
			.bind( "tablesawprev.swipetoggle", function(){
				advance( false );
			} )
			.bind( "tablesawdestroy.swipetoggle", function(){
				var $t = $( this );

				$t.removeClass( 'tablesaw-swipe' );
				$t.prev( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove();
				$( win ).off( "resize", fakeBreakpoints );

				$t.unbind( ".swipetoggle" );
			});

		fakeBreakpoints();
		$( win ).on( "resize", fakeBreakpoints );
	}

	// on tablecreate, init
	$( document ).on( "tablesawcreate", function( e, Tablesaw ){

		if( Tablesaw.mode === 'swipe' ){
			createSwipeTable( Tablesaw.$table );
		}

	} );

}( this, jQuery ));
<commit_msg>Only persist widths for swipe tables.<commit_after>/*
* tablesaw: A set of plugins for responsive tables
* Swipe Toggle: swipe gesture (or buttons) to navigate which columns are shown.
* Copyright (c) 2013 Filament Group, Inc.
* MIT License
*/

;(function( win, $, undefined ){

	$.extend( Tablesaw.config, {
		swipe: {
			horizontalThreshold: 15,
			verticalThreshold: 30
		}
	});

	var config = Tablesaw.config.swipe;

	function createSwipeTable( $table ){

		var $btns = $( "

" ),
			$prevBtn = $( "" ).appendTo( $btns ),
			$nextBtn = $( "" ).appendTo( $btns ),
			hideBtn = 'disabled',
			persistWidths = 'tablesaw-fix-persist',
			$headerCells = $table.find( "thead th" ),
			$headerCellsNoPersist = $headerCells.not( '[data-tablesaw-priority="persist"]' ),
			headerWidths = [],
			$head = $( document.head || 'head' ),
			tableId = $table.attr( 'id' ),
			// TODO switch this to an nth-child feature test
			isIE8 = $( 'html' ).is( '.ie-lte8' );

		if( !$headerCells.length ) {
			throw new Error( "tablesaw swipe: no header cells found. Are you using  inside of
?" );
		}

		// Calculate initial widths
		$table.css('width', 'auto');
		$headerCells.each(function() {
			headerWidths.push( $( this ).outerWidth() );
		});
		$table.css( 'width', '' );

		$btns.appendTo( $table.prev( '.tablesaw-bar' ) );

		$table.addClass( "tablesaw-swipe" );

		if( !tableId ) {
			tableId = 'tableswipe-' + Math.round( Math.random() * 10000 );
			$table.attr( 'id', tableId );
		}

		function $getCells( headerCell ) {
			return $( headerCell.cells ).add( headerCell );
		}

		function showColumn( headerCell ) {
			$getCells( headerCell ).removeClass( 'tablesaw-cell-hidden' );
		}

		function hideColumn( headerCell ) {
			$getCells( headerCell ).addClass( 'tablesaw-cell-hidden' );
		}

		function persistColumn( headerCell ) {
			$getCells( headerCell ).addClass( 'tablesaw-cell-persist' );
		}

		function isPersistent( headerCell ) {
			return $( headerCell ).is( '[data-tablesaw-priority="persist"]' );
		}

		function unmaintainWidths() {
			$table.removeClass( persistWidths );
			$( '#' + tableId + '-persist' ).remove();
		}

		function maintainWidths() {
			var prefix = '#' + tableId + '.tablesaw-swipe ',
				styles = [],
				tableWidth = $table.width();

			$headerCells.each(function( index ) {
				var width;
				if( isPersistent( this ) ) {
					width = $( this ).outerWidth();

					// Only save width on non-greedy columns (take up less than 75% of table width)
					if( width < tableWidth * 0.75 ) {
						styles.push( prefix + ' .tablesaw-cell-persist:nth-child(' + ( index + 1 ) + ') { width: ' + width + 'px; }' );
					}
				}
			});

			unmaintainWidths();
			$table.addClass( persistWidths );
			$head.append( $( '' ).attr( 'id', tableId + '-persist' ) );
		}

		function getNext(){
			var next = [],
				checkFound;

			$headerCellsNoPersist.each(function( i ) {
				var $t = $( this ),
					isHidden = $t.css( "display" ) === "none" || $t.is( ".tablesaw-cell-hidden" );

				if( !isHidden && !checkFound ) {
					checkFound = true;
					next[ 0 ] = i;
				} else if( isHidden && checkFound ) {
					next[ 1 ] = i;

					return false;
				}
			});

			return next;
		}

		function getPrev(){
			var next = getNext();
			return [ next[ 1 ] - 1 , next[ 0 ] - 1 ];
		}

		function nextpair( fwd ){
			return fwd ? getNext() : getPrev();
		}

		function canAdvance( pair ){
			return pair[ 1 ] > -1 && pair[ 1 ] < $headerCellsNoPersist.length;
		}

		function fakeBreakpoints() {
			var extraPaddingPixels = 20,
				containerWidth = $table.parent().width(),
				persist = [],
				sum = 0,
				sums = [],
				visibleNonPersistantCount = $headerCells.length;

			$headerCells.each(function( index ) {
				var $t = $( this ),
					isPersist = $t.is( '[data-tablesaw-priority="persist"]' );

				persist.push( isPersist );

				sum += headerWidths[ index ] + ( isPersist ? 0 : extraPaddingPixels );
				sums.push( sum );

				// is persistent or is hidden
				if( isPersist || sum > containerWidth ) {
					visibleNonPersistantCount--;
				}
			});

			var needsNonPersistentColumn = visibleNonPersistantCount === 0;

			$headerCells.each(function( index ) {
				if( persist[ index ] ) {

					// for visual box-shadow
					persistColumn( this );
					return;
				}

				if( sums[ index ] <= containerWidth || needsNonPersistentColumn ) {
					needsNonPersistentColumn = false;
					showColumn( this );
				} else {
					hideColumn( this );
				}
			});

			if( !isIE8 ) {
				unmaintainWidths();
			}
			$table.trigger( 'tablesawcolumns' );
		}

		function advance( fwd ){
			var pair = nextpair( fwd );
			if( canAdvance( pair ) ){
				if( isNaN( pair[ 0 ] ) ){
					if( fwd ){
						pair[0] = 0;
					}
					else {
						pair[0] = $headerCellsNoPersist.length - 1;
					}
				}

				if( !isIE8 ) {
					maintainWidths();
				}

				hideColumn( $headerCellsNoPersist.get( pair[ 0 ] ) );
				showColumn( $headerCellsNoPersist.get( pair[ 1 ] ) );

				$table.trigger( 'tablesawcolumns' );
			}
		}

		$prevBtn.add( $nextBtn ).click(function( e ){
			advance( !!$( e.target ).closest( $nextBtn ).length );
			e.preventDefault();
		});

		function getCoord( event, key ) {
			return ( event.touches || event.originalEvent.touches )[ 0 ][ key ];
		}

		$table
			.bind( "touchstart.swipetoggle", function( e ){
				var originX = getCoord( e, 'pageX' ),
					originY = getCoord( e, 'pageY' ),
					x,
					y;

				$( this )
					.bind( "touchmove", function( e ){
						x = getCoord( e, 'pageX' );
						y = getCoord( e, 'pageY' );

						if( Math.abs( x - originX ) > config.horizontalThreshold && Math.abs( y - originY ) < config.verticalThreshold ) {
							e.preventDefault();
						}
					})
					.bind( "touchend.swipetoggle", function(){
						if( x - originX < -1 * config.horizontalThreshold ){
							advance( true );
						}
						if( x - originX > config.horizontalThreshold ){
							advance( false );
						}

						$( this ).unbind( "touchmove touchend" );
					});

			})
			.bind( "tablesawcolumns.swipetoggle", function(){
				$prevBtn[ canAdvance( getPrev() ) ? "removeClass" : "addClass" ]( hideBtn );
				$nextBtn[ canAdvance( getNext() ) ? "removeClass" : "addClass" ]( hideBtn );
			})
			.bind( "tablesawnext.swipetoggle", function(){
				advance( true );
			} )
			.bind( "tablesawprev.swipetoggle", function(){
				advance( false );
			} )
			.bind( "tablesawdestroy.swipetoggle", function(){
				var $t = $( this );

				$t.removeClass( 'tablesaw-swipe' );
				$t.prev( '.tablesaw-bar' ).find( '.tablesaw-advance' ).remove();
				$( win ).off( "resize", fakeBreakpoints );

				$t.unbind( ".swipetoggle" );
			});

		fakeBreakpoints();
		$( win ).on( "resize", fakeBreakpoints );
	}

	// on tablecreate, init
	$( document ).on( "tablesawcreate", function( e, Tablesaw ){

		if( Tablesaw.mode === 'swipe' ){
			createSwipeTable( Tablesaw.$table );
		}

	} );

}( this, jQuery ));
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Vanderbilt University, All rights reserved.
 *
 * Author: Zsolt Lattmann
 */

// TODO: Use PluginManagerConfiguration
// TODO: Load ActiveSelection objects and pass it correctly
// TODO: Add more statistics to the result object
// TODO: Result object rename name -> pluginName, time -> finishTime)
// TODO: Make this class testable
// TODO: PluginManager should download the plugins

'use strict';
define([
    './PluginBase',
    './PluginContext',
    'logManager'],
    function (PluginBase, PluginContext, LogManager) {

        var PluginManagerBase = function (storage, Core, plugins) {
            this.logger = LogManager.create("PluginManager");
            this._Core = Core;       // webgme core is used to operate on objects
            this._storage = storage; // webgme storage
            this._plugins = plugins; // key value pair of pluginName: pluginType - plugins are already loaded/downloaded
            this._pluginConfigs = {}; // keeps track of the current configuration for each plugins by name

            var pluginNames = Object.keys(this._plugins);
            for (var i = 0; i < pluginNames.length; i += 1) {
                var p = new this._plugins[pluginNames[i]]();
                this._pluginConfigs[pluginNames[i]] = p.getDefaultConfig();
            }
        };

        PluginManagerBase.prototype.initialize = function (managerConfiguration, configCallback, callbackContext) {
            var self = this,
                plugins = this._plugins;

            //#1: PluginManagerBase should load the plugins

            //#2: PluginManagerBase iterates through each plugin and collects the config data
            var pluginConfigs = {};

            for (var p in plugins) {
                if (plugins.hasOwnProperty(p)) {
                    var plugin = new plugins[p]();
                    pluginConfigs[p] = plugin.getConfigStructure();
                }
            }

            if (configCallback) {
                configCallback.call(callbackContext, pluginConfigs, function (updatedPluginConfig) {
                    for (var p in updatedPluginConfig) {
                        if (updatedPluginConfig.hasOwnProperty(p)) {
                            //save it back to the plugin
                            self._pluginConfigs[p] = updatedPluginConfig[p];
                        }
                    }
                });
            }
        };

        /**
         * Gets a new instance of a plugin by name.
         *
         * @param {string} name
         * @returns {PluginBase}
         */
        PluginManagerBase.prototype.getPluginByName = function (name) {
            return this._plugins[name];
        };

        PluginManagerBase.prototype.loadMetaNodes = function (pluginContext, callback) {
            var self = this;

            this.logger.debug('Loading meta nodes');

            // get meta members
            var metaIDs = pluginContext.core.getMemberPaths(pluginContext.rootNode, 'MetaAspectSet');

            var len = metaIDs.length;

            var nodeObjs = [];

            var allObjectsLoadedHandler = function () {
                var len2 = nodeObjs.length;

                var nameObjMap = {};

                while (len2--) {
                    var nodeObj = nodeObjs[len2];

                    nameObjMap[pluginContext.core.getAttribute(nodeObj, 'name')] = nodeObj;
                }

                pluginContext.META = nameObjMap;

                self.logger.debug('Meta nodes are loaded');

                callback(null, pluginContext);
            };

            var loadedMetaObjectHandler = function (err, nodeObj) {
                nodeObjs.push(nodeObj);

                if (nodeObjs.length === metaIDs.length) {
                    allObjectsLoadedHandler();
                }
            };

            while (len--) {
                pluginContext.core.loadByPath(pluginContext.rootNode, metaIDs[len], loadedMetaObjectHandler);
            }
        };

        /**
         *
         * @param {PluginManagerConfiguration} managerConfiguration
         * @param {function} callback
         */
        PluginManagerBase.prototype.getPluginContext = function (managerConfiguration, callback) {

            // TODO: check if callback is a function

            var self = this;

            var pluginContext = new PluginContext();

            // based on the string values get the node objects
            // 1) Open project
            // 2) Load branch OR commit hash
            // 3) Load rootNode
            // 4) Load active object
            // 5) Load active selection
            // 6) Update context
            // 7) return

            pluginContext.project = this._storage;
            pluginContext.projectName = managerConfiguration.project;
            pluginContext.core = new self._Core(pluginContext.project, {corerel: 2});
            pluginContext.commitHash = managerConfiguration.commit;
            pluginContext.activeNode = managerConfiguration.activeNode;
            pluginContext.activeSelection = []; // managerConfiguration.activeSelection; // TODO: load all these objects

            // TODO: add activeNode and activeSelection

            var loadCommitHashAndRun = function (commitHash) {
                self.logger.info('Loading commit ' + commitHash);
                pluginContext.project.loadObject(commitHash, function (err, commitObj) {
                    if (!err && commitObj !== null && commitObj !== undefined) {
                        pluginContext.core.loadRoot(commitObj.root, function (err, rootNode) {
                            if (!err) {
                                pluginContext.rootNode = rootNode;
                                if (typeof pluginContext.activeNode === 'string') {
                                    pluginContext.core.loadByPath(pluginContext.rootNode, pluginContext.activeNode, function (err, activeNode) {
                                        if (!err) {
                                            pluginContext.activeNode = activeNode;
                                            self.loadMetaNodes(pluginContext, callback);
                                        } else {
                                            callback("unable to load selected object", pluginContext);
                                        }
                                    });
                                } else {
                                    pluginContext.activeNode = null;
                                    self.loadMetaNodes(pluginContext, callback);
                                }
                            } else {
                                callback("unable to load root", pluginContext);
                            }
                        });
                    } else {
                        callback('cannot find commit', pluginContext);
                    }

                });
            };

            // load commit hash and run based on branch name or commit hash
            if (managerConfiguration.branchName) {
                pluginContext.project.getBranchNames(function (err, branchNames) {
                    self.logger.debug(branchNames);

                    if (branchNames.hasOwnProperty(managerConfiguration.branchName)) {
                        pluginContext.commitHash = branchNames[managerConfiguration.branchName];
                        pluginContext.branchName = managerConfiguration.branchName;
                        loadCommitHashAndRun(pluginContext.commitHash);
                    } else {
                        callback('cannot find branch', pluginContext);
                    }
                });
            } else {
                loadCommitHashAndRun(pluginContext.commitHash);
            }

        };

        PluginManagerBase.prototype.executePlugin = function (name, managerConfiguration, done) {
            // TODO: check if name is a string
            // TODO: check if managerConfiguration is an instance of PluginManagerConfiguration
            // TODO: check if done is a function

            var PluginClass = this.getPluginByName(name);

            var plugin = new PluginClass();

            var pluginLogger = LogManager.create('Plugin.' + name);

            managerConfiguration.FS.createArtifact(name + '-output');
            // TODO: write some information about this execution into the output directory

            plugin.initialize(pluginLogger, managerConfiguration.FS);

            plugin.setCurrentConfig(this._pluginConfigs[name]);

            this.getPluginContext(managerConfiguration, function (err, pluginContext) {
                if (err) {
                    // TODO: this has to return with an empty PluginResult object and NOT with null.
                    done(err, null);

                } else {

                    //set logging level at least to INFO level since the plugins write messages with INFO level onto the console
                    var logLevel = LogManager.getLogLevel();
                    if (logLevel < LogManager.logLevels.INFO) {
                        // elevate log level if it is less then info
                        LogManager.setLogLevel(LogManager.logLevels.INFO);
                    }

                    // TODO: Would be nice to log to file and to console at the same time.
                    //LogManager.setFileLogPath('PluginManager.log');

                    plugin.configure(pluginContext);

                    var startTime = (new Date()).toISOString();

                    plugin.main(function (err, result) {
                        //set loglevel back to previous value
                        LogManager.setLogLevel(logLevel);

                        // set common information (meta info) about the plugin and measured execution times
                        result.setFinishTime((new Date()).toISOString());
                        result.setStartTime(startTime);

                        result.setPluginName(plugin.getName());
                        result.setError(err);

                        done(err, result);
                    });
                }
            });
        };

        return PluginManagerBase;
    });<commit_msg>Add active selection support to PluginManager<commit_after>/*
 * Copyright (C) 2014 Vanderbilt University, All rights reserved.
 *
 * Author: Zsolt Lattmann
 */

// TODO: Use PluginManagerConfiguration
// TODO: Load ActiveSelection objects and pass it correctly
// TODO: Add more statistics to the result object
// TODO: Result object rename name -> pluginName, time -> finishTime)
// TODO: Make this class testable
// TODO: PluginManager should download the plugins

'use strict';
define([
    './PluginBase',
    './PluginContext',
    'logManager'],
    function (PluginBase, PluginContext, LogManager) {

        var PluginManagerBase = function (storage, Core, plugins) {
            this.logger = LogManager.create("PluginManager");
            this._Core = Core;       // webgme core class is used to operate on objects
            this._storage = storage; // webgme storage
            this._plugins = plugins; // key value pair of pluginName: pluginType - plugins are already loaded/downloaded
            this._pluginConfigs = {}; // keeps track of the current configuration for each plugins by name

            var pluginNames = Object.keys(this._plugins);
            for (var i = 0; i < pluginNames.length; i += 1) {
                var p = new this._plugins[pluginNames[i]]();
                this._pluginConfigs[pluginNames[i]] = p.getDefaultConfig();
            }
        };

        PluginManagerBase.prototype.initialize = function (managerConfiguration, configCallback, callbackContext) {
            var self = this,
                plugins = this._plugins;

            //#1: PluginManagerBase should load the plugins

            //#2: PluginManagerBase iterates through each plugin and collects the config data
            var pluginConfigs = {};

            for (var p in plugins) {
                if (plugins.hasOwnProperty(p)) {
                    var plugin = new plugins[p]();
                    pluginConfigs[p] = plugin.getConfigStructure();
                }
            }

            if (configCallback) {
                configCallback.call(callbackContext, pluginConfigs, function (updatedPluginConfig) {
                    for (var p in updatedPluginConfig) {
                        if (updatedPluginConfig.hasOwnProperty(p)) {
                            //save it back to the plugin
                            self._pluginConfigs[p] = updatedPluginConfig[p];
                        }
                    }
                });
            }
        };

        /**
         * Gets a new instance of a plugin by name.
         *
         * @param {string} name
         * @returns {plugin.PluginBase}
         */
        PluginManagerBase.prototype.getPluginByName = function (name) {
            return this._plugins[name];
        };

        PluginManagerBase.prototype.loadMetaNodes = function (pluginContext, callback) {
            var self = this;

            this.logger.debug('Loading meta nodes');

            // get meta members
            var metaIDs = pluginContext.core.getMemberPaths(pluginContext.rootNode, 'MetaAspectSet');

            var len = metaIDs.length;

            var nodeObjs = [];

            var allObjectsLoadedHandler = function () {
                var len2 = nodeObjs.length;

                var nameObjMap = {};

                while (len2--) {
                    var nodeObj = nodeObjs[len2];

                    nameObjMap[pluginContext.core.getAttribute(nodeObj, 'name')] = nodeObj;
                }

                pluginContext.META = nameObjMap;

                self.logger.debug('Meta nodes are loaded');

                callback(null, pluginContext);
            };

            var loadedMetaObjectHandler = function (err, nodeObj) {
                nodeObjs.push(nodeObj);

                if (nodeObjs.length === metaIDs.length) {
                    allObjectsLoadedHandler();
                }
            };

            while (len--) {
                pluginContext.core.loadByPath(pluginContext.rootNode, metaIDs[len], loadedMetaObjectHandler);
            }
        };

        /**
         *
         * @param {plugin.PluginManagerConfiguration} managerConfiguration
         * @param {function} callback
         */
        PluginManagerBase.prototype.getPluginContext = function (managerConfiguration, callback) {

            // TODO: check if callback is a function

            var self = this;

            var pluginContext = new PluginContext();

            // based on the string values get the node objects
            // 1) Open project
            // 2) Load branch OR commit hash
            // 3) Load rootNode
            // 4) Load active object
            // 5) Load active selection
            // 6) Update context
            // 7) return

            pluginContext.project = this._storage;
            pluginContext.projectName = managerConfiguration.project;
            pluginContext.core = new self._Core(pluginContext.project, {corerel: 2});
            pluginContext.commitHash = managerConfiguration.commit;
            pluginContext.activeNode = null;    // active object
            pluginContext.activeSelection = []; // selected objects

            // add activeSelection
            var loadActiveSelectionAndMetaNodes = function () {
                  if (managerConfiguration.activeSelection.length === 0) {
                      self.loadMetaNodes(pluginContext, callback);
                  } else {
                      var remaining = managerConfiguration.activeSelection.length;

                      for (var i = 0; i < managerConfiguration.activeSelection.length; i += 1) {
                          (function(activeNodePath){
                              pluginContext.core.loadByPath(pluginContext.rootNode, activeNodePath, function (err, activeNode) {
                                  remaining -= 1;

                                  if (err) {
                                      self.logger.error('unable to load active selection: ' + activeNodePath);
                                      return;
                                  }

                                  pluginContext.activeSelection.push(activeNode);

                                  if (remaining === 0) {
                                      // all nodes from active selection are loaded
                                      self.loadMetaNodes(pluginContext, callback);
                                  }
                              });
                          })(managerConfiguration.activeSelection[i]);
                      }
                  }
            };

            // add activeNode
            var loadCommitHashAndRun = function (commitHash) {
                self.logger.info('Loading commit ' + commitHash);
                pluginContext.project.loadObject(commitHash, function (err, commitObj) {
                    if (err) {
                        callback(err, pluginContext);
                        return;
                    }

                    if (typeof commitObj === 'undefined' || commitObj === null) {
                        callback('cannot find commit', pluginContext);
                        return;
                    }

                    pluginContext.core.loadRoot(commitObj.root, function (err, rootNode) {
                        if (err) {
                            callback("unable to load root", pluginContext);
                            return;
                        }

                        pluginContext.rootNode = rootNode;
                        if (typeof managerConfiguration.activeNode === 'string') {
                            pluginContext.core.loadByPath(pluginContext.rootNode, managerConfiguration.activeNode, function (err, activeNode) {
                                if (err) {
                                    callback("unable to load selected object", pluginContext);
                                    return;
                                }

                                pluginContext.activeNode = activeNode;
                                loadActiveSelectionAndMetaNodes();
                            });
                        } else {
                            pluginContext.activeNode = null;
                            loadActiveSelectionAndMetaNodes();
                        }
                    });
               });
            };

            // load commit hash and run based on branch name or commit hash
            if (managerConfiguration.branchName) {
                pluginContext.project.getBranchNames(function (err, branchNames) {
                    self.logger.debug(branchNames);

                    if (branchNames.hasOwnProperty(managerConfiguration.branchName)) {
                        pluginContext.commitHash = branchNames[managerConfiguration.branchName];
                        pluginContext.branchName = managerConfiguration.branchName;
                        loadCommitHashAndRun(pluginContext.commitHash);
                    } else {
                        callback('cannot find branch', pluginContext);
                    }
                });
            } else {
                loadCommitHashAndRun(pluginContext.commitHash);
            }

        };

        PluginManagerBase.prototype.executePlugin = function (name, managerConfiguration, callback) {
            // TODO: check if name is a string
            // TODO: check if managerConfiguration is an instance of PluginManagerConfiguration
            // TODO: check if callback is a function

            var PluginClass = this.getPluginByName(name);

            var plugin = new PluginClass();

            var pluginLogger = LogManager.create('Plugin.' + name);

            managerConfiguration.FS.createArtifact(name + '-output');
            // TODO: write some information about this execution into the output directory

            plugin.initialize(pluginLogger, managerConfiguration.FS);

            plugin.setCurrentConfig(this._pluginConfigs[name]);

            this.getPluginContext(managerConfiguration, function (err, pluginContext) {
                if (err) {
                    // TODO: this has to return with an empty PluginResult object and NOT with null.
                    callback(err, null);
                    return;

                }

                //set logging level at least to INFO level since the plugins write messages with INFO level onto the console
                var logLevel = LogManager.getLogLevel();
                if (logLevel < LogManager.logLevels.INFO) {
                    // elevate log level if it is less then info
                    LogManager.setLogLevel(LogManager.logLevels.INFO);
                }

                // TODO: Would be nice to log to file and to console at the same time.
                //LogManager.setFileLogPath('PluginManager.log');

                plugin.configure(pluginContext);

                var startTime = (new Date()).toISOString();

                plugin.main(function (err, result) {
                    //set logging level back to previous value
                    LogManager.setLogLevel(logLevel);

                    // set common information (meta info) about the plugin and measured execution times
                    result.setFinishTime((new Date()).toISOString());
                    result.setStartTime(startTime);

                    result.setPluginName(plugin.getName());
                    result.setError(err);

                    callback(err, result);
                });

            });
        };

        return PluginManagerBase;
    });<|endoftext|>


<commit_before>/**
 * Created by GUY on 2015/9/7.
 * @class BI.DatePicker
 * @extends BI.Widget
 */
BI.DatePicker = BI.inherit(BI.Widget, {
    _defaultConfig: function () {
        var conf = BI.DatePicker.superclass._defaultConfig.apply(this, arguments);

        return BI.extend(conf, {
            baseCls: "bi-date-picker",
            height: 40,
            min: "1900-01-01", // 最小日期
            max: "2099-12-31", // 最大日期
        });
    },

    _init: function () {
        BI.DatePicker.superclass._init.apply(this, arguments);
        var self = this; var o = this.options;
        this._year = BI.getDate().getFullYear();
        this._month = BI.getDate().getMonth() + 1;
        this.left = BI.createWidget({
            type: "bi.icon_button",
            cls: "pre-page-h-font",
            width: 24,
            height: 24,
        });
        this.left.on(BI.IconButton.EVENT_CHANGE, function () {
            if (self._month === 1) {
                self.setValue({
                    year: self.year.getValue() - 1,
                    month: 12,
                });
            } else {
                self.setValue({
                    year: self.year.getValue(),
                    month: (self.month.getValue() - 1) || BI.getDate().getMonth(),
                });
            }
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
            self._checkLeftValid();
            self._checkRightValid();
        });

        this.right = BI.createWidget({
            type: "bi.icon_button",
            cls: "next-page-h-font",
            width: 24,
            height: 24,
        });

        this.right.on(BI.IconButton.EVENT_CHANGE, function () {
            if (self._month === 12) {
                self.setValue({
                    year: self.year.getValue() + 1,
                    month: 1,
                });
            } else {
                self.setValue({
                    year: self.year.getValue(),
                    month: (self.month.getValue() + 1) || (BI.getDate().getMonth() + 2),
                });
            }
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
            self._checkLeftValid();
            self._checkRightValid();
        });

        this.year = BI.createWidget({
            type: "bi.year_date_combo",
            behaviors: o.behaviors,
            min: o.min,
            max: o.max,
        });
        this.year.on(BI.YearDateCombo.EVENT_CHANGE, function () {
            self.setValue({
                year: self.year.getValue(),
                month: self._refreshMonth()
            });
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
        });
        this.month = BI.createWidget({
            type: "bi.month_date_combo",
            behaviors: o.behaviors,
            allowMonths: this._getAllowMonths()
        });
        this.month.on(BI.MonthDateCombo.EVENT_CHANGE, function () {
            self.setValue({
                year: self.year.getValue() || self._year,
                month: self.month.getValue(),
            });
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
        });

        BI.createWidget({
            type: "bi.htape",
            element: this,
            items: [{
                el: {
                    type: "bi.center_adapt",
                    items: [this.left],
                },
                width: 24,
            }, {
                type: "bi.center_adapt",
                items: [{
                    el: {
                        type: "bi.horizontal",
                        width: 120,
                        rgap: 10,
                        items: [{
                            el: this.year,
                            lgap: 10,
                        }, this.month],
                    },
                }],
            }, {
                el: {
                    type: "bi.center_adapt",
                    items: [this.right],
                },
                width: 24,
            }],
        });
        this.setValue({
            year: this._year,
            month: this._month,
        });
    },

    _refreshMonth: function () {
        var month = this.month.getValue();
        this.month.populate(this._getAllowMonths());
        var allowMonth = this._getAllowMonths();
        if (!BI.contains(allowMonth, month)) {
            month = allowMonth[0];
        }

        return month;
    },

    _getAllowMonths: function () {
        var obj = this._getCheckMinMaxDate();
        var year = this.year.getValue() || this._year;

        return BI.filter(BI.range(1, 13), function (idx, v) {
            return !BI.checkDateVoid(year, v, 1, obj.min, obj.max)[0];
        });
    },

    // 上一年月不合法则灰化
    _checkLeftValid: function () {
        var obj = this._getCheckMinMaxDate();
        var year = this._month === 1 ? this._year - 1 : this._year;
        var month = this._month === 1 ? 12 : this.month - 1;
        var valid = BI.isNull(BI.checkDateVoid(year, month, 1, obj.min, obj.max)[0]);
        this.left.setEnable(valid);

        return valid;
    },

    // 下一年月不合法则灰化
    _checkRightValid: function () {
        var obj = this._getCheckMinMaxDate();
        var year = this._month === 12 ? this._year + 1 : this._year;
        var month = this._month === 12 ? 1 : this.month + 1;
        var valid = BI.isNull(BI.checkDateVoid(year, month, 1, obj.min, obj.max)[0]);
        this.right.setEnable(valid);

        return valid;
    },

    _getCheckMinMaxDate: function() {
        var o = this.options;
        var minDate = BI.parseDateTime(o.min, "%Y-%X-%d");
        var maxDate = BI.parseDateTime(o.max, "%Y-%X-%d");
        minDate.setDate(1);
        maxDate.setDate(1);

        return {
            min: BI.print(minDate, "%Y-%X-%d"),
            max: BI.print(maxDate, "%Y-%X-%d")
        };
    },

    setMinDate: function (minDate) {
        this.options.min = minDate;
        this.year.setMinDate(minDate);
        this._refreshMonth();
        this._checkLeftValid();
        this._checkRightValid();
    },

    setMaxDate: function (maxDate) {
        this.options.max = maxDate;
        this.year.setMaxDate(maxDate);
        this._refreshMonth();
        this._checkLeftValid();
        this._checkRightValid();
    },

    setValue: function (ob) {
        this._year = BI.parseInt(ob.year);
        this._month = BI.parseInt(ob.month);
        this.year.setValue(ob.year);
        this._refreshMonth();
        this.month.setValue(ob.month);
        this._checkLeftValid();
        this._checkRightValid();
    },

    getValue: function () {
        return {
            year: this.year.getValue(),
            month: this.month.getValue(),
        };
    },
});
BI.DatePicker.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.date_picker", BI.DatePicker);
<commit_msg>Pull request #1419: 无JIRA任务 更新变量<commit_after>/**
 * Created by GUY on 2015/9/7.
 * @class BI.DatePicker
 * @extends BI.Widget
 */
BI.DatePicker = BI.inherit(BI.Widget, {
    _defaultConfig: function () {
        var conf = BI.DatePicker.superclass._defaultConfig.apply(this, arguments);

        return BI.extend(conf, {
            baseCls: "bi-date-picker",
            height: 40,
            min: "1900-01-01", // 最小日期
            max: "2099-12-31", // 最大日期
        });
    },

    _init: function () {
        BI.DatePicker.superclass._init.apply(this, arguments);
        var self = this; var o = this.options;
        this._year = BI.getDate().getFullYear();
        this._month = BI.getDate().getMonth() + 1;
        this.left = BI.createWidget({
            type: "bi.icon_button",
            cls: "pre-page-h-font",
            width: 24,
            height: 24,
        });
        this.left.on(BI.IconButton.EVENT_CHANGE, function () {
            if (self._month === 1) {
                self.setValue({
                    year: self.year.getValue() - 1,
                    month: 12,
                });
            } else {
                self.setValue({
                    year: self.year.getValue(),
                    month: (self.month.getValue() - 1) || BI.getDate().getMonth(),
                });
            }
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
            self._checkLeftValid();
            self._checkRightValid();
        });

        this.right = BI.createWidget({
            type: "bi.icon_button",
            cls: "next-page-h-font",
            width: 24,
            height: 24,
        });

        this.right.on(BI.IconButton.EVENT_CHANGE, function () {
            if (self._month === 12) {
                self.setValue({
                    year: self.year.getValue() + 1,
                    month: 1,
                });
            } else {
                self.setValue({
                    year: self.year.getValue(),
                    month: (self.month.getValue() + 1) || (BI.getDate().getMonth() + 2),
                });
            }
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
            self._checkLeftValid();
            self._checkRightValid();
        });

        this.year = BI.createWidget({
            type: "bi.year_date_combo",
            behaviors: o.behaviors,
            min: o.min,
            max: o.max,
        });
        this.year.on(BI.YearDateCombo.EVENT_CHANGE, function () {
            self.setValue({
                year: self.year.getValue(),
                month: self._refreshMonth()
            });
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
        });
        this.month = BI.createWidget({
            type: "bi.month_date_combo",
            behaviors: o.behaviors,
            allowMonths: this._getAllowMonths()
        });
        this.month.on(BI.MonthDateCombo.EVENT_CHANGE, function () {
            self.setValue({
                year: self.year.getValue() || self._year,
                month: self.month.getValue(),
            });
            self.fireEvent(BI.DatePicker.EVENT_CHANGE);
        });

        BI.createWidget({
            type: "bi.htape",
            element: this,
            items: [{
                el: {
                    type: "bi.center_adapt",
                    items: [this.left],
                },
                width: 24,
            }, {
                type: "bi.center_adapt",
                items: [{
                    el: {
                        type: "bi.horizontal",
                        width: 120,
                        rgap: 10,
                        items: [{
                            el: this.year,
                            lgap: 10,
                        }, this.month],
                    },
                }],
            }, {
                el: {
                    type: "bi.center_adapt",
                    items: [this.right],
                },
                width: 24,
            }],
        });
        this.setValue({
            year: this._year,
            month: this._month,
        });
    },

    _refreshMonth: function () {
        var month = this.month.getValue();
        this.month.populate(this._getAllowMonths());
        var allowMonth = this._getAllowMonths();
        if (!BI.contains(allowMonth, month)) {
            month = allowMonth[0];
        }

        return month;
    },

    _getAllowMonths: function () {
        var obj = this._getCheckMinMaxDate();
        var year = this.year.getValue() || this._year;

        return BI.filter(BI.range(1, 13), function (idx, v) {
            return !BI.checkDateVoid(year, v, 1, obj.min, obj.max)[0];
        });
    },

    // 上一年月不合法则灰化
    _checkLeftValid: function () {
        var obj = this._getCheckMinMaxDate();
        var year = this._month === 1 ? this._year - 1 : this._year;
        var month = this._month === 1 ? 12 : this._month - 1;
        var valid = BI.isNull(BI.checkDateVoid(year, month, 1, obj.min, obj.max)[0]);
        this.left.setEnable(valid);

        return valid;
    },

    // 下一年月不合法则灰化
    _checkRightValid: function () {
        var obj = this._getCheckMinMaxDate();
        var year = this._month === 12 ? this._year + 1 : this._year;
        var month = this._month === 12 ? 1 : this._month + 1;
        var valid = BI.isNull(BI.checkDateVoid(year, month, 1, obj.min, obj.max)[0]);
        this.right.setEnable(valid);

        return valid;
    },

    _getCheckMinMaxDate: function() {
        var o = this.options;
        var minDate = BI.parseDateTime(o.min, "%Y-%X-%d");
        var maxDate = BI.parseDateTime(o.max, "%Y-%X-%d");
        minDate.setDate(1);
        maxDate.setDate(1);

        return {
            min: BI.print(minDate, "%Y-%X-%d"),
            max: BI.print(maxDate, "%Y-%X-%d")
        };
    },

    setMinDate: function (minDate) {
        this.options.min = minDate;
        this.year.setMinDate(minDate);
        this._refreshMonth();
        this._checkLeftValid();
        this._checkRightValid();
    },

    setMaxDate: function (maxDate) {
        this.options.max = maxDate;
        this.year.setMaxDate(maxDate);
        this._refreshMonth();
        this._checkLeftValid();
        this._checkRightValid();
    },

    setValue: function (ob) {
        this._year = BI.parseInt(ob.year);
        this._month = BI.parseInt(ob.month);
        this.year.setValue(ob.year);
        this._refreshMonth();
        this.month.setValue(ob.month);
        this._checkLeftValid();
        this._checkRightValid();
    },

    getValue: function () {
        return {
            year: this.year.getValue(),
            month: this.month.getValue(),
        };
    },
});
BI.DatePicker.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.date_picker", BI.DatePicker);
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2013, Olivier BIOT
 * http://www.melonjs.org
 *
 * a simple debug panel plugin
 * usage : me.plugin.register(debugPanel, "debug");
 *
 * you can then use me.plugin.debug.show() or me.plugin.debug.hide()
 * to show or hide the panel, or press respectively the "S" and "H" keys.
 *
 * note :
 * Heap Memory information is available under Chrome when using
 * the "--enable-memory-info" parameter to launch Chrome
 */

(function($) {

    // ensure that me.debug is defined
    me.debug = me.debug || {};

    /**
     * @class
     * @public
     * @extends me.plugin.Base
     * @memberOf me
     * @constructor
     */
    debugPanel = me.plugin.Base.extend(
    /** @scope me.debug.Panel.prototype */
    {

        /** @private */
        init : function(showKey, hideKey) {
            // call the super constructor
            this._super(me.plugin.Base, 'init');

            // minimum melonJS version expected
            this.version = "1.1.0";

            // to hold the debug options
            // clickable rect area
            this.area = {};

            // panel position and size
            this.rect = null;

            // for z ordering
            // make it ridiculously high
            this.z = Infinity;

            // visibility flag
            this.visible = false;

            // frame update time in ms
            this.frameUpdateTime = 0;

            // frame draw time in ms
            this.frameDrawTime = 0;

            this.rect = new me.Rect(new me.Vector2d(0, 0), me.video.getWidth(), 35);

            // set the object GUID value
            this.GUID = "debug-" + me.utils.createGUID();

            // set the object entity name
            this.name = "me.debugPanel";

            // persistent
            this.isPersistent = true;

            // a floating object
            this.floating = true;

            // renderable
            this.isRenderable = true;

            // always update, even when not visible
            this.alwaysUpdate = true;

            // create a default font, with fixed char width
            var s = 10;
            this.mod = 1;
            if(me.game.viewport.width < 500) {
                s = 7;
                this.mod = 0.7;
            }
            this.font = new me.Font('courier', s, 'white');

            // clickable areas
            this.area.renderHitBox = new me.Rect(new me.Vector2d(160,5),15,15);
            this.area.renderVelocity = new me.Rect(new me.Vector2d(165,18),15,15);

            this.area.renderDirty = new me.Rect(new me.Vector2d(270,5),15,15);
            this.area.renderCollisionMap = new me.Rect(new me.Vector2d(270,18),15,15);

            // some internal string/length
            this.help_str      = "(s)how/(h)ide";
            this.help_str_len = this.font.measureText(me.video.getSystemContext(), this.help_str).width;
            this.fps_str_len = this.font.measureText(me.video.getSystemContext(), "00/00 fps").width;
            this.memoryPositionX = this.font.measureText(me.video.getSystemContext(), "Draw   : ").width * 3 + 310;

            // enable the FPS counter
            me.debug.displayFPS = true;

            // bind the "S" and "H" keys
            me.input.bindKey(showKey || me.input.KEY.S, "show", false, false);
            me.input.bindKey(hideKey || me.input.KEY.H, "hide", false, false);

            // add some keyboard shortcuts
            var self = this;
            this.keyHandler = me.event.subscribe(me.event.KEYDOWN, function (action, keyCode, edge) {
                if (action === "show") {
                    self.show();
                } else if (action === "hide") {
                    self.hide();
                }
            });

            // re-apply panel settings on level changes
            this.levelHandler = me.event.subscribe(me.event.LEVEL_LOADED, function () {
                var layer = me.game.currentLevel.getLayerByName("collision");
                if (layer) {
                    layer.setOpacity((me.debug.renderCollisionMap===true)?1:0);
                }
            });

            // memory heap sample points
            this.samples = [];

            //patch patch patch !
            this.patchSystemFn();

            // make it visible
            this.show();
        },

        /**
         * patch system fn to draw debug information
         */
        patchSystemFn : function() {

            // add a few new debug flag (if not yet defined)
            me.debug.renderHitBox = me.debug.renderHitBox || false;
            me.debug.renderVelocity = me.debug.renderVelocity || false;
            me.debug.renderCollisionMap = me.debug.renderCollisionMap || false;
            var _this = this;
            // patch timer.js
            me.plugin.patch(me.timer, "update", function (time) {
                // call the original me.timer.update function
                this.parent(time);

                // call the FPS counter
                me.timer.countFPS();
            });

            // patch me.game.update
            me.plugin.patch(me.game, 'update', function(time) {
                var frameUpdateStartTime = window.performance.now();

                this.parent(time);

                // calculate the update time
                _this.frameUpdateTime = window.performance.now() - frameUpdateStartTime;
            });

            // patch me.game.draw
            me.plugin.patch(me.game, 'draw', function() {
                var frameDrawStartTime = window.performance.now();

                this.parent();

                // calculate the drawing time
                _this.frameDrawTime = window.performance.now() - frameDrawStartTime;
            });

            // patch sprite.js
            me.plugin.patch(me.SpriteObject, "draw", function (context) {
                // call the original me.SpriteObject function
                this.parent(context);

                // draw the sprite rectangle
                if (me.debug.renderHitBox) {
                    context.strokeStyle =  "green";
                    context.strokeRect(this.left, this.top, this.width, this.height);
                }
            });

            // patch entities.js
            me.plugin.patch(me.ObjectEntity, "draw", function (context) {
                // call the original me.game.draw function
                this.parent(context);

                // check if debug mode is enabled
                if (me.debug.renderHitBox && this.shapes.length) {

                    // translate to the object position
                    var translateX = this.pos.x ;
                    var translateY = this.pos.y ;

                    context.translate(translateX, translateY);

                    // draw the original shape
                    this.getShape().draw(context, "red");
                     if (this.getShape().shapeType!=="Rectangle") {
                         // draw the corresponding bounding box
                        this.getShape().getBounds().draw(context, "red");
                    }

                    context.translate(-translateX, -translateY);

                }

                if (me.debug.renderVelocity) {
                    // draw entity current velocity
                    var x = ~~(this.pos.x + this.hWidth);
                    var y = ~~(this.pos.y + this.hHeight);

                    context.strokeStyle = "blue";
                    context.lineWidth = 1;
                    context.beginPath();
                    context.moveTo(x, y);
                    context.lineTo(
                        x + ~~(this.vel.x * this.hWidth),
                        y + ~~(this.vel.y * this.hHeight)
                    );
                    context.stroke();
                }
            });
        },

        /**
         * show the debug panel
         */
        show : function() {
            if (!this.visible) {
                // register a mouse event for the checkboxes
                me.input.registerPointerEvent('pointerdown', this.rect, this.onClick.bind(this), true);
                // add the debug panel to the game world
                me.game.world.addChild(this, Infinity);
                // mark it as visible
                this.visible = true;
            }
        },

        /**
         * hide the debug panel
         */
        hide : function() {
            if (this.visible) {
                // release the mouse event for the checkboxes
                me.input.releasePointerEvent('pointerdown', this.rect);
                // remove the debug panel from the game world
                me.game.world.removeChild(this);
                // mark it as invisible
                this.visible = false;
            }
        },

        /** @private */
        update : function() {
            if (me.input.isKeyPressed('show')) {
                this.show();
            }
            else if (me.input.isKeyPressed('hide')) {
                this.hide();
            }
            return true;
        },

        /**
         * @private
         */
        getBounds : function() {
            return this.rect;
        },

        /** @private */
        onClick : function(e)  {
            // check the clickable areas
            if (this.area.renderHitBox.containsPoint(e.gameX, e.gameY)) {
                me.debug.renderHitBox = !me.debug.renderHitBox;
            }
            else if (this.area.renderCollisionMap.containsPoint(e.gameX, e.gameY)) {
                var layer = me.game.currentLevel.getLayerByName("collision");
                if (layer) {
                    if (layer.getOpacity() === 0) {
                        layer.setOpacity(1);
                        me.debug.renderCollisionMap = true;
                    } else {
                        layer.setOpacity(0);
                        me.debug.renderCollisionMap = false;
                    }
                }
            } else if (this.area.renderVelocity.containsPoint(e.gameX, e.gameY)) {
                // does nothing for now, since velocity is
                // rendered together with hitboxes (is a global debug flag required?)
                me.debug.renderVelocity = !me.debug.renderVelocity;
            }
            // force repaint
            me.game.repaint();
        },

        /** @private */
        drawMemoryGraph : function (context, endX) {
            if (window.performance && window.performance.memory) {
                var usedHeap  = Number.prototype.round(window.performance.memory.usedJSHeapSize/1048576, 2);
                var totalHeap =  Number.prototype.round(window.performance.memory.totalJSHeapSize/1048576, 2);
                var len = endX - this.memoryPositionX;

                // remove the first item
                this.samples.shift();
                // add a new sample (25 is the height of the graph)
                this.samples[len] = (usedHeap / totalHeap)  * 25;

                // draw the graph
                for (var x = len;x--;) {
                    var where = endX - (len - x);
                    context.beginPath();
                    context.strokeStyle = "lightblue";
                    context.moveTo(where, 30);
                    context.lineTo(where, 30 - (this.samples[x] || 0));
                    context.stroke();
                }
                // display the current value
                this.font.draw(context, "Heap : " + usedHeap + '/' + totalHeap + ' MB', this.memoryPositionX * this.mod, 5 * this.mod);
            } else {
                // Heap Memory information not available
                this.font.draw(context, "Heap : ??/?? MB", this.memoryPositionX * this.mod, 5 * this.mod);
            }
        },

        /** @private */
        draw : function(context) {
            context.save();

            // draw the panel
            context.globalAlpha = 0.5;
            context.fillStyle = "black";
            context.fillRect(this.rect.left,  this.rect.top,
                             this.rect.width, this.rect.height);
            context.globalAlpha = 1.0;

            // # entities / draw
            this.font.draw(context, "#objects : " + me.game.world.children.length, 5 * this.mod, 5 * this.mod);
            this.font.draw(context, "#draws   : " + me.game.world.drawCount, 5 * this.mod, 18 * this.mod);

            // debug checkboxes
            this.font.draw(context, "?hitbox   ["+ (me.debug.renderHitBox?"x":" ") +"]",     100 * this.mod, 5 * this.mod);
            this.font.draw(context, "?velocity ["+ (me.debug.renderVelocity?"x":" ") +"]",     100 * this.mod, 18 * this.mod);

            this.font.draw(context, "?dirtyRect  [ ]",    200 * this.mod, 5 * this.mod);
            this.font.draw(context, "?col. layer ["+ (me.debug.renderCollisionMap?"x":" ") +"]", 200 * this.mod, 18 * this.mod);

            // draw the update duration
            this.font.draw(context, "Update : " + this.frameUpdateTime.toFixed(2) + " ms", 310 * this.mod, 5 * this.mod);
            // draw the draw duration
            this.font.draw(context, "Draw   : " + (this.frameDrawTime).toFixed(2) + " ms", 310 * this.mod, 18 * this.mod);

            // draw the memory heap usage
            var endX = this.rect.width - 25;
            this.drawMemoryGraph(context, endX - this.help_str_len);

            // some help string
            this.font.draw(context, this.help_str, endX - this.help_str_len, 18 * this.mod);

            //fps counter
            var fps_str = "" + me.timer.fps + "/"    + me.sys.fps + " fps";
            this.font.draw(context, fps_str, this.rect.width - this.fps_str_len - 5, 5 * this.mod);

            context.restore();

        },

        /** @private */
        onDestroyEvent : function() {
            // hide the panel
            this.hide();
            // unbind keys event
            me.input.unbindKey(me.input.KEY.S);
            me.input.unbindKey(me.input.KEY.H);
            me.event.unsubscribe(this.keyHandler);
            me.event.unsubscribe(this.levelHandler);
        }

    });

    /*---------------------------------------------------------*/
    // END END END
    /*---------------------------------------------------------*/
})(window);
<commit_msg>switched the loop to have an escape<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2013, Olivier BIOT
 * http://www.melonjs.org
 *
 * a simple debug panel plugin
 * usage : me.plugin.register(debugPanel, "debug");
 *
 * you can then use me.plugin.debug.show() or me.plugin.debug.hide()
 * to show or hide the panel, or press respectively the "S" and "H" keys.
 *
 * note :
 * Heap Memory information is available under Chrome when using
 * the "--enable-memory-info" parameter to launch Chrome
 */

(function($) {

    // ensure that me.debug is defined
    me.debug = me.debug || {};

    /**
     * @class
     * @public
     * @extends me.plugin.Base
     * @memberOf me
     * @constructor
     */
    debugPanel = me.plugin.Base.extend(
    /** @scope me.debug.Panel.prototype */
    {

        /** @private */
        init : function(showKey, hideKey) {
            // call the super constructor
            this._super(me.plugin.Base, 'init');

            // minimum melonJS version expected
            this.version = "1.1.0";

            // to hold the debug options
            // clickable rect area
            this.area = {};

            // panel position and size
            this.rect = null;

            // for z ordering
            // make it ridiculously high
            this.z = Infinity;

            // visibility flag
            this.visible = false;

            // frame update time in ms
            this.frameUpdateTime = 0;

            // frame draw time in ms
            this.frameDrawTime = 0;

            this.rect = new me.Rect(new me.Vector2d(0, 0), me.video.getWidth(), 35);

            // set the object GUID value
            this.GUID = "debug-" + me.utils.createGUID();

            // set the object entity name
            this.name = "me.debugPanel";

            // persistent
            this.isPersistent = true;

            // a floating object
            this.floating = true;

            // renderable
            this.isRenderable = true;

            // always update, even when not visible
            this.alwaysUpdate = true;

            // create a default font, with fixed char width
            var s = 10;
            this.mod = 1;
            if(me.game.viewport.width < 500) {
                s = 7;
                this.mod = 0.7;
            }
            this.font = new me.Font('courier', s, 'white');

            // clickable areas
            this.area.renderHitBox = new me.Rect(new me.Vector2d(160,5),15,15);
            this.area.renderVelocity = new me.Rect(new me.Vector2d(165,18),15,15);

            this.area.renderDirty = new me.Rect(new me.Vector2d(270,5),15,15);
            this.area.renderCollisionMap = new me.Rect(new me.Vector2d(270,18),15,15);

            // some internal string/length
            this.help_str      = "(s)how/(h)ide";
            this.help_str_len = this.font.measureText(me.video.getSystemContext(), this.help_str).width;
            this.fps_str_len = this.font.measureText(me.video.getSystemContext(), "00/00 fps").width;
            this.memoryPositionX = this.font.measureText(me.video.getSystemContext(), "Draw   : ").width * 2.2 + 310 * this.mod;

            // enable the FPS counter
            me.debug.displayFPS = true;

            // bind the "S" and "H" keys
            me.input.bindKey(showKey || me.input.KEY.S, "show", false, false);
            me.input.bindKey(hideKey || me.input.KEY.H, "hide", false, false);

            // add some keyboard shortcuts
            var self = this;
            this.keyHandler = me.event.subscribe(me.event.KEYDOWN, function (action, keyCode, edge) {
                if (action === "show") {
                    self.show();
                } else if (action === "hide") {
                    self.hide();
                }
            });

            // re-apply panel settings on level changes
            this.levelHandler = me.event.subscribe(me.event.LEVEL_LOADED, function () {
                var layer = me.game.currentLevel.getLayerByName("collision");
                if (layer) {
                    layer.setOpacity((me.debug.renderCollisionMap===true)?1:0);
                }
            });

            // memory heap sample points
            this.samples = [];

            //patch patch patch !
            this.patchSystemFn();

            // make it visible
            this.show();
        },

        /**
         * patch system fn to draw debug information
         */
        patchSystemFn : function() {

            // add a few new debug flag (if not yet defined)
            me.debug.renderHitBox = me.debug.renderHitBox || false;
            me.debug.renderVelocity = me.debug.renderVelocity || false;
            me.debug.renderCollisionMap = me.debug.renderCollisionMap || false;
            var _this = this;
            // patch timer.js
            me.plugin.patch(me.timer, "update", function (time) {
                // call the original me.timer.update function
                this.parent(time);

                // call the FPS counter
                me.timer.countFPS();
            });

            // patch me.game.update
            me.plugin.patch(me.game, 'update', function(time) {
                var frameUpdateStartTime = window.performance.now();

                this.parent(time);

                // calculate the update time
                _this.frameUpdateTime = window.performance.now() - frameUpdateStartTime;
            });

            // patch me.game.draw
            me.plugin.patch(me.game, 'draw', function() {
                var frameDrawStartTime = window.performance.now();

                this.parent();

                // calculate the drawing time
                _this.frameDrawTime = window.performance.now() - frameDrawStartTime;
            });

            // patch sprite.js
            me.plugin.patch(me.SpriteObject, "draw", function (context) {
                // call the original me.SpriteObject function
                this.parent(context);

                // draw the sprite rectangle
                if (me.debug.renderHitBox) {
                    context.strokeStyle =  "green";
                    context.strokeRect(this.left, this.top, this.width, this.height);
                }
            });

            // patch entities.js
            me.plugin.patch(me.ObjectEntity, "draw", function (context) {
                // call the original me.game.draw function
                this.parent(context);

                // check if debug mode is enabled
                if (me.debug.renderHitBox && this.shapes.length) {

                    // translate to the object position
                    var translateX = this.pos.x ;
                    var translateY = this.pos.y ;

                    context.translate(translateX, translateY);

                    // draw the original shape
                    this.getShape().draw(context, "red");
                     if (this.getShape().shapeType!=="Rectangle") {
                         // draw the corresponding bounding box
                        this.getShape().getBounds().draw(context, "red");
                    }

                    context.translate(-translateX, -translateY);

                }

                if (me.debug.renderVelocity) {
                    // draw entity current velocity
                    var x = ~~(this.pos.x + this.hWidth);
                    var y = ~~(this.pos.y + this.hHeight);

                    context.strokeStyle = "blue";
                    context.lineWidth = 1;
                    context.beginPath();
                    context.moveTo(x, y);
                    context.lineTo(
                        x + ~~(this.vel.x * this.hWidth),
                        y + ~~(this.vel.y * this.hHeight)
                    );
                    context.stroke();
                }
            });
        },

        /**
         * show the debug panel
         */
        show : function() {
            if (!this.visible) {
                // register a mouse event for the checkboxes
                me.input.registerPointerEvent('pointerdown', this.rect, this.onClick.bind(this), true);
                // add the debug panel to the game world
                me.game.world.addChild(this, Infinity);
                // mark it as visible
                this.visible = true;
            }
        },

        /**
         * hide the debug panel
         */
        hide : function() {
            if (this.visible) {
                // release the mouse event for the checkboxes
                me.input.releasePointerEvent('pointerdown', this.rect);
                // remove the debug panel from the game world
                me.game.world.removeChild(this);
                // mark it as invisible
                this.visible = false;
            }
        },

        /** @private */
        update : function() {
            if (me.input.isKeyPressed('show')) {
                this.show();
            }
            else if (me.input.isKeyPressed('hide')) {
                this.hide();
            }
            return true;
        },

        /**
         * @private
         */
        getBounds : function() {
            return this.rect;
        },

        /** @private */
        onClick : function(e)  {
            // check the clickable areas
            if (this.area.renderHitBox.containsPoint(e.gameX, e.gameY)) {
                me.debug.renderHitBox = !me.debug.renderHitBox;
            }
            else if (this.area.renderCollisionMap.containsPoint(e.gameX, e.gameY)) {
                var layer = me.game.currentLevel.getLayerByName("collision");
                if (layer) {
                    if (layer.getOpacity() === 0) {
                        layer.setOpacity(1);
                        me.debug.renderCollisionMap = true;
                    } else {
                        layer.setOpacity(0);
                        me.debug.renderCollisionMap = false;
                    }
                }
            } else if (this.area.renderVelocity.containsPoint(e.gameX, e.gameY)) {
                // does nothing for now, since velocity is
                // rendered together with hitboxes (is a global debug flag required?)
                me.debug.renderVelocity = !me.debug.renderVelocity;
            }
            // force repaint
            me.game.repaint();
        },

        /** @private */
        drawMemoryGraph : function (context, endX) {
            if (window.performance && window.performance.memory) {
                var usedHeap  = Number.prototype.round(window.performance.memory.usedJSHeapSize/1048576, 2);
                var totalHeap =  Number.prototype.round(window.performance.memory.totalJSHeapSize/1048576, 2);
                var len = endX - this.memoryPositionX;

                // remove the first item
                this.samples.shift();
                // add a new sample (25 is the height of the graph)
                this.samples[len] = (usedHeap / totalHeap)  * 25;

                // draw the graph
                for (var x = len; x >= 0; x--) {
                    var where = endX - (len - x);
                    context.beginPath();
                    context.strokeStyle = "lightblue";
                    context.moveTo(where, 30 * this.mod);
                    context.lineTo(where, (30 - (this.samples[x] || 0)) * this.mod);
                    context.stroke();
                }
                // display the current value
                this.font.draw(context, "Heap : " + usedHeap + '/' + totalHeap + ' MB', this.memoryPositionX, 5 * this.mod);
            } else {
                // Heap Memory information not available
                this.font.draw(context, "Heap : ??/?? MB", this.memoryPositionX, 5 * this.mod);
            }
        },

        /** @private */
        draw : function(context) {
            context.save();

            // draw the panel
            context.globalAlpha = 0.5;
            context.fillStyle = "black";
            context.fillRect(this.rect.left,  this.rect.top,
                             this.rect.width, this.rect.height);
            context.globalAlpha = 1.0;

            // # entities / draw
            this.font.draw(context, "#objects : " + me.game.world.children.length, 5 * this.mod, 5 * this.mod);
            this.font.draw(context, "#draws   : " + me.game.world.drawCount, 5 * this.mod, 18 * this.mod);

            // debug checkboxes
            this.font.draw(context, "?hitbox   ["+ (me.debug.renderHitBox?"x":" ") +"]",     100 * this.mod, 5 * this.mod);
            this.font.draw(context, "?velocity ["+ (me.debug.renderVelocity?"x":" ") +"]",     100 * this.mod, 18 * this.mod);

            this.font.draw(context, "?dirtyRect  [ ]",    200 * this.mod, 5 * this.mod);
            this.font.draw(context, "?col. layer ["+ (me.debug.renderCollisionMap?"x":" ") +"]", 200 * this.mod, 18 * this.mod);

            // draw the update duration
            this.font.draw(context, "Update : " + this.frameUpdateTime.toFixed(2) + " ms", 310 * this.mod, 5 * this.mod);
            // draw the draw duration
            this.font.draw(context, "Draw   : " + (this.frameDrawTime).toFixed(2) + " ms", 310 * this.mod, 18 * this.mod);

            // draw the memory heap usage
            var endX = this.rect.width - 25;
            this.drawMemoryGraph(context, endX - this.help_str_len);

            // some help string
            this.font.draw(context, this.help_str, endX - this.help_str_len, 18 * this.mod);

            //fps counter
            var fps_str = "" + me.timer.fps + "/"    + me.sys.fps + " fps";
            this.font.draw(context, fps_str, this.rect.width - this.fps_str_len - 5, 5 * this.mod);

            context.restore();

        },

        /** @private */
        onDestroyEvent : function() {
            // hide the panel
            this.hide();
            // unbind keys event
            me.input.unbindKey(me.input.KEY.S);
            me.input.unbindKey(me.input.KEY.H);
            me.event.unsubscribe(this.keyHandler);
            me.event.unsubscribe(this.levelHandler);
        }

    });

    /*---------------------------------------------------------*/
    // END END END
    /*---------------------------------------------------------*/
})(window);
<|endoftext|>