<commit_before>'use strict';

module.exports = function(grunt) {

    var pluginName = 'gruntNodeSass',
        sass = require('node-sass'),
        fs = require('fs'),
        path = require('path'),
        mkdirp =  require('mkdirp'),
        glob = require('glob');

    grunt.registerTask(pluginName, '', function() {

        var config = grunt.config(pluginName);

        // verify settings
        if (!config)
            grunt.log.error(pluginName + ' could not find expected config item "' + pluginName +'".');

        var done = this.async();
        var scssPath = config.scssPath,
            cssOutFolder = config.cssFolder;

        if (!scssPath)
            grunt.log.error(pluginName + ' could not find expected value "scssPath".');

        if (!cssOutFolder)
            grunt.log.error(pluginName + ' could not find expected value for "cssOutFolder".');

        if (!fs.existsSync(cssOutFolder))
            mkdirp.sync(cssOutFolder);

        glob(scssPath, config.globOptions, function (er, files) {

            if (er){
                console.log(er)
                done();
                return;
            }

            var processedCount = 0;
            function checkIfDone(){
                processedCount ++;
                if (processedCount === files.length){
                    done();
                }
            }

            files.forEach(function(file, i ){
                var outfile = path.join(
                    cssOutFolder,
                    path.basename(file).substr(0, path.basename(file).length - 5) + '.css'); // remove .scss extension

                // ignore partials
                if (path.basename(file).substr(0, 1) === '_'){
                    checkIfDone();
                    return;
                }

                var result = sass.render({
                    file: file,
                    sourceComments: true
                }, function(err, result){

                    if (err){
                        console.log('error compiling : ' + file);
                        console.log(err);
                    } else {
                        fs.writeFileSync(outfile, result.css);
                        console.log(pluginName + ' compiled ' + outfile);
                    }

                    checkIfDone();

                });

            });

        });

    });
};
<commit_msg>improved error handling<commit_after>'use strict';

module.exports = function(grunt) {

    var pluginName = 'gruntNodeSass',
        sass = require('node-sass'),
        fs = require('fs'),
        path = require('path'),
        mkdirp =  require('mkdirp'),
        glob = require('glob');

    grunt.registerTask(pluginName, '', function() {

        var config = grunt.config(pluginName);

        // verify settings
        if (!config)
            grunt.log.error(pluginName + ' could not find expected config item "' + pluginName +'".');

        var done = this.async();
        var scssPath = config.scssPath,
            cssOutFolder = config.cssFolder;

        if (!scssPath)
            grunt.log.error(pluginName + ' could not find expected value "scssPath".');

        if (!cssOutFolder)
            grunt.log.error(pluginName + ' could not find expected value for "cssOutFolder".');

        if (!fs.existsSync(cssOutFolder))
            mkdirp.sync(cssOutFolder);

        glob(scssPath, config.globOptions, function (er, files) {

            if (er){
                grunt.fail.warn(er);
                return;
            }

            var processedCount = 0;
            function checkIfDone(){
                processedCount ++;
                if (processedCount === files.length){
                    done();
                }
            }

            files.forEach(function(file, i ){
                var outfile = path.join(
                    cssOutFolder,
                    path.basename(file).substr(0, path.basename(file).length - 5) + '.css'); // remove .scss extension

                // ignore partials
                if (path.basename(file).substr(0, 1) === '_'){
                    checkIfDone();
                    return;
                }

                var result = sass.render({
                    file: file,
                    sourceComments: true
                }, function(err, result){

                    if (err){
                        grunt.fail.warn('error compiling : ' + file + ' : ' + err);
                    } else {
                        fs.writeFileSync(outfile, result.css);
                        console.log(pluginName + ' compiled ' + outfile);
                    }

                    checkIfDone();

                });

            });

        });

    });
};
<|endoftext|>


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

'use strict';

