Source: css/sprite.js

/**
 * @name prite<lib/css>
 * @author Yuhei Aihara <aihara_yuhei@cyberagent.co.jp>
 * copyright (c) Cyberagent Inc.
 */
var path = require('path');
var _ = require('lodash');

var fs = require('fs');
var fsys = require('../fsys');
var logger = require('../logger');
var constant = require('../constant');

var DEFAULT_OPTIONS = constant.CSS_SPRITE_DEFAULT_OPTIONS;

module.exports = {

    /**
     * build
     * @param {String} dstPath
     * @param {String} group
     * @param {Array} images
     * @param {Object} options
     * @param {Function} callback
     * @example
     * var dstPath = './test/image/sprite';
     * var group = 'logo';
     * var images = [ './test/image/sprite/sprite-logo-hoge.png', './test/image/sprite/sprite-logo-fuga.png',,,etc ];
     * var options = {
     *     ratios: [1, 1.3, 1.5, 2]
     *     separator: '-'
     * };
     * build(dstPath, group, images, options);
     * > make file 'sprite-logo.css', 'sprite-logo@10.png', 'sprite-logo@13.png', 'sprite-logo@15x.png' and 'sprite-logo@20x.png'
     */
    build: function (dstPath, group, images, options, callback) {
        options = _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var ratios = options.ratios;
        var separator = options.separator;
        var head = this.getHead(images[0], options);
        var name = constant.CSS_SPRITE_PREFIX + separator + group;
        var output = dstPath + path.sep + name;
        var checker = new fsys.Checker(images, options.logpath, options.logname);
        var isExist = true;
        var outputImage, builder;

        // node-spritesheet will be removed in a future version
        var Builder;
        try {
            Builder = options.sprite2 ? require('node-sprite').Builder : require('node-spritesheet').Builder;
        } catch (e) {
            logger.error(e);
            throw e;
        }

        // Make csssprite builder
        builder = new Builder(_.extend(options, {
            outputDirectory: dstPath,
            outputCss: head + separator + group + constant.EXTENSION_STYL,
            selector: constant.CSS_SPRITE_SELECTOR_PREFIX + group,
            images: images
        }));

        // Add builder options
        _.each(ratios, function (ratio) {
            outputImage = name + constant.CSS_SPRITE_RATIO_SEPARATOR + (ratio * 10) + constant.CSS_SPRITE_RATIO_SUFFIX + constant.EXTENSION_PNG;

            if (!fs.existsSync(dstPath + path.sep + outputImage)) {
                isExist = false;
            }

            builder.addConfiguration(ratio + constant.CSS_SPRITE_RATIO_SUFFIX, {
                pixelRatio: ratio,
                outputImage: outputImage
            });
        });

        // Check modified
        if (options.overwrite === false && isExist) {

            checker.read();
            if (!checker.isModified(output)) {
                logger.info('Skip build sprite. id: ', output);

                callback && callback();
                return;
            }
        }

        // Build !!
        builder.build(function (err, ret) {

            if (err) {
                logger.error('Fail to build spritesheet:', err);
                return callback(err);
            }

            // save log
            if (options.overwrite === false) {
                logger.trace('Save log id: ', output);

                checker.save(output);
                checker.write();
            }

            callback && callback(err, ret);
        });
    },

    /**
     * getImages
     * @param {String} dstPath
     * @param {String} group
     * @param {Object} options
     * @returns {Array} images is files path
     * @example
     * var dstPath = './test/image/sprite';
     * var group = 'logo';
     * var options = {
     *     separator: '-',
     * };
     * var out = getImages(dstPath, group, options);
     * console.log(out);
     * > [ './test/image/sprite/sprite-logo-hoge.png', './test/image/sprite/sprite-logo-fuga.png',,,etc ]
     */
    getImages: function (dstPath, group, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var separator = options.separator;
        var heads = options.heads;
        var extnames = options.extnames;
        var images = [];
        fsys.walk(dstPath, function(prefix, dir, file, stats) {
            for (var i = 0; i < heads.length; i++) {
                for (var j = 0; j < extnames.length; j++) {
                    var reg = new RegExp('^' + heads[i] + separator + group + separator +'.+\\' + extnames[j] + '$');
                    if (file.match(reg)) {
                        images.push(path.join(dir, file));
                    }
                }
            }
        });

        return images;
    },

    /**
     * getGroup
     * @param {String} srcPath
     * @param {Object} options
     * @returns {String}
     * @example
     * var srcPath = './test/image/sprite/sprite-logo@10x.png';
     * var out = getGroup(srcPath);
     * console.log(out);
     * > 'logo'
     */
    getGroup: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var separator = options.separator;
        var extname = path.extname(srcPath);
        var reg = new RegExp('^.+' + separator + '(.+)((' + separator + '.+)|(@\\d+x))\\' + extname + '$');
        if (extname === '.styl' || extname === '.css') {
            reg = new RegExp('^.+' + separator + '(.+)\\' + extname + '$');
        }
        var result = path.basename(srcPath).match(reg);
        return result && result[1];
    },

    /**
     * getName
     * @param {String} srcPath
     * @param {Object} options
     * @returns {String}
     * @example
     * var srcPath = './test/image/sprite/sprite-logo@10x.png';
     * var out = getHead(srcPath);
     * console.log(out);
     * > 'sprite'
     */
    getHead: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var separator = options.separator;
        var extname = path.extname(srcPath);
        var reg = new RegExp('^(.+)' + separator + '.+((' + separator + '.+)|(@\\d+x))\\' + extname + '$');
        if (extname === '.styl' || extname === '.css') {
            reg = new RegExp('^(.+)' + separator + '.+\\' + extname + '$');
        }
        var result = path.basename(srcPath).match(reg);
        return result && result[1];
    },

    /**
     * After building, then to get the files that are created
     * @param {String} srcPath
     * @param {Object} options
     * @returns {Array}
     * var srcPath = 'test/image/sprite/sprite-logo-hoge.png'
     * var options = {
     *     separator: '-',
     *     ratios: [ 1, 2 ]
     * };
     * var out = getCreateFile(srcPath, options):
     * console.log(out);
     * > [ 'sprite-logo.styl', 'sprite-logo@10x.png', 'sprite-logo@20x.png' ]
     */
    getCreateFile: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        if(!this.isSpriteImage(srcPath, options)) {
            logger.error('is not SpriteImage. srcPath:' + srcPath);
            return [];
        }

        var separator = options.separator;
        var ratios = options.ratios;
        var group = this.getGroup(srcPath, options);
        var head = this.getHead(srcPath, options);
        var files = [];
        files.push(head + separator + group + '.styl');
        for (var i = 0; i < ratios.length; i++) {
            files.push('sprite' + separator + group + '@' + (ratios[i] * 10) + 'x.png');
        }

        return files;
    },

    /**
     * isSpriteImage
     * @param {String} srcPath
     * @param {Object} options
     * @returns {Boolean}
     * @example
     * var srcPath = 'test/image/sprite/sprite-logo-hoge.png';
     * var options = {
     *     extnames: [ '.png' ],
     *     separator: '-',
     *     heads: ['sprite']
     * };
     * var out = isSpriteImage(srcPath, options);
     * console.log(out);
     * > true
     */
    isSpriteImage: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var heads = options.heads;
        var separator = options.separator;
        var extnames = options.extnames;

        return _.some(_.map(extnames, function (extname) {
            return _.some(_.map(heads, function (head) {
                var reg = new RegExp('^' + head + separator + '.+' + separator + '.+\\' + extname + '$');
                return reg.test(path.basename(srcPath));
            }));
        }));
    },

    /**
     * isSpriteSheet
     * @param {String} srcPath
     * @param {Object} options
     * @returns {Boolean}
     * @example
     * var srcPath = 'test/image/sprite/sprite-logo@10x.png';
     * var options = {};
     * var out = isSpriteSheet(srcPath, options);
     * console.log(out);
     * > true
     */
    isSpriteSheet: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var separator = options.separator;

        var reg = new RegExp('^sprite' + separator + '.+@\\d+x\\.png$');
        return reg.test(path.basename(srcPath));
    },

    /**
     * isSpriteStylus
     * @param {String} srcPath
     * @param {Object} options
     * @returns {Boolean}
     * @example
     * var srcPath = 'test/image/sprite/sprite-logo.styl';
     * var options = {};
     * var out = isSpriteStylus(srcPath, options);
     * console.log(out);
     * > true
     */
    isSpriteStylus: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var separator = options.separator;
        var heads = options.heads;

        return _.some(_.map(heads, function (head) {
            var reg = new RegExp('^' + head + separator + '.+\\.styl$');
            return reg.test(path.basename(srcPath));
        }));
    },

    /**
     * isSpriteCss
     * @param {String} srcPath
     * @param {Object} options
     * @returns {Boolean}
     * @example
     * var srcPath = 'test/image/sprite/sprite-logo.css';
     * var options = {};
     * var out = isSpriteStylus(srcPath, options);
     * console.log(out);
     * > true
     */
    isSpriteCss: function (srcPath, options) {
        _.defaults(options || (options = {}), DEFAULT_OPTIONS);
        var separator = options.separator;
        var heads = options.heads;

        return _.some(_.map(heads, function (head) {
            var reg = new RegExp('^' + head + separator + '.+\\.css$');
            return reg.test(path.basename(srcPath));
        }));
    }
};