<commit_before>// Dependencies
var Ul = require("ul")
  , FlatColors = require("flatcolors")
  , CliCircle = require("cli-circle")
  , Couleurs = require("couleurs")
  ;

/**
 * CliPie
 * Creates a new instance of `CliPie`.
 *
 * @name CliPie
 * @function
 * @param {Number} r The radius value.
 * @param {Array} data An array of objects in the following format:
 * @param {Object} options An object containing the following fields:
 *
 *  - `flat` (Boolean): If `true`, flat colors will be used (default: `true`).
 *  - `chr` (String): The character to draw the pie (default: `" "`).
 *  - `no_ansi` (Boolean): If `true`, ansi styles will not be used.
 *  - `legend` (Boolean): If `true`, a legend is added next to the pie.
 *  - `display_total` (Boolean): If `true`, the total is added to the legend.
 *  - `total_label` (String): The label for the total (default: `Total`)
 *  - `circle_opts` (Object): The options passed to the
 *    [`cli-circle`](https://github.com/IonicaBizau/node-cli-circle) module,
 *    which uses the
 *    [`cli-graph`](https://github.com/IonicaBizau/node-cli-graph) module to
 *    build the graph.
 *
 * @return {CliPie} The `CliPie` instance.
 */
function CliPie(r, data, options) {
    this.data = [];
    this.radius = r;
    this.total = 0;
    this.colors = {};
    this.cChar = -1;
    this.options = Ul.deepMerge(options, {
        flat: true
      , chr: " "
      , no_ansi: false
      , circle_opts: {
            aRatio: 1
        }
      , chars: "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")
    });
    if (Array.isArray(data)) {
        data.forEach(this.add.bind(this));
    } else if (data && y) {
        options = data;
    }
}

/**
 * CliPie.Item
 * Creates a new `CliPie.Item` instance.
 *
 * @name CLiPie.Item
 * @function
 * @param {Object} obj The `CliPie.Item` data containing:
 *
 *  - `value` (Number): The item value.
 *  - `label` (String): The item label.
 *  - `color` (String): The item color (used in ansi graphs).
 *  - `letter` (String): The item letter (used on non-ansi graphs).
 *
 * @return {CliPieItem} The `CliPieItem` containing:
 */
CliPie.Item = function (obj) {
    this.value = obj.value;
    this.label = obj.label;
    this.color = obj.color;
    this.percent = null;
    this.angle = null;
    this.letter = obj.letter;
};

/**
 * add
 * Adds new items in the cli pie.
 *
 * @name add
 * @function
 * @param {Object} item The item data.
 * @return {CliPie} The `CliPie` instance.
 */
CliPie.prototype.add = function (item) {
    item.letter = item.letter || this.options.chars[++this.cChar];
    var nItem = new CliPie.Item(item);
    this.total += nItem.value;
    this.data.push(nItem);
    return this;
};

/**
 * textColor
 * Adds foreground or/and background color(s) to the provided text.
 *
 * @name textColor
 * @function
 * @param {String} text The text to color.
 * @param {String} color The color value.
 * @param {Boolean} fg If `true`, the function will modify the foreground
 * color of the text (default: `true`).
 * @param {Boolean} bg If `true`, the function will modify the background
 * color of the text.
 * @return {String} The colored text.
 */
CliPie.prototype.textColor = function (text, color, fg, bg) {
    if (color && fg === undefined) {
        fg = true;
    }
    if (this.options.no_ansi || !(fg || bg) || !color) {
        return text;
    }
    if (fg && bg) {
        // return Couleurs(text).bg(color).fg(color);
        return Couleurs(" ").bg(color).fg(color);
    }

    return Couleurs[fg ? "fg" : "bg"](text, color);
};

/**
 * uniqueColor
 * Generates an unique color.
 *
 * @name uniqueColor
 * @function
 * @param {String} cColor The provided color.
 * @return {String} The unique color.
 */
CliPie.prototype.uniqueColor = function (cColor) {
    var self = this
      , color = null
      , clrStr = null
      ;

    if (self.options.no_ansi) {
        return cColor;
    }

    if (cColor === undefined) {
        color = FlatColors();
    } else if (this.options.flat && FlatColors.colors.indexOf(cColor) === -1 || Array.isArray(cColor) && cColor.length === 3) {
        color = FlatColors(cColor);
    } else {
        color = FlatColors.toRgb(cColor);
    }

    clrStr = color.join(",");
    if (this.colors[clrStr] && Object.keys(this.colors).length !== FlatColors.colors.length) {
        return this.uniqueColor();
    }

    this.colors[clrStr] = color;
    return color;
};

/**
 * toString
 * Stringifies the pie.
 *
 * @name toString
 * @function
 * @return {String} The stringified pie.
 */
CliPie.prototype.toString = function () {

    var self = this
      , circle = new CliCircle(self.radius, self.options.chr, self.options.circle_opts)
      ;

    function line(x1, y1, x2, y2, chr) {
        circle.graph.setFunctionX(function (x) {
            var y = (x - x1) * (y2 - y1) / (x2 - x1) + y1;
            return y;
        }, x1, x2, chr).setFunctionY(function (y) {
            var x = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
            return x;
        }, y1, y2, chr);
    }

    function toRad(a) {
        return a * (Math.PI / 180);
    }

    var cAngle = 90;
    function next(delta) {
        cAngle -= delta;
        return {
            x: self.radius * Math.cos(toRad(cAngle))
          , y: self.radius * Math.sin(toRad(cAngle))
        };
    }

    var i = 0
      , letters = {}
      , cPoint = null
      , str = ""
      , legend = null
      , cLegend = null
      ;

    self.data.forEach(function (cItem) {
        cItem.percent = (100 * cItem.value) / self.total;
        cItem.angle = cItem.percent * 3.6;
        cItem.color = letters[cItem.letter] = self.uniqueColor(cItem.color);
        for (i = 0; i <= cItem.angle; ++i) {
            cPoint = next(1);
            line(0, 0, cPoint.x, cPoint.y, cItem.letter);
        }
    });

    str = circle.toString().split("\n").map(function (c) {
        return c.split("").map(function (ch) {
            return new Array(self.options.circle_opts.aRatio + 2).join(ch);
        }).join("");
    }).join("\n");

    Object.keys(letters).forEach(function (c) {
        str = str.replace(new RegExp(c, "g"), self.textColor(c, letters[c], true, true));
    });

    if (self.options.legend) {
        legend = self.data.map(function (c) {
            return self.textColor(self.options.no_ansi ? c.letter : "▣", c.color) + " " + c.label + " (" + c.percent.toFixed(2) + "%)";

        });
        if (self.options.display_total) {
            legend.push("");
            var totalLabel = self.options.total_label || 'Total';
            legend.push(self.textColor("  " + totalLabel + ": " + this.total.toFixed(2)));
        }
        str = str.split("\n");
        str = str.map(function (c, i) {
            cLegend = legend[Math.round(i - str.length / 2 + self.data.length / 2)]
            if (cLegend) {
                return c + " " + cLegend;
            }
            return c;
        }).join("\n");
    }

    return str;
};

module.exports = CliPie;
<commit_msg>Replace the legend square with a non-emoji (#18)<commit_after>// Dependencies
var Ul = require("ul")
  , FlatColors = require("flatcolors")
  , CliCircle = require("cli-circle")
  , Couleurs = require("couleurs")
  ;

/**
 * CliPie
 * Creates a new instance of `CliPie`.
 *
 * @name CliPie
 * @function
 * @param {Number} r The radius value.
 * @param {Array} data An array of objects in the following format:
 * @param {Object} options An object containing the following fields:
 *
 *  - `flat` (Boolean): If `true`, flat colors will be used (default: `true`).
 *  - `chr` (String): The character to draw the pie (default: `" "`).
 *  - `no_ansi` (Boolean): If `true`, ansi styles will not be used.
 *  - `legend` (Boolean): If `true`, a legend is added next to the pie.
 *  - `display_total` (Boolean): If `true`, the total is added to the legend.
 *  - `total_label` (String): The label for the total (default: `Total`)
 *  - `circle_opts` (Object): The options passed to the
 *    [`cli-circle`](https://github.com/IonicaBizau/node-cli-circle) module,
 *    which uses the
 *    [`cli-graph`](https://github.com/IonicaBizau/node-cli-graph) module to
 *    build the graph.
 *
 * @return {CliPie} The `CliPie` instance.
 */
function CliPie(r, data, options) {
    this.data = [];
    this.radius = r;
    this.total = 0;
    this.colors = {};
    this.cChar = -1;
    this.options = Ul.deepMerge(options, {
        flat: true
      , chr: " "
      , no_ansi: false
      , circle_opts: {
            aRatio: 1
        }
      , chars: "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")
    });
    if (Array.isArray(data)) {
        data.forEach(this.add.bind(this));
    } else if (data && data.constructor === Object) {
        options = data;
    }
}

/**
 * CliPie.Item
 * Creates a new `CliPie.Item` instance.
 *
 * @name CLiPie.Item
 * @function
 * @param {Object} obj The `CliPie.Item` data containing:
 *
 *  - `value` (Number): The item value.
 *  - `label` (String): The item label.
 *  - `color` (String): The item color (used in ansi graphs).
 *  - `letter` (String): The item letter (used on non-ansi graphs).
 *
 * @return {CliPieItem} The `CliPieItem` containing:
 */
CliPie.Item = function (obj) {
    this.value = obj.value;
    this.label = obj.label;
    this.color = obj.color;
    this.percent = null;
    this.angle = null;
    this.letter = obj.letter;
};

/**
 * add
 * Adds new items in the cli pie.
 *
 * @name add
 * @function
 * @param {Object} item The item data.
 * @return {CliPie} The `CliPie` instance.
 */
CliPie.prototype.add = function (item) {
    item.letter = item.letter || this.options.chars[++this.cChar];
    var nItem = new CliPie.Item(item);
    this.total += nItem.value;
    this.data.push(nItem);
    return this;
};