module.exports = function (grunt) {

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

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

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

            //   partial
            //

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

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

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

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

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

                return;
            }

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

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

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

                return true;
            }

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

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

                lines = trimLines(lines, crop);

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

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

                // 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.processed     = processedLines.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.length > -1) ? matchFilename[1] : match;

                // get path from extract option
                matchPath = match.match(/^([^\s]+\/)/i);
                path = (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.length > -1) ? matchCategory[1] : false;
                    category = typeof category === 'string' ? _.startCase(category) : false;
                } else {
                    category = blockOptions.category;
                }

                // get name from filename if not in options
                if (!blockOptions.hasOwnProperty('name')) {
                    matchName = filename.match(/^([^\s]+)\./i);
                    name = (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(' ');
    }
};
<commit_msg>Formalize wrap value to fit the required format: {before: '', after: ''}<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 = formalizeWrap(block.options.wrap || options.viewWrap);
                var templateWrapOptions = optionsToDataString(block.options);

                lines = trimLines(lines, crop);

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

                // wrap partial if inline option viewWrap: exists
                if (viewWrap.before.length) {
                    processedLines = raiseIndent(processedLines);
                    processedLines.unshift('');
                    processedLines.unshift(viewWrap.before);
                    processedLines.push('');
                    processedLines.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.processed     = processedLines.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.length > -1) ? matchFilename[1] : match;

                // get path from extract option
                matchPath = match.match(/^([^\s]+\/)/i);
                path = (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.length > -1) ? matchCategory[1] : false;
                    category = typeof category === 'string' ? _.startCase(category) : false;
                } else {
                    category = blockOptions.category;
                }

                // get name from filename if not in options
                if (!blockOptions.hasOwnProperty('name')) {
                    matchName = filename.match(/^([^\s]+)\./i);
                    name = (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] : 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>'use strict';

var Q = require('q');
var gulpUtil = require('gulp-util');
var childProcess = require('child_process');
var jetpack = require('fs-jetpack');
var utils = require('./utils');

var projectDir;
var tmpDir;
var releasesDir;
var readyAppDir;
var manifest;

var init = function () {
    projectDir = jetpack;
    tmpDir = projectDir.dir('./tmp');
    releasesDir = projectDir.dir('./releases');
    manifest = projectDir.read('app/package.json', 'json');
    readyAppDir = tmpDir.cwd(manifest.name);

    return Q();
};

var copyRuntime = function () {
    return projectDir.copyAsync('node_modules/nw/nwjs', readyAppDir.path(), { overwrite: true });
};

var copyBuiltApp = function () {
    return projectDir.copyAsync('build', readyAppDir.path(), { overwrite: true });
};

var prepareOsSpecificThings = function () {
    return projectDir.copyAsync('resources/windows/icon.ico', readyAppDir.path('icon.ico'));
};

var createInstaller = function () {
    var deferred = Q.defer();

    var finalPackageName = manifest.name + '_' + manifest.version + '.exe';
    var installScript = projectDir.read('resources/windows/installer.nsi');
    installScript = utils.replace(installScript, {
        name: manifest.name,
        productName: manifest.productName,
        version: manifest.version,
        src: readyAppDir.path(),
        dest: releasesDir.path(finalPackageName),
        icon: readyAppDir.path('icon.ico'),
        setupIcon: projectDir.path('resources/windows/setup-icon.ico'),
        banner: projectDir.path('resources/windows/setup-banner.bmp'),
    });
    tmpDir.write('installer.nsi', installScript);

    gulpUtil.log('Building installer with NSIS...');

    // Note: NSIS have to be added to PATH (environment variables).
    var nsis = childProcess.spawn('makensis', [tmpDir.path('installer.nsi')]);
    nsis.stdout.pipe(process.stdout);
    nsis.stderr.pipe(process.stderr);
    nsis.on('close', function () {
        gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
        deferred.resolve();
    });

    return deferred.promise;
};

var cleanClutter = function () {
    return tmpDir.removeAsync('.');
};

module.exports = function () {
    return init()
    .then(copyRuntime)
    .then(copyBuiltApp)
    .then(prepareOsSpecificThings)
    .then(createInstaller)
    .then(cleanClutter);
};
<commit_msg>Remove previously released file<commit_after>'use strict';

var Q = require('q');
var gulpUtil = require('gulp-util');
var childProcess = require('child_process');
var jetpack = require('fs-jetpack');
var utils = require('./utils');

var projectDir;
var tmpDir;
var releasesDir;
var readyAppDir;
var manifest;

var init = function () {
    projectDir = jetpack;
    tmpDir = projectDir.dir('./tmp');
    releasesDir = projectDir.dir('./releases');
    manifest = projectDir.read('app/package.json', 'json');
    readyAppDir = tmpDir.cwd(manifest.name);

    return Q();
};

var copyRuntime = function () {
    return projectDir.copyAsync('node_modules/nw/nwjs', readyAppDir.path(), { overwrite: true });
};

var copyBuiltApp = function () {
    return projectDir.copyAsync('build', readyAppDir.path(), { overwrite: true });
};

var prepareOsSpecificThings = function () {
    return projectDir.copyAsync('resources/windows/icon.ico', readyAppDir.path('icon.ico'));
};

var createInstaller = function () {
    var deferred = Q.defer();

    var finalPackageName = manifest.name + '_' + manifest.version + '.exe';
    var installScript = projectDir.read('resources/windows/installer.nsi');
    installScript = utils.replace(installScript, {
        name: manifest.name,
        productName: manifest.productName,
        version: manifest.version,
        src: readyAppDir.path(),
        dest: releasesDir.path(finalPackageName),
        icon: readyAppDir.path('icon.ico'),
        setupIcon: projectDir.path('resources/windows/setup-icon.ico'),
        banner: projectDir.path('resources/windows/setup-banner.bmp'),
    });
    tmpDir.write('installer.nsi', installScript);

    gulpUtil.log('Building installer with NSIS...');

    // Remove destination file if already exists.
    releasesDir.remove(finalPackageName);

    // Note: NSIS have to be added to PATH (environment variables).
    var nsis = childProcess.spawn('makensis', [tmpDir.path('installer.nsi')]);
    nsis.stdout.pipe(process.stdout);
    nsis.stderr.pipe(process.stderr);
    nsis.on('close', function () {
        gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
        deferred.resolve();
    });

    return deferred.promise;
};

var cleanClutter = function () {
    return tmpDir.removeAsync('.');
};

module.exports = function () {
    return init()
    .then(copyRuntime)
    .then(copyBuiltApp)
    .then(prepareOsSpecificThings)
    .then(createInstaller)
    .then(cleanClutter);
};
<|endoftext|>


<commit_before>module.exports = function (grunt) {
  grunt.registerMultiTask('umbracoPackage', 'Create Umbraco Package', function () {
    var done = this.async();
    var Guid = require('guid');
    var path = require('path');
    var rimraf = require('rimraf');
    var archiver = require('archiver');
    var fs = require('fs');
    var fse = require('fs-extra');
    var chalk = require('chalk');
    var xml = require('js2xmlparser');

    var options = this.options({
      minimumUmbracoVersion: '',
      files: [],
      cwd: '/'
    });

    requireOptions(['name', 'version', 'license', 'licenseUrl', 'url', 'author', 'authorUrl'], options);
    validateDirectories(this.files, options);

    // Declare the name of the generated ZIP file
    var packageFileName = options.outputName ? options.outputName : options.name + '_' + options.version + '.zip';

    // Declare the path of the generated ZIP file
    var tmpOutput = path.join(options.outputDir, packageFileName);

    // Delete the ZIP file if it already exists (eg. already been generated for same version)
    if (fs.existsSync(tmpOutput)) fs.unlinkSync(tmpOutput);

    // Gather files
    var filesToPackage = [];
    this.files.forEach(function (file) {
      file.src.forEach(function (sourceDir) {
        var sourceDirFiles = getFilesRecursive(sourceDir);
        filesToPackage.push.apply(filesToPackage, sourceDirFiles.map(function (f) {
          return {
            guid: f.name,
            dir: f.dir.replace(sourceDir, ''),
            sourceDir: sourceDir,
            name: f.name,
            ext: f.name.split('.').pop()
          };
        }));
      });
    });

    // Avoid duplicate GUIDs
    var guids = {};
    filesToPackage.forEach(function(file) {
      if (guids[file.guid]) {
        file.guid = Guid.raw() + '_' + file.guid;
      }
      guids[file.guid] = true;
    });

    // Initialize the archive and it's output stream
    var output = fs.createWriteStream(tmpOutput);
    var archive = archiver('zip');

    // Listen for when the ZIP is closed
    output.on('close', function () {
      console.log('Package created at ' + chalk.cyan(tmpOutput) + " (" + chalk.cyan(archive.pointer()).toString() + " bytes)");
      done(true);
    });

    // Listen for ZIP errors
    archive.on('error', function (err) {
      throw err;
    });

    // Set the output stream of the ZIP
    archive.pipe(output);

    if (options.manifest) {

      // Load / transform XML Manifest
      options.files = filesToPackage;
      var manifest = grunt.file.read(options.manifest);
      manifest = grunt.template.process(manifest, { data: options });

      // Append the manifest to the ZIP file
      archive.append(manifest, { name:'package.xml' });

      // Append files from the source directory
      filesToPackage.forEach(function(file) {

        // Get the original path of the file
        var src = path.join(file.sourceDir, file.dir, file.name);

        // Append the file to the ZIP
        archive.append(fs.createReadStream(src), { name: file.guid });

      });

    } else {

      // Build the data for the package manifest
      var data = {
        info: {
          package: {
            name: options.name,
            version: options.version,
            license: {
              '@': {
                url: ''
              },
              '#': options.license
            },
            url: options.url,
            requirements: {
              major: 0,
              minor: 0,
              patch: 0
            }
          },
          author: {
            name: options.author
          },
          readme: options.readme
        },
        DocumentTypes: {},
        Templates: {},
        Stylesheets: {},
        Macros: {},
        DictionaryItems: {},
        Languages: {},
        DataTypes: {},
        control: {},
        Actions: {},
        files: {},
      };

      // Set optional URLs in the manifest
      if (options.licenseUrl) data.info.package.license['@'].url = options.licenseUrl;
      if (options.authorUrl) data.info.author['website'] = options.authorUrl;

      // Add files to the ZIP and manifest
      if (filesToPackage.length > 0) {
        data.files = [];

        filesToPackage.forEach(function(file) {

          // Get the original path of the file
          var src = path.join(file.sourceDir, file.dir, file.name);

          // Append the file to the ZIP
          archive.append(fs.createReadStream(src), { name: file.guid });

          // Append the file to the manifest
          data.files.push({
            guid: file.guid,
            orgPath: file.dir,
            orgName: file.name
          });

        });

      }

      // Append the manifest to the ZIP file
      archive.append(xml('umbPackage', data, {
        arrayMap: {
          files: 'file'
        }
      }), { name:'package.xml' });

    }

    archive.finalize();

    function getFilesRecursive(dir) {
      var ret = [];
      var files = fs.readdirSync(dir);
      for (var i in files) {
        if (!files.hasOwnProperty(i)) continue;

        var name = dir + '/' + files[i];
        if (fs.statSync(name).isDirectory()) {
          ret.push.apply(ret, getFilesRecursive(name));
        } else {
          ret.push({ dir: dir, name: files[i] });
        }
      }
      return ret;
    }

    function requireOptions (opts, options) {
      opts.forEach(function (opt) {
        if (!options.hasOwnProperty(opt) || options[opt] == null || options[opt].toString().length == 0) {
          grunt.fail.warn('Error creating Umbraco Package - required property missing: ' + opt);
          return;
        }
      })
    };

    function validateDirectories (files, options) {
      if (files.length < 1) {
        grunt.fail.warn('Error creating Umbraco Package - no source specified');
        return;
      }
      var src = files[0].src[0];
      var dest = files[0].dest;
      if (src == null || src.length == 0) {
        grunt.fail.warn('Error creating Umbraco Package - no source specified');
        return;
      }
      if (dest == null || dest.length == 0) {
        grunt.fail.warn('Error creating Umbraco Package - no source specified');
        return;
      }
      options.sourceDir = src;
      options.outputDir = dest;
    }

  });
};<commit_msg>Create output directory if it doesn't exist<commit_after>module.exports = function (grunt) {
  grunt.registerMultiTask('umbracoPackage', 'Create Umbraco Package', function () {
    var done = this.async();
    var Guid = require('guid');
    var path = require('path');
    var rimraf = require('rimraf');
    var archiver = require('archiver');
    var fs = require('fs');
    var fse = require('fs-extra');
    var chalk = require('chalk');
    var xml = require('js2xmlparser');

    var options = this.options({
      minimumUmbracoVersion: '',
      files: [],
      cwd: '/',
      readme: ''
    });

    requireOptions(['name', 'version', 'license', 'licenseUrl', 'url', 'author', 'authorUrl'], options);
    validateDirectories(this.files, options);

    // Declare the name of the generated ZIP file
    var packageFileName = options.outputName ? options.outputName : options.name + '_' + options.version + '.zip';

    // Ensure output directory exists
    if (!fs.existsSync(options.outputDir)) {
      fs.mkdirSync(options.outputDir);
    }

    // Declare the path of the generated ZIP file
    var tmpOutput = path.join(options.outputDir, packageFileName);

    // Delete the ZIP file if it already exists (eg. already been generated for same version)
    if (fs.existsSync(tmpOutput)) fs.unlinkSync(tmpOutput);

    // Gather files
    var filesToPackage = [];
    this.files.forEach(function (file) {
      file.src.forEach(function (sourceDir) {
        var sourceDirFiles = getFilesRecursive(sourceDir);
        filesToPackage.push.apply(filesToPackage, sourceDirFiles.map(function (f) {
          return {
            guid: f.name,
            dir: f.dir.replace(sourceDir, ''),
            sourceDir: sourceDir,
            name: f.name,
            ext: f.name.split('.').pop()
          };
        }));
      });
    });

    // Avoid duplicate GUIDs
    var guids = {};
    filesToPackage.forEach(function(file) {
      if (guids[file.guid]) {
        file.guid = Guid.raw() + '_' + file.guid;
      }
      guids[file.guid] = true;
    });

    // Initialize the archive and it's output stream
    var output = fs.createWriteStream(tmpOutput);
    var archive = archiver('zip');

    // Listen for when the ZIP is closed
    output.on('close', function () {
      console.log('Package created at ' + chalk.cyan(tmpOutput) + " (" + chalk.cyan(archive.pointer()).toString() + " bytes)");
      done(true);
    });

    // Listen for ZIP errors
    archive.on('error', function (err) {
      throw err;
    });

    // Set the output stream of the ZIP
    archive.pipe(output);

    if (options.manifest) {

      // Load / transform XML Manifest
      options.files = filesToPackage;
      var manifest = grunt.file.read(options.manifest);
      manifest = grunt.template.process(manifest, { data: options });

      // Append the manifest to the ZIP file
      archive.append(manifest, { name:'package.xml' });

      // Append files from the source directory
      filesToPackage.forEach(function(file) {

        // Get the original path of the file
        var src = path.join(file.sourceDir, file.dir, file.name);

        // Append the file to the ZIP
        archive.append(fs.createReadStream(src), { name: file.guid });

      });

    } else {

      // Build the data for the package manifest
      var data = {
        info: {
          package: {
            name: options.name,
            version: options.version,
            license: {
              '@': {
                url: ''
              },
              '#': options.license
            },
            url: options.url,
            requirements: {
              major: 0,
              minor: 0,
              patch: 0
            }
          },
          author: {
            name: options.author
          },
          readme: options.readme
        },
        DocumentTypes: {},
        Templates: {},
        Stylesheets: {},
        Macros: {},
        DictionaryItems: {},
        Languages: {},
        DataTypes: {},
        control: {},
        Actions: {},
        files: {},
      };

      // Set optional URLs in the manifest
      if (options.licenseUrl) data.info.package.license['@'].url = options.licenseUrl;
      if (options.authorUrl) data.info.author['website'] = options.authorUrl;

      // Add files to the ZIP and manifest
      if (filesToPackage.length > 0) {
        data.files = [];

        filesToPackage.forEach(function(file) {

          // Get the original path of the file
          var src = path.join(file.sourceDir, file.dir, file.name);

          // Append the file to the ZIP
          archive.append(fs.createReadStream(src), { name: file.guid });

          // Append the file to the manifest
          data.files.push({
            guid: file.guid,
            orgPath: file.dir,
            orgName: file.name
          });

        });

      }

      // Append the manifest to the ZIP file
      archive.append(xml('umbPackage', data, {
        arrayMap: {
          files: 'file'
        }
      }), { name:'package.xml' });

    }

    archive.finalize();

    function getFilesRecursive(dir) {
      var ret = [];
      var files = fs.readdirSync(dir);
      for (var i in files) {
        if (!files.hasOwnProperty(i)) continue;

        var name = dir + '/' + files[i];
        if (fs.statSync(name).isDirectory()) {
          ret.push.apply(ret, getFilesRecursive(name));
        } else {
          ret.push({ dir: dir, name: files[i] });
        }
      }
      return ret;
    }

    function requireOptions (opts, options) {
      opts.forEach(function (opt) {
        if (!options.hasOwnProperty(opt) || options[opt] == null || options[opt].toString().length == 0) {
          grunt.fail.warn('Error creating Umbraco Package - required property missing: ' + opt);
          return;
        }
      })
    };

    function validateDirectories (files, options) {
      if (files.length < 1) {
        grunt.fail.warn('Error creating Umbraco Package - no source specified');
        return;
      }
      var src = files[0].src[0];
      var dest = files[0].dest;
      if (src == null || src.length == 0) {
        grunt.fail.warn('Error creating Umbraco Package - no source specified');
        return;
      }
      if (dest == null || dest.length == 0) {
        grunt.fail.warn('Error creating Umbraco Package - no source specified');
        return;
      }
      options.sourceDir = src;
      options.outputDir = dest;
    }

  });
};<|endoftext|>


