<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 = formalizeWrap(block.options.wrap || options.viewWrap);
                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 add = false;
        var matches;
        var match;
        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])) {
                add = false;
                blocks.push(block);
            }

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

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

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

                // get path from extract option
                matchPath = match.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 = match.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 : match;

                // 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: [0: '
', 1: '
']
     * }
     *
     * @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;
        });

        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;

        for (el in options) {
            if (options.hasOwnProperty(el) === false) {
                continue;
            }

            var value = options[el];
            var preparedVal = JSON.stringify(options[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>Formalize wrap options as soon as possible<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 add = false;
        var matches;
        var match;
        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])) {
                add = false;
                blocks.push(block);
            }

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

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

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

                // get path from extract option
                matchPath = match.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 = match.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 : match;

                // 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: [0: '
', 1: '
']
     * }
     *
     * @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;

        for (el in options) {
            if (options.hasOwnProperty(el) === false) {
                continue;
            }

            var value = options[el];
            var preparedVal = JSON.stringify(options[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>define({
	// Application note on/off keyboard shortcuts.

	// Maps a key to an integer relative to the C *one octave below*
	// middle C (MIDI note number 48).
	//
	// To find the MIDI note number that will be output when a key is pressed,
	// add the relative offset from the note mapping section to this note anchor.
	"noteAnchor": 48,

	// Uused letters to avoid mishaps: 3, 6, and g.
	"keyMap": {
		"1":      {msg:"toggleNote", data:-4}, // GA
		"q":      {msg:"toggleNote", data:-3}, // A
		"2":      {msg:"toggleNote", data:-2}, // AB
		"w":      {msg:"toggleNote", data:-1}, // B
		"e":      {msg:"toggleNote", data:0}, // C
		"4":      {msg:"toggleNote", data:1}, // CD
		"r":      {msg:"toggleNote", data:2}, // D
		"5":      {msg:"toggleNote", data:3}, // DE
		"t":      {msg:"toggleNote", data:4}, // E
		"y":      {msg:"toggleNote", data:5}, // F
		"7":      {msg:"toggleNote", data:6}, // FG
		"u":      {msg:"toggleNote", data:7}, // G
		"a":      {msg:"toggleNote", data:16}, // E
		"z":      {msg:"toggleNote", data:17}, // F
		"s":      {msg:"toggleNote", data:18}, // FG
		"x":      {msg:"toggleNote", data:19}, // G
		"d":      {msg:"toggleNote", data:20}, // GA
		"c":      {msg:"toggleNote", data:21}, // A
		"f":      {msg:"toggleNote", data:22}, // AB
		"v":      {msg:"toggleNote", data:23}, // B
		"b":      {msg:"toggleNote", data:24}, // C
		"h":      {msg:"toggleNote", data:25}, // CD
		"n":      {msg:"toggleNote", data:26}, // D
		"j":      {msg:"toggleNote", data:27}, // DE
		"m":      {msg:"toggleNote", data:28}, // E
		"'":      {msg:"depressSustain"},
		";":      {msg:"retakeSustain"},
		".":      {msg:"releaseSustain"},
		"k":      {msg:"rotateKeyFlatward"},
		"l":      {msg:"rotateKeySharpward"},
		",":      {msg:"setKeyToC"},
		"o":      {msg:"setKeyToNone"},
		"/":      {msg:"toggleMetronome"},
		"ESC":    {msg:"toggleMode"},
		"ENTER":  {msg:"clearNotes"},
		"SPACE":  {msg:"bankChord"},
		"DOWN":   {msg:"bankChord"},
		"RIGHT":  {msg:"openNextExercise"} // not created yet
	},

	// Defines key code -> key name mappings.
	// This is not intended to be comprehensive. These key names
	// should be used in the note and control shortcut mappings.
	"keyCode": {
		"13": "ENTER",
		"27": "ESC",
		"32": "SPACE",
		"37": "LEFT",
		"38": "UP",
		"39": "RIGHT",
		"40": "DOWN",
		"48": "0",
		"49": "1",
		"50": "2",
		"51": "3",
		"52": "4",
		"53": "5",
		"54": "6",
		"55": "7",
		"56": "8",
		"57": "9",
		"65": "a",
		"66": "b",
		"67": "c",
		"68": "d",
		"69": "e",
		"70": "f",
		"71": "g",
		"72": "h",
		"73": "i",
		"74": "j",
		"75": "k",
		"76": "l",
		"77": "m",
		"78": "n",
		"79": "o",
		"80": "p",
		"81": "q",
		"82": "r",
		"83": "s",
		"84": "t",
		"85": "u",
		"86": "v",
		"87": "w",
		"88": "x",
		"89": "y",
		"90": "z",
		"186": ";",
		"188": ",",
		"190": ".",
		"191": "/",
		"192": "GRAVE",
		"222": "'"
	}
});
<commit_msg>Update keyboard_shortcuts.js<commit_after>define({
	// Application note on/off keyboard shortcuts.

	// Maps a key to an integer relative to the C *one octave below*
	// middle C (MIDI note number 48).
	//
	// To find the MIDI note number that will be output when a key is pressed,
	// add the relative offset from the note mapping section to this note anchor.
	"noteAnchor": 48,

	// Uused letters to avoid mishaps: 3, 6, and g.
	"keyMap": {
		"1":      {msg:"toggleNote", data:-4}, // GA
		"q":      {msg:"toggleNote", data:-3}, // A
		"2":      {msg:"toggleNote", data:-2}, // AB
		"w":      {msg:"toggleNote", data:-1}, // B
		"e":      {msg:"toggleNote", data:0}, // C
		"4":      {msg:"toggleNote", data:1}, // CD
		"r":      {msg:"toggleNote", data:2}, // D
		"5":      {msg:"toggleNote", data:3}, // DE
		"t":      {msg:"toggleNote", data:4}, // E
		"y":      {msg:"toggleNote", data:5}, // F
		"7":      {msg:"toggleNote", data:6}, // FG
		"u":      {msg:"toggleNote", data:7}, // G
		"a":      {msg:"toggleNote", data:16}, // E
		"z":      {msg:"toggleNote", data:17}, // F
		"s":      {msg:"toggleNote", data:18}, // FG
		"x":      {msg:"toggleNote", data:19}, // G
		"d":      {msg:"toggleNote", data:20}, // GA
		"c":      {msg:"toggleNote", data:21}, // A
		"f":      {msg:"toggleNote", data:22}, // AB
		"v":      {msg:"toggleNote", data:23}, // B
		"b":      {msg:"toggleNote", data:24}, // C
		"h":      {msg:"toggleNote", data:25}, // CD
		"n":      {msg:"toggleNote", data:26}, // D
		"j":      {msg:"toggleNote", data:27}, // DE
		"m":      {msg:"toggleNote", data:28}, // E
		"'":      {msg:"depressSustain"},
		";":      {msg:"retakeSustain"}, // not working?
		"\\":     {msg:"retakeSustain"},
		".":      {msg:"releaseSustain"},
		"k":      {msg:"rotateKeyFlatward"},
		"l":      {msg:"rotateKeySharpward"},
		",":      {msg:"setKeyToC"},
		"o":      {msg:"setKeyToNone"},
		"/":      {msg:"toggleMetronome"},
		"ESC":    {msg:"toggleMode"},
		"ENTER":  {msg:"clearNotes"},
		"SPACE":  {msg:"bankChord"},
		"DOWN":   {msg:"bankChord"},
		"[":      {msg:"toggleHighlights"}, // not created yet
		"]":      {msg:"toggleAnalysis"}, // not created yet
		"RIGHT":  {msg:"openNextExercise"} // not created yet
	},

	// Defines javascript key code -> key name mappings.
	// This is not intended to be comprehensive. These key names
	// should be used in the note and control shortcut mappings.
	"keyCode": {
		"13": "ENTER",
		"27": "ESC",
		"32": "SPACE",
		"37": "LEFT",
		"38": "UP",
		"39": "RIGHT",
		"40": "DOWN",
		"48": "0",
		"49": "1",
		"50": "2",
		"51": "3",
		"52": "4",
		"53": "5",
		"54": "6",
		"55": "7",
		"56": "8",
		"57": "9",
		"65": "a",
		"66": "b",
		"67": "c",
		"68": "d",
		"69": "e",
		"70": "f",
		"71": "g",
		"72": "h",
		"73": "i",
		"74": "j",
		"75": "k",
		"76": "l",
		"77": "m",
		"78": "n",
		"79": "o",
		"80": "p",
		"81": "q",
		"82": "r",
		"83": "s",
		"84": "t",
		"85": "u",
		"86": "v",
		"87": "w",
		"88": "x",
		"89": "y",
		"90": "z",
		"186": ";",
		"188": ",",
		"190": ".",
		"191": "/",
		"192": "GRAVE",
		"219": "[",
		"220": "\\",
		"221": "]",
		"222": "'"
	}
});
<|endoftext|>


<commit_before>/*jslint unparam: true, browser: true, indent: 2 */

;(function ($, window, document, undefined) {
  'use strict';

  Foundation.libs.topbar = {
    name : 'topbar',

    version: '4.3.2',

    settings : {
      index : 0,
      stickyClass : 'sticky',
      custom_back_text: true,
      back_text: 'Back',
      is_hover: true,
      mobile_show_parent_link: false,
      scrolltop : true, // jump to top when sticky nav menu toggle is clicked
      init : false
    },

    init : function (section, method, options) {
      Foundation.inherit(this, 'data_options addCustomRule');
      var self = this;

      if (typeof method === 'object') {
        $.extend(true, this.settings, method);
      } else if (typeof options !== 'undefined') {
        $.extend(true, this.settings, options);
      }

      if (typeof method !== 'string') {

        $('.top-bar, [data-topbar]').each(function () {
          $.extend(true, self.settings, self.data_options($(this)));
          self.settings.$w = $(window);
          self.settings.$topbar = $(this);
          self.settings.$section = self.settings.$topbar.find('section');
          self.settings.$titlebar = self.settings.$topbar.children('ul').first();
          self.settings.$topbar.data('index', 0);

          var topbarContainer = self.settings.$topbar.parent();
          if(topbarContainer.hasClass('fixed') || topbarContainer.hasClass(self.settings.stickyClass)) {
            self.settings.$topbar.data('height', self.outerHeight(topbarContainer));
            self.settings.$topbar.data('stickyoffset', topbarContainer.offset().top);
          } else {
            self.settings.$topbar.data('height', self.outerHeight(self.settings.$topbar));
          }

          var breakpoint = $("
").insertAfter(self.settings.$topbar);
          self.settings.breakPoint = breakpoint.width();
          breakpoint.remove();

          self.assemble();

          if (self.settings.is_hover) {
            self.settings.$topbar.find('.has-dropdown').addClass('not-click');
          }

          // Pad body when sticky (scrolled) or fixed.
          self.addCustomRule('.f-topbar-fixed { padding-top: ' + self.settings.$topbar.data('height') + 'px }');

          if (self.settings.$topbar.parent().hasClass('fixed')) {
            $('body').addClass('f-topbar-fixed');
          }
        });

        if (!self.settings.init) {
          this.events();
        }

        return this.settings.init;
      } else {
        // fire method
        return this[method].call(this, options);
      }
    },

    toggle: function() {
      var self = this;
      var topbar = $('.top-bar, [data-topbar]'),
          section = topbar.find('section, .section');

      if (self.breakpoint()) {
        if (!self.rtl) {
          section.css({left: '0%'});
          section.find('>.name').css({left: '100%'});
        } else {
          section.css({right: '0%'});
          section.find('>.name').css({right: '100%'});
        }

        section.find('li.moved').removeClass('moved');
        topbar.data('index', 0);

        topbar
          .toggleClass('expanded')
          .css('height', '');
      }

      if(self.settings.scrolltop)
      {
        if (!topbar.hasClass('expanded')) {
          if (topbar.hasClass('fixed')) {
            topbar.parent().addClass('fixed');
            topbar.removeClass('fixed');
            $('body').addClass('f-topbar-fixed');
          }
        } else if (topbar.parent().hasClass('fixed')) {
          if (self.settings.scrolltop) {
            topbar.parent().removeClass('fixed');
            topbar.addClass('fixed');
            $('body').removeClass('f-topbar-fixed');

            window.scrollTo(0,0);
          } else {
              topbar.parent().removeClass('expanded');
          }
        }
      } else {
        if(topbar.parent().hasClass(self.settings.stickyClass)) {
          topbar.parent().addClass('fixed');
        }

        if(topbar.parent().hasClass('fixed')) {
          if (!topbar.hasClass('expanded')) {
            topbar.removeClass('fixed');
            topbar.parent().removeClass('expanded');
            self.updateStickyPositioning();
          } else {
            topbar.addClass('fixed');
            topbar.parent().addClass('expanded');
          }
        }
      }
    },

    timer : null,

    events : function () {
      var self = this;
      $(this.scope)
        .off('.fndtn.topbar')
        .on('click.fndtn.topbar', '.top-bar .toggle-topbar, [data-topbar] .toggle-topbar', function (e) {
          e.preventDefault();
          self.toggle();
        })

        .on('click.fndtn.topbar', '.top-bar li.has-dropdown', function (e) {
          var li = $(this),
              target = $(e.target),
              topbar = li.closest('[data-topbar], .top-bar'),
              is_hover = topbar.data('topbar');

          if(target.data('revealId')) {
            self.toggle();
            return;
          }

          if (self.breakpoint()) return;
          if (self.settings.is_hover && !Modernizr.touch) return;

          e.stopImmediatePropagation();

          if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) {
            e.preventDefault();
          }

          if (li.hasClass('hover')) {
            li
              .removeClass('hover')
              .find('li')
              .removeClass('hover');

            li.parents('li.hover')
              .removeClass('hover');
          } else {
            li.addClass('hover');
          }
        })

        .on('click.fndtn.topbar', '.top-bar .has-dropdown>a, [data-topbar] .has-dropdown>a', function (e) {
          if (self.breakpoint() && $(window).width() != self.settings.breakPoint) {

            e.preventDefault();

            var $this = $(this),
                topbar = $this.closest('.top-bar, [data-topbar]'),
                section = topbar.find('section, .section'),
                dropdownHeight = $this.next('.dropdown').outerHeight(),
                $selectedLi = $this.closest('li');

            topbar.data('index', topbar.data('index') + 1);
            $selectedLi.addClass('moved');

            if (!self.rtl) {
              section.css({left: -(100 * topbar.data('index')) + '%'});
              section.find('>.name').css({left: 100 * topbar.data('index') + '%'});
            } else {
              section.css({right: -(100 * topbar.data('index')) + '%'});
              section.find('>.name').css({right: 100 * topbar.data('index') + '%'});
            }

            topbar.css('height', self.outerHeight($this.siblings('ul'), true) + self.settings.$topbar.data('height'));
          }
        });

      $(window).on('resize.fndtn.topbar', function () {
        var stickyContainer = self.settings.$topbar.parent('.' + this.settings.stickyClass);
        var stickyOffset;

        if (!self.breakpoint()) {
          var doToggle = self.settings.$topbar.hasClass('expanded');
          $('.top-bar, [data-topbar]')
            .css('height', '')
            .removeClass('expanded')
            .find('li')
            .removeClass('hover');

            if(doToggle) {
              self.toggle();
            }
        }

        if(stickyContainer.length > 0) {
          if(stickyContainer.hasClass('fixed')) {
            // Remove the fixed to allow for correct calculation of the offset.
            stickyContainer.removeClass('fixed');

            stickyOffset = stickyContainer.offset().top;
            if($(document.body).hasClass('f-topbar-fixed')) {
              stickyOffset -= self.settings.$topbar.data('height');
            }

            self.settings.$topbar.data('stickyoffset', stickyOffset);
            stickyContainer.addClass('fixed');
          } else {
            stickyOffset = stickyContainer.offset().top;
            self.settings.$topbar.data('stickyoffset', stickyOffset);
          }
        }
      }.bind(this));

      $('body').on('click.fndtn.topbar', function (e) {
        var parent = $(e.target).closest('li').closest('li.hover');

        if (parent.length > 0) {
          return;
        }

        $('.top-bar li, [data-topbar] li').removeClass('hover');
      });

      // Go up a level on Click
      $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back, [data-topbar] .has-dropdown .back', function (e) {
        e.preventDefault();

        var $this = $(this),
            topbar = $this.closest('.top-bar, [data-topbar]'),
            section = topbar.find('section, .section'),
            $movedLi = $this.closest('li.moved'),
            $previousLevelUl = $movedLi.parent();

        topbar.data('index', topbar.data('index') - 1);

        if (!self.rtl) {
          section.css({left: -(100 * topbar.data('index')) + '%'});
          section.find('>.name').css({left: 100 * topbar.data('index') + '%'});
        } else {
          section.css({right: -(100 * topbar.data('index')) + '%'});
          section.find('>.name').css({right: 100 * topbar.data('index') + '%'});
        }

        if (topbar.data('index') === 0) {
          topbar.css('height', '');
        } else {
          topbar.css('height', self.outerHeight($previousLevelUl, true) + self.settings.$topbar.data('height'));
        }

        setTimeout(function () {
          $movedLi.removeClass('moved');
        }, 300);
      });
    },

    breakpoint : function () {
      return $(document).width() <= this.settings.breakPoint || $('html').hasClass('lt-ie9');
    },

    assemble : function () {
      var self = this;
      // Pull element out of the DOM for manipulation
      this.settings.$section.detach();

      this.settings.$section.find('.has-dropdown>a').each(function () {
        var $link = $(this),
            $dropdown = $link.siblings('.dropdown'),
            url = $link.attr('href');

        if (self.settings.mobile_show_parent_link && url && url.length > 1) {
          var $titleLi = $('

' + $link.text() +'
');
        } else {
          var $titleLi = $('

');
        }

        // Copy link to subnav
        if (self.settings.custom_back_text == true) {
          $titleLi.find('h5>a').html(self.settings.back_text);
        } else {
          $titleLi.find('h5>a').html('« ' + $link.html());
        }
        $dropdown.prepend($titleLi);
      });

      // Put element back in the DOM
      this.settings.$section.appendTo(this.settings.$topbar);

      // check for sticky
      this.sticky();
    },

    height : function (ul) {
      var total = 0,
          self = this;

      ul.find('> li').each(function () { total += self.outerHeight($(this), true); });

      return total;
    },

    sticky : function () {
      var $window = $(window),
          self = this;

      $window.scroll(function() {
        self.updateStickyPositioning();
      });
    },

    updateStickyPositioning: function() {
      var klass = '.' + this.settings.stickyClass;
      var $window = $(window);

      if ($(klass).length > 0) {
        var distance = this.settings.$topbar.data('stickyoffset');
        if (!$(klass).hasClass('expanded')) {
          if ($window.scrollTop() > (distance)) {
            if (!$(klass).hasClass('fixed')) {
              $(klass).addClass('fixed');
              $('body').addClass('f-topbar-fixed');
            }
          } else if ($window.scrollTop() <= distance) {
            if ($(klass).hasClass('fixed')) {
              $(klass).removeClass('fixed');
              $('body').removeClass('f-topbar-fixed');
            }
          }
        }
      }
    },

    off : function () {
      $(this.scope).off('.fndtn.topbar');
      $(window).off('.fndtn.topbar');
    },

    reflow : function () {}
  };
}(Foundation.zj, this, this.document));
<commit_msg>don't attempt to interact with self.settings.$topbar if undefined<commit_after>/*jslint unparam: true, browser: true, indent: 2 */

;(function ($, window, document, undefined) {
  'use strict';

  Foundation.libs.topbar = {
    name : 'topbar',

    version: '4.3.2',

    settings : {
      index : 0,
      stickyClass : 'sticky',
      custom_back_text: true,
      back_text: 'Back',
      is_hover: true,
      mobile_show_parent_link: false,
      scrolltop : true, // jump to top when sticky nav menu toggle is clicked
      init : false
    },

    init : function (section, method, options) {
      Foundation.inherit(this, 'data_options addCustomRule');
      var self = this;

      if (typeof method === 'object') {
        $.extend(true, this.settings, method);
      } else if (typeof options !== 'undefined') {
        $.extend(true, this.settings, options);
      }

      if (typeof method !== 'string') {

        $('.top-bar, [data-topbar]').each(function () {
          $.extend(true, self.settings, self.data_options($(this)));
          self.settings.$w = $(window);
          self.settings.$topbar = $(this);
          self.settings.$section = self.settings.$topbar.find('section');
          self.settings.$titlebar = self.settings.$topbar.children('ul').first();
          self.settings.$topbar.data('index', 0);

          var topbarContainer = self.settings.$topbar.parent();
          if(topbarContainer.hasClass('fixed') || topbarContainer.hasClass(self.settings.stickyClass)) {
            self.settings.$topbar.data('height', self.outerHeight(topbarContainer));
            self.settings.$topbar.data('stickyoffset', topbarContainer.offset().top);
          } else {
            self.settings.$topbar.data('height', self.outerHeight(self.settings.$topbar));
          }

          var breakpoint = $("
").insertAfter(self.settings.$topbar);
          self.settings.breakPoint = breakpoint.width();
          breakpoint.remove();

          self.assemble();

          if (self.settings.is_hover) {
            self.settings.$topbar.find('.has-dropdown').addClass('not-click');
          }

          // Pad body when sticky (scrolled) or fixed.
          self.addCustomRule('.f-topbar-fixed { padding-top: ' + self.settings.$topbar.data('height') + 'px }');

          if (self.settings.$topbar.parent().hasClass('fixed')) {
            $('body').addClass('f-topbar-fixed');
          }
        });

        if (!self.settings.init) {
          this.events();
        }

        return this.settings.init;
      } else {
        // fire method
        return this[method].call(this, options);
      }
    },

    toggle: function() {
      var self = this;
      var topbar = $('.top-bar, [data-topbar]'),
          section = topbar.find('section, .section');

      if (self.breakpoint()) {
        if (!self.rtl) {
          section.css({left: '0%'});
          section.find('>.name').css({left: '100%'});
        } else {
          section.css({right: '0%'});
          section.find('>.name').css({right: '100%'});
        }

        section.find('li.moved').removeClass('moved');
        topbar.data('index', 0);

        topbar
          .toggleClass('expanded')
          .css('height', '');
      }

      if(self.settings.scrolltop)
      {
        if (!topbar.hasClass('expanded')) {
          if (topbar.hasClass('fixed')) {
            topbar.parent().addClass('fixed');
            topbar.removeClass('fixed');
            $('body').addClass('f-topbar-fixed');
          }
        } else if (topbar.parent().hasClass('fixed')) {
          if (self.settings.scrolltop) {
            topbar.parent().removeClass('fixed');
            topbar.addClass('fixed');
            $('body').removeClass('f-topbar-fixed');

            window.scrollTo(0,0);
          } else {
              topbar.parent().removeClass('expanded');
          }
        }
      } else {
        if(topbar.parent().hasClass(self.settings.stickyClass)) {
          topbar.parent().addClass('fixed');
        }

        if(topbar.parent().hasClass('fixed')) {
          if (!topbar.hasClass('expanded')) {
            topbar.removeClass('fixed');
            topbar.parent().removeClass('expanded');
            self.updateStickyPositioning();
          } else {
            topbar.addClass('fixed');
            topbar.parent().addClass('expanded');
          }
        }
      }
    },

    timer : null,

    events : function () {
      var self = this;
      $(this.scope)
        .off('.fndtn.topbar')
        .on('click.fndtn.topbar', '.top-bar .toggle-topbar, [data-topbar] .toggle-topbar', function (e) {
          e.preventDefault();
          self.toggle();
        })

        .on('click.fndtn.topbar', '.top-bar li.has-dropdown', function (e) {
          var li = $(this),
              target = $(e.target),
              topbar = li.closest('[data-topbar], .top-bar'),
              is_hover = topbar.data('topbar');

          if(target.data('revealId')) {
            self.toggle();
            return;
          }

          if (self.breakpoint()) return;
          if (self.settings.is_hover && !Modernizr.touch) return;

          e.stopImmediatePropagation();

          if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) {
            e.preventDefault();
          }

          if (li.hasClass('hover')) {
            li
              .removeClass('hover')
              .find('li')
              .removeClass('hover');

            li.parents('li.hover')
              .removeClass('hover');
          } else {
            li.addClass('hover');
          }
        })

        .on('click.fndtn.topbar', '.top-bar .has-dropdown>a, [data-topbar] .has-dropdown>a', function (e) {
          if (self.breakpoint() && $(window).width() != self.settings.breakPoint) {

            e.preventDefault();

            var $this = $(this),
                topbar = $this.closest('.top-bar, [data-topbar]'),
                section = topbar.find('section, .section'),
                dropdownHeight = $this.next('.dropdown').outerHeight(),
                $selectedLi = $this.closest('li');

            topbar.data('index', topbar.data('index') + 1);
            $selectedLi.addClass('moved');

            if (!self.rtl) {
              section.css({left: -(100 * topbar.data('index')) + '%'});
              section.find('>.name').css({left: 100 * topbar.data('index') + '%'});
            } else {
              section.css({right: -(100 * topbar.data('index')) + '%'});
              section.find('>.name').css({right: 100 * topbar.data('index') + '%'});
            }

            topbar.css('height', self.outerHeight($this.siblings('ul'), true) + self.settings.$topbar.data('height'));
          }
        });

      $(window).on('resize.fndtn.topbar', function () {
        if (typeof self.settings.$topbar === 'undefined') { return; }
        var stickyContainer = self.settings.$topbar.parent('.' + this.settings.stickyClass);
        var stickyOffset;

        if (!self.breakpoint()) {
          var doToggle = self.settings.$topbar.hasClass('expanded');
          $('.top-bar, [data-topbar]')
            .css('height', '')
            .removeClass('expanded')
            .find('li')
            .removeClass('hover');

            if(doToggle) {
              self.toggle();
            }
        }

        if(stickyContainer.length > 0) {
          if(stickyContainer.hasClass('fixed')) {
            // Remove the fixed to allow for correct calculation of the offset.
            stickyContainer.removeClass('fixed');

            stickyOffset = stickyContainer.offset().top;
            if($(document.body).hasClass('f-topbar-fixed')) {
              stickyOffset -= self.settings.$topbar.data('height');
            }

            self.settings.$topbar.data('stickyoffset', stickyOffset);
            stickyContainer.addClass('fixed');
          } else {
            stickyOffset = stickyContainer.offset().top;
            self.settings.$topbar.data('stickyoffset', stickyOffset);
          }
        }
      }.bind(this));

      $('body').on('click.fndtn.topbar', function (e) {
        var parent = $(e.target).closest('li').closest('li.hover');

        if (parent.length > 0) {
          return;
        }

        $('.top-bar li, [data-topbar] li').removeClass('hover');
      });

      // Go up a level on Click
      $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back, [data-topbar] .has-dropdown .back', function (e) {
        e.preventDefault();

        var $this = $(this),
            topbar = $this.closest('.top-bar, [data-topbar]'),
            section = topbar.find('section, .section'),
            $movedLi = $this.closest('li.moved'),
            $previousLevelUl = $movedLi.parent();

        topbar.data('index', topbar.data('index') - 1);

        if (!self.rtl) {
          section.css({left: -(100 * topbar.data('index')) + '%'});
          section.find('>.name').css({left: 100 * topbar.data('index') + '%'});
        } else {
          section.css({right: -(100 * topbar.data('index')) + '%'});
          section.find('>.name').css({right: 100 * topbar.data('index') + '%'});
        }

        if (topbar.data('index') === 0) {
          topbar.css('height', '');
        } else {
          topbar.css('height', self.outerHeight($previousLevelUl, true) + self.settings.$topbar.data('height'));
        }

        setTimeout(function () {
          $movedLi.removeClass('moved');
        }, 300);
      });
    },

    breakpoint : function () {
      return $(document).width() <= this.settings.breakPoint || $('html').hasClass('lt-ie9');
    },

    assemble : function () {
      var self = this;
      // Pull element out of the DOM for manipulation
      this.settings.$section.detach();

      this.settings.$section.find('.has-dropdown>a').each(function () {
        var $link = $(this),
            $dropdown = $link.siblings('.dropdown'),
            url = $link.attr('href');

        if (self.settings.mobile_show_parent_link && url && url.length > 1) {
          var $titleLi = $('

' + $link.text() +'
');
        } else {
          var $titleLi = $('

');
        }

        // Copy link to subnav
        if (self.settings.custom_back_text == true) {
          $titleLi.find('h5>a').html(self.settings.back_text);
        } else {
          $titleLi.find('h5>a').html('« ' + $link.html());
        }
        $dropdown.prepend($titleLi);
      });

      // Put element back in the DOM
      this.settings.$section.appendTo(this.settings.$topbar);

      // check for sticky
      this.sticky();
    },

    height : function (ul) {
      var total = 0,
          self = this;

      ul.find('> li').each(function () { total += self.outerHeight($(this), true); });

      return total;
    },

    sticky : function () {
      var $window = $(window),
          self = this;

      $window.scroll(function() {
        self.updateStickyPositioning();
      });
    },

    updateStickyPositioning: function() {
      var klass = '.' + this.settings.stickyClass;
      var $window = $(window);

      if ($(klass).length > 0) {
        var distance = this.settings.$topbar.data('stickyoffset');
        if (!$(klass).hasClass('expanded')) {
          if ($window.scrollTop() > (distance)) {
            if (!$(klass).hasClass('fixed')) {
              $(klass).addClass('fixed');
              $('body').addClass('f-topbar-fixed');
            }
          } else if ($window.scrollTop() <= distance) {
            if ($(klass).hasClass('fixed')) {
              $(klass).removeClass('fixed');
              $('body').removeClass('f-topbar-fixed');
            }
          }
        }
      }
    },

    off : function () {
      $(this.scope).off('.fndtn.topbar');
      $(window).off('.fndtn.topbar');
    },

    reflow : function () {}
  };
}(Foundation.zj, this, this.document));
<|endoftext|>


<commit_before>/*
 * grunt-partial-extract
 * https://github.com/tilmanjusten/grunt-partial-extract
 *
 * Copyright (c) 2014 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 options = {};

  grunt.registerMultiTask('partial-extract', 'Extract partials from any text based file and write to distinct file.', function() {
    // Merge task-specific and/or target-specific options with these defaults.
    options = this.options({
      pattern: [
        /<\!--\s*extract:\s*(([\w\/-_]+\/)([\w_\.-]+))\s*-->/,
        /<\!--\s*endextract\s*-->/
      ],
      partialWrap: [
        '<template id="partial">',
        '</template>'
      ]
    });

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

    var existingFiles = [];

    // 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);

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

      // Write blocks to separate files
      blocks.filter(function (block) {
        if (existingFiles.indexOf(block.dest) !== -1) {
          grunt.verbose.warn("Skip file " + block.dest + " which already exists.");
          return false;
        } else {
          return true;
        }
      }).map(function (block) {
        var lines = block.lines.map(properIndentation);
        var leadingWhitespace = lines.map(countWhitespace);
        var crop = leadingWhitespace.reduce(getLeadingWhitespace);

        lines = trimLines(lines, crop);

        if (options.partialWrap.length) {
          lines.unshift(options.partialWrap[0]);
          lines.push(options.partialWrap[1]);
        }

        grunt.file.write(options.dest + block.dest, lines.join(grunt.util.linefeed));
        existingFiles.push(block.dest);
      });

      grunt.verbose.writeln();
    });

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

  /**
   * extract partials
   *
   * @param lines
   * @returns {Array}
   */
  function getPartials(lines) {
    var block;
    var add = false;
    var match;
    var blocks = [];

    // Import blocks from file
    lines.forEach(function (line) {
      if (line.match(options.pattern[1])) {
        add = false;
        blocks.push(block);
      }

      if (add) {
        block.lines.push(line);
      }

      if (match = line.match(options.pattern[0])) {
        add = true;
        block = {dest: match[1], lines: []};
      }
    });

    return blocks;
  }

  /**
   * replace tabs by four spaces
   *
   * @param line
   * @return string
   */
  function properIndentation(line) {
    return line.replace(/\t/, '    ');
  }

  /**
   * 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);
    });
  }
};
<commit_msg>Add option wrap that allows wrapping the partial code in additional markup<commit_after>/*
 * grunt-partial-extract
 * https://github.com/tilmanjusten/grunt-partial-extract
 *
 * Copyright (c) 2014 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 options = {};

  grunt.registerMultiTask('partial-extract', 'Extract partials from any text based file and write to distinct file.', function() {
    // Merge task-specific and/or target-specific options with these defaults.
    options = this.options({
      pattern: [
        /<\!--\s*extract:\s*(([\w\/-_]+\/)([\w_\.-]+))(.*)-->/,
        /<\!--\s*endextract\s*-->/
      ],
      partialWrap: [
        '<template id="partial">',
        '</template>'
      ],
      wrap: []
    });

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

    var existingFiles = [];

    // 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);

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

      // Write blocks to separate files
      blocks.filter(function (block) {
        if (existingFiles.indexOf(block.dest) !== -1) {
          grunt.verbose.warn("Skip file " + block.dest + " which already exists.");
          return false;
        } else {
          return true;
        }
      }).map(function (block) {
        var lines = block.lines.map(properIndentation);
        var leadingWhitespace = lines.map(countWhitespace);
        var crop = leadingWhitespace.reduce(getLeadingWhitespace);
        var wrap = block.options.wrap || options.wrap;

        lines = trimLines(lines, crop);

        // wrap partial if inline option wrap: exists
        if (wrap.length) {
          lines = raiseIndent(lines);
          lines.unshift('');
          lines.unshift(block.options.wrap[0] || '');
          lines.push('');
          lines.push(block.options.wrap[1] || '');
        }

        // add partialWrap
        if (options.partialWrap.length) {
          lines.unshift(options.partialWrap[0]);
          lines.push(options.partialWrap[1]);
        }

        grunt.file.write(options.dest + block.dest, lines.join(grunt.util.linefeed));
        existingFiles.push(block.dest);
      });

      grunt.verbose.writeln();
    });

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

  /**
   * extract partials
   *
   * @param lines
   * @returns {Array}
   */
  function getPartials(lines) {
    var block;
    var add = false;
    var match;
    var blocks = [];

    // Import blocks from file
    lines.forEach(function (line) {
      if (line.match(options.pattern[1])) {
        add = false;
        blocks.push(block);
      }

      if (add) {
        block.lines.push(line);
      }

      if (match = line.match(options.pattern[0])) {
        add = true;
        block = {dest: match[1], lines: [], options: getBlockOptions(match[0])};
      }
    });

    return blocks;
  }

  /**
   * replace tabs by four spaces
   *
   * @param line
   * @return string
   */
  function properIndentation(line) {
    return line.replace(/\t/, '    ');
  }

  /**
   * 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',
   *   wrap: [0: '
', 1: '
']
   * }
   *
   * @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 || false;
    });
    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;
    });

    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;
    });
  }
};
<|endoftext|>


<commit_before>//var input = document.getElementsByTagName("pre")[0].innerText;

var input =  "^>v<";

function node(count) {
	this.visitCount = count;
}

//santa visits first house
var block = [[new node(1)]];

void calcHouses = function(dir){
	switch(expression) {
	    case '^':
	        block.unshift()
	        break;
	    case '>':
	        //code block
	        break;
        case 'v':
	        //code block
	        break;
        case '<':
	        //code block
	        break;
	    default:
	        default code block
	}
}

var housesVisited = 0;

for (var i = 0; i < input.length; i++) {
	calcHouse(input[i]);
};

<commit_msg>no message<commit_after>var input = document.getElementsByTagName("pre")[0].innerText;

//santa visits first house
var block = {}; block[[0,0]] = 1;
var santa = {x: 0, y:0} //x, y

var calcHouses = function(dir, person){
    if(dir == '^' && !([person.x, ++person.y] in block)){
    	block[[person.x, person.y]] = 1;
    } else if(dir == '>' && !([++person.x, person.y] in block)){
    	block[[person.x, person.y]] = 1;
    } else if(dir == 'v' && !([person.x, --person.y] in block)){
    	block[[person.x, person.y]] = 1;
    } else if (dir == '<' && !([--person.x, person.y] in block)){
    	block[[person.x, person.y]] = 1;
    }
}

//part 1
for (var i = 0; i < input.length; i++) {
	calcHouses(input[i], santa);
};
console.log(Object.keys(block).length);

//part 2
var block = {}; block[[0,0]] = 1;
var santa = {x: 0, y:0} //x, y
var robo = {x: 0, y:0} //x, y

for (var i = 0; i < input.length; i++) {
	calcHouses(input[i], (i%2 == 0) ? santa : robo);
};

console.log(Object.keys(block).length);

<|endoftext|>


<commit_before>!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),n.wellknown=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
module.exports = parse;
module.exports.parse = parse;
module.exports.stringify = stringify;

var numberRegexp = /^[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/;

 /*
 * Parse WKT and return GeoJSON.
 *
 * @param {string} _ A WKT geometry
 * @return {?Object} A GeoJSON geometry object
 */
function parse(_) {
    var parts = _.split(";"),
        _ = parts.pop(),
        srid = (parts.shift() || "").split("=").pop();

    var i = 0;

    function $(re) {
        var match = _.substring(i).match(re);
        if (!match) return null;
        else {
            i += match[0].length;
            return match[0];
        }
    }

    function crs(obj) {
        if (obj && srid.match(/\d+/)) {
            obj.crs = {
                type: 'name',
                properties: {
                    name: 'urn:ogc:def:crs:EPSG::' + srid
                }
            };
        }

        return obj;
    }

    function white() { $(/^\s*/); }

    function multicoords() {
        white();
        var depth = 0, rings = [], stack = [rings],
            pointer = rings, elem;

        while (elem =
            $(/^(\()/) ||
            $(/^(\))/) ||
            $(/^(\,)/) ||
            $(numberRegexp)) {
            if (elem == '(') {
                stack.push(pointer);
                pointer = [];
                stack[stack.length - 1].push(pointer);
                depth++;
            } else if (elem == ')') {
                pointer = stack.pop();
                // the stack was empty, input was malformed
                if (!pointer) return;
                depth--;
                if (depth === 0) break;
            } else if (elem === ',') {
                pointer = [];
                stack[stack.length - 1].push(pointer);
            } else if (!isNaN(parseFloat(elem))) {
                pointer.push(parseFloat(elem));
            } else {
                return null;
            }
            white();
        }

        if (depth !== 0) return null;
        return rings;
    }

    function coords() {
        var list = [], item, pt;
        while (pt =
            $(numberRegexp) ||
            $(/^(\,)/)) {
            if (pt == ',') {
                list.push(item);
                item = [];
            } else {
                if (!item) item = [];
                item.push(parseFloat(pt));
            }
            white();
        }
        if (item) list.push(item);
        return list.length ? list : null;
    }

    function point() {
        if (!$(/^(point)/i)) return null;
        white();
        if (!$(/^(\()/)) return null;
        var c = coords();
        if (!c) return null;
        white();
        if (!$(/^(\))/)) return null;
        return {
            type: 'Point',
            coordinates: c[0]
        };
    }

    function multipoint() {
        if (!$(/^(multipoint)/i)) return null;
        white();
        var c = multicoords();
        if (!c) return null;
        white();
        return {
            type: 'MultiPoint',
            coordinates: c
        };
    }

    function multilinestring() {
        if (!$(/^(multilinestring)/i)) return null;
        white();
        var c = multicoords();
        if (!c) return null;
        white();
        return {
            type: 'MultiLineString',
            coordinates: c
        };
    }

    function linestring() {
        if (!$(/^(linestring)/i)) return null;
        white();
        if (!$(/^(\()/)) return null;
        var c = coords();
        if (!c) return null;
        if (!$(/^(\))/)) return null;
        return {
            type: 'LineString',
            coordinates: c
        };
    }

    function polygon() {
        if (!$(/^(polygon)/i)) return null;
        white();
        return {
            type: 'Polygon',
            coordinates: multicoords()
        };
    }

    function multipolygon() {
        if (!$(/^(multipolygon)/i)) return null;
        white();
        return {
            type: 'MultiPolygon',
            coordinates: multicoords()
        };
    }

    function geometrycollection() {
        var geometries = [], geometry;

        if (!$(/^(geometrycollection)/i)) return null;
        white();

        if (!$(/^(\()/)) return null;
        while (geometry = root()) {
            geometries.push(geometry);
            white();
            $(/^(\,)/);
            white();
        }
        if (!$(/^(\))/)) return null;

        return {
            type: 'GeometryCollection',
            geometries: geometries
        };
    }

    function root() {
        return point() ||
            linestring() ||
            polygon() ||
            multipoint() ||
            multilinestring() ||
            multipolygon() ||
            geometrycollection();
    }

    return crs(root());
}

/**
 * Stringifies a GeoJSON object into WKT
 */
function stringify(gj) {
    if (gj.type === 'Feature') {
        gj = gj.geometry;
    }

    function pairWKT(c) {
        if (c.length === 2) {
            return c[0] + ' ' + c[1];
        } else if (c.length === 3) {
            return c[0] + ' ' + c[1] + ' ' + c[2];
        }
    }

    function ringWKT(r) {
        return r.map(pairWKT).join(', ');
    }

    function ringsWKT(r) {
        return r.map(ringWKT).map(wrapParens).join(', ');
    }

    function multiRingsWKT(r) {
        return r.map(ringsWKT).map(wrapParens).join(', ');
    }

    function wrapParens(s) { return '(' + s + ')'; }

    switch (gj.type) {
        case 'Point':
            return 'POINT (' + pairWKT(gj.coordinates) + ')';
        case 'LineString':
            return 'LINESTRING (' + ringWKT(gj.coordinates) + ')';
        case 'Polygon':
            return 'POLYGON (' + ringsWKT(gj.coordinates) + ')';
        case 'MultiPoint':
            return 'MULTIPOINT (' + ringWKT(gj.coordinates) + ')';
        case 'MultiPolygon':
            return 'MULTIPOLYGON (' + multiRingsWKT(gj.coordinates) + ')';
        case 'MultiLineString':
            return 'MULTILINESTRING (' + ringsWKT(gj.coordinates) + ')';
        case 'GeometryCollection':
            return 'GEOMETRYCOLLECTION (' + gj.geometries.map(stringify).join(', ') + ')';
        default:
            throw new Error('stringify requires a valid GeoJSON Feature or geometry object as input');
    }
}

},{}]},{},[1])
(1)
});<commit_msg>Rebuild standalone<commit_after>(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.wellknown = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*eslint-disable no-cond-assign */
module.exports = parse;
module.exports.parse = parse;
module.exports.stringify = stringify;

var numberRegexp = /[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/;
// Matches sequences like '100 100' or '100 100 100'.
var tuples = new RegExp('^' + numberRegexp.source + '\\s' + numberRegexp.source + '(\\s' + numberRegexp.source + ')?');

/*
 * Parse WKT and return GeoJSON.
 *
 * @param {string} _ A WKT geometry
 * @return {?Object} A GeoJSON geometry object
 */
function parse (input) {
  var parts = input.split(';');
  var _ = parts.pop();
  var srid = (parts.shift() || '').split('=').pop();

  var i = 0;

  function $ (re) {
    var match = _.substring(i).match(re);
    if (!match) return null;
    else {
      i += match[0].length;
      return match[0];
    }
  }

  function crs (obj) {
    if (obj && srid.match(/\d+/)) {
      obj.crs = {
        type: 'name',
        properties: {
          name: 'urn:ogc:def:crs:EPSG::' + srid
        }
      };
    }

    return obj;
  }

  function white () { $(/^\s*/); }

  function multicoords () {
    white();
    var depth = 0;
    var rings = [];
    var stack = [rings];
    var pointer = rings;
    var elem;

    while (elem =
           $(/^(\()/) ||
             $(/^(\))/) ||
               $(/^(\,)/) ||
                 $(tuples)) {
      if (elem === '(') {
        stack.push(pointer);
        pointer = [];
        stack[stack.length - 1].push(pointer);
        depth++;
      } else if (elem === ')') {
        // For the case: Polygon(), ...
        if (pointer.length === 0) return null;

        pointer = stack.pop();
        // the stack was empty, input was malformed
        if (!pointer) return null;
        depth--;
        if (depth === 0) break;
      } else if (elem === ',') {
        pointer = [];
        stack[stack.length - 1].push(pointer);
      } else if (!elem.split(/\s/g).some(isNaN)) {
        Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat));
      } else {
        return null;
      }
      white();
    }

    if (depth !== 0) return null;

    return rings;
  }

  function coords () {
    var list = [];
    var item;
    var pt;
    while (pt =
           $(tuples) ||
             $(/^(\,)/)) {
      if (pt === ',') {
        list.push(item);
        item = [];
      } else if (!pt.split(/\s/g).some(isNaN)) {
        if (!item) item = [];
        Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat));
      }
      white();
    }

    if (item) list.push(item);
    else return null;

    return list.length ? list : null;
  }

  function point () {
    if (!$(/^(point)/i)) return null;
    white();
    if (!$(/^(\()/)) return null;
    var c = coords();
    if (!c) return null;
    white();
    if (!$(/^(\))/)) return null;
    return {
      type: 'Point',
      coordinates: c[0]
    };
  }

  function multipoint () {
    if (!$(/^(multipoint)/i)) return null;
    white();
    var c = multicoords();
    if (!c) return null;
    white();
    return {
      type: 'MultiPoint',
      coordinates: c
    };
  }

  function multilinestring () {
    if (!$(/^(multilinestring)/i)) return null;
    white();
    var c = multicoords();
    if (!c) return null;
    white();
    return {
      type: 'MultiLineString',
      coordinates: c
    };
  }

  function linestring () {
    if (!$(/^(linestring)/i)) return null;
    white();
    if (!$(/^(\()/)) return null;
    var c = coords();
    if (!c) return null;
    if (!$(/^(\))/)) return null;
    return {
      type: 'LineString',
      coordinates: c
    };
  }

  function polygon () {
    if (!$(/^(polygon)/i)) return null;
    white();
    var c = multicoords();
    if (!c) return null;
    return {
      type: 'Polygon',
      coordinates: c
    };
  }

  function multipolygon () {
    if (!$(/^(multipolygon)/i)) return null;
    white();
    var c = multicoords();
    if (!c) return null;
    return {
      type: 'MultiPolygon',
      coordinates: c
    };
  }

  function geometrycollection () {
    var geometries = [];
    var geometry;

    if (!$(/^(geometrycollection)/i)) return null;
    white();

    if (!$(/^(\()/)) return null;
    while (geometry = root()) {
      geometries.push(geometry);
      white();
      $(/^(\,)/);
      white();
    }
    if (!$(/^(\))/)) return null;

    return {
      type: 'GeometryCollection',
      geometries: geometries
    };
  }

  function root () {
    return point() ||
      linestring() ||
      polygon() ||
      multipoint() ||
      multilinestring() ||
      multipolygon() ||
      geometrycollection();
  }

  return crs(root());
}