/**
 * textColor
 * Adds foreground or/and background color(s) to the provided text.
 *
 * @name textColor
 * @function
 * @param {String} text The text to color.
 * @param {String} color The color value.
 * @param {Boolean} fg If `true`, the function will modify the foreground
 * color of the text (default: `true`).
 * @param {Boolean} bg If `true`, the function will modify the background
 * color of the text.
 * @return {String} The colored text.
 */
CliPie.prototype.textColor = function (text, color, fg, bg) {
    if (color && fg === undefined) {
        fg = true;
    }
    if (this.options.no_ansi || !(fg || bg) || !color) {
        return text;
    }
    if (fg && bg) {
        return Couleurs(" ").bg(color).fg(color);
    }

    return Couleurs[fg ? "fg" : "bg"](text, color);
};

/**
 * uniqueColor
 * Generates an unique color.
 *
 * @name uniqueColor
 * @function
 * @param {String} cColor The provided color.
 * @return {String} The unique color.
 */
CliPie.prototype.uniqueColor = function (cColor) {
    var self = this
      , color = null
      , clrStr = null
      ;

    if (self.options.no_ansi) {
        return cColor;
    }

    if (cColor === undefined) {
        color = FlatColors();
    } else if (this.options.flat && FlatColors.colors.indexOf(cColor) === -1 || Array.isArray(cColor) && cColor.length === 3) {
        color = FlatColors(cColor);
    } else {
        color = FlatColors.toRgb(cColor);
    }

    clrStr = color.join(",");
    if (this.colors[clrStr] && Object.keys(this.colors).length !== FlatColors.colors.length) {
        return this.uniqueColor();
    }

    this.colors[clrStr] = color;
    return color;
};

/**
 * toString
 * Stringifies the pie.
 *
 * @name toString
 * @function
 * @return {String} The stringified pie.
 */
CliPie.prototype.toString = function () {

    var self = this
      , circle = new CliCircle(self.radius, self.options.chr, self.options.circle_opts)
      ;

    function line(x1, y1, x2, y2, chr) {
        circle.graph.setFunctionX(function (x) {
            var y = (x - x1) * (y2 - y1) / (x2 - x1) + y1;
            return y;
        }, x1, x2, chr).setFunctionY(function (y) {
            var x = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
            return x;
        }, y1, y2, chr);
    }

    function toRad(a) {
        return a * (Math.PI / 180);
    }

    var cAngle = 90;
    function next(delta) {
        cAngle -= delta;
        return {
            x: self.radius * Math.cos(toRad(cAngle))
          , y: self.radius * Math.sin(toRad(cAngle))
        };
    }

    var i = 0
      , letters = {}
      , cPoint = null
      , str = ""
      , legend = null
      , cLegend = null
      ;

    self.data.forEach(function (cItem) {
        cItem.percent = (100 * cItem.value) / self.total;
        cItem.angle = cItem.percent * 3.6;
        cItem.color = letters[cItem.letter] = self.uniqueColor(cItem.color);
        for (i = 0; i <= cItem.angle; ++i) {
            cPoint = next(1);
            line(0, 0, cPoint.x, cPoint.y, cItem.letter);
        }
    });

    str = circle.toString().split("\n").map(function (c) {
        return c.split("").map(function (ch) {
            return new Array(self.options.circle_opts.aRatio + 2).join(ch);
        }).join("");
    }).join("\n");

    Object.keys(letters).forEach(function (c) {
        str = str.replace(new RegExp(c, "g"), self.textColor(c, letters[c], true, true));
    });

    if (self.options.legend) {
        legend = self.data.map(function (c) {
            return self.textColor(self.options.no_ansi ? c.letter : "▣", c.color) + " " + c.label + " (" + c.percent.toFixed(2) + "%)";

        });
        if (self.options.display_total) {
            legend.push("");
            var totalLabel = self.options.total_label || 'Total';
            legend.push(self.textColor("  " + totalLabel + ": " + this.total.toFixed(2)));
        }
        str = str.split("\n");
        str = str.map(function (c, i) {
            cLegend = legend[Math.round(i - str.length / 2 + self.data.length / 2)]
            if (cLegend) {
                return c + " " + cLegend;
            }
            return c;
        }).join("\n");
    }

    return str;
};

module.exports = CliPie;
<|endoftext|>


<commit_before>var concat = require('gulp-concat');
var gulp = require('gulp');
var gulpif = require('gulp-if');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');

var config = {
    js: {
        src: [
            // Modernizr
            'django_backend/static/django_backend/libs/modernizr/modernizr.js',

            // jQuery
            'django_backend/static/django_backend/libs/jquery/dist/jquery.min.js',
            'django_backend/static/django_backend/libs/jquery-ui/ui/minified/jquery-ui.min.js',
            'django_backend/static/django_backend/js/jquery.django-csrf.js',

            // Bootstrap
            'django_backend/static/django_backend/libs/bootstrap-sass/assets/javascripts/bootstrap.min.js',
            'django_backend/static/django_backend/libs/bootstrap-tab-history/vendor/assets/javascripts/bootstrap-tab-history.js',

            // Stapes
            'django_backend/static/django_backend/libs/stapes/stapes.min.js',

            // RequireJS
            'django_backend/static/django_backend/libs/requirejs/require.js',
            'django_backend/static/django_backend/js/require-init.js',

            // Backend
            'django_backend/static/django_backend/js/django_backend.widget.js',
            'django_backend/static/django_backend/js/django_backend.dialog.js',
            'django_backend/static/django_backend/js/django_backend.formdata.js',
            'django_backend/static/django_backend/js/django_backend.ajaxdialog.js',
            'django_backend/static/django_backend/js/django_backend.selectall.js',
            'django_backend/static/django_backend/js/django_backend.instantsubmit.js',
            'django_backend/static/django_backend/js/django_backend.pagecontext.js',
            'django_backend/static/django_backend/js/django_backend.filterform.js',
            'django_backend/static/django_backend/js/django_backend.forms.js',
            'django_backend/static/django_backend/js/django_backend.formset.js',
            'django_backend/static/django_backend/js/django_backend.stackedajaxdialog.js',
            'django_backend/static/django_backend/js/django_backend.opendialog.js',
            'django_backend/static/django_backend/js/django_backend.selectrelated.js',
            'django_backend/static/django_backend/js/django_backend.inlinerelated.js',
            'django_backend/static/django_backend/js/django_backend.managerelated.js',
            // 'django_backend/static/django_backend/js/main.js',
            'django_backend/static/django_backend/js/init.js',
        ],
        watch: [
            'django_backend/static/django_bacend/js/**/*.js',
        ]
    },
    sass: {
        src: [
            'django_backend/static/django_backend/scss/django_backend.scss',
            'django_backend/static/django_backend/scss/jquery-ui.scss',
        ],
        watch: [
            'django_backend/static/django_backend/scss/**/*.scss',
        ]
    },
    dev: false
};

gulp.task('js', function () {
    return gulp.src(config.js.src)
        .pipe(gulpif(config.dev, sourcemaps.init()))
        .pipe(concat('django_backend.js'))
        .pipe(uglify())
        .pipe(gulpif(config.dev, sourcemaps.write()))
        .pipe(gulp.dest('django_backend/static/django_backend/dist/'));
});

gulp.task('js:watch', ['js'], function () {
    gulp.watch(config.js.watch, ['js']);
});

gulp.task('sass', function () {
    return gulp.src(config.sass.src)
        .pipe(gulpif(config.dev, sourcemaps.init()))
        .pipe(sass({
            outputStyle: 'compressed',
        }).on('error', sass.logError))
        .pipe(gulpif(config.dev, sourcemaps.write()))
        .pipe(gulp.dest('django_backend/static/django_backend/dist/'));
});

gulp.task('sass:watch', ['sass'], function () {
    gulp.watch(config.sass.watch, ['sass']);
});

gulp.task('dev', function () {
    config.dev = true;
});

gulp.task('build', ['js', 'sass'], function () {

});

gulp.task('watch', ['js:watch', 'sass:watch'], function () {

});

gulp.task('default', ['build'], function () {

});
<commit_msg>Fix dev watches in gulpfile<commit_after>var concat = require('gulp-concat');
var gulp = require('gulp');
var gulpif = require('gulp-if');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');

var config = {
    js: {
        src: [
            // Modernizr
            'django_backend/static/django_backend/libs/modernizr/modernizr.js',

            // jQuery
            'django_backend/static/django_backend/libs/jquery/dist/jquery.min.js',
            'django_backend/static/django_backend/libs/jquery-ui/ui/minified/jquery-ui.min.js',
            'django_backend/static/django_backend/js/jquery.django-csrf.js',

            // Bootstrap
            'django_backend/static/django_backend/libs/bootstrap-sass/assets/javascripts/bootstrap.min.js',
            'django_backend/static/django_backend/libs/bootstrap-tab-history/vendor/assets/javascripts/bootstrap-tab-history.js',

            // Stapes
            'django_backend/static/django_backend/libs/stapes/stapes.min.js',

            // RequireJS
            'django_backend/static/django_backend/libs/requirejs/require.js',
            'django_backend/static/django_backend/js/require-init.js',

            // Backend
            'django_backend/static/django_backend/js/django_backend.widget.js',
            'django_backend/static/django_backend/js/django_backend.dialog.js',
            'django_backend/static/django_backend/js/django_backend.formdata.js',
            'django_backend/static/django_backend/js/django_backend.ajaxdialog.js',
            'django_backend/static/django_backend/js/django_backend.selectall.js',
            'django_backend/static/django_backend/js/django_backend.instantsubmit.js',
            'django_backend/static/django_backend/js/django_backend.pagecontext.js',
            'django_backend/static/django_backend/js/django_backend.filterform.js',
            'django_backend/static/django_backend/js/django_backend.forms.js',
            'django_backend/static/django_backend/js/django_backend.formset.js',
            'django_backend/static/django_backend/js/django_backend.stackedajaxdialog.js',
            'django_backend/static/django_backend/js/django_backend.opendialog.js',
            'django_backend/static/django_backend/js/django_backend.selectrelated.js',
            'django_backend/static/django_backend/js/django_backend.inlinerelated.js',
            'django_backend/static/django_backend/js/django_backend.managerelated.js',
            // 'django_backend/static/django_backend/js/main.js',
            'django_backend/static/django_backend/js/init.js',
        ],
        watch: [
            'django_backend/static/django_backend/js/**/*.js',
        ]
    },
    sass: {
        src: [
            'django_backend/static/django_backend/scss/django_backend.scss',
            'django_backend/static/django_backend/scss/jquery-ui.scss',
        ],
        watch: [
            'django_backend/static/django_backend/scss/**/*.scss',
        ]
    },
    dev: false
};