<commit_before>(function() {
  'use strict';

  var gulp = require('gulp');
  var plug = require('gulp-load-plugins')();

  var path = require('path');
  var constants = require('../constants');
  var ROOT = constants.ROOT;
  var util = require('../utils');
  var args = util.args;

  exports.task = function() {
    var options = {};

    if (args.version) {
      options.version = args.version;
    } else {
      options.type = args.type;
    }

    return gulp.src([path.join(ROOT, 'bower.json'), path.join(ROOT, 'package.json')])
      .pipe(plug.bump(options))
      .pipe(gulp.dest(ROOT));
  };
})();
<commit_msg>style(bump.js) : typo<commit_after>(function() {
  'use strict';

  var gulp = require('gulp');
  var plug = require('gulp-load-plugins')();

  var path = require('path');
  var constants = require('../constants');
  var ROOT = constants.ROOT;
  var utils = require('../utils');
  var args = utils.args;

  exports.task = function() {
    var options = {};

    if (args.version) {
      options.version = args.version;
    } else {
      options.type = args.type;
    }

    return gulp.src([path.join(ROOT, 'bower.json'), path.join(ROOT, 'package.json')])
      .pipe(plug.bump(options))
      .pipe(gulp.dest(ROOT));
  };
})();
<|endoftext|>