/**
 * Stringifies a GeoJSON object into WKT
 */
function stringify (gj) {
  if (gj.type === 'Feature') {
    gj = gj.geometry;
  }

  function pairWKT (c) {
    if (c.length === 2) {
      return c[0] + ' ' + c[1];
    } else if (c.length === 3) {
      return c[0] + ' ' + c[1] + ' ' + c[2];
    }
  }

  function ringWKT (r) {
    return r.map(pairWKT).join(', ');
  }

  function ringsWKT (r) {
    return r.map(ringWKT).map(wrapParens).join(', ');
  }

  function multiRingsWKT (r) {
    return r.map(ringsWKT).map(wrapParens).join(', ');
  }

  function wrapParens (s) { return '(' + s + ')'; }

  switch (gj.type) {
    case 'Point':
      return 'POINT (' + pairWKT(gj.coordinates) + ')';
    case 'LineString':
      return 'LINESTRING (' + ringWKT(gj.coordinates) + ')';
    case 'Polygon':
      return 'POLYGON (' + ringsWKT(gj.coordinates) + ')';
    case 'MultiPoint':
      return 'MULTIPOINT (' + ringWKT(gj.coordinates) + ')';
    case 'MultiPolygon':
      return 'MULTIPOLYGON (' + multiRingsWKT(gj.coordinates) + ')';
    case 'MultiLineString':
      return 'MULTILINESTRING (' + ringsWKT(gj.coordinates) + ')';
    case 'GeometryCollection':
      return 'GEOMETRYCOLLECTION (' + gj.geometries.map(stringify).join(', ') + ')';
    default:
      throw new Error('stringify requires a valid GeoJSON Feature or geometry object as input');
  }
}

},{}]},{},[1])(1)
});<|endoftext|>


