<commit_before>/*
 * grunt-partial-extract
 * https://github.com/tilmanjusten/grunt-partial-extract
 *
 * Copyright (c) 2015 Tilman Justen
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function (grunt) {

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    var util = require('util');
    var _ = require('lodash');
    var path = require('path');
    var options = {};

    grunt.registerMultiTask('partial-extract', 'Extract partials from any text based file and write to individual files.', function () {
        // Merge task-specific and/or target-specific options with these defaults.
        options = this.options({
            // Find partials by pattern:
            //
            //
            //   partial
            //
            pattern: [
                /<\!--\s*extract:\s*(([\w\/-_]+\/)?([\w_\.-]+))(.*)-->/,
                /<\!--\s*endextract\s*-->/
            ],
            // Wrap partial in template element and add options as data attributes
            templateWrap: {
                before: '<template id="partial" {{options}}>',
                after:  '</template>'
            },
            // Wrap component for viewing purposes: e.g. add production context
            //
            //
            //   partial
            //
            //
            // results in
            //
            //

            //   partial
            //

            viewWrap: {
                before: '',
                after: ''
            },
            // Base directory
            base: './inventory',
            // Partial directory where individual partial files will be stored (relative to base)
            partials: './partials',
            // Remove path from partial destination, put files to partials directory
            flatten: false,
            // Store inventory data as JSON file
            storage: 'partial-extract.json',
            // Enable storing partials as individual files
            storePartials: false,
            // Set indent value of partial code
            indent: '    '
        });

        grunt.log.writeln('Destination: ' + options.base);
        grunt.verbose.writeln('Files: ' + this.files.length);
        grunt.log.writeln();

        var existingFiles = [];
        var processedBlocks = {
            options: options,
            length: 0,
            items: []
        };

        // Iterate over all specified file groups.
        this.files.forEach(function (file) {
            var content = grunt.util.normalizelf(grunt.file.read(file.src));

            if (!options.pattern[0].test(content)) {
                grunt.log.errorlns('No partials in file ' + file.src);
                grunt.verbose.writeln();

                return;
            }

            var lines = content.split(grunt.util.linefeed);
            var blocks = getPartials(lines);
            var origin = file.src;

            grunt.log.oklns('Found ' + blocks.length + ' partials in file ' + file.src);

            function filterByDest(block) {
                if (existingFiles.indexOf(block.dest) !== -1) {
                    grunt.verbose.warn("Skip file " + block.dest + " which already exists.");
                    return false;
                }

                return true;
            }

            // Write blocks to separate files
            blocks.filter(filterByDest).map(function (block) {
                if (!filterByDest(block)) {
                    return;
                }

                var lines = block.lines.map(properIndentation);
                var leadingWhitespace = lines.map(countWhitespace);
                var crop = leadingWhitespace.reduce(getLeadingWhitespace);
                var viewWrap = block.options.wrap;
                var templateWrapOptions = optionsToDataString(block.options);

                lines = trimLines(lines, crop);

                var viewLines = util._extend([], lines);
                var templateLines = util._extend([], lines);

                // wrap partial if inline option viewWrap: exists
                if (viewWrap.before.length) {
                    viewLines = raiseIndent(viewLines);
                    viewLines.unshift('');
                    viewLines.unshift(viewWrap.before);
                    viewLines.push('');
                    viewLines.push(viewWrap.after);
                }

                // add templateWrap
                if (typeof options.templateWrap === 'object') {
                    var before = options.templateWrap.before || '';
                    var after = options.templateWrap.after || '';

                    before = before.replace('{{options}}', templateWrapOptions);
                    after = after.replace('{{options}}', templateWrapOptions);

                    templateLines.unshift(before);
                    templateLines.push(after);
                }

                block.lines         = lines;
                block.partial       = lines.join(grunt.util.linefeed);
                block.view          = viewLines.join(grunt.util.linefeed);
                block.template      = templateLines.join(grunt.util.linefeed);
                block.optionsData   = templateWrapOptions;
                block.origin        = origin;

                existingFiles.push(block.dest);

                processedBlocks.items.push(block);

                if (options.storePartials) {
                    grunt.file.write(path.resolve(options.base, options.partials, block.dest), block.template);
                }
            });

            grunt.verbose.writeln();
        });

        processedBlocks.length = processedBlocks.items.length;

        grunt.file.write(path.resolve(options.base, options.storage), JSON.stringify(processedBlocks, null, '\t'));

        grunt.log.writeln();

        grunt.log.oklns('Extracted ' + existingFiles.length + ' unique partials.');
    });

    /**
     * extract partials
     *
     * @param lines
     * @returns {Array}
     */
    function getPartials(lines) {
        var block;
        var addCurrentLine = false;
        var matches;
        var annotationBegin;
        var blocks = [];
        var dest = '';
        var matchFilename;
        var matchPath;
        var matchCategory;
        var matchName;
        var path = '';
        var filename = '';
        var category = false;
        var blockOptions = {};
        var name = '';

        // Import blocks from file
        lines.forEach(function (line) {
            // add block to list and stop adding lines when close annotation is present in current line
            if (line.match(options.pattern[1]) && addCurrentLine) {
                addCurrentLine = false;
                blocks.push(block);
            }

            // add lines if set in previous step until a close annotation is reached
            if (addCurrentLine) {
                block.lines.push(line);
            }

            // create block if opening annotation is present in current line
            if (matches = line.match(options.pattern[0])) {
                addCurrentLine = true;
                annotationBegin = matches[1];
                blockOptions = getBlockOptions(matches[0]);

                // get filename from extract option
                matchFilename = annotationBegin.match(/\/([^\/^\s]+)$/i);
                filename = (matchFilename && matchFilename.length > -1) ? matchFilename[1] : match;

                // get path from extract option
                matchPath = annotationBegin.match(/^([^\s]+\/)/i);
                path = (matchPath && matchPath.length > -1) ? matchPath[1] : '';

                // set first folder as category name if not in item options
                if (!blockOptions.hasOwnProperty('category')) {
                    matchCategory = annotationBegin.match(/^([^\s\/]+)\//i);
                    category = (matchCategory && matchCategory.length > -1) ? matchCategory[1] : false;
                    category = typeof category === 'string' ? _.startCase(category) : false;
                } else {
                    category = _.startCase(blockOptions.category);
                }

                // get name from filename if not in options
                if (!blockOptions.hasOwnProperty('name')) {
                    matchName = filename.match(/^([^\s]+)\./i);
                    name = (matchName && matchName.length > -1) ? matchName[1] : '';
                    name = typeof name === 'string' ? _.startCase(name) : '';
                } else {
                    name = blockOptions.name;
                }

                // remove nested path from dest if required
                dest = options.flatten ? filename : annotationBegin;

                // prepare block data
                block = {
                    name: name,
                    category: category,
                    options: blockOptions,
                    path: path,
                    filename: filename,
                    dest: dest,
                    lines: []
                };
            }
        });

        return blocks;
    }

    /**
     * replace tabs by indent value
     *
     * @param line
     * @return string
     */
    function properIndentation(line) {
        return line.replace(/\t/, options.indent || '');
    }

    /**
     * count leading whitespace chars
     *
     * @param line
     * @return integer
     */
    function countWhitespace(line) {
        // return a somewhat high value for empty lines
        return line.length ? line.match(/^\s*/)[0].length : 9999;
    }

    /**
     * get lowest value of leading whitespace in a given block
     *
     * @param previous
     * @param current
     * @returns integer
     */
    function getLeadingWhitespace(previous, current) {
        return previous <= current ? previous : current;
    }

    /**
     * trim given number of leading characters
     *
     * @param lines
     * @param num Number of chars to be removed
     * @returns Array
     */
    function trimLines(lines, num) {
        return lines.map(function (line) {
            return line.substr(num);
        });
    }

    /**
     * read options from annotation
     *
     * e.g.:
     * gets:
     * {
     *   extract: 'teaser/content-teaser--small.html',
     *   viewWrap: {before: '
', after: '
'}
     * }
     *
     * @param annotation
     * @returns {{}}
     */
    function getBlockOptions(annotation) {
        var optionValues = annotation.split(/\w+\:/).map(function (item) {
            return item.replace(/<\!--\s?|\s?-->|^\s+|\s+$/, '');
        }).filter(function (item) {
            return !!item.length;
        });
        var optionKeys = annotation.match(/(\w+)\:/g).map(function (item) {
            return item.replace(/[^\w]/, '');
        });

        var opts = {};
        var patternMultiple = new RegExp(/\:/);

        optionValues.forEach(function (v, i) {
            var k = optionKeys[i];

            if (typeof k !== 'string') {
                return;
            }

            // Treat option value as array if it has a colon
            // @todo: Allow escaped colons to be ignored
            // RegEx lookbehind negate does not work :(
            // Should be /(?<!\\)\:/
            if (v.match(patternMultiple)) {
                v = v.split(patternMultiple);
            }

            opts[k] = v;
        });

        // Process options
        opts.wrap = formalizeWrap(opts.wrap || options.viewWrap);

        return opts;
    }

    /**
     * raise offset in lines
     *
     * @param lines
     * @param offset
     * @returns {Array}
     */
    function raiseIndent(lines, offset) {
        offset = offset || '    ';

        return lines.map(function (line) {
            return offset + line;
        });
    }

    /**
     * Format options as string of HTML data parameters
     *
     * @param options
     * @returns {string}
     */
    function optionsToDataString(options) {
        if (typeof options !== 'object') {
            return '';
        }

        var prepared = [];
        var el;
        var processedOptions = _.assign({}, options);

        // prepare wrap option
        if (processedOptions.hasOwnProperty('wrap')) {
            processedOptions['wrap-before'] = processedOptions.wrap.before;
            processedOptions['wrap-after'] = processedOptions.wrap.after;

            delete(processedOptions.wrap);
        }

        // create data attributes
        for (el in processedOptions) {
            if (processedOptions.hasOwnProperty(el) === false) {
                continue;
            }

            var value = processedOptions[el];
            var preparedVal = JSON.stringify(processedOptions[el]);
            var param = '';

            // Ignore callbacks
            if (typeof value === 'function') {
                continue;
            }

            // Cleanup: Remove leading and trailing " and ', replace " by ' (e.g. in stringified objects)
            preparedVal = preparedVal.replace(/^[\'\"]|[\'\"]$/g, '').replace(/\\?\"/g, '\'');

            // Build data parameter: data-name="value"
            param = 'data-' + el + '="' + preparedVal + '"';

            prepared.push(param);
        }

        return prepared.join(' ');
    }

    /**
     * Formalize any given value as wrap object
     *
     * @param wrap
     * @returns {{before: string, after: string}}
     */
    function formalizeWrap(wrap) {
        var result = {before: '', after: ''};

        if ((typeof wrap === 'string' && wrap.length > 0) || typeof wrap === 'number') {
            result.before = result.after = wrap;
        } else if  (Array.isArray(wrap) && wrap.length > 0) {
            result.before = [].slice.call(wrap, 0, 1)[0];
            result.after = wrap.length > 1 ? [].slice.call(wrap, 1, 2)[0] : result.before;
        } else if (_.isPlainObject(wrap)) {
            var i = 0;
            var el;

            // crappy method getting the value of the first and second item in object
            for (el in wrap) {
                if (!wrap.hasOwnProperty(el)) {
                    return;
                }

                if (i < 2) {
                    result.before = wrap[el];
                }

                i++;
            }

            // set value of after to the value of before if after is empty
            result.after = result.after.length < 1 ? result.before : result.after;
        }

        return result;
    }
};
<commit_msg>Fix - etter variable names<commit_after>/*
 * grunt-partial-extract
 * https://github.com/tilmanjusten/grunt-partial-extract
 *
 * Copyright (c) 2015 Tilman Justen
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function (grunt) {

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    var util = require('util');
    var _ = require('lodash');
    var path = require('path');
    var options = {};

    grunt.registerMultiTask('partial-extract', 'Extract partials from any text based file and write to individual files.', function () {
        // Merge task-specific and/or target-specific options with these defaults.
        options = this.options({
            // Find partials by pattern:
            //
            //
            //   partial
            //
            pattern: [
                /<\!--\s*extract:\s*(([\w\/-_]+\/)?([\w_\.-]+))(.*)-->/,
                /<\!--\s*endextract\s*-->/
            ],
            // Wrap partial in template element and add options as data attributes
            templateWrap: {
                before: '<template id="partial" {{options}}>',
                after:  '</template>'
            },
            // Wrap component for viewing purposes: e.g. add production context
            //
            //
            //   partial
            //
            //
            // results in
            //
            //

            //   partial
            //

            viewWrap: {
                before: '',
                after: ''
            },
            // Base directory
            base: './inventory',
            // Partial directory where individual partial files will be stored (relative to base)
            partials: './partials',
            // Remove path from partial destination, put files to partials directory
            flatten: false,
            // Store inventory data as JSON file
            storage: 'partial-extract.json',
            // Enable storing partials as individual files
            storePartials: false,
            // Set indent value of partial code
            indent: '    '
        });

        grunt.log.writeln('Destination: ' + options.base);
        grunt.verbose.writeln('Files: ' + this.files.length);
        grunt.log.writeln();

        var existingFiles = [];
        var processedBlocks = {
            options: options,
            length: 0,
            items: []
        };

        // Iterate over all specified file groups.
        this.files.forEach(function (file) {
            var content = grunt.util.normalizelf(grunt.file.read(file.src));

            if (!options.pattern[0].test(content)) {
                grunt.log.errorlns('No partials in file ' + file.src);
                grunt.verbose.writeln();

                return;
            }

            var lines = content.split(grunt.util.linefeed);
            var blocks = getPartials(lines);
            var origin = file.src;

            grunt.log.oklns('Found ' + blocks.length + ' partials in file ' + file.src);

            function filterByDest(block) {
                if (existingFiles.indexOf(block.dest) !== -1) {
                    grunt.verbose.warn("Skip file " + block.dest + " which already exists.");
                    return false;
                }

                return true;
            }

            // Write blocks to separate files
            blocks.filter(filterByDest).map(function (block) {
                if (!filterByDest(block)) {
                    return;
                }

                var lines = block.lines.map(properIndentation);
                var leadingWhitespace = lines.map(countWhitespace);
                var crop = leadingWhitespace.reduce(getLeadingWhitespace);
                var viewWrap = block.options.wrap;
                var templateWrapOptions = optionsToDataString(block.options);

                lines = trimLines(lines, crop);

                var viewLines = util._extend([], lines);
                var templateLines = util._extend([], lines);

                // wrap partial if inline option viewWrap: exists
                if (viewWrap.before.length) {
                    viewLines = raiseIndent(viewLines);
                    viewLines.unshift('');
                    viewLines.unshift(viewWrap.before);
                    viewLines.push('');
                    viewLines.push(viewWrap.after);
                }

                // add templateWrap
                if (typeof options.templateWrap === 'object') {
                    var before = options.templateWrap.before || '';
                    var after = options.templateWrap.after || '';

                    before = before.replace('{{options}}', templateWrapOptions);
                    after = after.replace('{{options}}', templateWrapOptions);

                    templateLines.unshift(before);
                    templateLines.push(after);
                }

                block.lines         = lines;
                block.partial       = lines.join(grunt.util.linefeed);
                block.view          = viewLines.join(grunt.util.linefeed);
                block.template      = templateLines.join(grunt.util.linefeed);
                block.optionsData   = templateWrapOptions;
                block.origin        = origin;

                existingFiles.push(block.dest);

                processedBlocks.items.push(block);

                if (options.storePartials) {
                    grunt.file.write(path.resolve(options.base, options.partials, block.dest), block.template);
                }
            });

            grunt.verbose.writeln();
        });

        processedBlocks.length = processedBlocks.items.length;

        grunt.file.write(path.resolve(options.base, options.storage), JSON.stringify(processedBlocks, null, '\t'));

        grunt.log.writeln();

        grunt.log.oklns('Extracted ' + existingFiles.length + ' unique partials.');
    });

    /**
     * extract partials
     *
     * @param lines
     * @returns {Array}
     */
    function getPartials(lines) {
        var block;
        var addCurrentLine = false;
        var matches;
        var annotationBegin;
        var blocks = [];
        var dest = '';
        var matchFilename;
        var matchPath;
        var matchCategory;
        var matchName;
        var path = '';
        var filename = '';
        var category = false;
        var blockOptions = {};
        var name = '';

        // Import blocks from file
        lines.forEach(function (line) {
            // add block to list and stop adding lines when close annotation is present in current line
            if (line.match(options.pattern[1]) && addCurrentLine) {
                addCurrentLine = false;
                blocks.push(block);
            }

            // add lines if set in previous step until a close annotation is reached
            if (addCurrentLine) {
                block.lines.push(line);
            }

            // create block if opening annotation is present in current line
            if (matches = line.match(options.pattern[0])) {
                addCurrentLine = true;
                annotationBegin = matches[1];
                blockOptions = getBlockOptions(matches[0]);

                // get filename from extract option
                matchFilename = annotationBegin.match(/\/([^\/^\s]+)$/i);
                filename = (matchFilename && matchFilename.length > -1) ? matchFilename[1] : annotationBegin;

                // get path from extract option
                matchPath = annotationBegin.match(/^([^\s]+\/)/i);
                path = (matchPath && matchPath.length > -1) ? matchPath[1] : '';

                // set first folder as category name if not in item options
                if (!blockOptions.hasOwnProperty('category')) {
                    matchCategory = annotationBegin.match(/^([^\s\/]+)\//i);
                    category = (matchCategory && matchCategory.length > -1) ? matchCategory[1] : false;
                    category = typeof category === 'string' ? _.startCase(category) : false;
                } else {
                    category = _.startCase(blockOptions.category);
                }

                // get name from filename if not in options
                if (!blockOptions.hasOwnProperty('name')) {
                    matchName = filename.match(/^([^\s]+)\./i);
                    name = (matchName && matchName.length > -1) ? matchName[1] : '';
                    name = typeof name === 'string' ? _.startCase(name) : '';
                } else {
                    name = blockOptions.name;
                }

                // remove nested path from dest if required
                dest = options.flatten ? filename : annotationBegin;

                // prepare block data
                block = {
                    name: name,
                    category: category,
                    options: blockOptions,
                    path: path,
                    filename: filename,
                    dest: dest,
                    lines: []
                };
            }
        });

        return blocks;
    }

    /**
     * replace tabs by indent value
     *
     * @param line
     * @return string
     */
    function properIndentation(line) {
        return line.replace(/\t/, options.indent || '');
    }

    /**
     * count leading whitespace chars
     *
     * @param line
     * @return integer
     */
    function countWhitespace(line) {
        // return a somewhat high value for empty lines
        return line.length ? line.match(/^\s*/)[0].length : 9999;
    }

    /**
     * get lowest value of leading whitespace in a given block
     *
     * @param previous
     * @param current
     * @returns integer
     */
    function getLeadingWhitespace(previous, current) {
        return previous <= current ? previous : current;
    }

    /**
     * trim given number of leading characters
     *
     * @param lines
     * @param num Number of chars to be removed
     * @returns Array
     */
    function trimLines(lines, num) {
        return lines.map(function (line) {
            return line.substr(num);
        });
    }

    /**
     * read options from annotation
     *
     * e.g.:
     * gets:
     * {
     *   extract: 'teaser/content-teaser--small.html',
     *   viewWrap: {before: '
', after: '
'}
     * }
     *
     * @param annotation
     * @returns {{}}
     */
    function getBlockOptions(annotation) {
        var optionValues = annotation.split(/\w+\:/).map(function (item) {
            return item.replace(/<\!--\s?|\s?-->|^\s+|\s+$/, '');
        }).filter(function (item) {
            return !!item.length;
        });
        var optionKeys = annotation.match(/(\w+)\:/g).map(function (item) {
            return item.replace(/[^\w]/, '');
        });

        var opts = {};
        var patternMultiple = new RegExp(/\:/);

        optionValues.forEach(function (v, i) {
            var k = optionKeys[i];

            if (typeof k !== 'string') {
                return;
            }

            // Treat option value as array if it has a colon
            // @todo: Allow escaped colons to be ignored
            // RegEx lookbehind negate does not work :(
            // Should be /(?<!\\)\:/
            if (v.match(patternMultiple)) {
                v = v.split(patternMultiple);
            }

            opts[k] = v;
        });

        // Process options
        opts.wrap = formalizeWrap(opts.wrap || options.viewWrap);

        return opts;
    }

    /**
     * raise offset in lines
     *
     * @param lines
     * @param offset
     * @returns {Array}
     */
    function raiseIndent(lines, offset) {
        offset = offset || '    ';

        return lines.map(function (line) {
            return offset + line;
        });
    }

    /**
     * Format options as string of HTML data parameters
     *
     * @param options
     * @returns {string}
     */
    function optionsToDataString(options) {
        if (typeof options !== 'object') {
            return '';
        }

        var prepared = [];
        var el;
        var processedOptions = _.assign({}, options);

        // prepare wrap option
        if (processedOptions.hasOwnProperty('wrap')) {
            processedOptions['wrap-before'] = processedOptions.wrap.before;
            processedOptions['wrap-after'] = processedOptions.wrap.after;

            delete(processedOptions.wrap);
        }

        // create data attributes
        for (el in processedOptions) {
            if (processedOptions.hasOwnProperty(el) === false) {
                continue;
            }

            var value = processedOptions[el];
            var preparedVal = JSON.stringify(processedOptions[el]);
            var param = '';

            // Ignore callbacks
            if (typeof value === 'function') {
                continue;
            }

            // Cleanup: Remove leading and trailing " and ', replace " by ' (e.g. in stringified objects)
            preparedVal = preparedVal.replace(/^[\'\"]|[\'\"]$/g, '').replace(/\\?\"/g, '\'');

            // Build data parameter: data-name="value"
            param = 'data-' + el + '="' + preparedVal + '"';

            prepared.push(param);
        }

        return prepared.join(' ');
    }

    /**
     * Formalize any given value as wrap object
     *
     * @param wrap
     * @returns {{before: string, after: string}}
     */
    function formalizeWrap(wrap) {
        var result = {before: '', after: ''};

        if ((typeof wrap === 'string' && wrap.length > 0) || typeof wrap === 'number') {
            result.before = result.after = wrap;
        } else if  (Array.isArray(wrap) && wrap.length > 0) {
            result.before = [].slice.call(wrap, 0, 1)[0];
            result.after = wrap.length > 1 ? [].slice.call(wrap, 1, 2)[0] : result.before;
        } else if (_.isPlainObject(wrap)) {
            var i = 0;
            var el;

            // crappy method getting the value of the first and second item in object
            for (el in wrap) {
                if (!wrap.hasOwnProperty(el)) {
                    return;
                }

                if (i < 2) {
                    result.before = wrap[el];
                }

                i++;
            }

            // set value of after to the value of before if after is empty
            result.after = result.after.length < 1 ? result.before : result.after;
        }

        return result;
    }
};
<|endoftext|>


<commit_before>/*
 * grunt-patch-wordpress
 * https://github.com/aaronjorbin/grunt-patch-wordpress
 * Based on https://gist.github.com/markjaquith/4219135
 *
 *
 * Copyright (c) 2013 Aaron Jorbin
 * Licensed under the MIT license.
 */

var request = require( 'request' ),
	exec =  require( 'child_process' ).exec,
	spawn = require( 'child_process' ).spawn,
	inquirer = require( 'inquirer' ),
	url = require( 'url' ),
	fs = require( 'fs' ),
	_ = require( 'underscore' ),
	trac = require( '../lib/trac.js' ),
	patch = require( '../lib/patch.js' ),
	regex = require( '../lib/regex.js' ),
	xmlrpc = require( 'xmlrpc' ),
	mapOldToNewFilePath = require( '../lib/map_old_to_new_file_path.js' );

_.str = _.str = require( 'underscore.string' );
_.mixin( _.str.exports() );

module.exports = function( grunt ) {
	var tempFile = 'wppatch.diff',
		defaults = {
			tracUrl: 'core.trac.wordpress.org'
		};

	function isSvn() {
		return fs.existsSync( '.svn' );
	}

	function workingDir() {
		return process.cwd();
	}

	function applyPatch( patchUrl, done, options ) {
		grunt.verbose.write( patchUrl );
		parsedUrl = url.parse( patchUrl );

		// What to do when either our patch is ready
		grunt.event.once( 'fileReady', function( level, moveToSrc ) {
			var patchOptions = {},
				patchArgs = [],
				patchProcess;

			// Set patch process to use the existing I/O streams, which will output
			// the command's results and allow for user input on patch error
			patchOptions.stdio = 'inherit';

			// Decide if we need to be in src
			if ( moveToSrc ) {
				patchOptions.cwd =  workingDir() + '/src';
				tempFile = workingDir() + '/' + tempFile;
			}

			// Set the patch command's arguments
			patchArgs.push( '-p' + level );
			patchArgs.push( '--input=' + tempFile );

			grunt.log.debug( 'patch options: ' + JSON.stringify( patchOptions ) );
			grunt.log.debug( 'patch arguments: ' + JSON.stringify( patchArgs ) );
			grunt.log.debug( 'patch tempFile: ' + JSON.stringify( tempFile ) );

			// Maps old file paths in patches to new file paths.
			if ( options.file_mappings ) {
				mapOldToNewFilePath( tempFile, options.file_mappings );
			}

			patchProcess = spawn( 'patch', patchArgs, patchOptions );

			patchProcess.on( 'exit', function( code, signal ) {
				if ( signal ) {
					grunt.log.debug( 'error signal: ' + signal );
				}

				// if debug is enabled, don't delete the file
				if ( grunt.option( 'debug' ) ) {
					grunt.log.debug( 'File Saved' );
				} else {
					grunt.file.delete( tempFile );
				}

				done( code );
			});
		});

		// or we know we have failed
		grunt.event.once( 'fileFail', function( msg ) {
			if ( 'string' === typeof msg ) {
				grunt.log.errorlns( msg );
			}

			done( false );
		});

		// if patchUrl is a github url
		if ( 'github.com' === parsedUrl.hostname ) {

			grunt.log.debug( 'github url detected: ' + patchUrl );
			if ( '.diff' !== patchUrl.slice( -5 ) &&  '.patch' !== patchUrl.slice( -6 ) ) {
				patchUrl += '.diff';
			}
			getPatch( patchUrl, options );

		// if patchUrl is a full url and is a raw-attachement, just apply it
		} else if ( parsedUrl.hostname === options.tracUrl && parsedUrl.pathname.match( /raw-attachment/ ) ) {
			getPatch( patchUrl, options );

		// if patchUrl is full url and is an attachment, convert it to a raw attachment
		} else if ( parsedUrl.hostname === options.tracUrl && parsedUrl.pathname.match( /attachment/ ) && parsedUrl.pathname.match( /(patch|diff)/ ) ) {
			getPatch( trac.convert_to_raw ( parsedUrl, options ) );

		// if patchUrl is just a ticket number, get a list of patches on that ticket and allow user to choose one
		} else if (  parsedUrl.hostname === options.tracUrl && parsedUrl.pathname.match( /ticket/ ) ) {
			getPatchFromTicket( patchUrl, options );

		// if we just enter a number, assume it is a ticket number
		} else if ( null === parsedUrl.hostname && ! parsedUrl.pathname.match( /\./ ) ) {
			getPatchFromTicketNumber( patchUrl, options );

		// if patchUrl is a local file, just use that
		} else if ( null === parsedUrl.hostname ) {
			getLocalPatch( patchUrl, options );

		// We've failed in our mission
		} else {
			grunt.event.emit( 'fileFile', 'All matching failed.  Please enter a ticket url, ticket number, patch url' );
		}
	}

	function getPatchFromTicketNumber( patchUrl, options  ) {
		grunt.log.debug( 'getPatchFromTicketNumber: ' + patchUrl );
		getPatchFromTicket( 'https://' + options.tracUrl + '/attachment/ticket/' + patchUrl + '/', options  );
	}

	function getPatchFromTicket( patchUrl, options ) {
		var matches,
			longMatches,
			matchUrl,
			possiblePatches;

		grunt.log.debug( 'getPatchFromTicket: ' + patchUrl );
		request( patchUrl, function( error, response, body ) {
			if ( ! error && 200 == response.statusCode ) {
				matches = regex.patchAttachments( body );
				grunt.log.debug( 'matches: ' + JSON.stringify( matches ) );

				if ( null == matches ) {
					grunt.event.emit( 'fileFail', patchUrl + '\ncontains no attachments' );
				} else if ( 1 === matches.length ) {
					matchUrl = options.tracUrl + regex.urlsFromAttachmentList(   matches[0])[1];
					getPatch( trac.convert_to_raw ( url.parse( 'https://' + matchUrl  ) ), options  );
				} else {
					longMatches = regex.longMatches( body );
					possiblePatches = regex.possiblePatches( longMatches );

					grunt.log.debug( 'possiblePatches: ' + JSON.stringify( possiblePatches ) );
					grunt.log.debug( 'longMatches: ' + JSON.stringify( longMatches ) );
					inquirer.prompt([
						{	type: 'list',
							name: 'patch_name',
							message: 'Please select a patch to apply',
							choices: possiblePatches,

							// preselect the most recent patch
							default: possiblePatches.length - 1
						}
					], function( answers ) {
						grunt.log.debug( 'answers:' + JSON.stringify( answers ) );
						matchUrl = options.tracUrl +
						regex.urlsFromAttachmentList( matches[ _.indexOf( possiblePatches, answers.patch_name ) ])[1];
						getPatch( trac.convert_to_raw ( url.parse( 'https://' + matchUrl  ) ), options  );

					});
				}
			} else {

				// something went wrong
				grunt.event.emit( 'fileFail', 'getPatchFromTicket fail \n status: ' + response.statusCode );
			}
		});

		grunt.event.emit( 'fileFile', 'method not available yet' );
	}

	function getLocalPatch( patchUrl ) {
		var body = grunt.file.read( patchUrl ),
			level = patch.isAb( body ) ? 1 : 0,
			moveToSrc = patch.moveToSrc( body );

		grunt.file.copy( patchUrl, tempFile );
		grunt.event.emit( 'fileReady', level, moveToSrc );
	}

	function getPatch( patchUrl ) {
		grunt.log.debug( 'getting patch: ' + patchUrl );
		request( patchUrl, function( error, response, body ) {
			if ( ! error && 200 == response.statusCode ) {
				const level = patch.isAb( body ) ? 1 : 0;
				const moveToSrc = patch.moveToSrc( body );

				grunt.file.write( tempFile, body );
				grunt.event.emit( 'fileReady', level, moveToSrc );
			} else {

				// something went wrong
				grunt.event.emit( 'fileFail', 'getPatch_fail \n status: ' + response.statusCode );
			}
		});
	}

	function fileFail( done, msg ) {
		grunt.log.errorlns( 'Nothing to patch.' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( 'To use this command, please:' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '1) have a diff or patch in your WordPress Directory' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '2) enter a ticket number, e.g. grunt patch:15705' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '3) enter a ticket url, e.g. grunt patch:https://core.trac.wordpress.org/ticket/15705' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '4) enter a patch url, e.g. grunt patch:https://core.trac.wordpress.org/attachment/ticket/11817/13711.diff' );
		grunt.log.errorlns( '' );

		if ( 'string' === typeof( msg ) ) {
			grunt.verbose.errorlns( 'msg: ' + msg );
		}

		done( false );
	}

	function localFile( error, result, code, done, options ) {
		if ( ! error ) {
			files = _.filter( result.split( '\n' ), function( file ) {
				return ( _.str.include( file, 'patch' ) || _.str.include( file, 'diff' ) );
			});
			grunt.log.debug( 'files: ' + JSON.stringify( files ) );

			if ( 0 === files.length ) {
				fileFail( done );
			} else if ( 1 === files.length ) {
				applyPatch( regex.localFileClean( files[0]), done, options );
			} else {
				inquirer.prompt([
					{	type: 'list',
						name: 'file',
						message: 'Please select a file to apply',
						choices: files
					}
				], function( answers ) {
					var file = regex.localFileClean( answers.file );
					applyPatch( file, done, options );
				});
			}
		} else {
			fileFail( done, 'local file fail' );
		}
	}

	grunt.registerTask( 'patch_wordpress', 'Patch your develop-wordpress directory like a boss', function( ticket, afterProtocal ) {
		var done = this.async();
		var options = this.options( defaults );

		// since URLs contain a : which is the seperator for grunt, we
		// need to reassemble the url.
		if ( 'undefined' !== typeof afterProtocal ) {
			ticket = ticket + ':' + afterProtocal;
		}

		grunt.log.debug( 'ticket: ' + ticket );
		grunt.log.debug( 'options: ' + JSON.stringify( options ) );

		if ( 'undefined' === typeof ticket ) {

			// look for diffs and patches in the root of the checkout and
			// prompt using inquirer to pick one

			const fileFinderCommand = isSvn() ? 'svn status ' : 'git ls-files --other --exclude-standard';

			exec( fileFinderCommand, function( error, result, code ) {
				localFile( error, result, code, done, options );
			});
		} else {
			applyPatch( ticket, done, options );

		}

	});

	grunt.registerTask( 'upload_patch', 'Upload the current diff of your develop-wordpress directory to Trac', function( ticketNumber ) {
		var done = this.async();
		var options = this.options( defaults );

		grunt.log.debug( 'ticketNumber: ' + ticketNumber );
		grunt.log.debug( 'options: ' + JSON.stringify( options ) );

		ticketNumber = parseInt( ticketNumber, 10 );
		if ( 'number' != typeof ticketNumber ) {
			grunt.fail.warn( 'A ticket number is required to upload a patch.' );
		}

		const uploadPatchWithCredentials = function( username, password ) {
			var diffCommand = isSvn() ? 'svn diff --diff-cmd diff' : 'git diff';

			exec( diffCommand, function( error, result, code ) {
				var client = xmlrpc.createSecureClient({
					hostname: options.tracUrl,
					port: 443,
					path: '/login/xmlrpc',
					basic_auth: { // eslint-disable-line camelcase
						user: username,
						pass: password
					}
				});
				client.methodCall(
					'ticket.putAttachment',
					[
						ticketNumber,
						ticketNumber + '.diff',
						'', // description. empty for now.
						new Buffer( new Buffer( result ).toString( 'base64' ), 'base64' ),
						false // never overwrite the old file
					], function( err, value ) {
						if ( null === err ) {
							grunt.log.writeln( 'Uploaded patch.' );
							done();
						} else {
							grunt.fail.warn( 'Something went wrong when attempting to upload the patch. Please confirm your credentials and the ticket number. ' + err );
						}
					}
				);
			});
		};
		inquirer.prompt(
			[
				{ type: 'input', name: 'username', message: 'Enter your WordPress.org username' },
				{ type: 'password', name: 'password', message: 'Enter your WordPress.org password' }
			],
			function( answers ) {
				uploadPatchWithCredentials( answers.username, answers.password );
			}
		);
	});

};
<commit_msg>Update inquirer to use promises<commit_after>/*
 * grunt-patch-wordpress
 * https://github.com/aaronjorbin/grunt-patch-wordpress
 * Based on https://gist.github.com/markjaquith/4219135
 *
 *
 * Copyright (c) 2013 Aaron Jorbin
 * Licensed under the MIT license.
 */

var request = require( 'request' ),
	exec =  require( 'child_process' ).exec,
	spawn = require( 'child_process' ).spawn,
	inquirer = require( 'inquirer' ),
	url = require( 'url' ),
	fs = require( 'fs' ),
	_ = require( 'underscore' ),
	trac = require( '../lib/trac.js' ),
	patch = require( '../lib/patch.js' ),
	regex = require( '../lib/regex.js' ),
	xmlrpc = require( 'xmlrpc' ),
	mapOldToNewFilePath = require( '../lib/map_old_to_new_file_path.js' );

_.str = _.str = require( 'underscore.string' );
_.mixin( _.str.exports() );

module.exports = function( grunt ) {
	var tempFile = 'wppatch.diff',
		defaults = {
			tracUrl: 'core.trac.wordpress.org'
		};

	function isSvn() {
		return fs.existsSync( '.svn' );
	}

	function workingDir() {
		return process.cwd();
	}

	function applyPatch( patchUrl, done, options ) {
		grunt.verbose.write( patchUrl );
		parsedUrl = url.parse( patchUrl );

		// What to do when either our patch is ready
		grunt.event.once( 'fileReady', function( level, moveToSrc ) {
			var patchOptions = {},
				patchArgs = [],
				patchProcess;

			// Set patch process to use the existing I/O streams, which will output
			// the command's results and allow for user input on patch error
			patchOptions.stdio = 'inherit';

			// Decide if we need to be in src
			if ( moveToSrc ) {
				patchOptions.cwd =  workingDir() + '/src';
				tempFile = workingDir() + '/' + tempFile;
			}

			// Set the patch command's arguments
			patchArgs.push( '-p' + level );
			patchArgs.push( '--input=' + tempFile );

			grunt.log.debug( 'patch options: ' + JSON.stringify( patchOptions ) );
			grunt.log.debug( 'patch arguments: ' + JSON.stringify( patchArgs ) );
			grunt.log.debug( 'patch tempFile: ' + JSON.stringify( tempFile ) );

			// Maps old file paths in patches to new file paths.
			if ( options.file_mappings ) {
				mapOldToNewFilePath( tempFile, options.file_mappings );
			}

			patchProcess = spawn( 'patch', patchArgs, patchOptions );

			patchProcess.on( 'exit', function( code, signal ) {
				if ( signal ) {
					grunt.log.debug( 'error signal: ' + signal );
				}

				// if debug is enabled, don't delete the file
				if ( grunt.option( 'debug' ) ) {
					grunt.log.debug( 'File Saved' );
				} else {
					grunt.file.delete( tempFile );
				}

				done( code );
			});
		});

		// or we know we have failed
		grunt.event.once( 'fileFail', function( msg ) {
			if ( 'string' === typeof msg ) {
				grunt.log.errorlns( msg );
			}

			done( false );
		});

		// if patchUrl is a github url
		if ( 'github.com' === parsedUrl.hostname ) {

			grunt.log.debug( 'github url detected: ' + patchUrl );
			if ( '.diff' !== patchUrl.slice( -5 ) &&  '.patch' !== patchUrl.slice( -6 ) ) {
				patchUrl += '.diff';
			}
			getPatch( patchUrl, options );

		// if patchUrl is a full url and is a raw-attachement, just apply it
		} else if ( parsedUrl.hostname === options.tracUrl && parsedUrl.pathname.match( /raw-attachment/ ) ) {
			getPatch( patchUrl, options );

		// if patchUrl is full url and is an attachment, convert it to a raw attachment
		} else if ( parsedUrl.hostname === options.tracUrl && parsedUrl.pathname.match( /attachment/ ) && parsedUrl.pathname.match( /(patch|diff)/ ) ) {
			getPatch( trac.convert_to_raw ( parsedUrl, options ) );

		// if patchUrl is just a ticket number, get a list of patches on that ticket and allow user to choose one
		} else if (  parsedUrl.hostname === options.tracUrl && parsedUrl.pathname.match( /ticket/ ) ) {
			getPatchFromTicket( patchUrl, options );

		// if we just enter a number, assume it is a ticket number
		} else if ( null === parsedUrl.hostname && ! parsedUrl.pathname.match( /\./ ) ) {
			getPatchFromTicketNumber( patchUrl, options );

		// if patchUrl is a local file, just use that
		} else if ( null === parsedUrl.hostname ) {
			getLocalPatch( patchUrl, options );

		// We've failed in our mission
		} else {
			grunt.event.emit( 'fileFile', 'All matching failed.  Please enter a ticket url, ticket number, patch url' );
		}
	}

	function getPatchFromTicketNumber( patchUrl, options  ) {
		grunt.log.debug( 'getPatchFromTicketNumber: ' + patchUrl );
		getPatchFromTicket( 'https://' + options.tracUrl + '/attachment/ticket/' + patchUrl + '/', options  );
	}

	function getPatchFromTicket( patchUrl, options ) {
		var matches,
			longMatches,
			matchUrl,
			possiblePatches;

		grunt.log.debug( 'getPatchFromTicket: ' + patchUrl );
		request( patchUrl, function( error, response, body ) {
			if ( ! error && 200 == response.statusCode ) {
				matches = regex.patchAttachments( body );
				grunt.log.debug( 'matches: ' + JSON.stringify( matches ) );

				if ( null == matches ) {
					grunt.event.emit( 'fileFail', patchUrl + '\ncontains no attachments' );
				} else if ( 1 === matches.length ) {
					matchUrl = options.tracUrl + regex.urlsFromAttachmentList(   matches[0])[1];
					getPatch( trac.convert_to_raw ( url.parse( 'https://' + matchUrl  ) ), options  );
				} else {
					longMatches = regex.longMatches( body );
					possiblePatches = regex.possiblePatches( longMatches );

					grunt.log.debug( 'possiblePatches: ' + JSON.stringify( possiblePatches ) );
					grunt.log.debug( 'longMatches: ' + JSON.stringify( longMatches ) );
					inquirer.prompt([
						{	type: 'list',
							name: 'patch_name',
							message: 'Please select a patch to apply',
							choices: possiblePatches,

							// preselect the most recent patch
							default: possiblePatches.length - 1
						}
					]).then(  answers => {
						grunt.log.debug( 'answers:' + JSON.stringify( answers ) );
						matchUrl = options.tracUrl +
						regex.urlsFromAttachmentList( matches[ _.indexOf( possiblePatches, answers.patch_name ) ])[1];
						getPatch( trac.convert_to_raw ( url.parse( 'https://' + matchUrl  ) ), options  );

					});
				}
			} else {

				// something went wrong
				grunt.event.emit( 'fileFail', 'getPatchFromTicket fail \n status: ' + response.statusCode );
			}
		});

		grunt.event.emit( 'fileFile', 'method not available yet' );
	}

	function getLocalPatch( patchUrl ) {
		var body = grunt.file.read( patchUrl ),
			level = patch.isAb( body ) ? 1 : 0,
			moveToSrc = patch.moveToSrc( body );

		grunt.file.copy( patchUrl, tempFile );
		grunt.event.emit( 'fileReady', level, moveToSrc );
	}

	function getPatch( patchUrl ) {
		grunt.log.debug( 'getting patch: ' + patchUrl );
		request( patchUrl, function( error, response, body ) {
			if ( ! error && 200 == response.statusCode ) {
				const level = patch.isAb( body ) ? 1 : 0;
				const moveToSrc = patch.moveToSrc( body );

				grunt.file.write( tempFile, body );
				grunt.event.emit( 'fileReady', level, moveToSrc );
			} else {

				// something went wrong
				grunt.event.emit( 'fileFail', 'getPatch_fail \n status: ' + response.statusCode );
			}
		});
	}

	function fileFail( done, msg ) {
		grunt.log.errorlns( 'Nothing to patch.' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( 'To use this command, please:' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '1) have a diff or patch in your WordPress Directory' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '2) enter a ticket number, e.g. grunt patch:15705' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '3) enter a ticket url, e.g. grunt patch:https://core.trac.wordpress.org/ticket/15705' );
		grunt.log.errorlns( '' );
		grunt.log.errorlns( '4) enter a patch url, e.g. grunt patch:https://core.trac.wordpress.org/attachment/ticket/11817/13711.diff' );
		grunt.log.errorlns( '' );

		if ( 'string' === typeof( msg ) ) {
			grunt.verbose.errorlns( 'msg: ' + msg );
		}

		done( false );
	}

	function localFile( error, result, code, done, options ) {
		if ( ! error ) {
			files = _.filter( result.split( '\n' ), function( file ) {
				return ( _.str.include( file, 'patch' ) || _.str.include( file, 'diff' ) );
			});
			grunt.log.debug( 'files: ' + JSON.stringify( files ) );

			if ( 0 === files.length ) {
				fileFail( done );
			} else if ( 1 === files.length ) {
				applyPatch( regex.localFileClean( files[0]), done, options );
			} else {
				inquirer.prompt([
					{	type: 'list',
						name: 'file',
						message: 'Please select a file to apply',
						choices: files
					}
				]).then( answers => {
					var file = regex.localFileClean( answers.file );
					applyPatch( file, done, options );
				});
			}
		} else {
			fileFail( done, 'local file fail' );
		}
	}

	grunt.registerTask( 'patch_wordpress', 'Patch your develop-wordpress directory like a boss', function( ticket, afterProtocal ) {
		var done = this.async();
		var options = this.options( defaults );

		// since URLs contain a : which is the seperator for grunt, we
		// need to reassemble the url.
		if ( 'undefined' !== typeof afterProtocal ) {
			ticket = ticket + ':' + afterProtocal;
		}

		grunt.log.debug( 'ticket: ' + ticket );
		grunt.log.debug( 'options: ' + JSON.stringify( options ) );

		if ( 'undefined' === typeof ticket ) {

			// look for diffs and patches in the root of the checkout and
			// prompt using inquirer to pick one

			const fileFinderCommand = isSvn() ? 'svn status ' : 'git ls-files --other --exclude-standard';

			exec( fileFinderCommand, function( error, result, code ) {
				localFile( error, result, code, done, options );
			});
		} else {
			applyPatch( ticket, done, options );

		}

	});

	grunt.registerTask( 'upload_patch', 'Upload the current diff of your develop-wordpress directory to Trac', function( ticketNumber ) {
		var done = this.async();
		var options = this.options( defaults );

		grunt.log.debug( 'ticketNumber: ' + ticketNumber );
		grunt.log.debug( 'options: ' + JSON.stringify( options ) );

		ticketNumber = parseInt( ticketNumber, 10 );
		if ( 'number' != typeof ticketNumber ) {
			grunt.fail.warn( 'A ticket number is required to upload a patch.' );
		}

		const uploadPatchWithCredentials = function( username, password ) {
			var diffCommand = isSvn() ? 'svn diff --diff-cmd diff' : 'git diff';

			exec( diffCommand, function( error, result, code ) {
				var client = xmlrpc.createSecureClient({
					hostname: options.tracUrl,
					port: 443,
					path: '/login/xmlrpc',
					basic_auth: { // eslint-disable-line camelcase
						user: username,
						pass: password
					}
				});
				client.methodCall(
					'ticket.putAttachment',
					[
						ticketNumber,
						ticketNumber + '.diff',
						'', // description. empty for now.
						new Buffer( new Buffer( result ).toString( 'base64' ), 'base64' ),
						false // never overwrite the old file
					], function( err, value ) {
						if ( null === err ) {
							grunt.log.writeln( 'Uploaded patch.' );
							done();
						} else {
							grunt.fail.warn( 'Something went wrong when attempting to upload the patch. Please confirm your credentials and the ticket number. ' + err );
						}
					}
				);
			});
		};
		inquirer.prompt(
			[
				{ type: 'input', name: 'username', message: 'Enter your WordPress.org username' },
				{ type: 'password', name: 'password', message: 'Enter your WordPress.org password' }
			]).then(  answers => {
			uploadPatchWithCredentials( answers.username, answers.password );
		}
		);
	});

};
<|endoftext|>


<commit_before>/**
 * @license
 * resol-vbus - A JavaScript library for processing RESOL VBus data
 */
'use strict';

var fs = require('fs');
var path = require('path');

var _ = require('lodash');
var moment = require('moment');
var sprintf = require('sprintf').sprintf;

var Recorder = require('./recorder');

var optionKeys = [
    'directory',
    'filePattern',
    'columnSeparator',
    'lineSeparator',
    'specification',
];

var TextFileRecorder = Recorder.extend({

    directory: '.',

    filePattern: 'YYYYMMDD[.csv]',

    columnSeparator: '\t',

    lineSeparator: '\r\n',

    specification: null,

    lastFilePath: null,

    lastIdList: null,

    constructor: function(options) {
        Recorder.call(this);

        _.extend(this, _.pick(options, optionKeys));
    },

    recordHeaderSet: function(headerSet) {
        var _this = this;

        var spec = this.specification;

        var headers = headerSet.getSortedHeaders();

        var packetFields = spec.getPacketFieldsForHeaders(headers);

        var filename = moment().lang(spec.language).format(this.filePattern);
        var filepath = path.join(this.directory, filename);

        var idList = _.pluck(packetFields, 'id').join(',');

        var content = '', columns;

        var appendColumnsToContent = function() {
            content += columns.join(_this.columnSeparator) + _this.lineSeparator;
        };

        var needHeaderLines = false;
        if (this.lastIdList !== idList) {
            this.lastIdList = idList;
            needHeaderLines = true;
        }
        if (this.lastFilePath !== filepath) {
            this.lastFilePath = filepath;
            needHeaderLines = true;
        }

        if (needHeaderLines) {
            var lastPacketSpec = null;

            columns = [ '' ];
            _.forEach(packetFields, function(packetField) {
                if (lastPacketSpec !== packetField.packetSpec) {
                    lastPacketSpec = packetField.packetSpec;

                    var packetDesc;
                    if (packetField.packet && packetField.packetSpec) {
                        packetDesc = sprintf('VBus #%d: %s => %s', packetField.packet.channel, packetField.packetSpec.sourceDevice.name, packetField.packetSpec.destinationDevice.name);
                    } else {
                        packetDesc = '';
                    }
                    columns.push(packetDesc);
                } else {
                    columns.push('');
                }
            });
            appendColumnsToContent();

            columns = [ 'Date / Time' ];
            _.forEach(packetFields, function(packetField) {
                var columnDesc;
                if (packetField.packetFieldSpec) {
                    columnDesc = packetField.packetFieldSpec.name.ref;
                } else {
                    columnDesc = '';
                }
                columns.push(columnDesc);
            });
            appendColumnsToContent();
        }

        columns = [ moment(headerSet.timestamp).lang(spec.language).format('L HH:mm:ss') ];
        _.forEach(packetFields, function(packetField) {
            var textValue;
            if (packetField.packet && packetField.packetSpec && packetField.packetFieldSpec) {
                var rawValue = spec.getRawValue(packetField.packetFieldSpec, packetField.packet.frameData);
                textValue = spec.formatTextValueFromRawValue(packetField.packetFieldSpec, rawValue, 'None');
            } else {
                textValue = '';
            }
            columns.push(textValue);
        });
        appendColumnsToContent();

        fs.appendFile(filepath, content, function (err) {
            if (err) {
                console.log(err);
            }
        });
    },

});

module.exports = TextFileRecorder;
<commit_msg>- Refactored language support in text file recorder<commit_after>/**
 * @license
 * resol-vbus - A JavaScript library for processing RESOL VBus data
 */
'use strict';

var fs = require('fs');
var path = require('path');

var _ = require('lodash');
var moment = require('moment');
var sprintf = require('sprintf').sprintf;

var Recorder = require('./recorder');

var optionKeys = [
    'directory',
    'filePattern',
    'columnSeparator',
    'lineSeparator',
    'specification',
];

var TextFileRecorder = Recorder.extend({

    directory: '.',

    filePattern: 'YYYYMMDD[.csv]',

    columnSeparator: '\t',

    lineSeparator: '\r\n',

    specification: null,

    lastFilePath: null,

    lastIdList: null,

    constructor: function(options) {
        Recorder.call(this);

        _.extend(this, _.pick(options, optionKeys));
    },

    recordHeaderSet: function(headerSet) {
        var _this = this;

        var spec = this.specification;

        var language = spec.language || 'en';

        var headers = headerSet.getSortedHeaders();

        var packetFields = spec.getPacketFieldsForHeaders(headers);

        var filename = moment().lang(language).format(this.filePattern);
        var filepath = path.join(this.directory, filename);

        var idList = _.pluck(packetFields, 'id').join(',');

        var content = '', columns;

        var appendColumnsToContent = function() {
            content += columns.join(_this.columnSeparator) + _this.lineSeparator;
        };

        var needHeaderLines = false;
        if (this.lastIdList !== idList) {
            this.lastIdList = idList;
            needHeaderLines = true;
        }
        if (this.lastFilePath !== filepath) {
            this.lastFilePath = filepath;
            needHeaderLines = true;
        }

        if (needHeaderLines) {
            var lastPacketSpec = null;

            columns = [ '' ];
            _.forEach(packetFields, function(packetField) {
                var packetDesc;
                if (lastPacketSpec !== packetField.packetSpec) {
                    lastPacketSpec = packetField.packetSpec;

                    if (packetField.packet && packetField.packetSpec) {
                        packetDesc = sprintf('VBus #%d: %s => %s', packetField.packet.channel, packetField.packetSpec.sourceDevice.name, packetField.packetSpec.destinationDevice.name);
                    } else {
                        packetDesc = '';
                    }
                } else {
                    packetDesc = '';
                }
                columns.push(packetDesc);
            });
            appendColumnsToContent();

            columns = [ 'Date / Time' ];
            _.forEach(packetFields, function(packetField) {
                var columnDesc;
                if (packetField.packetFieldSpec) {
                    var names = packetField.packetFieldSpec.name;
                    columnDesc = names [language] || names.en || names.de || names.ref;

                    var type = packetField.packetFieldSpec.type;
                    if (type && type.unit && type.unit.unitText) {
                        columnDesc += ' [' + type.unit.unitText + ']';
                    }
                } else {
                    columnDesc = '';
                }
                columns.push(columnDesc);
            });
            appendColumnsToContent();
        }

        columns = [ moment(headerSet.timestamp).lang(language).format('L HH:mm:ss') ];
        _.forEach(packetFields, function(packetField) {
            var textValue;
            if (packetField.packet && packetField.packetSpec && packetField.packetFieldSpec) {
                var rawValue = spec.getRawValue(packetField.packetFieldSpec, packetField.packet.frameData);
                textValue = spec.formatTextValueFromRawValue(packetField.packetFieldSpec, rawValue, 'None');
            } else {
                textValue = '';
            }
            columns.push(textValue);
        });
        appendColumnsToContent();

        fs.appendFile(filepath, content, function (err) {
            if (err) {
                console.log(err);
            }
        });
    },

});

module.exports = TextFileRecorder;
<|endoftext|>


<commit_before>'use strict';

var krusovice = krusovice || {};

/**
 * Visualize show timeline for diagnosis
 *
 * @param {Object} config Configuration options
 *
 */
krusovice.TimelineVisualizer = function(config) {

	$.extend(this, config)

	if(!this.rhytmData) {
		this.rhytmData = null;
	}

    // length in pixels
    this.lineLength = this.duration / this.secondsPerPixel;
}

krusovice.TimelineVisualizer.prototype = {

    /**
     * @cfg {Object} plan Show plan data
     */
    plan : null,

    /**
     * @cfg {Object} rhytmData Rhytm data JSON from Echo Nest Remix API or null if no music
     */
    rhytmData : null,

    /**
     * @cfg {Number} secondsPerPixel How long timeline is covered by single pixel on the timeline. Modify this to change the zoom level.
     */
    secondsPerPixel :  0.1,

    /**
     * @cfg {Number} duration How long timeline we render
     */
    duration : 60,

    /**
     * @cfg {Number} clockSpan How often render time hint labels on the timeline (pixels)
     */
    clockSpan : 100,

    /**
     * @cfg {Number} lineHeight One visualization line height in pixels
     */
    lineHeight : 80,

    /**
     * @type Number
     * No of rendered  beats (for testing purposes)
     */
    renderedBeats : 0,

    /**
     * @type Number
     * No of rendered show elements (for testing purposes)
     */
    renderedElements : 0,

	formatClock : function(time) {
		var minutes = Math.floor(time/60);
		var seconds = time - minutes;
		return minutes + ":" + Math.round(seconds, 2);
	},

	/**
	 * Does this visualization have data for beats
	 */
	hasBeats : function() {
		return (this.rhytmData != null);
	},

	createLabelLine : function() {
		var line = $("
");
		return line;
	},

	createLabel : function(parent, x, text) {
		var label = $("

");
		label.css("left", x + "px");
		label.text(text);
		parent.append(label);
		return label;
	},

	createDataLine : function() {
		var canvas = $("<canvas class=timeline width=" + this.lineLength + " height=" + this.lineHeight + ">");
		canvas.css("width", this.lineLength + "px");
		var context = canvas.get(0).getContext("2d");
		return [canvas, context];
	},

	/**
	 * Show time hints for the timelint
	 */
	createClockLine : function () {

		var line = this.createLabelLine ();

		var i=0;

		for(i=0; i<this.lineLength; i+=this.clockSpan) {
			var time = i * this.secondsPerPixel;
			var text = this.formatClock(time);
			this.createLabel(line, i, text);
		}

		return line;
	},

	/**
	 * Show beat data on timeline
	 */
	createBeatLine : function() {

		console.log("Rendering beat line");

		var line = this.createDataLine();

		var canvas = line[0];
		var context = line[1];

		var i=0;

		var clock = 0;

		context.strokeStyle = "#ff0000";
		context.lineWidth = 1;

		if(this.hasBeats()) {

			var beats = this.rhytmData.beats;
			var currentBeat = 0;

			//console.log("Rendering beats");

			// Render each pixel of the timeline image
			for(i=0; i<this.lineLength; i++) {

				 var currentClock = i*this.secondsPerPixel;
				 var nextClock =  currentClock + this.secondsPerPixel;

				 //console.log("Rendering clock span " + currentClock + "-" + nextClock);
				 //console.log(beats[currentBeat]);

				 // max of all beat values during the clock span
				 var peakBeat = 0;

				 var beatsHit = 0;

				 // Note: beat data clocks are in milliseconds

				 // Wind beat cursor to the start of current clock span
				 while(currentBeat < beats.length && beats[currentBeat].start/1000 < currentClock) {
					 currentBeat++;
				 }

				 while(beats[currentBeat].start/1000 >= currentClock && beats[currentBeat].start/1000 < nextClock) {
					 peakBeat = Math.max(beats[currentBeat].confidence, peakBeat);
					 beatsHit++;
					 currentBeat++;
				 }

				 // console.log("Pixel " + i + " beat peak " + peakBeat + " hits:" + beatsHit + " current beat:" + currentBeat);

				 // Beat line height in pixels
				 var height =  peakBeat * this.lineHeight;

				 this.renderedBeats += 1;

				 // canvas assumes 0.5 is in the middle of pixel
				 // hurray for subpixel mess
				 context.beginPath();
				 context.moveTo(i + 0.5, this.lineHeight - height);
				 context.lineTo(i + 0.5, this.lineHeight);
				 context.stroke();
			}
		}

		return canvas;

	},

	/**
	 * Create timeline which shows element ease in, on screen and ease out times
	 */
	createElementLine : function() {

		console.log("Rendering element line");

		var line = this.createDataLine();

		var canvas = line[0];
		var context = line[1];

		var i;

		context.lineWidth = 1;

		for(i=0; i<this.plan.length; i++) {
			var elem = this.plan[i];

			// Add 50% alpha
			var acolor = krusovice.utils.pickRandomColor(128);
			context.strokeStyle = acolor;

			var startX = elem.wakeUpTime / this.secondsPerPixel;

			if(elem.animations.length != 4) {
				throw "This visualization code can handle animations only with three states: in, screen and out + gone state";
			}

			var totalDuration = elem.animations[0].duration + elem.animations[1].duration + elem.animations[2].duration;

			// span length in pixels
			var length = totalDuration / this.secondsPerPixel;

			console.log("Rendering element:" + elem + " x:" + startX + " duration:" + totalDuration + " length:" + length + " color:" + acolor);

			for(var l=0; l<length; l++) {
				var clock = l*this.secondsPerPixel;

				//console.log("Calculating ease for");
				//console.log(elem);

				var animation = krusovice.utils.calculateElementEase(elem, clock);
				var value;

				if(animation.animation == "onscreen") {
				   value = 1;
				} else if(animation.animation == "transitionout") {
				   value = 1 - animation.value;
				}  else {
				   value = animation.value;
				}

				if(value > 1) {
				        console.error("Elem:");
				        console.error(elem);
				        console.error("Clock:" + clock);
                                            console.error("Animation:");
				        console.error(animation);
				        throw new "Bad easing calculation yield too high value > 1";
				}

				var x = startX + l;
				var height = value * this.lineHeight;

				// console.log("Rendering " + animation.animation + " x:" + l + " value:" + value);

				if(height == undefined) {
				        throw "Height calculation failed for an animation";
				}

				context.beginPath();
				context.moveTo(x + 0.5, this.lineHeight - height);
				context.lineTo(x + 0.5, this.lineHeight);
				context.stroke();
			}

			this.renderedElements++;

		}

		return canvas;
	},

	/**
	 * Create position indicator line
	 */
	createPositionIndicator : function() {
		return $("
");
	},

	render : function (elem) {

		var elem = $(elem);

		elem.addClass("timeline-visualization");

		elem.css("width", this.lineLength + "px");

		if(elem.size() == 0) {
			throw "Render target cannot be empty for timeline visualizer";
		}

		var clock = this.createClockLine();
		var beats = this.createBeatLine();
		var elements = this.createElementLine();
		elem.append(clock);
		elem.append(beats);
		elem.append(elements);

		this.positionIndicator = this.createPositionIndicator();
		elem.append(this.positionIndicator)

		this.elem = elem;

	},

	/**
	 * Set position indicating cursor to a
	 */
	setPositionIndicator : function(time, visible) {

		if(visible) {
			this.positionIndicator.show();
		} else {
			this.positionIndicator.hide();
		}

		//console.log("Position update:" + time);

		var x = time / this.secondsPerPixel;

		var parent = this.positionIndicator.parent().parent();
		var poffset = parent.offset();

		var clip = "rect(" + poffset.top + "px " +
			(poffset.left + parent.width()) + "px " +
			(poffset.top + parent.height()) + "px " +
			poffset.left + "px)";

		console.log(clip)

		this.positionIndicator.css("clip", clip);

		// Draw marker as styled div over the parent elem
		this.positionIndicator.css({
			left : x + poffset.left,
			top : poffset.top,
			width : "1 px",
			height : this.elem.height() + "px"
		});
	}

};

/**
 *  Play song over timeline visualization to see if beats match song data
 *
 *  @param {String|HTMLAudio} src URL to a music or <audio> elem
 */
krusovice.TimelinePlayer = function(visualization, src) {

	this.visualization = visualization;

	if(typeof(src) == "string") {

		// http://dev.opera.com/articles/view/everything-you-need-to-know-about-html5-video-and-audio/
		this.audio = document.createElement("audio");

		this.audio.controls = true;

		this.audio.setAttribute('src', src);

	} else {
		this.audio = src;
	}

	if(!this.audio) {
		throw "Who silenced the stereo?";
	}

	$(this.audio).bind("load", function() {
		console.log("Loaded:" + src);
	});

	$(this.audio).bind("Error", function() {
		console.log("Error:" + src);
	});

	$(this.audio).bind("timeupdate", $.proxy(this.onTimeUpdate, this));
	$(this.audio).bind("stop", $.proxy(this.stop, this));

}

krusovice.TimelinePlayer.prototype = {

	stop : function() {
		this.visualization.setPositionIndicator(0, false);
	},

	onTimeUpdate : function() {
		var ctime = this.audio.currentTime;
		this.visualization.setPositionIndicator(ctime, true);
	}

}

/**
 * @static
 *
 * Display a simple pop-up during the loading of a show.
 */
krusovice.attachSimpleLoadingNote = function(show) {

    var note = $("
");

    note.css({
        position : "absolute",
        margin : "5px",
        padding : "5px",
        background : "white",
        color : "black",
        border : "2px solid black",
        width : "250px",
        height : "50px"
    });

    $(show).bind("loadstart", function() {
        var container = $(show.elem).parent();
        container.append(note);

        note.offset({
        	top : container.offset().top + 10,
        	left : container.offset().left + 10
        });

    });

    $(show).bind("loadprogress", function(progress) {
    	if(krusovice.utils.isNumber(progress)) {
	        var number = Math.round(progress, 2);
	        note.text("Loading " + number + "%");
    	}
    });

    $(show).bind("loaderror", function(event, msg) {
        note.text(msg);
    });

    $(show).bind("loadend", function() {
        note.remove();
    });

}
<commit_msg>Added effect labels to timeline<commit_after>'use strict';

var krusovice = krusovice || {};

/**
 * Visualize show timeline for diagnosis
 *
 * @param {Object} config Configuration options
 *
 */
krusovice.TimelineVisualizer = function(config) {

	$.extend(this, config)

	if(!this.rhytmData) {
		this.rhytmData = null;
	}

    // length in pixels
    this.lineLength = this.duration / this.secondsPerPixel;
}

krusovice.TimelineVisualizer.prototype = {

    /**
     * @cfg {Object} plan Show plan data
     */
    plan : null,

    /**
     * @cfg {Object} rhytmData Rhytm data JSON from Echo Nest Remix API or null if no music
     */
    rhytmData : null,

    /**
     * @cfg {Number} secondsPerPixel How long timeline is covered by single pixel on the timeline. Modify this to change the zoom level.
     */
    secondsPerPixel :  0.1,

    /**
     * @cfg {Number} duration How long timeline we render
     */
    duration : 60,

    /**
     * @cfg {Number} clockSpan How often render time hint labels on the timeline (pixels)
     */
    clockSpan : 100,

    /**
     * @cfg {Number} lineHeight One visualization line height in pixels
     */
    lineHeight : 80,

    /**
     * @cfg {Boolean} autoscroll Keep cursor visible all teh time
     */
    autoscroll : true,

    /**
     * @type Number
     * No of rendered  beats (for testing purposes)
     */
    renderedBeats : 0,

    /**
     * @type Number
     * No of rendered show elements (for testing purposes)
     */
    renderedElements : 0,

	formatClock : function(time) {
		var minutes = Math.floor(time/60);
		var seconds = time - minutes;
		return minutes + ":" + Math.round(seconds, 2);
	},

	/**
	 * Does this visualization have data for beats
	 */
	hasBeats : function() {
		return (this.rhytmData != null);
	},

	createLabelLine : function() {
		var line = $("
");
		return line;
	},

	createLabel : function(parent, x, text) {
		var label = $("

");
		label.css("left", x + "px");
		label.text(text);
		parent.append(label);
		return label;
	},

	createDataLine : function() {
		var canvas = $("<canvas class=timeline width=" + this.lineLength + " height=" + this.lineHeight + ">");
		canvas.css("width", this.lineLength + "px");
		var context = canvas.get(0).getContext("2d");
		return [canvas, context];
	},

	/**
	 * Show time hints for the timelint
	 */
	createClockLine : function () {

		var line = this.createLabelLine ();

		var i=0;

		for(i=0; i<this.lineLength; i+=this.clockSpan) {
			var time = i * this.secondsPerPixel;
			var text = this.formatClock(time);
			this.createLabel(line, i, text);
		}

		return line;
	},

	/**
	 * Show beat data on timeline
	 */
	createBeatLine : function() {

		console.log("Rendering beat line");

		var line = this.createDataLine();

		var canvas = line[0];
		var context = line[1];

		var i=0;

		var clock = 0;

		context.strokeStyle = "#ff0000";
		context.lineWidth = 1;

		if(this.hasBeats()) {

			var beats = this.rhytmData.beats;
			var currentBeat = 0;

			//console.log("Rendering beats");

			// Render each pixel of the timeline image
			for(i=0; i<this.lineLength; i++) {

				 var currentClock = i*this.secondsPerPixel;
				 var nextClock =  currentClock + this.secondsPerPixel;

				 //console.log("Rendering clock span " + currentClock + "-" + nextClock);
				 //console.log(beats[currentBeat]);

				 // max of all beat values during the clock span
				 var peakBeat = 0;

				 var beatsHit = 0;

				 // Note: beat data clocks are in milliseconds

				 // Wind beat cursor to the start of current clock span
				 while(currentBeat < beats.length && beats[currentBeat].start/1000 < currentClock) {
					 currentBeat++;
				 }

				 while(beats[currentBeat].start/1000 >= currentClock && beats[currentBeat].start/1000 < nextClock) {
					 peakBeat = Math.max(beats[currentBeat].confidence, peakBeat);
					 beatsHit++;
					 currentBeat++;
				 }

				 // console.log("Pixel " + i + " beat peak " + peakBeat + " hits:" + beatsHit + " current beat:" + currentBeat);

				 // Beat line height in pixels
				 var height =  peakBeat * this.lineHeight;

				 this.renderedBeats += 1;

				 // canvas assumes 0.5 is in the middle of pixel
				 // hurray for subpixel mess
				 context.beginPath();
				 context.moveTo(i + 0.5, this.lineHeight - height);
				 context.lineTo(i + 0.5, this.lineHeight);
				 context.stroke();
			}
		}

		return canvas;

	},

	/**
	 * Create timeline which shows element ease in, on screen and ease out times
	 */
	createElementLine : function() {

		console.log("Rendering element line");

		var line = this.createDataLine();

		var canvas = line[0];
		var context = line[1];

		var i;

		context.lineWidth = 1;

		for(i=0; i<this.plan.length; i++) {
			var elem = this.plan[i];

			// Pick random color with 50% alpha for this element
			var acolor = krusovice.utils.pickRandomColor(128);
			context.strokeStyle = acolor;

			var startX = elem.wakeUpTime / this.secondsPerPixel;

			if(elem.animations.length != 4) {
				throw "This visualization code can handle animations only with three states: in, screen and out + gone state";
			}

			var totalDuration = elem.animations[0].duration + elem.animations[1].duration + elem.animations[2].duration;

			// span length in pixels
			var length = totalDuration / this.secondsPerPixel;

			console.log("Rendering element:" + elem + " x:" + startX + " duration:" + totalDuration + " length:" + length + " color:" + acolor);

			context.save();
			for(var l=0; l<length; l++) {
				var clock = l*this.secondsPerPixel;

				//console.log("Calculating ease for");
				//console.log(elem);

				var animation = krusovice.utils.calculateElementEase(elem, clock);
				var value;

				if(animation.animation == "onscreen") {
				   value = 1;
				} else if(animation.animation == "transitionout") {
				   value = 1 - animation.value;
				}  else {
				   value = animation.value;
				}

				if(value > 1) {
				        console.error("Elem:");
				        console.error(elem);
				        console.error("Clock:" + clock);
                                            console.error("Animation:");
				        console.error(animation);
				        throw new "Bad easing calculation yield too high value > 1";
				}

				var x = startX + l;
				var height = value * this.lineHeight;

				// console.log("Rendering " + animation.animation + " x:" + l + " value:" + value);

				if(height == undefined) {
				        throw "Height calculation failed for an animation";
				}

				context.beginPath();
				context.moveTo(x + 0.5, this.lineHeight - height);
				context.lineTo(x + 0.5, this.lineHeight);
				context.stroke();

			}
			context.restore();

			// Draw animation labels for this element
			context.font = "10px sans-serif"
			context.strokeStyle = "#000000";
			x = startX;
			for(l=0; l<elem.animations.length-1; l++) {
				var anim = elem.animations[l];
				var label = anim.effectType.toUpperCase();
				// console.log("rendering anim x:" + x + "  label:" + label);
				context.fillText(label, x, 10);
				x += anim.duration / this.secondsPerPixel;
			}

			this.renderedElements++;

		}

		return canvas;
	},

	/**
	 * Create position indicator line
	 */
	createPositionIndicator : function() {
		return $("
");
	},

	render : function (elem) {

		var elem = $(elem);

		elem.addClass("timeline-visualization");

		elem.css("width", this.lineLength + "px");

		if(elem.size() == 0) {
			throw "Render target cannot be empty for timeline visualizer";
		}

		var clock = this.createClockLine();
		var beats = this.createBeatLine();
		var elements = this.createElementLine();
		elem.append(clock);
		elem.append(beats);
		elem.append(elements);

		this.positionIndicator = this.createPositionIndicator();
		elem.append(this.positionIndicator)

		this.elem = elem;

	},

	/**
	 * Set position indicating cursor to a
	 */
	setPositionIndicator : function(time, visible) {

		if(visible) {
			this.positionIndicator.show();
		} else {
			this.positionIndicator.hide();
		}

		//console.log("Position update:" + time);

		var x = time / this.secondsPerPixel;

		var parent = this.positionIndicator.parent().parent();

		var poffset = parent.offset();

		// Calcualte visible area
		var cx = parent.scrollLeft();
		var cx2 = parent.scrollLeft() + parent.width();

		// console.log("cx:" + cx + " cx2:" + cx2);

		// Do manual clip
		if(x < cx ||x >= cx2) {
			this.positionIndicator.hide();
			if(this.autoscroll) {
				this.doAutoscroll(x);
			}
			return;
		}

		x -= cx;

		// Draw marker as styled div over the parent elem
		this.positionIndicator.css({
			left : x + poffset.left,
			top : poffset.top,
			width : "1 px",
			height : this.elem.height() + "px"
		});
	},

	/**
	 * Put viewport to X position on the timeline
	 */
	doAutoscroll : function(x) {
		var parent = this.positionIndicator.parent().parent();
		parent.scrollLeft(x);
	}

};

/**
 *  Play song over timeline visualization to see if beats match song data
 *
 *  @param {String|HTMLAudio} src URL to a music or <audio> elem
 */
krusovice.TimelinePlayer = function(visualization, src) {

	this.visualization = visualization;

	if(typeof(src) == "string") {

		// http://dev.opera.com/articles/view/everything-you-need-to-know-about-html5-video-and-audio/
		this.audio = document.createElement("audio");

		this.audio.controls = true;

		this.audio.setAttribute('src', src);

	} else {
		this.audio = src;
	}

	if(!this.audio) {
		throw "Who silenced the stereo?";
	}

	$(this.audio).bind("load", function() {
		console.log("Loaded:" + src);
	});

	$(this.audio).bind("Error", function() {
		console.log("Error:" + src);
	});

	$(this.audio).bind("timeupdate", $.proxy(this.onTimeUpdate, this));
	$(this.audio).bind("stop", $.proxy(this.stop, this));

}

krusovice.TimelinePlayer.prototype = {

	stop : function() {
		this.visualization.setPositionIndicator(0, false);
	},

	onTimeUpdate : function() {
		var ctime = this.audio.currentTime;
		this.visualization.setPositionIndicator(ctime, true);
	}

}

/**
 * @static
 *
 * Display a simple pop-up during the loading of a show.
 */
krusovice.attachSimpleLoadingNote = function(show) {

    var note = $("
");

    note.css({
        position : "absolute",
        margin : "5px",
        padding : "5px",
        background : "white",
        color : "black",
        border : "2px solid black",
        width : "250px",
        height : "50px"
    });

    $(show).bind("loadstart", function() {
        var container = $(show.elem).parent();
        container.append(note);

        note.offset({
        	top : container.offset().top + 10,
        	left : container.offset().left + 10
        });

    });

    $(show).bind("loadprogress", function(progress) {
    	if(krusovice.utils.isNumber(progress)) {
	        var number = Math.round(progress, 2);
	        note.text("Loading " + number + "%");
    	}
    });

    $(show).bind("loaderror", function(event, msg) {
        note.text(msg);
    });

    $(show).bind("loadend", function() {
        note.remove();
    });

}
<|endoftext|>


<commit_before>'use strict';

var viewer;

var OSG = window.OSG;
OSG.globalify();

var osg = window.osg;
var osgGA = window.osgGA;
var osgUtil = window.osgUtil;
var osgDB = window.osgDB;
var osgViewer = window.osgViewer;

var viewer;

function setupScene( viewer, sceneData ) {
    // load scene... as usual
    viewer.setSceneData( sceneData );

    // setup manipulator
    viewer.setupManipulator( new osgGA.OrbitManipulator() );
    viewer.getManipulator().setNode( viewer.getSceneData() );
    viewer.getManipulator().computeHomePosition();
    viewer.getManipulator().setEyePosition( [ 0, -10, 5 ] );

    viewer.run();
}

function onSceneLoaded( viewer, data ) {

    new Q( osgDB.parseSceneGraph( data ) ).then( function ( sceneData ) {
        setupScene( viewer, sceneData );
    } );
}

function onURLloaded( url, viewer ) {
    osg.log( 'loading ' + url );
    var req = new XMLHttpRequest();
    req.open( 'GET', url, true );
    req.onload = function () {
        onSceneLoaded( viewer, JSON.parse( req.responseText ) );
        osg.log( 'success ' + url );
    };
    req.onerror = function () {
        osg.log( 'error ' + url );
    };
    req.send( null );
}

// Find the right method, call on correct element
function launchFullscreen( element, hmd ) {
    if ( element.requestFullscreen ) {
        element.requestFullscreen( hmd );
    } else if ( element.mozRequestFullScreen ) {
        element.mozRequestFullScreen( hmd );
    } else if ( element.webkitRequestFullscreen ) {
        element.webkitRequestFullscreen( hmd );
    } else if ( element.msRequestFullscreen ) {
        element.msRequestFullscreen( hmd );
    }
}

function exitFullscreen() {
    if ( document.exitFullscreen ) {
        document.exitFullscreen();
    } else if ( document.mozCancelFullScreen ) {
        document.mozCancelFullScreen();
    } else if ( document.webkitExitFullscreen ) {
        document.webkitExitFullscreen();
    }
}

var vrNode;
var modelNode;
var vrState = false;
var fullscreen = false;

function toggleVR() {

    var sceneData = viewer.getSceneData();

    // Enable VR
    if ( !vrState ) {

        // Detach the model from sceneData and cache it
        modelNode = sceneData.getChildren()[ 0 ];
        sceneData.removeChild( modelNode );

        // If no vrNode (first time vr is toggled), create one
        // The modelNode will be attached to it
        if ( !vrNode ) {
            if ( navigator.getVRDevices )
                vrNode = osgUtil.WebVR.createScene( viewer, modelNode, viewer._eventProxy.Oculus._hmd );
            else
                vrNode = osgUtil.Oculus.createScene( viewer, modelNode );
        }

        // Attach the vrNode to sceneData instead of the model
        sceneData.addChild( vrNode );
    }
    // Disable VR
    else {
        // Detach the vrNode and reattach the modelNode
        sceneData.removeChild( vrNode );
        sceneData.addChild( modelNode );
    }

    vrState = !vrState;
}

// On some platforms, (chrome * android), this event is fired too early
// And the WebVR retrieve the canvas size before the canvas gets resized to fullscreen
// So we could add a little delay via setTimeout to ensure we get the fullscreen size

// Prefixed on Firefox up to v33
document.addEventListener( 'mozfullscreenchange', function () {
    toggleVR();
    fullscreen = !fullscreen;
} );
// Prefixed on Chrome up to v38
document.addEventListener( 'webkitfullscreenchange', function () {
    setTimeout( toggleVR, 500 );
    fullscreen = !fullscreen;
} );
document.addEventListener( 'msfullscreenchange', function () {
    toggleVR();
    fullscreen = !fullscreen;
}, false );
document.addEventListener( 'fullscreenchange', function () {
    toggleVR();
    fullscreen = !fullscreen;
} );

function requestVRFullscreen() {

    if ( !navigator.getVRDevices ) {
        osg.log( 'WebVR Api is not supported by your navigator' );
    }

    var canvas = viewer.getGraphicContext().canvas;

    if ( fullscreen === false )
        launchFullscreen( canvas, {
            vrDisplay: viewer._eventProxy.Oculus.getHmd();
        } );
    else
        exitFullscreen();

    // var fullscreenRect = canvas.getBoundingClientRect();
    // console.log(fullscreenRect.width, fullscreenRect.height);
}

function init() {

    var canvas = document.getElementById( 'View' );

    viewer = new osgViewer.Viewer( canvas );

    viewer.init();
    viewer.setLightingMode( osgViewer.View.LightingMode.SKYLIGHT );

    // onSceneLoaded( viewer, getPokerScene() );
    // setupScene( viewer, getSimpleScene() );
    onURLloaded( 'models/ogre.osgjs', viewer );

    var button = document.getElementById( 'button' );
    button.addEventListener( 'click', requestVRFullscreen, false );

    window.addEventListener( 'keypress',
        function ( event ) {
            if ( event.charCode === 'f'.charCodeAt( 0 ) )
                requestVRFullscreen();
        }, true );

}

window.addEventListener( 'load', init, true );
<commit_msg>Fix dualcam example<commit_after>'use strict';

var viewer;

var OSG = window.OSG;
OSG.globalify();

var osg = window.osg;
var osgGA = window.osgGA;
var osgUtil = window.osgUtil;
var osgDB = window.osgDB;
var osgViewer = window.osgViewer;

var viewer;

function setupScene( viewer, sceneData ) {
    // load scene... as usual
    viewer.setSceneData( sceneData );

    // setup manipulator
    viewer.setupManipulator( new osgGA.OrbitManipulator() );
    viewer.getManipulator().setNode( viewer.getSceneData() );
    viewer.getManipulator().computeHomePosition();
    viewer.getManipulator().setEyePosition( [ 0, -10, 5 ] );

    viewer.run();
}

function onSceneLoaded( viewer, data ) {

    new Q( osgDB.parseSceneGraph( data ) ).then( function ( sceneData ) {
        setupScene( viewer, sceneData );
    } );
}

function onURLloaded( url, viewer ) {
    osg.log( 'loading ' + url );
    var req = new XMLHttpRequest();
    req.open( 'GET', url, true );
    req.onload = function () {
        onSceneLoaded( viewer, JSON.parse( req.responseText ) );
        osg.log( 'success ' + url );
    };
    req.onerror = function () {
        osg.log( 'error ' + url );
    };
    req.send( null );
}

// Find the right method, call on correct element
function launchFullscreen( element, hmd ) {
    if ( element.requestFullscreen ) {
        element.requestFullscreen( hmd );
    } else if ( element.mozRequestFullScreen ) {
        element.mozRequestFullScreen( hmd );
    } else if ( element.webkitRequestFullscreen ) {
        element.webkitRequestFullscreen( hmd );
    } else if ( element.msRequestFullscreen ) {
        element.msRequestFullscreen( hmd );
    }
}

function exitFullscreen() {
    if ( document.exitFullscreen ) {
        document.exitFullscreen();
    } else if ( document.mozCancelFullScreen ) {
        document.mozCancelFullScreen();
    } else if ( document.webkitExitFullscreen ) {
        document.webkitExitFullscreen();
    }
}

var vrNode;
var modelNode;
var vrState = false;
var fullscreen = false;

function toggleVR() {

    var sceneData = viewer.getSceneData();

    // Enable VR
    if ( !vrState ) {

        // Detach the model from sceneData and cache it
        modelNode = sceneData.getChildren()[ 0 ];
        sceneData.removeChild( modelNode );

        // If no vrNode (first time vr is toggled), create one
        // The modelNode will be attached to it
        if ( !vrNode ) {
            if ( navigator.getVRDevices || navigator.mozGetVRDevices )
                vrNode = osgUtil.WebVR.createScene( viewer, modelNode, viewer._eventProxy.Oculus.getHmd() );
            else
                vrNode = osgUtil.Oculus.createScene( viewer, modelNode );
        }

        // Attach the vrNode to sceneData instead of the model
        sceneData.addChild( vrNode );
    }
    // Disable VR
    else {
        // Detach the vrNode and reattach the modelNode
        sceneData.removeChild( vrNode );
        sceneData.addChild( modelNode );
    }

    vrState = !vrState;
}

// On some platforms, (chrome * android), this event is fired too early
// And the WebVR retrieve the canvas size before the canvas gets resized to fullscreen
// So we could add a little delay via setTimeout to ensure we get the fullscreen size

// Prefixed on Firefox up to v33
document.addEventListener( 'mozfullscreenchange', function () {
    toggleVR();
    fullscreen = !fullscreen;
} );
// Prefixed on Chrome up to v38
document.addEventListener( 'webkitfullscreenchange', function () {
    setTimeout( toggleVR, 500 );
    fullscreen = !fullscreen;
} );
document.addEventListener( 'msfullscreenchange', function () {
    toggleVR();
    fullscreen = !fullscreen;
}, false );
document.addEventListener( 'fullscreenchange', function () {
    toggleVR();
    fullscreen = !fullscreen;
} );

function requestVRFullscreen() {

    if ( !navigator.getVRDevices && !navigator.mozGetVRDevices ) {
        osg.log( 'WebVR Api is not supported by your navigator' );
    }

    var canvas = viewer.getGraphicContext().canvas;

    if ( fullscreen === false )
        launchFullscreen( canvas, {
            vrDisplay: viewer._eventProxy.Oculus.getHmd()
        } );
    else
        exitFullscreen();

    // var fullscreenRect = canvas.getBoundingClientRect();
    // console.log(fullscreenRect.width, fullscreenRect.height);
}

function init() {

    var canvas = document.getElementById( 'View' );

    viewer = new osgViewer.Viewer( canvas );

    viewer.init();
    viewer.setLightingMode( osgViewer.View.LightingMode.SKYLIGHT );

    // onSceneLoaded( viewer, getPokerScene() );
    // setupScene( viewer, getSimpleScene() );
    onURLloaded( 'models/ogre.osgjs', viewer );

    var button = document.getElementById( 'button' );
    button.addEventListener( 'click', requestVRFullscreen, false );

    window.addEventListener( 'keypress',
        function ( event ) {
            if ( event.charCode === 'f'.charCodeAt( 0 ) )
                requestVRFullscreen();
        }, true );

}

window.addEventListener( 'load', init, true );
<|endoftext|>