<commit_before><commit_msg>show billable time<commit_after><|endoftext|>


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

angular.module('mm.addons.mod_feedback')

/**
 * Feedback index controller.
 *
 * @module mm.addons.mod_feedback
 * @ngdoc controller
 * @name mmaModFeedbackIndexCtrl
 */
.controller('mmaModFeedbackIndexCtrl', function($scope, $stateParams, $mmaModFeedback, $mmUtil, $mmCourseHelper, $q, $mmCourse,
            $mmText, mmaModFeedbackComponent, $mmEvents, $mmApp, $translate, $mmGroups, $mmaModFeedbackHelper, $mmaModFeedbackSync,
            mmCoreEventOnlineStatusChanged, $state, mmaModFeedbackEventFormSubmitted, $mmaModFeedbackOffline) {
    var module = $stateParams.module || {},
        courseId = $stateParams.courseid,
        feedback,
        onlineObserver,
        obsSubmitted,
        analysisLoaded = false,
        overviewLoaded = false;

    // Constants for display modes.
    $scope.DISPLAY_OVERVIEW = 'overview';
    $scope.DISPLAY_ANALYSIS = 'analysis';

    $scope.title = module.name;
    $scope.description = module.description;
    $scope.moduleUrl = module.url;
    $scope.moduleName = $mmCourse.translateModuleName('feedback');
    $scope.courseId = courseId;
    $scope.refreshIcon = 'spinner';
    $scope.syncIcon = 'spinner';
    $scope.component = mmaModFeedbackComponent;
    $scope.componentId = module.id;
    $scope.selectedGroup = $stateParams.group || 0;
    $scope.selectedTab = $stateParams.tab || $scope.DISPLAY_OVERVIEW;
    $scope.overview = {};
    $scope.tabSwitched = false;
    $scope.chartOptions = {
        'legend': {
            'display': true,
            'position': 'bottom',
            'labels': {
                'generateLabels': function(chart) {
                    var data = chart.data;
                    if (data.labels.length && data.labels.length) {
                        var datasets = data.datasets[0];
                        return data.labels.map(function(label, i) {
                            return {
                                text: label + ': ' + datasets.data[i],
                                fillStyle: datasets.backgroundColor[i],
                                // Below is extra data used for toggling the datasets
                                datasetIndex: i
                            };
                        });
                    } else {
                        return [];
                    }
                }
            }
        }
    };

    function fetchGroupInfo(module) {
        return $mmGroups.getActivityGroupInfo(module).then(function(groupInfo) {
            $scope.groupInfo = groupInfo;
            return $scope.setGroup($scope.selectedGroup);
        });
    }

    function fetchFeedbackData(refresh, sync, showErrors) {
        $scope.isOnline = $mmApp.isOnline();

        return $mmaModFeedback.getFeedback(courseId, module.id).then(function(feedbackData) {
            feedback = feedbackData;

            $scope.title = feedback.name || $scope.title;
            $scope.description = feedback.intro || $scope.description;

            $scope.feedback = feedback;

            if (sync) {
                // Try to synchronize the feedback.
                return syncFeedback(showErrors).catch(function() {
                    // Ignore errors.
                });
            }
        }).then(function() {
            return $mmaModFeedback.getFeedbackAccessInformation(feedback.id);
        }).then(function(accessData) {
            $scope.access = accessData;

            if ($scope.selectedTab == $scope.DISPLAY_ANALYSIS) {
                return fetchFeedbackAnalysisData(accessData);
            }
            return fetchFeedbackOverviewData(accessData);
        }).then(function() {
            // All data obtained, now fill the context menu.
            $mmCourseHelper.fillContextMenu($scope, module, courseId, refresh, mmaModFeedbackComponent);

            // Check if there are responses stored in offline.
            return $mmaModFeedbackOffline.hasFeedbackOfflineData(feedback.id).then(function(hasOffline) {
                $scope.hasOffline = !!hasOffline;
            });
        }).catch(function(message) {
            if (!refresh) {
                // Some call failed, retry without using cache since it might be a new activity.
                return refreshAllData();
            }

            $mmUtil.showErrorModalDefault(message, 'mm.course.errorgetmodule', true);
            return $q.reject();
        }).finally(function(){
            $scope.feedbackLoaded = true;
            $scope.tabSwitched = true;
        });
    }

    // Convenience function to get feedback data.
    function fetchFeedbackOverviewData(accessData) {
        var promises = [];

        if (accessData.cancomplete && accessData.cansubmit && accessData.isopen) {
            promises.push($mmaModFeedback.getResumePage(feedback.id).then(function(goPage) {
                $scope.goPage = goPage > 0 ? goPage : false;
            }));
        }

        if (accessData.canedititems) {
            $scope.overview.timeopen = parseInt(feedback.timeopen) * 1000 || false;
            $scope.overview.openTimeReadable = $scope.overview.timeopen ?
                moment($scope.overview.timeopen).format('LLL') : false;
            $scope.overview.timeclose = parseInt(feedback.timeclose) * 1000 || false;
            $scope.overview.closeTimeReadable = $scope.overview.timeclose ?
                moment($scope.overview.timeclose).format('LLL') : false;

            // Get groups (only for teachers).
            promises.push(fetchGroupInfo(feedback.coursemodule));
        }

        return $q.all(promises).finally(function(){
            overviewLoaded = true;
        });
    }

    // Convenience function to get feedback data.
    function fetchFeedbackAnalysisData(accessData) {
        var promise;
        if (accessData.canviewanalysis) {
            // Get groups (only for teachers).
            promise = fetchGroupInfo(feedback.coursemodule);
        } else {
            $scope.setTab($scope.DISPLAY_OVERVIEW);
            promise = $q.when();
        }

        return promise.finally(function(){
            analysisLoaded = true;
        });
    }

    // Set group to see the analysis.
    $scope.setGroup = function(groupId) {
        $scope.selectedGroup = groupId;

        return $mmaModFeedback.getAnalysis(feedback.id, groupId).then(function(analysis) {
            $scope.feedback.completedCount = analysis.completedcount;
            $scope.feedback.itemsCount = analysis.itemscount;

            if ($scope.selectedTab == $scope.DISPLAY_ANALYSIS) {
                var number = 1;
                $scope.items = analysis.itemsdata.map(function(item) {
                    // Move data inside item.
                    item.item.data = item.data;
                    item = item.item;
                    item.number = number++;
                    if (item.data && item.data.length) {
                        return parseAnalysisInfo(item);
                    }
                    return false;
                }).filter(function(item) {
                    return item;
                });

                $scope.warning = "";
                if (analysis.warnings.length) {
                    for (var x in analysis.warnings) {
                        var warning = analysis.warnings[x];
                        if (warning.warningcode == 'insufficientresponsesforthisgroup') {
                            $scope.warning = warning.message;
                        }
                    }
                }
            }
        });
    };

    // Set group to see the analysis.
    $scope.setTab = function(tab) {
        $scope.selectedTab = tab == $scope.DISPLAY_OVERVIEW ? $scope.DISPLAY_OVERVIEW : $scope.DISPLAY_ANALYSIS;

        if (($scope.selectedTab == $scope.DISPLAY_OVERVIEW && !overviewLoaded) ||
                ($scope.selectedTab == $scope.DISPLAY_ANALYSIS && !analysisLoaded)) {
            $scope.tabSwitched = false;
            return fetchFeedbackData(false, false, true);
        }
    };

    // Parse the analysis info to show the info correctly formatted.
    function parseAnalysisInfo(item) {
        switch (item.typ) {
            case 'numeric':
                item.average = item.data.reduce(function (prev, current) {
                    return prev + current;
                }) / item.data.length;
                item.template = 'numeric';
                break;

            case 'info':
                item.data = item.data.map(function(dataItem) {
                    dataItem = $mmText.parseJSON(dataItem);
                    return typeof dataItem.show != "undefined" ? dataItem.show : false;
                }).filter(function(dataItem) {
                    // Filter false entries.
                    return dataItem;
                });
            case 'textfield':
            case 'textarea':
                item.template = 'list';
                break;

            case 'multichoicerated':
            case 'multichoice':
                item.data = item.data.map(function(dataItem) {
                    dataItem = $mmText.parseJSON(dataItem);
                    return typeof dataItem.answertext != "undefined" ? dataItem : false;
                }).filter(function(dataItem) {
                    // Filter false entries.
                    return dataItem;
                });

                // Format labels.
                item.labels = item.data.map(function(dataItem) {
                    dataItem.quotient = parseFloat(dataItem.quotient * 100).toFixed(2);
                    var label = "";

                    if (typeof dataItem.value != "undefined") {
                        label = '(' + dataItem.value + ') ';
                    }
                    label += dataItem.answertext;
                    label += dataItem.quotient > 0 ? ' (' + dataItem.quotient + '%)' : "";
                    return label;
                });
                item.chartData = item.data.map(function(dataItem) {
                    return dataItem.answercount;
                });

                if (item.typ == 'multichoicerated') {
                    item.average = item.data.reduce(function (prev, current) {
                        return prev + current.avg;
                    }, 0.0);
                }

                var subtype = item.presentation.charAt(0);
                item.single = subtype != 'c';

                item.template = 'chart';
                break;
        }

        return item;
    }

    // Convenience function to refresh all the data.
    function refreshAllData(sync, showErrors) {
        var promises = [];

        promises.push($mmaModFeedback.invalidateFeedbackData(courseId));
        if (feedback) {
            promises.push($mmaModFeedback.invalidateFeedbackAccessInformationData(feedback.id));
            promises.push($mmaModFeedback.invalidateAnalysisData(feedback.id));
            promises.push($mmGroups.invalidateActivityAllowedGroups(feedback.coursemodule));
            promises.push($mmGroups.invalidateActivityGroupMode(feedback.coursemodule));
            promises.push($mmaModFeedback.invalidateResumePageData(feedback.id));
        }

        analysisLoaded = false;
        overviewLoaded = false;

        return $q.all(promises).finally(function() {
            return fetchFeedbackData(true, sync, showErrors);
        });
    }

    // Tries to synchronize the feedback.
    function syncFeedback(showErrors) {
        return $mmaModFeedbackSync.syncFeedback(feedback.id).then(function(result) {
            if (result.warnings && result.warnings.length) {
                $mmUtil.showErrorModal(result.warnings[0]);
            }

            return result.updated;
        }).catch(function(error) {
            if (showErrors) {
                $mmUtil.showErrorModalDefault(error, 'mm.core.errorsync', true);
            }
            return $q.reject();
        });
    }

    fetchFeedbackData(false, true).then(function() {
        $mmaModFeedback.logView(feedback.id).then(function() {
            $mmCourse.checkModuleCompletion(courseId, module.completionstatus);
        });
    }).finally(function() {
        $scope.refreshIcon = 'ion-refresh';
        $scope.syncIcon = 'ion-loop';
    });

    // Confirm and Remove action.
    $scope.removeFiles = function() {
        $mmCourseHelper.confirmAndRemove(module, courseId);
    };

    // Context Menu Prefetch action.
    $scope.prefetch = function() {
        $mmCourseHelper.contextMenuPrefetch($scope, module, courseId);
    };
    // Context Menu Description action.
    $scope.expandDescription = function() {
        $mmText.expandText($translate.instant('mm.core.description'), $scope.description, false, mmaModFeedbackComponent, module.id);
    };

    // Pull to refresh.
    $scope.refreshFeedback = function(showErrors) {
        if ($scope.feedbackLoaded) {
            $scope.refreshIcon = 'spinner';
            $scope.syncIcon = 'spinner';
            return refreshAllData(true, showErrors).finally(function() {
                $scope.refreshIcon = 'ion-refresh';
                $scope.syncIcon = 'ion-loop';
                $scope.$broadcast('scroll.refreshComplete');
            });
        }
    };

    // Function to link implemented features.
    $scope.openFeature = function(feature) {
        $mmaModFeedbackHelper.openFeature(feature, module, courseId, $scope.selectedGroup);
    };

    // Function to go to the questions form.
    $scope.gotoAnswerQuestions = function(preview) {
        var stateParams = {
            module: module,
            moduleid: module.id,
            courseid: courseId,
            preview: !!preview
        };
        $state.go('site.mod_feedback-form', stateParams);
    };

    // Listen for form submit events.
    obsSubmitted = $mmEvents.on(mmaModFeedbackEventFormSubmitted, function(data) {
        // Go to review attempt if an attempt in this quiz was finished and synced.
        if (data.feedbackId === feedback.id) {
            analysisLoaded = false;
            overviewLoaded = false;
            $scope.feedbackLoaded = false;
            if (data.tab != $scope.selectedTab) {
                $scope.setTab(data.tab);
            } else {
                fetchFeedbackData(true);
            }
        }
    });

    // Refresh online status when changes.
    onlineObserver = $mmEvents.on(mmCoreEventOnlineStatusChanged, function(online) {
        $scope.isOnline = online;
    });

    $scope.$on('$destroy', function() {
        onlineObserver && onlineObserver.off && onlineObserver.off();
        obsSubmitted && obsSubmitted.off && obsSubmitted.off();
    });
});
<commit_msg>MOBILE-2103 feedback: Fix average calculation<commit_after>// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