<commit_before>/**
  * Simple gesture controllers with some common gestures that emit
  * gesture events.
  *
  * Much adapted from github.com/EightMedia/Hammer.js
  */
(function(window, document, framework) {
  // Gesture support
  framework.Gesture = {}

  // Simple touch gesture that triggers an event when an element is touched
  framework.Gesture.Touch = {
    handle: function(e) {
      framework.EventController.trigger('touch', {
        cancelable: true,
        bubbles: true
      });
    }
  };

  // Simple tap gesture
  framework.Gesture.Tap = {
    handle: function(e) {
      switch(e.type) {
        case 'touchstart':
          // We are now touching
          this._isTouching = true;
          // Reset the movement indicator
          this._hasMoved = false;
          break;
        case 'touchmove':
          this._touchStartX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
          this._touchStartY = e.type === 'touchstart' ? e.touches[0].clientY : e.clientY;

          var x = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX,
              y = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY;

          // Check if the finger moved more than 10px, and then indicate we should cancel the tap
          if (Math.abs(x - this._touchStartX) > 10 || Math.abs(y - this._touchStartY) > 10) {
            this._hasMoved = true;
          }
          break;
        case 'touchend':
          if(this._hasMoved == false) {
            framework.EventController.trigger('tap', {
              cancelable: true,
              bubbles: true
            });
          }
          break;
      }
    }
  };

  // A swipe-left gesture that emits the 'swipeleft' event when a left swipe
  // is performed
  framework.Gesture.SwipeLeft = {
    handle: function(e) {
    }
  };

  framework.GestureController  = {
    gestures: [
      framework.Gesture.Touch,
      framework.Gesture.Tap
    ],
    detectGesture: function(e) {
      var i;

      for(i = 0; i < this.gestures.length; i++) {
        if(this.gestures[i].handle(e)) {
          console.log('GESTURECONTROLLER: Gesture handled');
          return;
        }
      }
    },

    /**
     * find if a node is in the given parent
     * used for event delegation tricks
     * @param   {HTMLElement}   node
     * @param   {HTMLElement}   parent
     * @returns {boolean}       has_parent
     */
    hasParent: function(node, parent) {
        while(node){
            if(node == parent) {
                return true;
            }
            node = node.parentNode;
        }
        return false;
    },

    /**
     * get the center of all the touches
     * @param   {Array}     touches
     * @returns {Object}    center
     */
    getCenter: function getCenter(touches) {
        var valuesX = [], valuesY = [];

        for(var t= 0,len=touches.length; t<len; t++) {
            valuesX.push(touches[t].pageX);
            valuesY.push(touches[t].pageY);
        }

        return {
            pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),
            pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)
        };
    },

    /**
     * calculate the velocity between two points
     * @param   {Number}    delta_time
     * @param   {Number}    delta_x
     * @param   {Number}    delta_y
     * @returns {Object}    velocity
     */
    getVelocity: function getVelocity(delta_time, delta_x, delta_y) {
        return {
            x: Math.abs(delta_x / delta_time) || 0,
            y: Math.abs(delta_y / delta_time) || 0
        };
    },

    /**
     * calculate the angle between two coordinates
     * @param   {Touch}     touch1
     * @param   {Touch}     touch2
     * @returns {Number}    angle
     */
    getAngle: function getAngle(touch1, touch2) {
        var y = touch2.pageY - touch1.pageY,
            x = touch2.pageX - touch1.pageX;
        return Math.atan2(y, x) * 180 / Math.PI;
    },

    /**
     * angle to direction define
     * @param   {Touch}     touch1
     * @param   {Touch}     touch2
     * @returns {String}    direction constant, like Hammer.DIRECTION_LEFT
     */
    getDirection: function getDirection(touch1, touch2) {
        var x = Math.abs(touch1.pageX - touch2.pageX),
            y = Math.abs(touch1.pageY - touch2.pageY);

        if(x >= y) {
            return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
        }
        else {
            return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
        }
    },

    /**
     * calculate the distance between two touches
     * @param   {Touch}     touch1
     * @param   {Touch}     touch2
     * @returns {Number}    distance
     */
    getDistance: function getDistance(touch1, touch2) {
        var x = touch2.pageX - touch1.pageX,
            y = touch2.pageY - touch1.pageY;
        return Math.sqrt((x*x) + (y*y));
    },

    /**
     * calculate the scale factor between two touchLists (fingers)
     * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
     * @param   {Array}     start
     * @param   {Array}     end
     * @returns {Number}    scale
     */
    getScale: function getScale(start, end) {
        // need two fingers...
        if(start.length >= 2 && end.length >= 2) {
            return this.getDistance(end[0], end[1]) /
                this.getDistance(start[0], start[1]);
        }
        return 1;
    },

    /**
     * calculate the rotation degrees between two touchLists (fingers)
     * @param   {Array}     start
     * @param   {Array}     end
     * @returns {Number}    rotation
     */
    getRotation: function getRotation(start, end) {
        // need two fingers
        if(start.length >= 2 && end.length >= 2) {
            return this.getAngle(end[1], end[0]) -
                this.getAngle(start[1], start[0]);
        }
        return 0;
    },

    /**
     * boolean if the direction is vertical
     * @param    {String}    direction
     * @returns  {Boolean}   is_vertical
     */
    isVertical: function isVertical(direction) {
        return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN);
    },

    /**
     * stop browser default behavior with css props
     * @param   {HtmlElement}   element
     * @param   {Object}        css_props
     */
    stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) {
        var prop,
            vendors = ['webkit','khtml','moz','Moz','ms','o',''];

        if(!css_props || !element.style) {
            return;
        }

        // with css properties for modern browsers
        for(var i = 0; i < vendors.length; i++) {
            for(var p in css_props) {
                if(css_props.hasOwnProperty(p)) {
                    prop = p;

                    // vender prefix at the property
                    if(vendors[i]) {
                        prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
                    }

                    // set the style
                    element.style[prop] = css_props[p];
                }
            }
        }

        // also the disable onselectstart
        if(css_props.userSelect == 'none') {
            element.onselectstart = function() {
                return false;
            };
        }

        // and disable ondragstart
        if(css_props.userDrag == 'none') {
            element.ondragstart = function() {
                return false;
            };
        }
    }

  }

})(this, document, FM = this.FM || {});
<commit_msg>Touch and tap gestures working<commit_after>/**
  * Simple gesture controllers with some common gestures that emit
  * gesture events.
  *
  * Much adapted from github.com/EightMedia/Hammer.js
  */