gulp.task('js', function () {
    return gulp.src(config.js.src)
        .pipe(gulpif(config.dev, sourcemaps.init()))
        .pipe(concat('django_backend.js'))
        .pipe(gulpif(!config.dev, uglify()))
        .pipe(gulpif(config.dev, sourcemaps.write()))
        .pipe(gulp.dest('django_backend/static/django_backend/dist/'));
});

gulp.task('js:watch', ['js'], function () {
    gulp.watch(config.js.watch, ['js']);
});

gulp.task('sass', function () {
    return gulp.src(config.sass.src)
        .pipe(gulpif(config.dev, sourcemaps.init()))
        .pipe(gulpif(!config.dev, sass({
            outputStyle: 'compressed',
        }).on('error', sass.logError)))
        .pipe(gulpif(config.dev, sass({}).on('error', sass.logError)))
        .pipe(gulpif(config.dev, sourcemaps.write()))
        .pipe(gulp.dest('django_backend/static/django_backend/dist/'));
});

gulp.task('sass:watch', ['sass'], function () {
    gulp.watch(config.sass.watch, ['sass']);
});

gulp.task('dev', function () {
    config.dev = true;
});

gulp.task('build', ['js', 'sass'], function () {

});

gulp.task('watch', ['js:watch', 'sass:watch'], function () {

});

gulp.task('default', ['build'], function () {

});
<|endoftext|>


<commit_before>const KeenQuery = require('./keen-query');
const parser = require('./parser');
const Aggregator = require('./aggregator');

function build (queryConf, name) {
	if (typeof queryConf === 'string') {
		queryConf = parser(queryConf);
	}

	if (queryConf.aggregator) {
		return Aggregator.factory(queryConf)
	}
	const keenQuery = new KeenQuery(queryConf.event);
	keenQuery.name = name;
	keenQuery._printer = queryConf.print;
	keenQuery._postProcessing = queryConf.postProcessing;
	return queryConf.functions.reduce((keenQuery, transform) => {
		return keenQuery[transform.func].apply(keenQuery, transform.params || []);
	}, keenQuery);
}

module.exports = KeenQuery;
module.exports.Aggregator = Aggregator;
module.exports.defineAggregator (name, constructor) {
	return Aggregator.define(name, constructor);
}

module.exports.build = build;
module.exports.execute = str => build(str).print();
module.exports.parseFilter = parser.parseFilter;
<commit_msg>fixed compilation error<commit_after>const KeenQuery = require('./keen-query');
const parser = require('./parser');
const Aggregator = require('./aggregator');

function build (queryConf, name) {
	if (typeof queryConf === 'string') {
		queryConf = parser(queryConf);
	}

	if (queryConf.aggregator) {
		return Aggregator.factory(queryConf)
	}
	const keenQuery = new KeenQuery(queryConf.event);
	keenQuery.name = name;
	keenQuery._printer = queryConf.print;
	keenQuery._postProcessing = queryConf.postProcessing;
	return queryConf.functions.reduce((keenQuery, transform) => {
		return keenQuery[transform.func].apply(keenQuery, transform.params || []);
	}, keenQuery);
}

module.exports = KeenQuery;
module.exports.Aggregator = Aggregator;
module.exports.defineAggregator = (name, constructor) => {
	return Aggregator.define(name, constructor);
}

module.exports.build = build;
module.exports.execute = str => build(str).print();
module.exports.parseFilter = parser.parseFilter;
<|endoftext|>


<commit_before>// @flow
import path from 'path';
import EventEmitter from 'events';
import {Readable} from 'stream';
import url from 'url';
import arrayUniq from 'array-uniq';
import arrayDiffer from 'array-differ';
import easydate from 'easydate';
import fsWriteStreamAtomic from 'fs-write-stream-atomic';
import getRes from 'get-res';
import logSymbols from 'log-symbols';
import mem from 'mem';
import makeDir from 'make-dir';
import del from 'del';
import screenshotStream from 'screenshot-stream';
import viewportList from 'viewport-list';
import protocolify from 'protocolify';
import filenamify from 'filenamify';
import filenamifyUrl from 'filenamify-url';
import template from 'lodash.template';
import plur from 'plur';
import unusedFilename from 'unused-filename';

type PageresStream = Readable & {filename: string};

type Options = {
	delay?: number;
	timeout?: number;
	crop?: boolean;
	incrementalName?: boolean;
	css?: string;
	cookies?: Array<string> | {[key: string]: string};
	filename?: string;
	selector?: string;
	hide?: Array<string>;
	username?: string;
	password?: string;
	scale?: number;
	format?: string;
	userAgent?: string;
	headers?: {[key: string]: string};
};

type Src = {
	url: string;
	sizes: Array<string>;
	options: Options;
};

type Viewport = {
	url: string;
	sizes: Array<string>;
	keywords: Array<string>;
};

type SrcFn<DestValue> =
	& ((_: void, _: void, _: void) => Array<Src>)
	& ((url: string, sizes: Array<string>, options: Options) => Pageres<DestValue>);

type DestFn<DestValue> =
	& ((_: void) => DestValue)
	& ((dir: DestValue) => Pageres<DestValue>);

const getResMem = mem(getRes);
const viewportListMem = mem(viewportList);

let listener;

export default class Pageres<DestValue: string> extends EventEmitter {
	options: Options;
	stats: Object;
	items: Array<PageresStream>;
	sizes: Array<string>;
	urls: Array<string>;
	_src: Array<Src>;
	_dest: DestValue;

	constructor(options: Options) {
		super();

		this.options = Object.assign({}, options);
		this.options.filename = this.options.filename || '<%= url %>-<%= size %><%= crop %>';
		this.options.format = this.options.format || 'png';
		this.options.incrementalName = this.options.incrementalName || false;

		this.stats = {};
		this.items = [];
		this.sizes = [];
		this.urls = [];
		this._src = [];
	}

	src: SrcFn<DestValue>;
	src(url: string, sizes: Array<string>, options: Options) {
		if (url === undefined) {
			return this._src;
		}

		this._src.push({url, sizes, options});
		return this;
	}

	dest: DestFn<DestValue>;
	dest(dir: DestValue) {
		if (dir === undefined) {
			return this._dest;
		}

		this._dest = dir;
		return this;
	}

	async run(): Promise<PageresStream[]> {
		await Promise.all(this.src().map(src => { // eslint-disable-line array-callback-return
			const options = Object.assign({}, this.options, src.options);
			const sizes = arrayUniq(src.sizes.filter(/./.test, /^\d{2,4}x\d{2,4}$/i));
			const keywords = arrayDiffer(src.sizes, sizes);

			if (!src.url) {
				throw new Error('URL required');
			}

			this.urls.push(src.url);

			if (sizes.length === 0 && keywords.indexOf('w3counter') !== -1) {
				return this.resolution(src.url, options);
			}

			if (keywords.length > 0) {
				return this.viewport({url: src.url, sizes, keywords}, options);
			}

			for (const size of sizes) {
				this.sizes.push(size);
				this.items.push(this.create(src.url, size, options));
			}
		}));

		this.stats.urls = arrayUniq(this.urls).length;
		this.stats.sizes = arrayUniq(this.sizes).length;
		this.stats.screenshots = this.items.length;

		if (!this.dest()) {
			return this.items;
		}

		await this.save(this.items);

		return this.items;
	}

	async resolution(url: string, options: Options) {
		for (const item of await getResMem()) {
			this.sizes.push(item.item);
			this.items.push(this.create(url, item.item, options));
		}
	}

	async viewport(obj: Viewport, options: Options) {
		for (const item of await viewportListMem(obj.keywords)) {
			this.sizes.push(item.size);
			obj.sizes.push(item.size);
		}

		for (const size of arrayUniq(obj.sizes)) {
			this.items.push(this.create(obj.url, size, options));
		}
	}

	save(streams: Array<PageresStream>) {
		const files = [];

		const end = () => del(files, {force: true});

		if (!listener) {
			listener = process.on('SIGINT', async () => {
				await end();
				process.exit(1);
			});
		}

		return Promise.all(streams.map(stream =>
			new Promise(async (resolve, reject) => {
				await makeDir(this.dest());

				const dest = path.join(this.dest(), stream.filename);
				const write = fsWriteStreamAtomic(dest);

				files.push(write.__atomicTmp);

				stream.on('warning', this.emit.bind(this, 'warning'));
				stream.on('warn', this.emit.bind(this, 'warn'));
				stream.on('error', async err => {
					await end();
					reject(err);
				});

				write.on('finish', resolve);
				write.on('error', async err => {
					await end();
					reject(err);
				});

				stream.pipe(write);
			})
		));
	}

	create(uri: string, size: string, options: Options) {
		const sizes = size.split('x');
		const stream = screenshotStream(protocolify(uri), size, options);

		// Coercing to string here to please Flow
		// TODO: Should fix the Flow type so this isn't necessary
		const filename = template(`${String(options.filename)}.${String(options.format)}`);

		let hash = url.parse(uri).hash || '';

		if (path.isAbsolute(uri)) {
			uri = path.basename(uri);
		}

		// Strip empty hash fragments: `#` `#/` `#!/`
		if (/^#!?\/?$/.test(hash)) {
			hash = '';
		}

		stream.filename = filename({
			crop: options.crop ? '-cropped' : '',
			date: easydate('Y-M-d'),
			time: easydate('h-m-s'),
			size,
			width: sizes[0],
			height: sizes[1],
			url: filenamifyUrl(uri) + filenamify(hash)
		});

		if (options.incrementalName) {
			stream.filename = unusedFilename.sync(stream.filename);
		}

		return stream;
	}