angular.module('mm.addons.mod_feedback')

/**
 * Feedback index controller.
 *
 * @module mm.addons.mod_feedback
 * @ngdoc controller
 * @name mmaModFeedbackIndexCtrl
 */
.controller('mmaModFeedbackIndexCtrl', function($scope, $stateParams, $mmaModFeedback, $mmUtil, $mmCourseHelper, $q, $mmCourse,
            $mmText, mmaModFeedbackComponent, $mmEvents, $mmApp, $translate, $mmGroups, $mmaModFeedbackHelper, $mmaModFeedbackSync,
            mmCoreEventOnlineStatusChanged, $state, mmaModFeedbackEventFormSubmitted, $mmaModFeedbackOffline) {
    var module = $stateParams.module || {},
        courseId = $stateParams.courseid,
        feedback,
        onlineObserver,
        obsSubmitted,
        analysisLoaded = false,
        overviewLoaded = false;

    // Constants for display modes.
    $scope.DISPLAY_OVERVIEW = 'overview';
    $scope.DISPLAY_ANALYSIS = 'analysis';

    $scope.title = module.name;
    $scope.description = module.description;
    $scope.moduleUrl = module.url;
    $scope.moduleName = $mmCourse.translateModuleName('feedback');
    $scope.courseId = courseId;
    $scope.refreshIcon = 'spinner';
    $scope.syncIcon = 'spinner';
    $scope.component = mmaModFeedbackComponent;
    $scope.componentId = module.id;
    $scope.selectedGroup = $stateParams.group || 0;
    $scope.selectedTab = $stateParams.tab || $scope.DISPLAY_OVERVIEW;
    $scope.overview = {};
    $scope.tabSwitched = false;
    $scope.chartOptions = {
        'legend': {
            'display': true,
            'position': 'bottom',
            'labels': {
                'generateLabels': function(chart) {
                    var data = chart.data;
                    if (data.labels.length && data.labels.length) {
                        var datasets = data.datasets[0];
                        return data.labels.map(function(label, i) {
                            return {
                                text: label + ': ' + datasets.data[i],
                                fillStyle: datasets.backgroundColor[i],
                                // Below is extra data used for toggling the datasets
                                datasetIndex: i
                            };
                        });
                    } else {
                        return [];
                    }
                }
            }
        }
    };

    function fetchGroupInfo(module) {
        return $mmGroups.getActivityGroupInfo(module).then(function(groupInfo) {
            $scope.groupInfo = groupInfo;
            return $scope.setGroup($scope.selectedGroup);
        });
    }

    function fetchFeedbackData(refresh, sync, showErrors) {
        $scope.isOnline = $mmApp.isOnline();

        return $mmaModFeedback.getFeedback(courseId, module.id).then(function(feedbackData) {
            feedback = feedbackData;

            $scope.title = feedback.name || $scope.title;
            $scope.description = feedback.intro || $scope.description;

            $scope.feedback = feedback;

            if (sync) {
                // Try to synchronize the feedback.
                return syncFeedback(showErrors).catch(function() {
                    // Ignore errors.
                });
            }
        }).then(function() {
            return $mmaModFeedback.getFeedbackAccessInformation(feedback.id);
        }).then(function(accessData) {
            $scope.access = accessData;

            if ($scope.selectedTab == $scope.DISPLAY_ANALYSIS) {
                return fetchFeedbackAnalysisData(accessData);
            }
            return fetchFeedbackOverviewData(accessData);
        }).then(function() {
            // All data obtained, now fill the context menu.
            $mmCourseHelper.fillContextMenu($scope, module, courseId, refresh, mmaModFeedbackComponent);

            // Check if there are responses stored in offline.
            return $mmaModFeedbackOffline.hasFeedbackOfflineData(feedback.id).then(function(hasOffline) {
                $scope.hasOffline = !!hasOffline;
            });
        }).catch(function(message) {
            if (!refresh) {
                // Some call failed, retry without using cache since it might be a new activity.
                return refreshAllData();
            }

            $mmUtil.showErrorModalDefault(message, 'mm.course.errorgetmodule', true);
            return $q.reject();
        }).finally(function(){
            $scope.feedbackLoaded = true;
            $scope.tabSwitched = true;
        });
    }

    // Convenience function to get feedback data.
    function fetchFeedbackOverviewData(accessData) {
        var promises = [];

        if (accessData.cancomplete && accessData.cansubmit && accessData.isopen) {
            promises.push($mmaModFeedback.getResumePage(feedback.id).then(function(goPage) {
                $scope.goPage = goPage > 0 ? goPage : false;
            }));
        }

        if (accessData.canedititems) {
            $scope.overview.timeopen = parseInt(feedback.timeopen) * 1000 || false;
            $scope.overview.openTimeReadable = $scope.overview.timeopen ?
                moment($scope.overview.timeopen).format('LLL') : false;
            $scope.overview.timeclose = parseInt(feedback.timeclose) * 1000 || false;
            $scope.overview.closeTimeReadable = $scope.overview.timeclose ?
                moment($scope.overview.timeclose).format('LLL') : false;

            // Get groups (only for teachers).
            promises.push(fetchGroupInfo(feedback.coursemodule));
        }

        return $q.all(promises).finally(function(){
            overviewLoaded = true;
        });
    }

    // Convenience function to get feedback data.
    function fetchFeedbackAnalysisData(accessData) {
        var promise;
        if (accessData.canviewanalysis) {
            // Get groups (only for teachers).
            promise = fetchGroupInfo(feedback.coursemodule);
        } else {
            $scope.setTab($scope.DISPLAY_OVERVIEW);
            promise = $q.when();
        }

        return promise.finally(function(){
            analysisLoaded = true;
        });
    }

    // Set group to see the analysis.
    $scope.setGroup = function(groupId) {
        $scope.selectedGroup = groupId;

        return $mmaModFeedback.getAnalysis(feedback.id, groupId).then(function(analysis) {
            $scope.feedback.completedCount = analysis.completedcount;
            $scope.feedback.itemsCount = analysis.itemscount;

            if ($scope.selectedTab == $scope.DISPLAY_ANALYSIS) {
                var number = 1;
                $scope.items = analysis.itemsdata.map(function(item) {
                    // Move data inside item.
                    item.item.data = item.data;
                    item = item.item;
                    item.number = number++;
                    if (item.data && item.data.length) {
                        return parseAnalysisInfo(item);
                    }
                    return false;
                }).filter(function(item) {
                    return item;
                });

                $scope.warning = "";
                if (analysis.warnings.length) {
                    for (var x in analysis.warnings) {
                        var warning = analysis.warnings[x];
                        if (warning.warningcode == 'insufficientresponsesforthisgroup') {
                            $scope.warning = warning.message;
                        }
                    }
                }
            }
        });
    };

    // Set group to see the analysis.
    $scope.setTab = function(tab) {
        $scope.selectedTab = tab == $scope.DISPLAY_OVERVIEW ? $scope.DISPLAY_OVERVIEW : $scope.DISPLAY_ANALYSIS;

        if (($scope.selectedTab == $scope.DISPLAY_OVERVIEW && !overviewLoaded) ||
                ($scope.selectedTab == $scope.DISPLAY_ANALYSIS && !analysisLoaded)) {
            $scope.tabSwitched = false;
            return fetchFeedbackData(false, false, true);
        }
    };

    // Parse the analysis info to show the info correctly formatted.
    function parseAnalysisInfo(item) {
        switch (item.typ) {
            case 'numeric':
                item.average = item.data.reduce(function (prev, current) {
                    return prev + parseInt(current, 10);
                }, 0) / item.data.length;
                item.template = 'numeric';
                break;

            case 'info':
                item.data = item.data.map(function(dataItem) {
                    dataItem = $mmText.parseJSON(dataItem);
                    return typeof dataItem.show != "undefined" ? dataItem.show : false;
                }).filter(function(dataItem) {
                    // Filter false entries.
                    return dataItem;
                });
            case 'textfield':
            case 'textarea':
                item.template = 'list';
                break;

            case 'multichoicerated':
            case 'multichoice':
                item.data = item.data.map(function(dataItem) {
                    dataItem = $mmText.parseJSON(dataItem);
                    return typeof dataItem.answertext != "undefined" ? dataItem : false;
                }).filter(function(dataItem) {
                    // Filter false entries.
                    return dataItem;
                });

                // Format labels.
                item.labels = item.data.map(function(dataItem) {
                    dataItem.quotient = parseFloat(dataItem.quotient * 100).toFixed(2);
                    var label = "";

                    if (typeof dataItem.value != "undefined") {
                        label = '(' + dataItem.value + ') ';
                    }
                    label += dataItem.answertext;
                    label += dataItem.quotient > 0 ? ' (' + dataItem.quotient + '%)' : "";
                    return label;
                });
                item.chartData = item.data.map(function(dataItem) {
                    return dataItem.answercount;
                });

                if (item.typ == 'multichoicerated') {
                    item.average = item.data.reduce(function (prev, current) {
                        return prev + parseFloat(current.avg);
                    }, 0.0);
                }

                var subtype = item.presentation.charAt(0);
                item.single = subtype != 'c';

                item.template = 'chart';
                break;
        }

        return item;
    }

    // Convenience function to refresh all the data.
    function refreshAllData(sync, showErrors) {
        var promises = [];

        promises.push($mmaModFeedback.invalidateFeedbackData(courseId));
        if (feedback) {
            promises.push($mmaModFeedback.invalidateFeedbackAccessInformationData(feedback.id));
            promises.push($mmaModFeedback.invalidateAnalysisData(feedback.id));
            promises.push($mmGroups.invalidateActivityAllowedGroups(feedback.coursemodule));
            promises.push($mmGroups.invalidateActivityGroupMode(feedback.coursemodule));
            promises.push($mmaModFeedback.invalidateResumePageData(feedback.id));
        }

        analysisLoaded = false;
        overviewLoaded = false;

        return $q.all(promises).finally(function() {
            return fetchFeedbackData(true, sync, showErrors);
        });
    }

    // Tries to synchronize the feedback.
    function syncFeedback(showErrors) {
        return $mmaModFeedbackSync.syncFeedback(feedback.id).then(function(result) {
            if (result.warnings && result.warnings.length) {
                $mmUtil.showErrorModal(result.warnings[0]);
            }

            return result.updated;
        }).catch(function(error) {
            if (showErrors) {
                $mmUtil.showErrorModalDefault(error, 'mm.core.errorsync', true);
            }
            return $q.reject();
        });
    }

    fetchFeedbackData(false, true).then(function() {
        $mmaModFeedback.logView(feedback.id).then(function() {
            $mmCourse.checkModuleCompletion(courseId, module.completionstatus);
        });
    }).finally(function() {
        $scope.refreshIcon = 'ion-refresh';
        $scope.syncIcon = 'ion-loop';
    });

    // Confirm and Remove action.
    $scope.removeFiles = function() {
        $mmCourseHelper.confirmAndRemove(module, courseId);
    };

    // Context Menu Prefetch action.
    $scope.prefetch = function() {
        $mmCourseHelper.contextMenuPrefetch($scope, module, courseId);
    };
    // Context Menu Description action.
    $scope.expandDescription = function() {
        $mmText.expandText($translate.instant('mm.core.description'), $scope.description, false, mmaModFeedbackComponent, module.id);
    };

    // Pull to refresh.
    $scope.refreshFeedback = function(showErrors) {
        if ($scope.feedbackLoaded) {
            $scope.refreshIcon = 'spinner';
            $scope.syncIcon = 'spinner';
            return refreshAllData(true, showErrors).finally(function() {
                $scope.refreshIcon = 'ion-refresh';
                $scope.syncIcon = 'ion-loop';
                $scope.$broadcast('scroll.refreshComplete');
            });
        }
    };

    // Function to link implemented features.
    $scope.openFeature = function(feature) {
        $mmaModFeedbackHelper.openFeature(feature, module, courseId, $scope.selectedGroup);
    };

    // Function to go to the questions form.
    $scope.gotoAnswerQuestions = function(preview) {
        var stateParams = {
            module: module,
            moduleid: module.id,
            courseid: courseId,
            preview: !!preview
        };
        $state.go('site.mod_feedback-form', stateParams);
    };

    // Listen for form submit events.
    obsSubmitted = $mmEvents.on(mmaModFeedbackEventFormSubmitted, function(data) {
        // Go to review attempt if an attempt in this quiz was finished and synced.
        if (data.feedbackId === feedback.id) {
            analysisLoaded = false;
            overviewLoaded = false;
            $scope.feedbackLoaded = false;
            if (data.tab != $scope.selectedTab) {
                $scope.setTab(data.tab);
            } else {
                fetchFeedbackData(true);
            }
        }
    });

    // Refresh online status when changes.
    onlineObserver = $mmEvents.on(mmCoreEventOnlineStatusChanged, function(online) {
        $scope.isOnline = online;
    });

    $scope.$on('$destroy', function() {
        onlineObserver && onlineObserver.off && onlineObserver.off();
        obsSubmitted && obsSubmitted.off && obsSubmitted.off();
    });
});
<|endoftext|>