(function(window, document, framework) {
  // Gesture support
  framework.Gesture = {}

  // Simple touch gesture that triggers an event when an element is touched
  framework.Gesture.Touch = {
    handle: function(e) {
      if(e.type == 'touchstart') {
        framework.EventController.trigger('touch', {
          cancelable: true,
          bubbles: true
        });
      }
    }
  };

  // Simple tap gesture
  framework.Gesture.Tap = {
    handle: function(e) {
      switch(e.type) {
        case 'touchstart':
          this._touchStartX = e.touches[0].clientX;
          this._touchStartY = e.touches[0].clientY;

          // We are now touching
          this._isTouching = true;
          // Reset the movement indicator
          this._hasMoved = false;
          break;
        case 'touchmove':
          var x = e.touches[0].clientX;
              y = e.touches[0].clientY;

          // Check if the finger moved more than 10px, and then indicate we should cancel the tap
          if (Math.abs(x - this._touchStartX) > 10 || Math.abs(y - this._touchStartY) > 10) {
            console.log('HAS MOVED');
            this._hasMoved = true;
          }
          break;
        case 'touchend':
          if(this._hasMoved == false) {
            framework.EventController.trigger('tap', {
              cancelable: true,
              bubbles: true
            });
          }
          break;
      }
    }
  };

  // A swipe-left gesture that emits the 'swipeleft' event when a left swipe
  // is performed
  framework.Gesture.SwipeLeft = {
    handle: function(e) {
    }
  };

  framework.GestureController  = {
    gestures: [
      framework.Gesture.Touch,
      framework.Gesture.Tap
    ],
    detectGesture: function(e) {
      var i;

      for(i = 0; i < this.gestures.length; i++) {
        if(this.gestures[i].handle(e)) {
          console.log('GESTURECONTROLLER: Gesture handled');
          return;
        }
      }
    },

    /**
     * find if a node is in the given parent
     * used for event delegation tricks
     * @param   {HTMLElement}   node
     * @param   {HTMLElement}   parent
     * @returns {boolean}       has_parent
     */
    hasParent: function(node, parent) {
        while(node){
            if(node == parent) {
                return true;
            }
            node = node.parentNode;
        }
        return false;
    },

    /**
     * get the center of all the touches
     * @param   {Array}     touches
     * @returns {Object}    center
     */
    getCenter: function getCenter(touches) {
        var valuesX = [], valuesY = [];

        for(var t= 0,len=touches.length; t<len; t++) {
            valuesX.push(touches[t].pageX);
            valuesY.push(touches[t].pageY);
        }

        return {
            pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),
            pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)
        };
    },

    /**
     * calculate the velocity between two points
     * @param   {Number}    delta_time
     * @param   {Number}    delta_x
     * @param   {Number}    delta_y
     * @returns {Object}    velocity
     */
    getVelocity: function getVelocity(delta_time, delta_x, delta_y) {
        return {
            x: Math.abs(delta_x / delta_time) || 0,
            y: Math.abs(delta_y / delta_time) || 0
        };
    },

    /**
     * calculate the angle between two coordinates
     * @param   {Touch}     touch1
     * @param   {Touch}     touch2
     * @returns {Number}    angle
     */
    getAngle: function getAngle(touch1, touch2) {
        var y = touch2.pageY - touch1.pageY,
            x = touch2.pageX - touch1.pageX;
        return Math.atan2(y, x) * 180 / Math.PI;
    },

    /**
     * angle to direction define
     * @param   {Touch}     touch1
     * @param   {Touch}     touch2
     * @returns {String}    direction constant, like Hammer.DIRECTION_LEFT
     */
    getDirection: function getDirection(touch1, touch2) {
        var x = Math.abs(touch1.pageX - touch2.pageX),
            y = Math.abs(touch1.pageY - touch2.pageY);

        if(x >= y) {
            return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
        }
        else {
            return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
        }
    },

    /**
     * calculate the distance between two touches
     * @param   {Touch}     touch1
     * @param   {Touch}     touch2
     * @returns {Number}    distance
     */
    getDistance: function getDistance(touch1, touch2) {
        var x = touch2.pageX - touch1.pageX,
            y = touch2.pageY - touch1.pageY;
        return Math.sqrt((x*x) + (y*y));
    },

    /**
     * calculate the scale factor between two touchLists (fingers)
     * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
     * @param   {Array}     start
     * @param   {Array}     end
     * @returns {Number}    scale
     */
    getScale: function getScale(start, end) {
        // need two fingers...
        if(start.length >= 2 && end.length >= 2) {
            return this.getDistance(end[0], end[1]) /
                this.getDistance(start[0], start[1]);
        }
        return 1;
    },

    /**
     * calculate the rotation degrees between two touchLists (fingers)
     * @param   {Array}     start
     * @param   {Array}     end
     * @returns {Number}    rotation
     */
    getRotation: function getRotation(start, end) {
        // need two fingers
        if(start.length >= 2 && end.length >= 2) {
            return this.getAngle(end[1], end[0]) -
                this.getAngle(start[1], start[0]);
        }
        return 0;
    },

    /**
     * boolean if the direction is vertical
     * @param    {String}    direction
     * @returns  {Boolean}   is_vertical
     */
    isVertical: function isVertical(direction) {
        return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN);
    },

    /**
     * stop browser default behavior with css props
     * @param   {HtmlElement}   element
     * @param   {Object}        css_props
     */
    stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) {
        var prop,
            vendors = ['webkit','khtml','moz','Moz','ms','o',''];

        if(!css_props || !element.style) {
            return;
        }

        // with css properties for modern browsers
        for(var i = 0; i < vendors.length; i++) {
            for(var p in css_props) {
                if(css_props.hasOwnProperty(p)) {
                    prop = p;

                    // vender prefix at the property
                    if(vendors[i]) {
                        prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
                    }

                    // set the style
                    element.style[prop] = css_props[p];
                }
            }
        }

        // also the disable onselectstart
        if(css_props.userSelect == 'none') {
            element.onselectstart = function() {
                return false;
            };
        }

        // and disable ondragstart
        if(css_props.userDrag == 'none') {
            element.ondragstart = function() {
                return false;
            };
        }
    }

  }

})(this, document, FM = this.FM || {});
<|endoftext|>