	successMessage() {
		const stats = this.stats;
		const {screenshots, sizes, urls} = stats;
		const words = {
			screenshots: plur('screenshot', screenshots),
			sizes: plur('size', sizes),
			urls: plur('url', urls)
		};

		console.log(`\n${logSymbols.success} Generated ${screenshots} ${words.screenshots} from ${urls} ${words.urls} and ${sizes} ${words.sizes}`);
	}
}
<commit_msg>Fix linting<commit_after>// @flow
import path from 'path';
import EventEmitter from 'events';
import {Readable} from 'stream';
import url from 'url';
import arrayUniq from 'array-uniq';
import arrayDiffer from 'array-differ';
import easydate from 'easydate';
import fsWriteStreamAtomic from 'fs-write-stream-atomic';
import getRes from 'get-res';
import logSymbols from 'log-symbols';
import mem from 'mem';
import makeDir from 'make-dir';
import del from 'del';
import screenshotStream from 'screenshot-stream';
import viewportList from 'viewport-list';
import protocolify from 'protocolify';
import filenamify from 'filenamify';
import filenamifyUrl from 'filenamify-url';
import template from 'lodash.template';
import plur from 'plur';
import unusedFilename from 'unused-filename';

type PageresStream = Readable & {filename: string};

type Options = {
	delay?: number;
	timeout?: number;
	crop?: boolean;
	incrementalName?: boolean;
	css?: string;
	cookies?: Array<string> | {[key: string]: string};
	filename?: string;
	selector?: string;
	hide?: Array<string>;
	username?: string;
	password?: string;
	scale?: number;
	format?: string;
	userAgent?: string;
	headers?: {[key: string]: string};
};

type Src = {
	url: string;
	sizes: Array<string>;
	options: Options;
};

type Viewport = {
	url: string;
	sizes: Array<string>;
	keywords: Array<string>;
};

type SrcFn<DestValue> =
	& ((_: void, _: void, _: void) => Array<Src>)
	& ((url: string, sizes: Array<string>, options: Options) => Pageres<DestValue>);

type DestFn<DestValue> =
	& ((_: void) => DestValue)
	& ((dir: DestValue) => Pageres<DestValue>);

const getResMem = mem(getRes);
const viewportListMem = mem(viewportList);

let listener;

export default class Pageres<DestValue: string> extends EventEmitter {
	options: Options;

	stats: Object;

	items: Array<PageresStream>;

	sizes: Array<string>;

	urls: Array<string>;

	_src: Array<Src>;

	_dest: DestValue;

	constructor(options: Options) {
		super();

		this.options = Object.assign({}, options);
		this.options.filename = this.options.filename || '<%= url %>-<%= size %><%= crop %>';
		this.options.format = this.options.format || 'png';
		this.options.incrementalName = this.options.incrementalName || false;

		this.stats = {};
		this.items = [];
		this.sizes = [];
		this.urls = [];
		this._src = [];
	}

	src: SrcFn<DestValue>;

	src(url: string, sizes: Array<string>, options: Options) {
		if (url === undefined) {
			return this._src;
		}

		this._src.push({url, sizes, options});
		return this;
	}

	dest: DestFn<DestValue>;

	dest(dir: DestValue) {
		if (dir === undefined) {
			return this._dest;
		}

		this._dest = dir;
		return this;
	}

	async run(): Promise<PageresStream[]> {
		await Promise.all(this.src().map(src => { // eslint-disable-line array-callback-return
			const options = Object.assign({}, this.options, src.options);
			const sizes = arrayUniq(src.sizes.filter(/./.test, /^\d{2,4}x\d{2,4}$/i));
			const keywords = arrayDiffer(src.sizes, sizes);

			if (!src.url) {
				throw new Error('URL required');
			}

			this.urls.push(src.url);

			if (sizes.length === 0 && keywords.indexOf('w3counter') !== -1) {
				return this.resolution(src.url, options);
			}

			if (keywords.length > 0) {
				return this.viewport({url: src.url, sizes, keywords}, options);
			}

			for (const size of sizes) {
				this.sizes.push(size);
				this.items.push(this.create(src.url, size, options));
			}
		}));

		this.stats.urls = arrayUniq(this.urls).length;
		this.stats.sizes = arrayUniq(this.sizes).length;
		this.stats.screenshots = this.items.length;

		if (!this.dest()) {
			return this.items;
		}

		await this.save(this.items);

		return this.items;
	}

	async resolution(url: string, options: Options) {
		for (const item of await getResMem()) {
			this.sizes.push(item.item);
			this.items.push(this.create(url, item.item, options));
		}
	}

	async viewport(obj: Viewport, options: Options) {
		for (const item of await viewportListMem(obj.keywords)) {
			this.sizes.push(item.size);
			obj.sizes.push(item.size);
		}

		for (const size of arrayUniq(obj.sizes)) {
			this.items.push(this.create(obj.url, size, options));
		}
	}

	save(streams: Array<PageresStream>) {
		const files = [];

		const end = () => del(files, {force: true});

		if (!listener) {
			listener = process.on('SIGINT', async () => {
				await end();
				process.exit(1);
			});
		}

		return Promise.all(streams.map(stream =>
			new Promise(async (resolve, reject) => {
				await makeDir(this.dest());

				const dest = path.join(this.dest(), stream.filename);
				const write = fsWriteStreamAtomic(dest);

				files.push(write.__atomicTmp);

				stream.on('warning', this.emit.bind(this, 'warning'));
				stream.on('warn', this.emit.bind(this, 'warn'));
				stream.on('error', async err => {
					await end();
					reject(err);
				});

				write.on('finish', resolve);
				write.on('error', async err => {
					await end();
					reject(err);
				});

				stream.pipe(write);
			})));
	}

	create(uri: string, size: string, options: Options) {
		const sizes = size.split('x');
		const stream = screenshotStream(protocolify(uri), size, options);

		// Coercing to string here to please Flow
		// TODO: Should fix the Flow type so this isn't necessary
		const filename = template(`${String(options.filename)}.${String(options.format)}`);

		let hash = url.parse(uri).hash || '';

		if (path.isAbsolute(uri)) {
			uri = path.basename(uri);
		}

		// Strip empty hash fragments: `#` `#/` `#!/`
		if (/^#!?\/?$/.test(hash)) {
			hash = '';
		}

		stream.filename = filename({
			crop: options.crop ? '-cropped' : '',
			date: easydate('Y-M-d'),
			time: easydate('h-m-s'),
			size,
			width: sizes[0],
			height: sizes[1],
			url: filenamifyUrl(uri) + filenamify(hash)
		});

		if (options.incrementalName) {
			stream.filename = unusedFilename.sync(stream.filename);
		}

		return stream;
	}

	successMessage() {
		const stats = this.stats;
		const {screenshots, sizes, urls} = stats;
		const words = {
			screenshots: plur('screenshot', screenshots),
			sizes: plur('size', sizes),
			urls: plur('url', urls)
		};

		console.log(`\n${logSymbols.success} Generated ${screenshots} ${words.screenshots} from ${urls} ${words.urls} and ${sizes} ${words.sizes}`);
	}
}
<|endoftext|>