<commit_before>var fs = require('fs');

exports.svg = function(test) {

	var jsdom    = require("jsdom").jsdom;
	global.document = jsdom("");
	global.window   = global.document.createWindow();

	var Rickshaw = require('../rickshaw');
	new Rickshaw.Compat.ClassList();

	var el = document.createElement("div");

	var graph = new Rickshaw.Graph({
	   element: el,
	   width: 960,
	   height: 500,
	   renderer: 'scatterplot',
	   series: [{
		color: 'steelblue',
		data: [
		    { x: 0, y: 40 },
		    { x: 1, y: 49 },
		    { x: 2, y: 38 },
		    { x: 3, y: 30 },
		    { x: 4, y: 32 } ]
	    }]
	} );

	graph.renderer.dotSize = 6;
	graph.render();

	var generatedSVG = el.innerHTML;

	var exampleSVGFilename = __dirname + '/data/simple.svg';
	var exampleSVG = fs.readFileSync(exampleSVGFilename, 'utf8').trim();

	test.equal(generatedSVG, exampleSVG, "simple graph svg content matches");

	test.done();
}
<commit_msg>add tests for throwing errors for trying to stack inconsistent series<commit_after>var fs = require('fs');

exports.svg = function(test) {

	var jsdom    = require("jsdom").jsdom;
	global.document = jsdom("");
	global.window   = global.document.createWindow();

	var Rickshaw = require('../rickshaw');
	new Rickshaw.Compat.ClassList();

	var el = document.createElement("div");

	var graph = new Rickshaw.Graph({
	   element: el,
	   width: 960,
	   height: 500,
	   renderer: 'scatterplot',
	   series: [{
		color: 'steelblue',
		data: [
		    { x: 0, y: 40 },
		    { x: 1, y: 49 },
		    { x: 2, y: 38 },
		    { x: 3, y: 30 },
		    { x: 4, y: 32 } ]
	    }]
	} );

	graph.renderer.dotSize = 6;
	graph.render();

	var generatedSVG = el.innerHTML;

	var exampleSVGFilename = __dirname + '/data/simple.svg';
	var exampleSVG = fs.readFileSync(exampleSVGFilename, 'utf8').trim();

	test.equal(generatedSVG, exampleSVG, "simple graph svg content matches");

	test.done();
}

exports.inconsistent = function(test) {

	var jsdom    = require("jsdom").jsdom;
	global.document = jsdom("");
	global.window   = global.document.createWindow();

	var Rickshaw = require('../rickshaw');
	new Rickshaw.Compat.ClassList();

	var el = document.createElement("div");

	var series = [
		{
			color: 'steelblue',
			data: [ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 } ],
		}, {
			color: 'red',
			data: [ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 } ],
		}
	];

	test.doesNotThrow( function() {

		var graph = new Rickshaw.Graph({
			element: el,
			width: 960,
			height: 500,
			renderer: 'stack',
			series: series
		});

	}, "create basic graph okay" );

	series[0].data.push( { x: 3, y: 88 } );

	test.doesNotThrow( function() {

		var graph = new Rickshaw.Graph({
			element: el,
			width: 960,
			height: 500,
			renderer: 'line',
			series: series
		});

	}, "we don't throw for inconsistent length series for lines" );

	test.throws( function() {

		var graph = new Rickshaw.Graph({
			element: el,
			width: 960,
			height: 500,
			renderer: 'stack',
			series: series
		});

	}, null, "throw for inconsistent stacked series for stack renderer" );

	test.done();
}

<|endoftext|>


<commit_before><commit_msg>[IMP] stock: improve generic stock controller<commit_after><|endoftext|>


<commit_before><commit_msg>Add more code comments<commit_after><|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var ba = require('../');

// HEXFLIFY
// One letter
assert.equal(ba.hexlify('A'), '41');

// String
assert.equal(ba.hexlify('Pamietamy 44'), '50616d696574616d79203434');
<commit_msg>[BINASCII] - :mouse2: unhexify tests<commit_after>'use strict';

var assert = require('assert');
var ba = require('../');

// HEXFLIFY
// One letter
assert.equal(ba.hexlify('A'), '41');

// String
assert.equal(ba.hexlify('Pamietamy 44'), '50616d696574616d79203434');