<commit_before>module.exports = {
  extends: ['eslint:recommended', 'plugin:node/recommended'],
  plugins: ['node'],
  rules: {
    // override recomennded
    'no-console': 'off',
    'no-empty': ['error', { allowEmptyCatch: true }],
    'no-unused-vars': ['error', { args: 'none' }],
    // Possible Errors
    'no-extra-parens': ['error', 'all', {
      conditionalAssign: false,
      returnAssign: false,
      nestedBinaryExpressions: false
    }],
    // Best Practices
    'array-callback-return': 'error',
    'block-scoped-var': 'error',
    'curly': ['error', 'multi-line'],
    'dot-location': ['error', 'property'],
    'dot-notation': 'error',
    'eqeqeq': ['error', 'allow-null'],
    'no-else-return': 'error',
    'no-eval': 'error',
    'no-extend-native': 'error',
    'no-extra-bind': 'error',
    'no-implicit-globals': 'error',
    'no-implied-eval': 'error',
    'no-lone-blocks': 'error',
    'no-loop-func': 'error',
    'no-multi-spaces': 'error',
    'no-multi-str': 'error',
    'no-new': 'error',
    'no-new-func': 'error',
    'no-new-wrappers': 'error',
    'no-self-compare': 'error',
    'no-sequences': 'error',
    'no-throw-literal': 'error',
    'no-unused-expressions': ['error', {
      allowShortCircuit: true,
      allowTernary: true
    }],
    'no-useless-call': 'error',
    'no-useless-concat': 'error',
    'no-useless-return': 'error',
    'no-proto': 'error',
    'no-return-assign': 'error',
    'no-void': 'error',
    'no-with': 'error',
    'prefer-promise-reject-errors': 'error',
    'radix': 'error',
    'wrap-iife': 'error',
    'yoda': ['error', 'never'],
    // Strict Mode
    'strict': 'error',
    // Variables
    'no-label-var': 'error',
    'no-shadow-restricted-names': 'error',
    'no-undef-init': 'error',
    'no-use-before-define': ['error', 'nofunc'],
    // Node.js and CommonJS
    'handle-callback-err': 'error',
    // Stylistic Issues
    'no-path-concat': 'error',
    // ECMAScript 6
    // other
    'indent': ['error', 2, {
      SwitchCase: 1
    }],
    'linebreak-style': ['error', 'unix'],
    'quotes': ['error', 'single'],
    'semi': ['error', 'always'],
    'brace-style': ['error', '1tbs', {
      allowSingleLine: true
    }],
    'no-multiple-empty-lines': 'error',
    'one-var': ['error', {
      uninitialized: 'always',
      initialized: 'never'
    }],
    'operator-linebreak': ['error', 'before'],
    'key-spacing': ['error', {
      beforeColon: false,
      afterColon: true
    }],
    'space-unary-ops': ['error', {
      words: false,
      nonwords: false
    }],
    'comma-spacing': ['error', {
      before: false,
      after: true
    }],
    'semi-spacing': ['error', {
      before: false,
      after: true
    }],
    'func-call-spacing': 'error',
    'space-before-function-paren': ['error', 'never'],
    'space-in-parens': ['error', 'never'],
    'comma-dangle': ['error', 'never'],
    'no-trailing-spaces': 'error',
    'comma-style': ['error', 'last'],
    'eol-last': 'error',
    'space-infix-ops': 'error',
    'keyword-spacing': ['error', {}],
    'space-before-blocks': ['error', 'always'],
    'no-extra-label': 'error',
    'array-bracket-spacing': ['error', 'never'],
    'computed-property-spacing': ['error', 'never'],
    'block-spacing': ['error', 'always'],
    'lines-around-comment': ['error', {
      beforeBlockComment: true
    }],
    'new-cap': 'error',
    'new-parens': 'error',
    'no-array-constructor': 'error',
    'no-nested-ternary': 'error',
    'no-new-object': 'error',
    'no-unneeded-ternary': 'error',
    'no-whitespace-before-property': 'error',
    'spaced-comment': ['error', 'always'],
    'template-tag-spacing': ['error', 'never'],
    'unicode-bom': ['error', 'never'],
    'arrow-spacing': 'error',
    'generator-star-spacing': ['error', 'after'],
    'no-confusing-arrow': ['error', {
      allowParens: true
    }],
    'no-duplicate-imports': 'error',
    'no-useless-computed-key': 'error',
    'no-useless-constructor': 'error',
    'no-useless-rename': 'error',
    'rest-spread-spacing': ['error', 'never'],
    'template-curly-spacing': ['error', 'never'],
    'yield-star-spacing': ['error', 'after'],
    'no-mixed-operators': 'error'
  },
  env: {
    node: true,
    es6: true
  },
  parserOptions: {
    ecmaVersion: 6
  }
};
<commit_msg>[WIP] Category Sort: Node.js and CommonJS<commit_after>module.exports = {
  extends: ['eslint:recommended', 'plugin:node/recommended'],
  plugins: ['node'],
  rules: {
    // override recomennded
    'no-console': 'off',
    'no-empty': ['error', { allowEmptyCatch: true }],
    'no-unused-vars': ['error', { args: 'none' }],
    // Possible Errors
    'no-extra-parens': ['error', 'all', {
      conditionalAssign: false,
      returnAssign: false,
      nestedBinaryExpressions: false
    }],
    // Best Practices
    'array-callback-return': 'error',
    'block-scoped-var': 'error',
    'curly': ['error', 'multi-line'],
    'dot-location': ['error', 'property'],
    'dot-notation': 'error',
    'eqeqeq': ['error', 'allow-null'],
    'no-else-return': 'error',
    'no-eval': 'error',
    'no-extend-native': 'error',
    'no-extra-bind': 'error',
    'no-implicit-globals': 'error',
    'no-implied-eval': 'error',
    'no-lone-blocks': 'error',
    'no-loop-func': 'error',
    'no-multi-spaces': 'error',
    'no-multi-str': 'error',
    'no-new': 'error',
    'no-new-func': 'error',
    'no-new-wrappers': 'error',
    'no-self-compare': 'error',
    'no-sequences': 'error',
    'no-throw-literal': 'error',
    'no-unused-expressions': ['error', {
      allowShortCircuit: true,
      allowTernary: true
    }],
    'no-useless-call': 'error',
    'no-useless-concat': 'error',
    'no-useless-return': 'error',
    'no-proto': 'error',
    'no-return-assign': 'error',
    'no-void': 'error',
    'no-with': 'error',
    'prefer-promise-reject-errors': 'error',
    'radix': 'error',
    'wrap-iife': 'error',
    'yoda': ['error', 'never'],
    // Strict Mode
    'strict': 'error',
    // Variables
    'no-label-var': 'error',
    'no-shadow-restricted-names': 'error',
    'no-undef-init': 'error',
    'no-use-before-define': ['error', 'nofunc'],
    // Node.js and CommonJS
    'handle-callback-err': 'error',
    'no-mixed-operators': 'error',
    'no-path-concat': 'error',
    // Stylistic Issues
    // ECMAScript 6
    // other
    'indent': ['error', 2, {
      SwitchCase: 1
    }],
    'linebreak-style': ['error', 'unix'],
    'quotes': ['error', 'single'],
    'semi': ['error', 'always'],
    'brace-style': ['error', '1tbs', {
      allowSingleLine: true
    }],
    'no-multiple-empty-lines': 'error',
    'one-var': ['error', {
      uninitialized: 'always',
      initialized: 'never'
    }],
    'operator-linebreak': ['error', 'before'],
    'key-spacing': ['error', {
      beforeColon: false,
      afterColon: true
    }],
    'space-unary-ops': ['error', {
      words: false,
      nonwords: false
    }],
    'comma-spacing': ['error', {
      before: false,
      after: true
    }],
    'semi-spacing': ['error', {
      before: false,
      after: true
    }],
    'func-call-spacing': 'error',
    'space-before-function-paren': ['error', 'never'],
    'space-in-parens': ['error', 'never'],
    'comma-dangle': ['error', 'never'],
    'no-trailing-spaces': 'error',
    'comma-style': ['error', 'last'],
    'eol-last': 'error',
    'space-infix-ops': 'error',
    'keyword-spacing': ['error', {}],
    'space-before-blocks': ['error', 'always'],
    'no-extra-label': 'error',
    'array-bracket-spacing': ['error', 'never'],
    'computed-property-spacing': ['error', 'never'],
    'block-spacing': ['error', 'always'],
    'lines-around-comment': ['error', {
      beforeBlockComment: true
    }],
    'new-cap': 'error',
    'new-parens': 'error',
    'no-array-constructor': 'error',
    'no-nested-ternary': 'error',
    'no-new-object': 'error',
    'no-unneeded-ternary': 'error',
    'no-whitespace-before-property': 'error',
    'spaced-comment': ['error', 'always'],
    'template-tag-spacing': ['error', 'never'],
    'unicode-bom': ['error', 'never'],
    'arrow-spacing': 'error',
    'generator-star-spacing': ['error', 'after'],
    'no-confusing-arrow': ['error', {
      allowParens: true
    }],
    'no-duplicate-imports': 'error',
    'no-useless-computed-key': 'error',
    'no-useless-constructor': 'error',
    'no-useless-rename': 'error',
    'rest-spread-spacing': ['error', 'never'],
    'template-curly-spacing': ['error', 'never'],
    'yield-star-spacing': ['error', 'after']
  },
  env: {
    node: true,
    es6: true
  },
  parserOptions: {
    ecmaVersion: 6
  }
};
<|endoftext|>


<commit_before>//= require ./core

(function () {

	var __hasProp = {}.hasOwnProperty;

	Marbles.Utils = {
		// @function (obj, [other [, other...]])
		extend: function (obj) {
			var others = Array.prototype.slice.call(arguments, 1);
			for (var i = 0, _len = others.length; i < _len; i++) {
				var other = others[i];

				for (var key in other) {
					if (__hasProp.call(other, key)) {
						obj[key] = other[key];
					}
				}
			}

			return obj;
		},

		// @function (child, parent)
		// The prototype of child is made to inherit from parent.
		// Returns child.
		// A `__super__` property is added to child
		// for access to the parent prototype.
		inheritPrototype: function(child, parent) {
			Marbles.Utils.extend(child, parent);

			function ctor() {
				this.constructor = child;
			}
			ctor.prototype = parent.prototype;
			child.prototype = new ctor();
			child.__super__ = parent.prototype;
			return child;
		},

		// @function (proto)
		// returns constructor function
		createClass: function (proto) {
			var ctor,
					willInitialize = proto.willInitialize,
					didInitialize = proto.didInitialize;

			delete proto.willInitialize;
			delete proto.didInitialize;

			if (proto.hasOwnProperty('parentClass')) {
				var parent = proto.parentClass;
				delete proto.parentClass;

				ctor = function () {
					// Handle any initialization before
					// we call the parent constructor
					if (typeof willInitialize === 'function') {
						willInitialize.apply(this, arguments);
					}

					// Call the parent constructor
					ctor.__super__.constructor.apply(this, arguments);

					// Handle any initialization after
					// we call the parent constructor
					if (typeof didInitialize === 'function') {
						didInitialize.apply(this, arguments);
					}

					return this;
				};

				Marbles.Utils.inheritPrototype(ctor, parent);
			} else {
				ctor = function () {
					// Call initialization functions
					if (typeof willInitialize === 'function') {
						willInitialize.apply(this, arguments);
					}
					if (typeof didInitialize === 'function') {
						didInitialize.apply(this, arguments);
					}
					return this;
				};
			}

			// If a displayName property is given
			// move it to the constructor
			if (proto.hasOwnProperty('displayName')) {
				ctor.displayName = proto.displayName;
				delete proto.displayName;
			}

			// Grab any given mixins from proto
			var mixins = [];
			if (proto.hasOwnProperty('mixins')) {
				mixins = proto.mixins;
				delete proto.mixins;
			}

			// Add all remaining properties
			// on proto to the prototype
			for (var k in proto) {
				if (!proto.hasOwnProperty(k)) {
					continue;
				}
				ctor.prototype[k] = proto[k];
			}

			// Extend the prototype with any given mixins
			Marbles.Utils.extend.apply(null, [].concat([ctor.prototype]).concat(mixins));

			return ctor;
		}
	};

})();
<commit_msg>Allow overriding ctor return value in Utils.createClass via willInitialize hook<commit_after>//= require ./core

(function () {

	var __hasProp = {}.hasOwnProperty;

	Marbles.Utils = {
		// @function (obj, [other [, other...]])
		extend: function (obj) {
			var others = Array.prototype.slice.call(arguments, 1);
			for (var i = 0, _len = others.length; i < _len; i++) {
				var other = others[i];

				for (var key in other) {
					if (__hasProp.call(other, key)) {
						obj[key] = other[key];
					}
				}
			}

			return obj;
		},

		// @function (child, parent)
		// The prototype of child is made to inherit from parent.
		// Returns child.
		// A `__super__` property is added to child
		// for access to the parent prototype.
		inheritPrototype: function(child, parent) {
			Marbles.Utils.extend(child, parent);

			function ctor() {
				this.constructor = child;
			}
			ctor.prototype = parent.prototype;
			child.prototype = new ctor();
			child.__super__ = parent.prototype;
			return child;
		},

		// @function (proto)
		// returns constructor function
		createClass: function (proto) {
			var ctor,
					willInitialize = proto.willInitialize,
					didInitialize = proto.didInitialize;

			delete proto.willInitialize;
			delete proto.didInitialize;

			if (proto.hasOwnProperty('parentClass')) {
				var parent = proto.parentClass;
				delete proto.parentClass;

				ctor = function () {
					// Handle any initialization before
					// we call the parent constructor
					if (typeof willInitialize === 'function') {
						var res = willInitialize.apply(this, arguments);
						if (res) {
							return res;
						}
					}

					// Call the parent constructor
					ctor.__super__.constructor.apply(this, arguments);

					// Handle any initialization after
					// we call the parent constructor
					if (typeof didInitialize === 'function') {
						didInitialize.apply(this, arguments);
					}

					return this;
				};

				Marbles.Utils.inheritPrototype(ctor, parent);
			} else {
				ctor = function () {
					// Call initialization functions
					if (typeof willInitialize === 'function') {
						var res = willInitialize.apply(this, arguments);
						if (res && res.constructor === ctor) {
							return res;
						}
					}
					if (typeof didInitialize === 'function') {
						didInitialize.apply(this, arguments);
					}
					return this;
				};
			}

			// If a displayName property is given
			// move it to the constructor
			if (proto.hasOwnProperty('displayName')) {
				ctor.displayName = proto.displayName;
				delete proto.displayName;
			}

			// Grab any given mixins from proto
			var mixins = [];
			if (proto.hasOwnProperty('mixins')) {
				mixins = proto.mixins;
				delete proto.mixins;
			}

			// Add all remaining properties
			// on proto to the prototype
			for (var k in proto) {
				if (!proto.hasOwnProperty(k)) {
					continue;
				}
				ctor.prototype[k] = proto[k];
			}

			// Extend the prototype with any given mixins
			Marbles.Utils.extend.apply(null, [].concat([ctor.prototype]).concat(mixins));

			return ctor;
		}
	};

})();
<|endoftext|>


<commit_before>let counter;

export function resetStep() {
  counter = 1;
}

export function step(expectedValue, description, assert) {
  assert.equal(counter, expectedValue, `Step ${expectedValue}: description`);
  counter++;
}
<commit_msg>Actually assert the description in assert-step<commit_after>let counter;

export function resetStep() {
  counter = 1;
}

export function step(expectedValue, description, assert) {
  assert.equal(counter, expectedValue, `Step ${expectedValue}: ${description}`);
  counter++;
}
<|endoftext|>


<commit_before>const expect = require('chai').expect;
const std = require('../../src/index');

describe('變形工時', () => {
  describe('雙週', () => {
    it('雙週班表工時' +
       '[10, 10, 10, 10, 0, 0, 0], ' +
       '[10, 10, 10, 10, 0, 0, 0]，' +
       '為合法雙週變形工時，無加班費（勞基法 30 條）', () => {
      let result = std.transformedWorkshift([
        [10, 10, 10, 10, 0, 0, 0],
        [10, 10, 10, 10, 0, 0, 0]
      ], std.DUAL_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ])
      expect(result.reference[0].id).eq('LSA-30');
    });

    it('雙週班表工時' +
       '[10, 10, 10, 10, 4, 0, 0], ' +
       '[10, 10, 10, 10, 4, 0, 0]，' +
       '為合法雙週變形工時，無加班費（勞基法 30 條）', () => {
      let result = std.transformedWorkshift([
        [10, 10, 10, 10, 4, 0, 0],
        [10, 10, 10, 10, 4, 0, 0]
      ], std.DUAL_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 4, 0, 0],
        [0, 0, 0, 0, 4, 0, 0]
      ])
      expect(result.reference[0].id).eq('LSA-30');
    });
  });

  describe('四週', () => {
    it('四週班表工時' +
       '[0, 0, 0, 9, 9, 9, 9], ' +
       '[9, 9, 9, 9, 0, 0, 0], ' +
       '[0, 0, 9, 9, 9, 9, 9], ' +
       '[9, 9, 9, 8, 8, 0, 0]，' +
       '為合法四週變形工時，無加班費（勞基法 30-1條）', () => {
      let result = std.transformedWorkshift([
        [0, 0, 0, 9, 9, 9, 9],
        [9, 9, 9, 9, 0, 0, 0],
        [0, 0, 9, 9, 9, 9, 9],
        [9, 9, 9, 8, 8, 0, 0]
      ], std.QUAD_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ])
      expect(result.reference[0].id).eq('LSA-30-1');
    });

    it('四週班表工時' +
       '[0, 0, 0, 10, 10, 10, 10], ' +
       '[10, 10, 10, 10, 10, 0, 0], ' +
       '[0, 0, 0, 0, 0, 10, 10], ' +
       '[10, 10, 10, 10, 10, 0, 0]，' +
       '為合法四週變形工時，無加班費（勞基法 30-1條）', () => {
      let result = std.transformedWorkshift([
        [0, 0, 0, 10, 10, 10, 10],
        [10, 10, 10, 10, 10, 0, 0],
        [0, 0, 0, 0, 0, 10, 10],
        [10, 10, 10, 10, 10, 0, 0]
      ], std.QUAD_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30-1');
    });
  });

  describe('八週', () => {
    it('八週班表工時' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 0, 0], ' +
       '[0, 0, 0, 0, 0, 0, 0], ' +
       '為合法八週變形工時，無加班費（勞基法 30條）', () => {
      let result = std.transformedWorkshift([
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ], std.OCTA_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30-1');
    });
  });
});
<commit_msg>Fix missing semicolon<commit_after>const expect = require('chai').expect;
const std = require('../../src/index');