// UNHEXLIFY
assert.equal(ba.unhexlify('50616d696574616d79203434'), 'Pamietamy 44');
<|endoftext|>


<commit_before>/**
 * @file Render module - tests
 * @author StefanoMagrassi <stefano.magrassi@gmail.com>
 */

// Imports
// -------
import test from 'tape';
import puzzle from '../../lib/puzzle';

// Constants
// ---------
const IMAGE_PATH = 'http://i.telegraph.co.uk/multimedia/archive/02792/mountCook2_2792612b.jpg';

// Tests
// -----
test('run() returns the collection of image pieces', function(t) {
  t.plan(1);

  testImage(function() {
    let image    = this;
    let rows     = 10;
    let cols     = 10;
    let rendered = puzzle.run(image, rows, cols);
    let actual   = rendered.length;
    let expect   = rows * cols;

    t.equal(actual, expect, 'rendered should be as length as the number of rows and columns');
  });

  t.timeoutAfter(10 * 1000);
});

test('run() with existent data', function(t) {
  t.plan(1);

  let data = [
    [{row:0, col:0, width:100, height:100, x:0, y:0},{position:0, bgX:0, bgY:0}],
    [{row:0, col:1, width:100, height:100, x:100, y:0},{position:1, bgX:-100, bgY:0}],
    [{row:1, col:0, width:100, height:100, x:0, y:-100},{position:2, bgX:0, bgY:-100}],
    [{row:1, col:1, width:100, height:100, x:-100, y:-100},{position:3, bgX:-100, bgY:-100}]
  ];

  testImage(function() {
    let image  = this;
    let rows   = 2;
    let cols   = 2;
    let actual = puzzle.run(image, rows, cols, data);

    t.deepEqual(actual, data, 'should returns the passed data');
  });

  t.timeoutAfter(10 * 1000);
});

test('update() refresh puzzle pieces', function(t) {
  t.plan(1);

  testImage(function() {
    let image = this;

    t.ok(puzzle.update(image), 'should not have errors');
  });

  t.timeoutAfter(10 * 1000);
});

test('last() gets or sets the last computed collection', function(t) {
  let data = [
    [{row:0, col:0, width:100, height:100, x:0, y:0},{position:0, bgX:0, bgY:0}],
    [{row:0, col:1, width:100, height:100, x:100, y:0},{position:1, bgX:-100, bgY:0}],
    [{row:1, col:0, width:100, height:100, x:0, y:-100},{position:2, bgX:0, bgY:-100}],
    [{row:1, col:1, width:100, height:100, x:-100, y:-100},{position:3, bgX:-100, bgY:-100}]
  ];
  let actual = puzzle.last(data);
  let expect = [
    [{row:0, col:0, width:100, height:100, x:0, y:0},{position:0, bgX:0, bgY:0}],
    [{row:0, col:1, width:100, height:100, x:100, y:0},{position:1, bgX:-100, bgY:0}],
    [{row:1, col:0, width:100, height:100, x:0, y:-100},{position:2, bgX:0, bgY:-100}],
    [{row:1, col:1, width:100, height:100, x:-100, y:-100},{position:3, bgX:-100, bgY:-100}]
  ];

  t.deepEqual(actual, expect, 'should set and returns the last computed collection');
  t.deepEqual(puzzle.last(), expect, 'should returns the last computed collection');
  t.end();
});

// Private methods
// ---------------
/**
 * Creates the image on which run the tests.
 * @private
 * @param  {function} cb - Callback on image load
 * @return {undefined}
 */
function testImage(cb) {
  const ID         = 'img-puzzle_img';

  let image = document.getElementById(ID);

  if (image) {
    return cb.call(image);
  }

  let container = document.body.appendChild(document.createElement('div'));
  image         = document.createElement('img');
  image.id      = ID;
  image.src     = IMAGE_PATH;
  image.onload  = cb;

  return container.appendChild(image);
}
<commit_msg>better tests for puzzle<commit_after>/**
 * @file Puzzle module - tests
 * @author StefanoMagrassi <stefano.magrassi@gmail.com>
 */

// Imports
// -------
import test from 'tape';
import puzzle from '../../lib/puzzle';

// Constants
// ---------
const IMAGE_ID   = 'img-puzzle_img';
const IMAGE_PATH = 'http://i.telegraph.co.uk/multimedia/archive/02792/mountCook2_2792612b.jpg';

// Tests
// -----
test('run() returns the collection of image pieces', function(t) {
  t.plan(1);

  testImage(IMAGE_ID, function() {
    let image    = this;
    let rows     = 10;
    let cols     = 10;
    let rendered = puzzle.run(image, rows, cols);
    let actual   = rendered.length;
    let expect   = rows * cols;

    t.equal(actual, expect, 'rendered should be as length as the number of rows and columns');
  });

  t.timeoutAfter(10 * 1000);
});

test('run() with existent data', function(t) {
  t.plan(1);

  let data = [
    [{row:0, col:0, width:100, height:100, x:0, y:0},{position:0, bgX:0, bgY:0}],
    [{row:0, col:1, width:100, height:100, x:100, y:0},{position:1, bgX:-100, bgY:0}],
    [{row:1, col:0, width:100, height:100, x:0, y:-100},{position:2, bgX:0, bgY:-100}],
    [{row:1, col:1, width:100, height:100, x:-100, y:-100},{position:3, bgX:-100, bgY:-100}]
  ];

  testImage(IMAGE_ID, function() {
    let image  = this;
    let rows   = 2;
    let cols   = 2;
    let actual = puzzle.run(image, rows, cols, data);

    t.deepEqual(actual, data, 'should returns the passed data');
  });

  t.timeoutAfter(10 * 1000);
});

test('update() refresh puzzle pieces', function(t) {
  t.plan(1);

  testImage(IMAGE_ID, function() {
    let image = this;

    t.ok(puzzle.update(image), 'should not have errors');
  });

  t.timeoutAfter(10 * 1000);
});

test('last() gets or sets the last computed collection', function(t) {
  let data = [
    [{row:0, col:0, width:100, height:100, x:0, y:0},{position:0, bgX:0, bgY:0}],
    [{row:0, col:1, width:100, height:100, x:100, y:0},{position:1, bgX:-100, bgY:0}],
    [{row:1, col:0, width:100, height:100, x:0, y:-100},{position:2, bgX:0, bgY:-100}],
    [{row:1, col:1, width:100, height:100, x:-100, y:-100},{position:3, bgX:-100, bgY:-100}]
  ];
  let actual = puzzle.last(data);
  let expect = [
    [{row:0, col:0, width:100, height:100, x:0, y:0},{position:0, bgX:0, bgY:0}],
    [{row:0, col:1, width:100, height:100, x:100, y:0},{position:1, bgX:-100, bgY:0}],
    [{row:1, col:0, width:100, height:100, x:0, y:-100},{position:2, bgX:0, bgY:-100}],
    [{row:1, col:1, width:100, height:100, x:-100, y:-100},{position:3, bgX:-100, bgY:-100}]
  ];

  t.deepEqual(actual, expect, 'should set and returns the last computed collection');
  t.deepEqual(puzzle.last(), expect, 'should returns the last computed collection');
  t.end();
});

// Private methods
// ---------------
/**
 * Creates the image on which run the tests.
 * @private
 * @param  {string}   id - Image id
 * @param  {function} cb - Callback on image load
 */
function testImage(id, cb) {
  let image = document.getElementById(id);

  if (image) {
    return cb.call(image);
  }

  let container = document.body.appendChild(document.createElement('div'));
  image         = document.createElement('img');
  image.id      = id;
  image.src     = IMAGE_PATH;
  image.onload  = cb;

  return container.appendChild(image);
}
<|endoftext|>


<commit_before><commit_msg>Refactor<commit_after><|endoftext|>