describe('變形工時', () => {
  describe('雙週', () => {
    it('雙週班表工時' +
       '[10, 10, 10, 10, 0, 0, 0], ' +
       '[10, 10, 10, 10, 0, 0, 0]，' +
       '為合法雙週變形工時，無加班費（勞基法 30 條）', () => {
      let result = std.transformedWorkshift([
        [10, 10, 10, 10, 0, 0, 0],
        [10, 10, 10, 10, 0, 0, 0]
      ], std.DUAL_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30');
    });

    it('雙週班表工時' +
       '[10, 10, 10, 10, 4, 0, 0], ' +
       '[10, 10, 10, 10, 4, 0, 0]，' +
       '為合法雙週變形工時，無加班費（勞基法 30 條）', () => {
      let result = std.transformedWorkshift([
        [10, 10, 10, 10, 4, 0, 0],
        [10, 10, 10, 10, 4, 0, 0]
      ], std.DUAL_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 4, 0, 0],
        [0, 0, 0, 0, 4, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30');
    });
  });

  describe('四週', () => {
    it('四週班表工時' +
       '[0, 0, 0, 9, 9, 9, 9], ' +
       '[9, 9, 9, 9, 0, 0, 0], ' +
       '[0, 0, 9, 9, 9, 9, 9], ' +
       '[9, 9, 9, 8, 8, 0, 0]，' +
       '為合法四週變形工時，無加班費（勞基法 30-1條）', () => {
      let result = std.transformedWorkshift([
        [0, 0, 0, 9, 9, 9, 9],
        [9, 9, 9, 9, 0, 0, 0],
        [0, 0, 9, 9, 9, 9, 9],
        [9, 9, 9, 8, 8, 0, 0]
      ], std.QUAD_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30-1');
    });

    it('四週班表工時' +
       '[0, 0, 0, 10, 10, 10, 10], ' +
       '[10, 10, 10, 10, 10, 0, 0], ' +
       '[0, 0, 0, 0, 0, 10, 10], ' +
       '[10, 10, 10, 10, 10, 0, 0]，' +
       '為合法四週變形工時，無加班費（勞基法 30-1條）', () => {
      let result = std.transformedWorkshift([
        [0, 0, 0, 10, 10, 10, 10],
        [10, 10, 10, 10, 10, 0, 0],
        [0, 0, 0, 0, 0, 10, 10],
        [10, 10, 10, 10, 10, 0, 0]
      ], std.QUAD_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30-1');
    });
  });

  describe('八週', () => {
    it('八週班表工時' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 8, 8], ' +
       '[0, 8, 8, 8, 8, 0, 0], ' +
       '[0, 0, 0, 0, 0, 0, 0], ' +
       '為合法八週變形工時，無加班費（勞基法 30條）', () => {
      let result = std.transformedWorkshift([
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 8, 8],
        [0, 8, 8, 8, 8, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ], std.OCTA_WEEK_WORKSHIFT);
      expect(result.overtime).eq([
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]
      ]);
      expect(result.reference[0].id).eq('LSA-30-1');
    });
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Paper, Typography, TextField, Select, Input, Radio, Switch } from 'material-ui';
import { InputLabel } from 'material-ui/Input';
import { FormControl, FormGroup, FormControlLabel, FormLabel } from 'material-ui/Form';
import { MenuItem } from 'material-ui/Menu';
import { RadioGroup } from 'material-ui/Radio';

import { INITIAL } from '../constants';

/**
 * @name render - rendering component
 */

const renderInput = ({ input, meta: { touched, error }, ...other }) => (

      <TextField {...input} {...other} />

    {touched && error &&
{error}
}

);

const renderSelect = ({ input, label, meta: { touched, error }, list, ...other }) => (

      <InputLabel htmlFor={label}>{label}</InputLabel>
      <Select
        {...input}
        {...other}
        input={<Input id={label} style={{ width: '7rem' }} />}
      >
        <MenuItem value="">None</MenuItem>
        {
          list.map(({ value, label }, index) => (
            <MenuItem key={index} value={value}>{label}</MenuItem>
          ))
        }
      </Select>

    {touched && error &&
{error}
}

);

const renderMultipleSelect = ({ input, label, meta: { touched, error }, list, ...other }) => (

      <InputLabel htmlFor={label}>{label}</InputLabel>
      <Select
        {...input}
        {...other}
        multiple
        value={input.value || []}
        input={<Input id={label} style={{ width: '15rem' }} />}
      >
        {
          list.map((item, index) => (
            <MenuItem key={index} value={item}>{item}</MenuItem>
          ))
        }
      </Select>

    {touched && error &&
{error}
}

);

const renderRadioButtons = ({ input, meta: { touched, error }, ...other }) => (

      <FormLabel component="legend">Gender</FormLabel>
      <RadioGroup
        {...input}
        {...other}
        style={{ display: 'flex', flexDirection: 'row' }}
        aria-label="gender"
        name="gender"
      >
        <FormControlLabel value="male" control={<Radio />} label="Male" />
        <FormControlLabel value="female" control={<Radio />} label="Female" />
        <FormControlLabel value="other" control={<Radio />} label="Other" />
      </RadioGroup>

    {touched && error &&
{error}
}

);

const renderSwitch = ({ input, label, meta: { touched, error }, ...other }) => (

      <FormGroup row>
        <FormLabel component="legend" style={{ alignSelf: 'center' }}>{label}</FormLabel>
        <Switch
          {...input}
          {...other}
        />
      </FormGroup>

    {touched && error &&
{error}
}

);

/**
 * @name Component
 */

let WithReduxForm = ({ selector }) => {

  return (

      <Paper>
        <Typography type="title" gutterBottom style={{ padding: '1rem 1rem 0' }}>
          With Redux Form
        </Typography>

        <form className="form">

            {/* input */}
            <FormControl>
              <Field name="name" component={renderInput} label="Name" />
            </FormControl>

{selector('name')}

            {/* select */}
            <FormControl>
              <Field name="age" component={renderSelect} label="Age" list={INITIAL['listOfage']} />
            </FormControl>

{selector('age')}

            {/* multiple select */}
            <FormControl>
              <Field name="countries" component={renderMultipleSelect} label="Countries" list={INITIAL['listOfCountries']} />
            </FormControl>

{selector('countries') ? selector('countries').join(', ') : selector('countries')}

            {/* radio buttons */}
            <FormControl component="fieldset">
              <Field name="gender" component={renderRadioButtons} />
            </FormControl>

              {selector('gender') ? `${selector('gender')}`.charAt(0).toUpperCase() + `${selector('gender')}`.slice(1) : ''}

            {/* switch */}
            <FormControl component="fieldset">
              <Field name="autoplay" component={renderSwitch} label="Autoplay" />
            </FormControl>

              {selector('autoplay') ? `${selector('autoplay')}`.charAt(0).toUpperCase() + `${selector('autoplay')}`.slice(1) : ''}

            ...

        </form>
      </Paper>

  );
};

WithReduxForm = reduxForm({ form: 'example' })(WithReduxForm);

export default connect(
  state => ({ selector: field => formValueSelector('example')(state, field) })
)(WithReduxForm);
<commit_msg>205th Commit<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Paper, Typography, TextField, Select, Input, Radio, Switch } from 'material-ui';
import { InputLabel } from 'material-ui/Input';
import { FormControl, FormGroup, FormControlLabel, FormLabel, FormHelperText } from 'material-ui/Form';
import { MenuItem } from 'material-ui/Menu';
import { RadioGroup } from 'material-ui/Radio';

import { INITIAL } from '../constants';

/**
 * @name render - rendering component
 */

const renderInput = ({ input, meta: { touched, error }, ...other }) => (

    <TextField
      required
      error={!!(touched && error)}
      {...input}
      {...other}
    />
    {
      touched && error &&
      <FormHelperText error={!!(touched && error)}>{error}</FormHelperText>
    }

);

const renderSelect = ({ input, label, meta: { touched, error }, list, ...other }) => (

    <InputLabel htmlFor={label}>{label}</InputLabel>
    <Select
      {...input}
      {...other}
      input={<Input id={label} style={{ width: '7rem' }} />}
    >
      <MenuItem value="">None</MenuItem>
      {
        list.map(({ value, label }, index) => (
          <MenuItem key={index} value={value}>{label}</MenuItem>
        ))
      }
    </Select>
    {
      touched && error &&
      <FormHelperText error={!!(touched && error)}>{error}</FormHelperText>
    }

);

const renderMultipleSelect = ({ input, label, meta: { touched, error }, list, ...other }) => (

    <InputLabel htmlFor={label}>{label}</InputLabel>
    <Select
      {...input}
      {...other}
      multiple
      value={input.value || []}
      input={<Input id={label} style={{ width: '15rem' }} />}
    >
      {
        list.map((item, index) => (
          <MenuItem key={index} value={item}>{item}</MenuItem>
        ))
      }
    </Select>
    {
      touched && error &&
      <FormHelperText error={!!(touched && error)}>{error}</FormHelperText>
    }

);

const renderRadioButtons = ({ input, meta: { touched, error }, ...other }) => (

    <FormLabel component="legend">Gender</FormLabel>
    <RadioGroup
      {...input}
      {...other}
      style={{ display: 'flex', flexDirection: 'row' }}
      aria-label="gender"
      name="gender"
    >
      <FormControlLabel value="male" control={<Radio />} label="Male" />
      <FormControlLabel value="female" control={<Radio />} label="Female" />
      <FormControlLabel value="other" control={<Radio />} label="Other" />
    </RadioGroup>
    {
      touched && error &&
      <FormHelperText error={!!(touched && error)}>{error}</FormHelperText>
    }

);

const renderSwitch = ({ input, label, meta: { touched, error }, ...other }) => (

    <FormGroup row>
      <FormLabel component="legend" style={{ alignSelf: 'center' }}>{label}</FormLabel>
      <Switch
        value={String(input.value)}
        {...input}
        {...other}
      />
    </FormGroup>
    {
      touched && error &&
      <FormHelperText error={!!(touched && error)}>{error}</FormHelperText>
    }

);

/**
 * @name validate - form validation
 */

const validate = ({ name }) => {
  const errors = {};

  // input
  if (!name) {
    errors.name = 'Required';
  } else if (name.length > 15) {
    errors.name = 'Must be 20 characters or less';
  }

  return errors;
};

/**
 * @name Component
 */

let WithReduxForm = ({ selector }) => {

  return (

      <Paper>
        <Typography type="title" gutterBottom style={{ padding: '1rem 1rem 0' }}>
          With Redux Form
        </Typography>

        <form className="form">

            {/* input */}
            <FormControl>
              <Field name="name" component={renderInput} label="Name" />
            </FormControl>

{selector('name')}

            {/* select */}
            <FormControl>
              <Field name="age" component={renderSelect} label="Age" list={INITIAL['listOfage']} />
            </FormControl>

{selector('age')}

            {/* multiple select */}
            <FormControl>
              <Field name="countries" component={renderMultipleSelect} label="Countries" list={INITIAL['listOfCountries']} />
            </FormControl>

{selector('countries') ? selector('countries').join(', ') : selector('countries')}

            {/* radio buttons */}
            <FormControl component="fieldset">
              <Field name="gender" component={renderRadioButtons} />
            </FormControl>

              {selector('gender') ? `${selector('gender')}`.charAt(0).toUpperCase() + `${selector('gender')}`.slice(1) : ''}

            {/* switch */}
            <FormControl component="fieldset">
              <Field name="autoplay" component={renderSwitch} label="Autoplay" />
            </FormControl>

              {selector('autoplay') ? `${selector('autoplay')}`.charAt(0).toUpperCase() + `${selector('autoplay')}`.slice(1) : ''}

            ...

        </form>
      </Paper>

  );
};

WithReduxForm = reduxForm({ form: 'example', validate })(WithReduxForm);

export default connect(
  state => ({ selector: field => formValueSelector('example')(state, field) })
)(WithReduxForm);
<|endoftext|>


<commit_before>// Global vars
var pymChild = null;
var isMobile = false;

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function () {
    if (Modernizr.svg) {
        formatData();

        pymChild = new pym.Child({
            renderCallback: render,
        });
    } else {
        pymChild = new pym.Child({});
    }
};

/*
 * Format graphic data for processing by D3.
 */
var formatData = function () {
    DATA.forEach(function (d) {
        d.amt = +d.amt;
    });
};

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function (containerWidth) {
    containerWidth = containerWidth || DEFAULT_WIDTH;
    isMobile = (containerWidth <= MOBILE_THRESHOLD);

    // Render the chart
    renderWaffleChart();

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
};

/*
 * Render a waffle chart.
 */
var renderWaffleChart = function () {
    /*
     * Setup
     */
    var labelWidth = parseInt(LABELS.labelWidth || 85);
    var labelMargin = parseInt(LABELS.labelMargin || 6);

    var margins = {
        top: parseInt(LABELS.marginTop || 0),
        right: parseInt(LABELS.marginRight || 0),
        bottom: parseInt(LABELS.marginBottom || 0),
        left: parseInt(LABELS.marginLeft || (labelWidth + labelMargin)),
    };

    // Clear existing graphic (for redraw)
    var containerElement = d3.select('#waffle-chart');
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphics-wrapper');

    // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = (innerWidth - margins.left - margins.right >= parseInt(LABELS.maxWidth))
        ? parseInt(LABELS.maxWidth) : innerWidth - margins.left - margins.right;
    var chartHeight = chartWidth;

    var chartElement = chartWrapper.append('svg')
        .attr({
            width: chartWidth + margins.left + margins.right,
            height: chartHeight + margins.top + margins.bottom,
        })
        .append('g')
            .attr('transform', makeTranslate(margins.left, margins.top));

    var total = 0; // Init total number of squares

    var widthSquares = 10,
        heightSquares = 10,
        squareSize = chartHeight / heightSquares,
        squareValue = 0, // Set later
        gap = 1,
        squareData = []; // Data for individual squares

    var colorList = colorArray(LABELS, MULTICOLORS);
    var colorScale = d3.scale.ordinal()
        .range(colorList);

    // Total of all data
    total = d3.sum(DATA, function(d) { return d.amt; });

    // Value of a square
    squareValue = total / (heightSquares * widthSquares);

    // Remap data for individual squares
    DATA.forEach(function(d, i) {
        d.amt = +d.amt;

        d.units = Math.floor(d.amt/squareValue);
        squareData = squareData.concat(
            Array(d.units+1).join(1).split('').map(function()
            {
                return {
                    squareValue:squareValue,
                    units: d.units,
                    amt: d.amt,
                    groupIndex: i
                };
            })
        );
    });

    // Create a transparent SVG to use as a pattern
    var svg = d3.select("#waffle-chart").append("svg")
        .attr('width', 0)
        .attr('height', 0)
        .style('position', 'absolute') // so it doesn't affect flow
        .append('defs')
        .append('pattern')
            .attr('id', 'diagonalHatch')
            .attr('patternUnits', 'userSpaceOnUse')
            .attr('width', 4)
            .attr('height', 4)
        .append('path')
            .attr('d', 'M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2')
            .attr('stroke', 'white')
            .attr('stroke-width', 1);

    /*
     * Render the squares
     */
    var squares = chartElement.selectAll('rect')
        .data(squareData)
        .enter()
        .call( function(selection) {
            // Render bottom layer of squares
            selection.append('rect')
                .attr("width", squareSize - gap)
                .attr("height", squareSize - gap)
                .attr("fill", function(d) {
                    return colorScale(d.groupIndex);
                })
                .attr("x", function(d, i)
                {
                    col = i%widthSquares;
                    var x = (col * (squareSize - gap)) + (col * gap);
                    return x;
                })
                .attr("y", function(d, i) {
                    //group n squares for column
                    row = Math.floor(i/widthSquares);
                    return (row * (squareSize - gap)) + (row*gap);
                })
                .append("title")
                    .text(function (d, i) {
                        return "Label: " + DATA[d.groupIndex].label + " | " +  d.amt + " , " + d.units + "%"
                    });
            // Render top layer of squares
            selection.append('rect')
                .attr("width", squareSize - gap)
                .attr("height", squareSize - gap)
                .attr("fill", function (d,i) {
                    console.log(d);
                    if (d.groupIndex % 2 !== 0) { // odds and even groups
                        return "url(#diagonalHatch)";
                    } else {
                        return "rgba(255,255,255,0.0)"; // fully transparent
                    }
                })
                .attr("x", function(d, i) {
                    col = i%widthSquares;
                    var x = (col * (squareSize - gap)) + (col * gap);
                    return x;
                })
                .attr("y", function(d, i) {
                    //group n squares for column
                    row = Math.floor(i/widthSquares);
                    return (row * (squareSize - gap)) + (row*gap);
                })
                .append("title")
                    .text(function (d, i) {
                        return "Label: " + DATA[d.groupIndex].label + " | " +  d.amt + " , " + d.units + "%"
                    });
        })

    /*
     * Output the legend
     */

    // Create a legend div wrapper
    var chartLegend = chartWrapper.append("ul")
        .attr('class', 'labels')
        .style({
            width: labelWidth + 'px',
            top: '0px',
            left: 0,
        });

    // Append a li per data
    chartLegend.selectAll("li")
    .data(DATA)
    .enter()
    .append("li")
        .style("color", function(d, i) {
            return colorScale(i);
        })
        .style({
            width: labelWidth + 'px',
            left: 0,
            position: "relative"
        })
        .append("span")
        .html(function(d, i) {
            console.log(d);
            return d.label + " " + "" + d.units + "%";
        });
};

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;<commit_msg>alternating diagonal hatching<commit_after>// Global vars
var pymChild = null;
var isMobile = false;

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function () {
    if (Modernizr.svg) {
        formatData();

        pymChild = new pym.Child({
            renderCallback: render,
        });
    } else {
        pymChild = new pym.Child({});
    }
};

/*
 * Format graphic data for processing by D3.
 */
var formatData = function () {
    DATA.forEach(function (d) {
        d.amt = +d.amt;
    });
};

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function (containerWidth) {
    containerWidth = containerWidth || DEFAULT_WIDTH;
    isMobile = (containerWidth <= MOBILE_THRESHOLD);

    // Render the chart
    renderWaffleChart();

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
};

/*
 * Render a waffle chart.
 */
var renderWaffleChart = function () {
    /*
     * Setup
     */
    var labelWidth = parseInt(LABELS.labelWidth || 85);
    var labelMargin = parseInt(LABELS.labelMargin || 6);

    var margins = {
        top: parseInt(LABELS.marginTop || 0),
        right: parseInt(LABELS.marginRight || 0),
        bottom: parseInt(LABELS.marginBottom || 0),
        left: parseInt(LABELS.marginLeft || (labelWidth + labelMargin)),
    };

    // Clear existing graphic (for redraw)
    var containerElement = d3.select('#waffle-chart');
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphics-wrapper');

    // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = (innerWidth - margins.left - margins.right >= parseInt(LABELS.maxWidth))
        ? parseInt(LABELS.maxWidth) : innerWidth - margins.left - margins.right;
    var chartHeight = chartWidth;

    var chartElement = chartWrapper.append('svg')
        .attr({
            width: chartWidth + margins.left + margins.right,
            height: chartHeight + margins.top + margins.bottom,
        })
        .append('g')
            .attr('transform', makeTranslate(margins.left, margins.top));

    var total = 0; // Init total number of squares

    var widthSquares = 10,
        heightSquares = 10,
        squareSize = chartHeight / heightSquares,
        squareValue = 0, // Set later
        gap = 1,
        squareData = []; // Data for individual squares

    var colorList = colorArray(LABELS, MULTICOLORS);
    var colorScale = d3.scale.ordinal()
        .range(colorList);

    // Total of all data
    total = d3.sum(DATA, function(d) { return d.amt; });

    // Value of a square
    squareValue = total / (heightSquares * widthSquares);

    // Remap data for individual squares
    DATA.forEach(function(d, i) {
        d.amt = +d.amt;

        d.units = Math.floor(d.amt/squareValue);
        squareData = squareData.concat(
            Array(d.units+1).join(1).split('').map(function()
            {
                return {
                    squareValue:squareValue,
                    units: d.units,
                    amt: d.amt,
                    groupIndex: i
                };
            })
        );
    });

    // Create a transparent SVG to use as a pattern
    var svg = d3.select("#waffle-chart").append("svg")
        .attr('width', 0)
        .attr('height', 0)
        .style('position', 'absolute'); // so it doesn't affect flow

        svgDefs = svg.append('defs');

        svgDefs.append('pattern')
            .attr('id', 'diagonalHatchRight') // ID of pattern
            .attr('patternUnits', 'userSpaceOnUse')
            .attr('width', 4)
            .attr('height', 4)
        .append('path')
            .attr('d', 'M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2')
            //.attr('d', 'M5,1 l-2,-2 M4,4 l-4,-4 M1,5 l-2,-2')
            .attr('stroke', 'white')
            .attr('stroke-width', 1)

        svgDefs.append('pattern')
            .attr('id', 'diagonalHatchLeft') // ID of pattern
            .attr('patternUnits', 'userSpaceOnUse')
            .attr('width', 4)
            .attr('height', 4)
        .append('path')
            .attr('d', 'M5,1 l-2,-2 M4,4 l-4,-4 M1,5 l-2,-2')
            .attr('stroke', 'white')
            .attr('stroke-width', 1);

    /*
     * Render the squares
     */
    var squares = chartElement.selectAll('rect')
        .data(squareData)
        .enter()
        .call( function(selection) {
            // Render bottom layer of squares
            selection.append('rect')
                .attr("width", squareSize - gap)
                .attr("height", squareSize - gap)
                .attr("fill", function(d) {
                    return colorScale(d.groupIndex);
                })
                .attr("x", function(d, i)
                {
                    col = i%widthSquares;
                    var x = (col * (squareSize - gap)) + (col * gap);
                    return x;
                })
                .attr("y", function(d, i) {
                    //group n squares for column
                    row = Math.floor(i/widthSquares);
                    return (row * (squareSize - gap)) + (row*gap);
                })
                .append("title")
                    .text(function (d, i) {
                        return "Label: " + DATA[d.groupIndex].label + " | " +  d.amt + " , " + d.units + "%"
                    });
            // Render top layer of squares
            selection.append('rect')
                .attr("width", squareSize - gap)
                .attr("height", squareSize - gap)
                .attr("fill", function (d,i) {
                     switch (d.groupIndex % 4) {
                        case 0: return "rgba(255,255,255,0.0)"; // fully transparent
                        case 1: return "url(#diagonalHatchLeft)";
                        case 2: return "rgba(255,255,255,0.0)"; // fully transparent
                        case 3: return "url(#diagonalHatchRight)";
                    }
                })
                .attr("x", function(d, i) {
                    col = i%widthSquares;
                    var x = (col * (squareSize - gap)) + (col * gap);
                    return x;
                })
                .attr("y", function(d, i) {
                    //group n squares for column
                    row = Math.floor(i/widthSquares);
                    return (row * (squareSize - gap)) + (row*gap);
                })
                .append("title")
                    .text(function (d, i) {
                        return "Label: " + DATA[d.groupIndex].label + " | " +  d.amt + " , " + d.units + "%"
                    });
        });

    /*
     * Output the legend
     */

    // Create a legend div wrapper
    var chartLegend = chartWrapper.append("ul")
        .attr('class', 'labels')
        .style({
            width: labelWidth + 'px',
            top: '0px',
            left: 0,
        });

    // Append a li per data
    chartLegend.selectAll("li")
    .data(DATA)
    .enter()
    .append("li")
        .style("color", function(d, i) {
            return colorScale(i);
        })
        .style({
            width: labelWidth + 'px',
            left: 0,
            position: "relative"
        })
        .append("span")
        .html(function(d, i) {
            console.log(d);
            return d.label + " " + "" + d.units + "%";
        });
};

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;<|endoftext|>