<commit_before>/*
 * fis
 * http://fis.baidu.com/
 */

'use strict';

var CACHE_DIR;

var exports = module.exports = function(file){
    if(!CACHE_DIR){
        fis.log.error('uninitialized compile cache directory.');
    }
    file = fis.file.wrap(file);
    if(file.isText()){
        fis.log.debug('compile [' + file.realpath + '] start');
        if(file.isFile()){
            var cache = file.cache = fis.cache(file.realpath, CACHE_DIR),
                revertObj = {};
            if(cache.revert(revertObj)){
                exports.settings.beforeCacheRevert(file);
                file.requires = revertObj.info.requires;
                file.extras = revertObj.info.extras;
                file.setContent(revertObj.content);
                exports.settings.afterCacheRevert(file);
            } else {
                exports.settings.beforeCompile(file);
                file.setContent(fis.util.read(file.realpath));
                process(file);
                exports.settings.afterCompile(file);
                revertObj = {
                    requires : file.requires,
                    extras : file.extras
                };
                cache.save(file.getContent(), revertObj);
            }
        } else if(!file.realpath){
            process(file);
        } else {
            error('unable to compile [' + file.realpath + ']: Invalid file path.');
        }
        fis.log.debug('compile [' + file.realpath + '] end');
    } else if(file.realpath) {
        file.setContent(fis.util.read(file.realpath));
    }
    file.compiled = true;
    embeddedUnlock(file);
    return file;
};

exports.settings = {
    unique   : false,
    debug    : false,
    optimize : false,
    lint     : false,
    hash     : false,
    domain   : false,
    beforeCacheRevert : function(){},
    afterCacheRevert : function(){},
    beforeCompile : function(){},
    afterCompile : function(){}
};

exports.setup = function(opt){
    var settings = exports.settings;
    if(opt){
        fis.util.map(settings, function(key){
            if(typeof opt[key] !== 'undefined'){
                settings[key] = opt[key];
            }
        });
    }
    CACHE_DIR = 'compile/';
    if(settings.unique){
        CACHE_DIR += Date.now() + Math.random();
    } else {
        CACHE_DIR += ''
            + (settings.debug    ? 'debug'     : 'release')
            + (settings.optimize ? '-optimize' : '')
            + (settings.hash     ? '-hash'     : '')
            + (settings.domain   ? '-domain'   : '');
    }
    return CACHE_DIR;
};

exports.clean = function(name){
    if(name){
        fis.cache.clean('compile/' + name);
    } else if(CACHE_DIR) {
        fis.cache.clean(CACHE_DIR);
    } else {
        fis.cache.clean('compile');
    }
};

var map = exports.lang = (function(){
    var keywords = ['require', 'embed', 'uri', 'dep'],
        LD = '<[{', RD = '}]>',
        qLd = fis.util.escapeReg(LD),
        qRd = fis.util.escapeReg(RD),
        map = {
            reg : new RegExp(
                qLd + '(' + keywords.join('|') + ')\\(([\\s\\S]+?)\\)' + qRd,
                'g'
            )
        };
    keywords.forEach(function(key){
        map[key] = {};
        map[key]['ld'] = LD + key + '(';
        map[key]['rd'] = ')' + RD;
    });
    return map;
})();

//"abc?__inline" return true
//"abc?__inlinee" return false
//"abc?a=1&__inline"" return true
function isInline(info){
    return /[?&]__inline(?:[=&'"]|$)/.test(info.query);
}

//analyse [@require id] syntax in comment
function analyseComment(comment, callback){
    var reg = /(@require\s+)('[^']+'|"[^"]+"|[^\s;!@#%^&*()]+)/g;
    callback = callback || function(m, prefix, value){
        return prefix + map.require.ld + value + map.require.rd;
    };
    return comment.replace(reg, callback);
}

//expand javascript
//[@require id] in comment to require resource
//__inline(path) to embedd resource content or base64 encodings
//__uri(path) to locate resource
//require(path) to require resource
function extJs(content, callback){
    var reg = /"(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|(\/\/[^\r\n\f]+|\/\*[\s\S]*?(?:\*\/|$))|\b(__inline|__uri|require)\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*')\s*\)/g;
    callback = callback || function(m, comment, type, value){
        if(type){
            switch (type){
                case '__inline':
                    m = map.embed.ld + value + map.embed.rd;
                    break;
                case '__uri':
                    m = map.uri.ld + value + map.uri.rd;
                    break;
                case 'require':
                    m = 'require(' + map.require.ld + value + map.require.rd + ')';
                    break;
            }
        } else if(comment){
            m = analyseComment(comment);
        }
        return m;
    };
    return content.replace(reg, callback);
}

//expand css
//[@require id] in comment to require resource
//[@import url(path?__inline)] to embed resource content
//url(path) to locate resource
//url(path?__inline) to embed resource content or base64 encodings
//src=path to locate resource
function extCss(content, callback){
    var reg = /(\/\*[\s\S]*?(?:\*\/|$))|(?:@import\s+)?\burl\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^)}]+)\s*\)(\s*;?)|\bsrc\s*=\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^\s}]+)/g;
    callback = callback || function(m, comment, url, last, filter){
        if(url){
            var key = isInline(fis.util.query(url)) ? 'embed' : 'uri';
            if(m.indexOf('@') === 0){
                if(key === 'embed'){
                    m = map.embed.ld + url + map.embed.rd + last;
                } else {
                    m = '@import url(' + map.uri.ld + url + map.uri.rd + ')' + last;
                }
            } else {
                m = 'url(' + map[key].ld + url + map[key].rd + ')' + last;
            }
        } else if(filter) {
            m = 'src=' + map.uri.ld + filter + map.uri.rd;
        } else if(comment) {
            m = analyseComment(comment);
        }
        return m;
    };
    return content.replace(reg, callback);
}

//expand html
//[@require id] in comment to require resource
//<!--inline[path]--> to embed resource content
//<img|embed|audio|video|link|object ... (data-)?src="path"/> to locate resource
//<img|embed|audio|video|link|object ... (data-)?src="path?__inline"/> to embed resource content
//<script|style ... src="path"></script|style> to locate js|css resource
//<script|style ... src="path?__inline"></script|style> to embed js|css resource
//<script|style ...>...</script|style> to analyse as js|css
function extHtml(content, callback){
    var reg = /(<script(?:(?=\s)[\s\S]*?["'\s\w\/\-]>|>))([\s\S]*?)(?=<\/script\s*>|$)|(<style(?:(?=\s)[\s\S]*?["'\s\w\/\-]>|>))([\s\S]*?)(?=<\/style\s*>|$)|<(img|embed|audio|video|link|object)\s+[\s\S]*?["'\s\w\/\-](?:>|$)|<!--inline\[([^\]]+)\]-->|<!--(?!\[)([\s\S]*?)(-->|$)/ig;
    callback = callback || function(m, $1, $2, $3, $4, $5, $6, $7, $8){
        if($1){//<script>
            var embed = '';
            $1 = $1.replace(/(\s(?:data-)?src\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                if(isInline(fis.util.query(value))){
                    embed += map.embed.ld + value + map.embed.rd;
                    return '';
                } else {
                    return prefix + map.uri.ld + value + map.uri.rd;
                }
            });
            m = $1 + (embed || extJs($2));
        } else if($3){//<style>
            m = $3 + extCss($4);
        } else if($5){//<img|embed|audio|video|link|object>
            var tag = $5.toLowerCase();
            if(tag === 'link'){
                var inline = '';
                m = m.replace(/(\s(?:data-)?href\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                    if(isInline(fis.util.query(value))){
                        inline += '<style>' + map.embed.ld + value + map.embed.rd + '</style>';
                        return '';
                    } else {
                        return prefix + map.uri.ld + value + map.uri.rd;
                    }
                });
                m = inline || m;
            } else if(tag === 'object'){
                m = m.replace(/(\sdata\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                    return prefix + map.uri.ld + value + map.uri.rd;
                });
            } else {
                m = m.replace(/(\s(?:data-)?src\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                    var key = isInline(fis.util.query(value)) ? 'embed' : 'uri';
                    return prefix + map[key]['ld'] + value + map[key]['rd'];
                });
            }
        } else if($6){
            m = map.embed.ld + $6 + map.embed.rd;
        } else if($7){
            m = '<!--' + analyseComment($7) + $8;
        }
        return m;
    };
    return content.replace(reg, callback);
}

function process(file){
    pipe(file, 'parser', file.ext);
    pipe(file, 'preprocessor', file.rExt);
    standard(file);
    pipe(file, 'postprocessor', file.rExt);
    if(exports.settings.lint){
        pipe(file, 'lint', file.rExt, true);
    }
    if(exports.settings.test){
        pipe(file, 'test', file.rExt, true);
    }
    if(exports.settings.optimize){
        pipe(file, 'optimizer', file.rExt);
    }
}

function pipe(file, type, ext, keep){
    var key = type + ext;
    fis.util.pipe(key, function(processor, settings, key){
        settings.filename = file.realpath;
        var content = file.getContent();
        try {
            fis.log.debug('pipe [' + key + '] start');
            var result = processor(content, file, settings);
            fis.log.debug('pipe [' + key + '] end');
            if(keep){
                file.setContent(content);
            } else if(typeof result === 'undefined'){
                fis.log.warning('invalid content return of pipe [' + key + ']');
            } else {
                file.setContent(result);
            }
        } catch(e) {
            //log error
            fis.log.debug('pipe [' + key + '] fail');
            var msg = key + ': ' + (e.message || e) + ' [' + file.subpath;
            if(e.hasOwnProperty('line')){
                msg += ':' + e.line;
                if(e.hasOwnProperty('col')){
                    msg += ':' + e.col;
                } else if(e.hasOwnProperty('column')) {
                    msg += ':' + e.column;
                }
            }
            msg += ']';
            e.message = msg;
            error(e);
        }
    });
}

var embeddedMap = {};

function error(msg){
    //for watching, unable to exit
    embeddedMap = {};
    fis.log.error(msg);
}

function embeddedCheck(main, embedded){
    main = fis.file.wrap(main).realpath;
    embedded = fis.file.wrap(embedded).realpath;
    if(main === embedded){
        error('unable to embed file[' + main + '] into itself.');
    } else if(embeddedMap[embedded]) {
        var next = embeddedMap[embedded],
            msg = [embedded];
        while(next && next !== embedded){
            msg.push(next);
            next = embeddedMap[next];
        }
        msg.push(embedded);
        error('circular dependency on [' + msg.join('] -> [') + '].');
    }
    embeddedMap[embedded] = main;
    return true;
}

function embeddedUnlock(file){
    delete embeddedMap[file.realpath];
}

function addDeps(a, b){
    if(a && a.cache && b){
        if(b.cache){
            a.cache.mergeDeps(b.cache);
        }
        a.cache.addDeps(b.realpath || b);
    }
}

function standard(file){
    var path = file.realpath,
        content = file.getContent();
    if(typeof content === 'string'){
        //expand language ability
        if(file.isHtmlLike){
            content = extHtml(content);
        } else if(file.isJsLike){
            content = extJs(content);
        } else if(file.isCssLike){
            content = extCss(content);
        }
        content = content.replace(map.reg, function(all, type, value){
            var ret = '', info;
            switch(type){
                case 'require':
                    info = fis.uri.getId(value, file.dirname);
                    file.addRequire(info.id);
                    ret = info.quote + info.id + info.quote;
                    break;
                case 'uri':
                    info = fis.uri(value, file.dirname);
                    if(info.file && info.file.isFile()){
                        if(exports.settings.hash){
                            if(embeddedCheck(file, info.file)){
                                exports(info.file);
                                addDeps(file, info.file);
                            }
                        }
                        ret = info.quote + info.file.getUrl(exports.settings.hash, exports.settings.domain) + info.query + info.quote;
                    } else {
                        ret = value;
                    }
                    break;
                case 'dep':
                    if(file.cache){
                        info = fis.uri(value, file.dirname);
                        addDeps(file, info.file);
                    } else {
                        fis.log.warning('unable to add deps to file [' + path + ']');
                    }
                    break;
                case 'embed':
                    info = fis.uri(value, file.dirname);
                    var f;
                    if(info.file){
                        f = info.file;
                    } else if(fis.util.isAbsolute(info.rest)){
                        f = fis.file(info.rest);
                    }
                    if(f && f.isFile()){
                        if(embeddedCheck(file, f)){
                            exports(f);
                            addDeps(file, f);
                            f.requires.forEach(function(id){
                                file.addRequire(id);
                            });
                            if(f.isText()){
                                ret = f.getContent();
                            } else {
                                ret = info.quote + f.getBase64() + info.quote;
                            }
                        }
                    } else {
                        error('unable to embed file[' + value + '] into file[' + path + ']: No such file.');
                    }
                    break;
            }
            return ret;
        });
        file.setContent(content);
    }
}

exports.extJs = extJs;
exports.extCss = extCss;
exports.extHtml = extHtml;
exports.isInline = isInline;
exports.analyseComment = analyseComment;<commit_msg>修复css的@import url结尾分号的bug<commit_after>/*
 * fis
 * http://fis.baidu.com/
 */

'use strict';

var CACHE_DIR;

var exports = module.exports = function(file){
    if(!CACHE_DIR){
        fis.log.error('uninitialized compile cache directory.');
    }
    file = fis.file.wrap(file);
    if(file.isText()){
        fis.log.debug('compile [' + file.realpath + '] start');
        if(file.isFile()){
            var cache = file.cache = fis.cache(file.realpath, CACHE_DIR),
                revertObj = {};
            if(cache.revert(revertObj)){
                exports.settings.beforeCacheRevert(file);
                file.requires = revertObj.info.requires;
                file.extras = revertObj.info.extras;
                file.setContent(revertObj.content);
                exports.settings.afterCacheRevert(file);
            } else {
                exports.settings.beforeCompile(file);
                file.setContent(fis.util.read(file.realpath));
                process(file);
                exports.settings.afterCompile(file);
                revertObj = {
                    requires : file.requires,
                    extras : file.extras
                };
                cache.save(file.getContent(), revertObj);
            }
        } else if(!file.realpath){
            process(file);
        } else {
            error('unable to compile [' + file.realpath + ']: Invalid file path.');
        }
        fis.log.debug('compile [' + file.realpath + '] end');
    } else if(file.realpath) {
        file.setContent(fis.util.read(file.realpath));
    }
    file.compiled = true;
    embeddedUnlock(file);
    return file;
};

exports.settings = {
    unique   : false,
    debug    : false,
    optimize : false,
    lint     : false,
    hash     : false,
    domain   : false,
    beforeCacheRevert : function(){},
    afterCacheRevert : function(){},
    beforeCompile : function(){},
    afterCompile : function(){}
};

exports.setup = function(opt){
    var settings = exports.settings;
    if(opt){
        fis.util.map(settings, function(key){
            if(typeof opt[key] !== 'undefined'){
                settings[key] = opt[key];
            }
        });
    }
    CACHE_DIR = 'compile/';
    if(settings.unique){
        CACHE_DIR += Date.now() + Math.random();
    } else {
        CACHE_DIR += ''
            + (settings.debug    ? 'debug'     : 'release')
            + (settings.optimize ? '-optimize' : '')
            + (settings.hash     ? '-hash'     : '')
            + (settings.domain   ? '-domain'   : '');
    }
    return CACHE_DIR;
};

exports.clean = function(name){
    if(name){
        fis.cache.clean('compile/' + name);
    } else if(CACHE_DIR) {
        fis.cache.clean(CACHE_DIR);
    } else {
        fis.cache.clean('compile');
    }
};

var map = exports.lang = (function(){
    var keywords = ['require', 'embed', 'uri', 'dep'],
        LD = '<[{', RD = '}]>',
        qLd = fis.util.escapeReg(LD),
        qRd = fis.util.escapeReg(RD),
        map = {
            reg : new RegExp(
                qLd + '(' + keywords.join('|') + ')\\(([\\s\\S]+?)\\)' + qRd,
                'g'
            )
        };
    keywords.forEach(function(key){
        map[key] = {};
        map[key]['ld'] = LD + key + '(';
        map[key]['rd'] = ')' + RD;
    });
    return map;
})();

//"abc?__inline" return true
//"abc?__inlinee" return false
//"abc?a=1&__inline"" return true
function isInline(info){
    return /[?&]__inline(?:[=&'"]|$)/.test(info.query);
}

//analyse [@require id] syntax in comment
function analyseComment(comment, callback){
    var reg = /(@require\s+)('[^']+'|"[^"]+"|[^\s;!@#%^&*()]+)/g;
    callback = callback || function(m, prefix, value){
        return prefix + map.require.ld + value + map.require.rd;
    };
    return comment.replace(reg, callback);
}

//expand javascript
//[@require id] in comment to require resource
//__inline(path) to embedd resource content or base64 encodings
//__uri(path) to locate resource
//require(path) to require resource
function extJs(content, callback){
    var reg = /"(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|(\/\/[^\r\n\f]+|\/\*[\s\S]*?(?:\*\/|$))|\b(__inline|__uri|require)\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*')\s*\)/g;
    callback = callback || function(m, comment, type, value){
        if(type){
            switch (type){
                case '__inline':
                    m = map.embed.ld + value + map.embed.rd;
                    break;
                case '__uri':
                    m = map.uri.ld + value + map.uri.rd;
                    break;
                case 'require':
                    m = 'require(' + map.require.ld + value + map.require.rd + ')';
                    break;
            }
        } else if(comment){
            m = analyseComment(comment);
        }
        return m;
    };
    return content.replace(reg, callback);
}

//expand css
//[@require id] in comment to require resource
//[@import url(path?__inline)] to embed resource content
//url(path) to locate resource
//url(path?__inline) to embed resource content or base64 encodings
//src=path to locate resource
function extCss(content, callback){
    var reg = /(\/\*[\s\S]*?(?:\*\/|$))|(?:@import\s+)?\burl\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^)}]+)\s*\)(\s*;?)|\bsrc\s*=\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^\s}]+)/g;
    callback = callback || function(m, comment, url, last, filter){
        if(url){
            var key = isInline(fis.util.query(url)) ? 'embed' : 'uri';
            if(m.indexOf('@') === 0){
                if(key === 'embed'){
                    m = map.embed.ld + url + map.embed.rd + last.replace(/;$/, '');
                } else {
                    m = '@import url(' + map.uri.ld + url + map.uri.rd + ')' + last;
                }
            } else {
                m = 'url(' + map[key].ld + url + map[key].rd + ')' + last;
            }
        } else if(filter) {
            m = 'src=' + map.uri.ld + filter + map.uri.rd;
        } else if(comment) {
            m = analyseComment(comment);
        }
        return m;
    };
    return content.replace(reg, callback);
}

//expand html
//[@require id] in comment to require resource
//<!--inline[path]--> to embed resource content
//<img|embed|audio|video|link|object ... (data-)?src="path"/> to locate resource
//<img|embed|audio|video|link|object ... (data-)?src="path?__inline"/> to embed resource content
//<script|style ... src="path"></script|style> to locate js|css resource
//<script|style ... src="path?__inline"></script|style> to embed js|css resource
//<script|style ...>...</script|style> to analyse as js|css
function extHtml(content, callback){
    var reg = /(<script(?:(?=\s)[\s\S]*?["'\s\w\/\-]>|>))([\s\S]*?)(?=<\/script\s*>|$)|(<style(?:(?=\s)[\s\S]*?["'\s\w\/\-]>|>))([\s\S]*?)(?=<\/style\s*>|$)|<(img|embed|audio|video|link|object)\s+[\s\S]*?["'\s\w\/\-](?:>|$)|<!--inline\[([^\]]+)\]-->|<!--(?!\[)([\s\S]*?)(-->|$)/ig;
    callback = callback || function(m, $1, $2, $3, $4, $5, $6, $7, $8){
        if($1){//<script>
            var embed = '';
            $1 = $1.replace(/(\s(?:data-)?src\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                if(isInline(fis.util.query(value))){
                    embed += map.embed.ld + value + map.embed.rd;
                    return '';
                } else {
                    return prefix + map.uri.ld + value + map.uri.rd;
                }
            });
            m = $1 + (embed || extJs($2));
        } else if($3){//<style>
            m = $3 + extCss($4);
        } else if($5){//<img|embed|audio|video|link|object>
            var tag = $5.toLowerCase();
            if(tag === 'link'){
                var inline = '';
                m = m.replace(/(\s(?:data-)?href\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                    if(isInline(fis.util.query(value))){
                        inline += '<style>' + map.embed.ld + value + map.embed.rd + '</style>';
                        return '';
                    } else {
                        return prefix + map.uri.ld + value + map.uri.rd;
                    }
                });
                m = inline || m;
            } else if(tag === 'object'){
                m = m.replace(/(\sdata\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                    return prefix + map.uri.ld + value + map.uri.rd;
                });
            } else {
                m = m.replace(/(\s(?:data-)?src\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value){
                    var key = isInline(fis.util.query(value)) ? 'embed' : 'uri';
                    return prefix + map[key]['ld'] + value + map[key]['rd'];
                });
            }
        } else if($6){
            m = map.embed.ld + $6 + map.embed.rd;
        } else if($7){
            m = '<!--' + analyseComment($7) + $8;
        }
        return m;
    };
    return content.replace(reg, callback);
}

function process(file){
    pipe(file, 'parser', file.ext);
    pipe(file, 'preprocessor', file.rExt);
    standard(file);
    pipe(file, 'postprocessor', file.rExt);
    if(exports.settings.lint){
        pipe(file, 'lint', file.rExt, true);
    }
    if(exports.settings.test){
        pipe(file, 'test', file.rExt, true);
    }
    if(exports.settings.optimize){
        pipe(file, 'optimizer', file.rExt);
    }
}

function pipe(file, type, ext, keep){
    var key = type + ext;
    fis.util.pipe(key, function(processor, settings, key){
        settings.filename = file.realpath;
        var content = file.getContent();
        try {
            fis.log.debug('pipe [' + key + '] start');
            var result = processor(content, file, settings);
            fis.log.debug('pipe [' + key + '] end');
            if(keep){
                file.setContent(content);
            } else if(typeof result === 'undefined'){
                fis.log.warning('invalid content return of pipe [' + key + ']');
            } else {
                file.setContent(result);
            }
        } catch(e) {
            //log error
            fis.log.debug('pipe [' + key + '] fail');
            var msg = key + ': ' + (e.message || e) + ' [' + file.subpath;
            if(e.hasOwnProperty('line')){
                msg += ':' + e.line;
                if(e.hasOwnProperty('col')){
                    msg += ':' + e.col;
                } else if(e.hasOwnProperty('column')) {
                    msg += ':' + e.column;
                }
            }
            msg += ']';
            e.message = msg;
            error(e);
        }
    });
}

var embeddedMap = {};

function error(msg){
    //for watching, unable to exit
    embeddedMap = {};
    fis.log.error(msg);
}

function embeddedCheck(main, embedded){
    main = fis.file.wrap(main).realpath;
    embedded = fis.file.wrap(embedded).realpath;
    if(main === embedded){
        error('unable to embed file[' + main + '] into itself.');
    } else if(embeddedMap[embedded]) {
        var next = embeddedMap[embedded],
            msg = [embedded];
        while(next && next !== embedded){
            msg.push(next);
            next = embeddedMap[next];
        }
        msg.push(embedded);
        error('circular dependency on [' + msg.join('] -> [') + '].');
    }
    embeddedMap[embedded] = main;
    return true;
}

function embeddedUnlock(file){
    delete embeddedMap[file.realpath];
}

function addDeps(a, b){
    if(a && a.cache && b){
        if(b.cache){
            a.cache.mergeDeps(b.cache);
        }
        a.cache.addDeps(b.realpath || b);
    }
}

function standard(file){
    var path = file.realpath,
        content = file.getContent();
    if(typeof content === 'string'){
        //expand language ability
        if(file.isHtmlLike){
            content = extHtml(content);
        } else if(file.isJsLike){
            content = extJs(content);
        } else if(file.isCssLike){
            content = extCss(content);
        }
        content = content.replace(map.reg, function(all, type, value){
            var ret = '', info;
            switch(type){
                case 'require':
                    info = fis.uri.getId(value, file.dirname);
                    file.addRequire(info.id);
                    ret = info.quote + info.id + info.quote;
                    break;
                case 'uri':
                    info = fis.uri(value, file.dirname);
                    if(info.file && info.file.isFile()){
                        if(exports.settings.hash){
                            if(embeddedCheck(file, info.file)){
                                exports(info.file);
                                addDeps(file, info.file);
                            }
                        }
                        ret = info.quote + info.file.getUrl(exports.settings.hash, exports.settings.domain) + info.query + info.quote;
                    } else {
                        ret = value;
                    }
                    break;
                case 'dep':
                    if(file.cache){
                        info = fis.uri(value, file.dirname);
                        addDeps(file, info.file);
                    } else {
                        fis.log.warning('unable to add deps to file [' + path + ']');
                    }
                    break;
                case 'embed':
                    info = fis.uri(value, file.dirname);
                    var f;
                    if(info.file){
                        f = info.file;
                    } else if(fis.util.isAbsolute(info.rest)){
                        f = fis.file(info.rest);
                    }
                    if(f && f.isFile()){
                        if(embeddedCheck(file, f)){
                            exports(f);
                            addDeps(file, f);
                            f.requires.forEach(function(id){
                                file.addRequire(id);
                            });
                            if(f.isText()){
                                ret = f.getContent();
                            } else {
                                ret = info.quote + f.getBase64() + info.quote;
                            }
                        }
                    } else {
                        error('unable to embed file[' + value + '] into file[' + path + ']: No such file.');
                    }
                    break;
            }
            return ret;
        });
        file.setContent(content);
    }
}

exports.extJs = extJs;
exports.extCss = extCss;
exports.extHtml = extHtml;
exports.isInline = isInline;
exports.analyseComment = analyseComment;<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

make.typed = {
  byte: function (limit = null) {
    // [-128, 127]
    let value = (limit !== null) ? random.number(limit) : random.number(129)
    value = random.chance(10) ? -value : value
    return 'new Uint8Array([' + value + '])[0]'
  },
  octet: function (limit = null) {
    // [0, 255]
    let value = (limit !== null) ? random.number(limit) : random.number(256)
    return 'new Int8Array([' + value + '])[0]'
  },
  short: function (limit = null) {
    // [-32768, 32767]
    let value = (limit !== null) ? random.number(limit) : random.number(32769)
    value = random.chance(10) ? -value : value
    return 'new Int16Array([' + value + '])[0]'
  },
  unsignedShort: function (limit = null) {
    // [0, 65535]
    let value = (limit !== null) ? random.number(limit) : random.number(65535)
    return 'new Uint16Array([' + value + '])[0]'
  },
  long: function (limit = null) {
    // [-2147483648, 2147483647]
    let value = (limit !== null) ? random.number(limit) : random.number(2147483649)
    value = random.chance(10) ? -value : value
    return 'new Int32Array([' + value + '])[0]'
  },
  unsignedLong: function (limit = null) {
    // [0, 4294967295]
    let value = (limit !== null) ? random.number(limit) : random.number(4294967296)
    return 'new Uint32Array([' + value + '])[0]'
  },
  // ToDo: Add support for longlong and ulonglong
  /*
   longLong: function () {},
   unsignedLongLong: function () {},
   */
  float: function (limit = null) {
    let base = (limit !== null) ? random.number(limit) : random.number()
    let value = random.chance(10) ? -(base + random.float()) : (base + random.float())
    return 'new Float32Array([' + value + '])[0]'
  },
  unrestrictedFloat: function (limit = null) {
    if (random.chance(100)) {
      return random.pick([NaN, +Infinity, -Infinity])
    } else {
      let base = (limit !== null) ? random.number(limit) : random.number()
      return 'new Float32Array([' + (base + random.float()) + '])[0]'
    }
  },
  double: function (limit = null) {
    let base = (limit !== null) ? random.number(limit) : random.number()
    let value = random.chance(10) ? -(base + random.float()) : (base + random.float())
    return 'new Float64Array([' + value + '])[0]'
  },
  unrestrictedDouble: function (limit = null) {
    if (random.chance(100)) {
      return random.pick([NaN, +Infinity, -Infinity])
    } else {
      let base = (limit !== null) ? random.number(limit) : random.number()
      let value = random.chance(10) ? -(base + random.float()) : (base + random.float())
      return 'new Float64Array([' + value + '])[0]'
    }
  },
  any: function () {
    let value = random.choose([
      [1, [this.byte, this.octet]],
      [1, [this.short, this.unsignedShort]],
      [1, [this.long, this.unsignedLong]],
      [1, [this.float, this.unrestrictedFloat]],
      [1, [this.double, this.unrestrictedDouble]],
      [1, [make.number.range, make.number.tiny]]
    ])
    return value
  },
  arrayBuffer: function (byteLength = null) {
    let length = (byteLength !== null) ? byteLength : this.unsignedShort()
    return 'new ArrayBuffer(' + length + ')'
  },
  dataView: function (byteLength = null) {
    let length = (byteLength !== null) ? byteLength : this.unsignedShort()
    return 'new DataView(' + this.arrayBuffer(length) + ')'
  },
  typedArray: function (byteLength = null) {
    let length = (byteLength !== null) ? byteLength : this.unsignedShort()
    let arrType = random.item([
      'Int8', 'Uint8', 'Uint8Clamped',
      'Int16', 'Uint16',
      'Int32', 'Uint32',
      'Float32', 'Float64'
    ])
    let method = 'new ' + arrType + 'Array'
    switch (random.number(16)) {
      case 0:
        return method + '()'
      case 1:
        return method + '(' + this.typedArray() + ')'
      default:
        return method + '(' + length + ')'
    }
  },
  bufferSource: function () {
    return random.choose([
      [1, [this.arrayBuffer, this.dataView]],
      [1, [this.typedArray]]
    ])
  }
}
<commit_msg>Further cleanup in make.typed.any()<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

make.typed = {
  byte: function (limit = null) {
    // [-128, 127]
    let value = (limit !== null) ? random.number(limit) : random.number(129)
    value = random.chance(10) ? -value : value
    return 'new Uint8Array([' + value + '])[0]'
  },
  octet: function (limit = null) {
    // [0, 255]
    let value = (limit !== null) ? random.number(limit) : random.number(256)
    return 'new Int8Array([' + value + '])[0]'
  },
  short: function (limit = null) {
    // [-32768, 32767]
    let value = (limit !== null) ? random.number(limit) : random.number(32769)
    value = random.chance(10) ? -value : value
    return 'new Int16Array([' + value + '])[0]'
  },
  unsignedShort: function (limit = null) {
    // [0, 65535]
    let value = (limit !== null) ? random.number(limit) : random.number(65535)
    return 'new Uint16Array([' + value + '])[0]'
  },
  long: function (limit = null) {
    // [-2147483648, 2147483647]
    let value = (limit !== null) ? random.number(limit) : random.number(2147483649)
    value = random.chance(10) ? -value : value
    return 'new Int32Array([' + value + '])[0]'
  },
  unsignedLong: function (limit = null) {
    // [0, 4294967295]
    let value = (limit !== null) ? random.number(limit) : random.number(4294967296)
    return 'new Uint32Array([' + value + '])[0]'
  },
  // ToDo: Add support for longlong and ulonglong
  /*
   longLong: function () {},
   unsignedLongLong: function () {},
   */
  float: function (limit = null) {
    let base = (limit !== null) ? random.number(limit) : random.number()
    let value = random.chance(10) ? -(base + random.float()) : (base + random.float())
    return 'new Float32Array([' + value + '])[0]'
  },
  unrestrictedFloat: function (limit = null) {
    if (random.chance(100)) {
      return random.pick([NaN, +Infinity, -Infinity])
    } else {
      let base = (limit !== null) ? random.number(limit) : random.number()
      return 'new Float32Array([' + (base + random.float()) + '])[0]'
    }
  },
  double: function (limit = null) {
    let base = (limit !== null) ? random.number(limit) : random.number()
    let value = random.chance(10) ? -(base + random.float()) : (base + random.float())
    return 'new Float64Array([' + value + '])[0]'
  },
  unrestrictedDouble: function (limit = null) {
    if (random.chance(100)) {
      return random.pick([NaN, +Infinity, -Infinity])
    } else {
      let base = (limit !== null) ? random.number(limit) : random.number()
      let value = random.chance(10) ? -(base + random.float()) : (base + random.float())
      return 'new Float64Array([' + value + '])[0]'
    }
  },
  any: function () {
    return random.choose([
      [1, [this.byte, this.octet]],
      [1, [this.short, this.unsignedShort]],
      [1, [this.long, this.unsignedLong]],
      [1, [this.float, this.unrestrictedFloat]],
      [1, [this.double, this.unrestrictedDouble]],
      [1, [make.number.range, make.number.tiny]]
    ])
  },
  arrayBuffer: function (byteLength = null) {
    let length = (byteLength !== null) ? byteLength : this.unsignedShort()
    return 'new ArrayBuffer(' + length + ')'
  },
  dataView: function (byteLength = null) {
    let length = (byteLength !== null) ? byteLength : this.unsignedShort()
    return 'new DataView(' + this.arrayBuffer(length) + ')'
  },
  typedArray: function (byteLength = null) {
    let length = (byteLength !== null) ? byteLength : this.unsignedShort()
    let arrType = random.item([
      'Int8', 'Uint8', 'Uint8Clamped',
      'Int16', 'Uint16',
      'Int32', 'Uint32',
      'Float32', 'Float64'
    ])
    let method = 'new ' + arrType + 'Array'
    switch (random.number(16)) {
      case 0:
        return method + '()'
      case 1:
        return method + '(' + this.typedArray() + ')'
      default:
        return method + '(' + length + ')'
    }
  },
  bufferSource: function () {
    return random.choose([
      [1, [this.arrayBuffer, this.dataView]],
      [1, [this.typedArray]]
    ])
  }
}
<|endoftext|>


<commit_before>var po = require('pofile');
var _ = require('lodash');

var formats = {
    javascript: {
        addLocale: function (locale, strings) {
            return "    gettextCatalog.setStrings('" + locale + "', " + JSON.stringify(strings) + ");\n";
        },
        format: function (locales, options) {
            return "angular.module('" + options.module + "').run(['gettextCatalog', function (gettextCatalog) {\n/* jshint -W100 */\n" + locales.join('') + "/* jshint +W100 */\n}]);";
        }
    },
    json: {
        addLocale: function (locale, strings) {
            return {name: locale, strings: strings};
        },
        format: function (locales, options) {
            var result = {};
            locales.forEach(function (locale) {
                result[locale.name] = locale.strings;
            });
            return JSON.stringify(result);
        }
    }
};

var Compiler = (function () {
    function Compiler(options) {
        this.options = _.extend({
            format: 'javascript',
            module: 'gettext'
        }, options);
    }

    Compiler.hasFormat = function (format) {
        return formats.hasOwnProperty(format);
    };

    Compiler.prototype.convertPo = function (inputs) {
        var format = formats[this.options.format];
        var locales = [];

        inputs.forEach(function (input) {
            var catalog = po.parse(input);

            if (!catalog.headers.Language) {
                throw new Error('No Language header found!');
            }

            var strings = {};
            for (var i = 0; i < catalog.items.length; i++) {
                var item = catalog.items[i];
                if (item.msgstr[0].length > 0) {
                    strings[item.msgid] = item.msgstr.length === 1 ? item.msgstr[0] : item.msgstr;
                }
            }

            locales.push(format.addLocale(catalog.headers.Language, strings));

        });

        return format.format(locales, this.options);
    };

    return Compiler;
})();

module.exports = Compiler;
<commit_msg>Adding support to requirejs<commit_after>var po = require('pofile');
var _ = require('lodash');

var formats = {
    javascript: {
        addLocale: function(locale, strings) {
            return "    gettextCatalog.setStrings('" + locale + "', " + JSON.stringify(strings) + ");\n";
        },
        format: function(locales, options) {
            if (options.requirejs) {
                return "define(['angular', '" + options.modulePath + "'],function(angular){angular.module('" + options.module + "').run(['gettextCatalog', function (gettextCatalog) {\n/* jshint -W100 */\n" + locales.join('') + "/* jshint +W100 */\n}]);});";
            } else {
                return "angular.module('" + options.module + "').run(['gettextCatalog', function (gettextCatalog) {\n/* jshint -W100 */\n" + locales.join('') + "/* jshint +W100 */\n}]);";
            }
        }
    },
    json: {
        addLocale: function(locale, strings) {
            return {
                name: locale,
                strings: strings
            };
        },
        format: function(locales, options) {
            var result = {};
            locales.forEach(function(locale) {
                result[locale.name] = locale.strings;
            });
            return JSON.stringify(result);
        }
    }
};

var Compiler = (function() {
    function Compiler(options) {
        this.options = _.extend({
            format: 'javascript',
            module: 'gettext'
        }, options);
    }

    Compiler.hasFormat = function(format) {
        return formats.hasOwnProperty(format);
    };

    Compiler.prototype.convertPo = function(inputs) {
        var format = formats[this.options.format];
        var locales = [];

        inputs.forEach(function(input) {
            var catalog = po.parse(input);

            if (!catalog.headers.Language) {
                throw new Error('No Language header found!');
            }

            var strings = {};
            for (var i = 0; i < catalog.items.length; i++) {
                var item = catalog.items[i];
                if (item.msgstr[0].length > 0) {
                    strings[item.msgid] = item.msgstr.length === 1 ? item.msgstr[0] : item.msgstr;
                }
            }

            locales.push(format.addLocale(catalog.headers.Language, strings));

        });

        return format.format(locales, this.options);
    };

    return Compiler;
})();

module.exports = Compiler;<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

 make.webgl = {
  internalFormat: {
    'RGB': {format: 'RGB', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_5_6_5']},
    'RGBA': {format: 'RGBA', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_4_4_4_4', 'UNSIGNED_SHORT_5_5_5_1']},
    'LUMINANCE_ALPHA': {format: 'LUMINANCE_ALPHA', type: ['UNSIGNED_BYTE']},
    'LUMINANCE': {format: 'LUMINANCE', type: ['UNSIGNED_BYTE']},
    'ALPHA': {format: 'ALPHA', type: ['UNSIGNED_BYTE']},
    'R8': {format: 'RED', type: ['UNSIGNED_BYTE']},
    'R16F': {format: 'RED', type: ['HALF_FLOAT', 'FLOAT']},
    'R32F': {format: 'RED', type: ['FLOAT']},
    'R8UI': {format: 'RED_INTEGER', type: ['UNSIGNED_BYTE']},
    'RG8': {format: 'RG', type: ['UNSIGNED_BYTE']},
    'RG16F': {format: 'RG', type: ['HALF_FLOAT', 'FLOAT']},
    'RG32F': {format: 'RG', type: ['FLOAT']},
    'RG8UI': {format: 'RG_INTEGER', type: ['UNSIGNED_BYTE']},
    'RGB8': {format: 'RGB', type: ['UNSIGNED_BYTE']},
    'SRGB8': {format: 'RGB', type: ['UNSIGNED_BYTE']},
    'RGB565': {format: 'RGB', type: ['RGB', 'UNSIGNED_BYTE', 'UNSIGNED_SHORT_5_6_5']},
    'R11F_G11F_B10F': {format: 'RGB', type: ['UNSIGNED_INT_10F_11F_11F_REV', 'HALF_FLOAT', 'FLOAT']},
    'RGB9_E5': {format: 'RGB', type: ['HALF_FLOAT', 'FLOAT']},
    'RGB16F': {format: 'RGB', type: ['HALF_FLOAT', 'FLOAT']},
    'RGB32F': {format: 'RGB', type: ['FLOAT']},
    'RGB8UI': {format: 'RGB_INTEGER', type: ['UNSIGNED_BYTE']},
    'RGBA8': {format: 'RGBA', type: ['UNSIGNED_BYTE']},
    'SRGB8_ALPHA8': {format: 'RGBA', type: ['UNSIGNED_BYTE']},
    'RGB5_A1': {format: 'RGBA', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_5_5_5_1']},
    'RGB10_A2': {format: 'RGBA', type: ['UNSIGNED_INT_2_10_10_10_REV']},
    'RGBA4': {format: 'RGBA', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_4_4_4_4']},
    'RGBA16F': {format: 'RGBA', type: ['HALF_FLOAT', 'FLOAT']},
    'RGBA32F': {format: 'RGBA', type: ['FLOAT']},
    'RGBA8UI': {format: 'RGBA_INTEGER', type: ['UNSIGNED_BYTE']}
  },
  buffetTarget: () => {
    return random.item(['ARRAY_BUFFER', 'ELEMENT_ARRAY_BUFFER'])
  },
  textureTarget: (isWebGL2) => {
    let target = ['TEXTURE_2D', 'TEXTURE_CUBE_MAP']
    if (isWebGL2) {
      target.extend(['TEXTURE_3D', 'TEXTURE_2D_ARRAY'])
    }
    return random.item(target)
  },
  framebufferTarget: (isWebGL2) => {
    let target = ['FRAMEBUFFER']
    if (isWebGL2) {
      target.extends(['DRAW_FRAMEBUFFER', 'READ_FRAMEBUFFER'])
    }
    return random.item(target)
  },
  renderbufferTarget: () => {
    return 'RENDERBUFFER'
  },
  textureSources: () => {
    sources = [
      'HTMLCanvasElement',
      'HTMLImageElement',
      'HTMLVideoElement',
      'ImageBitmap',
      'ImageData',
      'ArrayBufferView'
    ]
    return random.item(sources)
  }
}
<commit_msg>Add parseUniforms and parseAttributes for WebGL shaders<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

 make.webgl = {
  internalFormat: {
    'RGB': {format: 'RGB', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_5_6_5']},
    'RGBA': {format: 'RGBA', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_4_4_4_4', 'UNSIGNED_SHORT_5_5_5_1']},
    'LUMINANCE_ALPHA': {format: 'LUMINANCE_ALPHA', type: ['UNSIGNED_BYTE']},
    'LUMINANCE': {format: 'LUMINANCE', type: ['UNSIGNED_BYTE']},
    'ALPHA': {format: 'ALPHA', type: ['UNSIGNED_BYTE']},
    'R8': {format: 'RED', type: ['UNSIGNED_BYTE']},
    'R16F': {format: 'RED', type: ['HALF_FLOAT', 'FLOAT']},
    'R32F': {format: 'RED', type: ['FLOAT']},
    'R8UI': {format: 'RED_INTEGER', type: ['UNSIGNED_BYTE']},
    'RG8': {format: 'RG', type: ['UNSIGNED_BYTE']},
    'RG16F': {format: 'RG', type: ['HALF_FLOAT', 'FLOAT']},
    'RG32F': {format: 'RG', type: ['FLOAT']},
    'RG8UI': {format: 'RG_INTEGER', type: ['UNSIGNED_BYTE']},
    'RGB8': {format: 'RGB', type: ['UNSIGNED_BYTE']},
    'SRGB8': {format: 'RGB', type: ['UNSIGNED_BYTE']},
    'RGB565': {format: 'RGB', type: ['RGB', 'UNSIGNED_BYTE', 'UNSIGNED_SHORT_5_6_5']},
    'R11F_G11F_B10F': {format: 'RGB', type: ['UNSIGNED_INT_10F_11F_11F_REV', 'HALF_FLOAT', 'FLOAT']},
    'RGB9_E5': {format: 'RGB', type: ['HALF_FLOAT', 'FLOAT']},
    'RGB16F': {format: 'RGB', type: ['HALF_FLOAT', 'FLOAT']},
    'RGB32F': {format: 'RGB', type: ['FLOAT']},
    'RGB8UI': {format: 'RGB_INTEGER', type: ['UNSIGNED_BYTE']},
    'RGBA8': {format: 'RGBA', type: ['UNSIGNED_BYTE']},
    'SRGB8_ALPHA8': {format: 'RGBA', type: ['UNSIGNED_BYTE']},
    'RGB5_A1': {format: 'RGBA', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_5_5_5_1']},
    'RGB10_A2': {format: 'RGBA', type: ['UNSIGNED_INT_2_10_10_10_REV']},
    'RGBA4': {format: 'RGBA', type: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_4_4_4_4']},
    'RGBA16F': {format: 'RGBA', type: ['HALF_FLOAT', 'FLOAT']},
    'RGBA32F': {format: 'RGBA', type: ['FLOAT']},
    'RGBA8UI': {format: 'RGBA_INTEGER', type: ['UNSIGNED_BYTE']}
  },
  buffetTarget: () => {
    return random.item(['ARRAY_BUFFER', 'ELEMENT_ARRAY_BUFFER'])
  },
  textureTarget: (isWebGL2) => {
    let target = ['TEXTURE_2D', 'TEXTURE_CUBE_MAP']
    if (isWebGL2) {
      target.extend(['TEXTURE_3D', 'TEXTURE_2D_ARRAY'])
    }
    return random.item(target)
  },
  framebufferTarget: (isWebGL2) => {
    let target = ['FRAMEBUFFER']
    if (isWebGL2) {
      target.extends(['DRAW_FRAMEBUFFER', 'READ_FRAMEBUFFER'])
    }
    return random.item(target)
  },
  renderbufferTarget: () => {
    return 'RENDERBUFFER'
  },
  textureSources: () => {
    sources = [
      'HTMLCanvasElement',
      'HTMLImageElement',
      'HTMLVideoElement',
      'ImageBitmap',
      'ImageData',
      'ArrayBufferView'
    ]
    return random.item(sources)
  },
  parseUniforms: (shader) => {
    let names = [], result = shader.match(/uniform .* (\w+)(?=;)/gm)
    if (result) {
      result.forEach(function (v) {
        names.push(v.split(" ").pop())
      })
    }
    return names
  },
  parseAttributes: (shader) => {
    let names = [], result = shader.match(/attribute .* (\w+)(?=;)/gm)
    if (result) {
      result.forEach(function (v) {
        names.push(v.split(" ").pop())
      })
    }
    return names
  }
}
<|endoftext|>


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

  var colors         = require('colors');
  var strip          = require('cli-color/strip');
  var fs             = require('fs');
  var prompt         = require('prompt-sync');
  var child_process  = require('child_process');
  var pkg            = require('./package.json');
  var oldVersion     = pkg.version;
  var abortCmds      = [ 'git reset --hard', 'git checkout master', 'rm abort push' ];
  var pushCmds       = [ 'rm abort push'];
  var cleanupCmds    = [];
  var defaultOptions = { encoding: 'utf-8' };
  var origin         = 'https://github.com/angular/material.git';
  var lineWidth      = 80;
  var lastMajorVer   = JSON.parse(exec('curl https://material.angularjs.org/docs.json')).latest;
  var newVersion;
  var dryRun;

  header();
  write('Is this a dry-run? {{"[yes/no]".cyan}} ');
  dryRun = prompt() !== 'no';

  if (dryRun) {
    write('What would you like the old version to be? (default: {{oldVersion.cyan}}) ');
    oldVersion = prompt() || oldVersion;
    build();
  } else if (validate()) {
    build();
  }

  function build () {
    newVersion = getNewVersion();

    line();

    checkoutVersionBranch();
    updateVersion();
    createChangelog();
    commitChanges();
    tagRelease();
    cloneRepo('bower-material');
    updateBowerVersion();
    cloneRepo('code.material.angularjs.org');
    updateSite();
    updateMaster();
    writeScript('abort', abortCmds.concat(cleanupCmds));
    if (!dryRun) writeScript('push', pushCmds.concat(cleanupCmds));

    line();
    log('Your repo is ready to be pushed.');
    log('Please look over {{"CHANGELOG.md".cyan}} and make any changes.');
    log('When you are ready, please run "{{"./push".cyan}}" to finish the process.');
    log('If you would like to cancel this release, please run "./abort"');
  }

  //-- utility methods

  function validate () {
    if (exec('npm whoami') !== 'angularcore') {
      err('You must be authenticated with npm as "angularcore" to perform a release.');
    } else if (exec('git rev-parse --abbrev-ref HEAD') !== 'master') {
      err('Releases can only performed from master at this time.');
    } else if (exec('git pull -q --rebase {{origin}} master') instanceof Error) {
      err('Please make sure your local branch is synced with origin/master.');
    } else {
      return true;
    }
    function err (msg) {
      var str = 'Error: ' + msg;
      log(str.red);
    }
  }

  function checkoutVersionBranch () {
    exec('git checkout -q -b release/{{newVersion}}');
    abortCmds.push('git branch -D release/{{newVersion}}');
  }

  function updateVersion () {
    start('Updating {{"package.json".cyan}} version from {{oldVersion.cyan}} to {{newVersion.cyan}}...');
    pkg.version = newVersion;
    fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2));
    done();
    abortCmds.push('git checkout package.json');
    pushCmds.push('git add package.json');
  }

  function createChangelog () {
    start('Generating changelog from {{oldVersion.cyan}} to {{newVersion.cyan}}...');
    exec([
      'git fetch --tags',
      'git checkout v{{lastMajorVer}} -- CHANGELOG.md',
      'gulp changelog --sha=$(git merge-base v{{lastMajorVer}} HEAD)'
    ]);
    done();
    abortCmds.push('git checkout CHANGELOG.md');
    pushCmds.push('git add CHANGELOG.md');
  }

  function clear () {
    write("\u001b[2J\u001b[0;0H");
  }

  function getNewVersion () {
    header();
    var options = getVersionOptions(oldVersion), key, type, version;
    log('The current version is {{oldVersion.cyan}}.');
    log('');
    log('What should the next version be?');
    for (key in options) { log((+key + 1) + ') ' + options[key].cyan); }
    log('');
    write('Please select a new version: ');
    type = prompt();

    if (options[type - 1]) version = options[type - 1];
    else if (type.match(/^\d+\.\d+\.\d+(-rc\d+)?$/)) version = type;
    else throw new Error('Your entry was invalid.');

    log('');
    log('The new version will be ' + version.cyan + '.');
    write('Is this correct? {{"[yes/no]".cyan}} ');
    return prompt() === 'yes' ? version : getNewVersion();

    function getVersionOptions (version) {
      return version.match(/-rc\d+$/)
          ? [ increment(version, 'rc'),
              increment(version, 'minor') ]
          : [ increment(version, 'patch'),
              increment(version, 'minor'),
              increment(version, 'major') ].map(addRC);

      function increment (versionString, type) {
        var version = parseVersion(versionString);
        if (version.rc) {
          switch (type) {
            case 'minor': version.rc = 0; break;
            case 'rc': version.rc++; break;
          }
        } else {
          version[type]++;
          //-- reset any version numbers lower than the one changed
          switch (type) {
            case 'major': version.minor = 0;
            case 'minor': version.patch = 0;
            case 'patch': version.rc = 0;
          }
        }
        return getVersionString(version);

        function parseVersion (version) {
          var parts = version.split(/\.|\-rc/g);
          return { string: version, major: parts[0], minor: parts[1], patch: parts[2], rc: parts[3] || 0 };
        }

        function getVersionString(version) {
          var str = version.major + '.' + version.minor + '.' + version.patch;
          if (version.rc) str += '-rc' + version.rc;
          return str;
        }
      }

      function addRC (str) {
        return str + '-rc1';
      }
    }
  }

  function tagRelease () {
    pushCmds.push(
        'git tag v{{newVersion}}',
        'git push {{origin}} HEAD',
        'git push --tags'
    );
  }

  function commitChanges () {
    start('Committing changes...');
    exec('git commit -am "release: version {{newVersion}}"');
    done();
    pushCmds.push('git commit --amend --no-edit');
  }

  function cloneRepo (repo) {
    start('Cloning ' + repo.cyan + ' from Github...');
    exec('rm -rf ' + repo);
    exec('git clone https://github.com/angular/' + repo + '.git --depth=1');
    done();
    cleanupCmds.push('rm -rf ' + repo);
  }

  function fill(str) {
    return str.replace(/\{\{[^\}]+\}\}/g, function (match) {
      return eval(match.substr(2, match.length - 4));
    });
  }

  function writeScript (name, cmds) {
    fs.writeFileSync(name, '#!/usr/bin/env bash\n\n' + fill(cmds.join('\n')));
    exec('chmod +x ' + name);
  }

  function updateBowerVersion () {
    start('Updating bower version...');
    var options = { cwd: './bower-material' },
        bower = require(options.cwd + '/bower.json'),
        pkg = require(options.cwd + '/package.json');
    //-- update versions in config files
    bower.version = pkg.version = newVersion;
    fs.writeFileSync(options.cwd + '/package.json', JSON.stringify(pkg, null, 2));
    fs.writeFileSync(options.cwd + '/bower.json', JSON.stringify(bower, null, 2));
    done();
    start('Building bower files...');
    //-- build files for bower
    exec([
      'rm -rf dist',
      'gulp build',
      'gulp build-all-modules --mode=default',
      'gulp build-all-modules --mode=closure',
      'rm -rf dist/demos',
    ]);
    done();
    start('Copy files into bower repo...');
    //-- copy files over to bower repo
    exec([
      'cp -Rf ../dist/* ./',
      'git add -A',
      'git commit -m "release: version {{newVersion}}"',
      'rm -rf ../dist'
    ], options);
    done();
    //-- add steps to push script
    pushCmds.push(
        comment('push to bower (master and tag) and publish to npm'),
        'cd ' + options.cwd,
        'cp ../CHANGELOG.md .',
        'git add CHANGELOG.md',
        'git commit --amend --no-edit',
        'git tag -f v{{newVersion}}',
        'git pull --rebase',
        'git push',
        'git push --tags',
        ( newVersion.indexOf('rc') < 0 ? 'npm publish' : '# skipped npm publish due to RC version' ),
        'cd ..'
    );
  }

  function updateSite () {
    start('Adding new version of the docs site...');
    var options = { cwd: './code.material.angularjs.org' },
        config  = require(options.cwd + '/docs.json');
    config.versions = config.versions.filter(function (version) {
      return version.indexOf('rc') < 0;
    });
    config.versions.unshift(newVersion);
    //-- only set to default if not a release candidate
    if (newVersion.indexOf('rc') < 0) config.latest = newVersion;
    fs.writeFileSync(options.cwd + '/docs.json', JSON.stringify(config, null, 2));
    //-- build files for bower
    exec([
      'rm -rf dist',
      'gulp docs',
      'sed -i \'\' \'s,http:\\/\\/localhost:8080\\/angular-material,https:\\/\\/gitcdn.xyz/repo/angular/bower-material/v{{newVersion}}/angular-material,g\' dist/docs/docs.js'
    ]);

    //-- copy files over to site repo
    exec([
      'rm -rf ./*-rc*',
      'cp -Rf ../dist/docs {{newVersion}}',
      ( newVersion.indexOf('rc') < 0 ? 'rm -rf latest && cp -Rf ../dist/docs latest' : '# skipped latest because this is a release candidate' ),
      'git add -A',
      'git commit -m "release: version {{newVersion}}"',
      'rm -rf ../dist'
    ], options);
    done();
    //-- add steps to push script
    pushCmds.push(
        comment('push the site'),
        'cd ' + options.cwd,
        'git pull --rebase',
        'git push',
        'cd ..'
    );
  }

  function updateMaster () {
    pushCmds.push(
        comment('update package.json in master'),
        'git co master',
        'git pull --rebase {{origin}} master',
        'git checkout release/{{newVersion}} -- CHANGELOG.md',
        'node -e "' + stringifyFunction(buildCommand) + '"',
        'git add CHANGELOG.md',
        'git add package.json',
        'git commit -m "update version number in package.json to {{newVersion}}"',
        'git push'
    );
    function buildCommand () {
      require('fs').writeFileSync('package.json', JSON.stringify(getUpdatedJson(), null, 2));
      function getUpdatedJson () {
        var json = require('./package.json');
        json.version = '{{newVersion}}';
        return json;
      }
    }
    function stringifyFunction (method) {
      return method
          .toString()
          .split('\n')
          .slice(1, -1)
          .map(function (line) { return line.trim(); })
          .join(' ')
          .replace(/"/g, '\\"');
    }
  }

  function header () {
    clear();
    line();
    log(center('Angular Material Release'));
    line();
  }

  function center (msg) {
    msg = ' ' + msg.trim() + ' ';
    var length = msg.length;
    var spaces = Math.floor((lineWidth - length) / 2);
    return Array(spaces + 1).join('-') + msg.green + Array(lineWidth - msg.length - spaces + 1).join('-');
  }

  function done () {
    log('done'.green);
  }

  function exec (cmd, userOptions) {
    if (cmd instanceof Array) {
      return cmd.map(function (cmd) { return exec(cmd, userOptions); });
    }
    try {
      var options = Object.create(defaultOptions);
      for (var key in userOptions) options[key] = userOptions[key];
      return child_process.execSync(fill(cmd) + ' 2> /dev/null', options).trim();
    } catch (err) {
      return err;
    }
  }

  function comment (msg) {
    return '\n# ' + msg + '\n';
  }

  function start (msg) {
    var parsedMsg = fill(msg),
        msgLength = strip(parsedMsg).length,
        diff = lineWidth - 4 - msgLength;
    write(parsedMsg + Array(diff + 1).join(' '));
  }

  function log (msg) {
    msg = msg || '';
    console.log(fill(msg));
  }

  function write (msg) {
    process.stdout.write(fill(msg));
  }

  function line () {
    log(Array(lineWidth + 1).join('-'));
  }
})();
<commit_msg>update release script<commit_after>(function () {
  'use strict';

  var colors         = require('colors');
  var strip          = require('cli-color/strip');
  var fs             = require('fs');
  var prompt         = require('prompt-sync');
  var child_process  = require('child_process');
  var pkg            = require('./package.json');
  var oldVersion     = pkg.version;
  var abortCmds      = [ 'git reset --hard', 'git checkout staging', 'rm abort push' ];
  var pushCmds       = [ 'rm abort push'];
  var cleanupCmds    = [];
  var defaultOptions = { encoding: 'utf-8' };
  var origin         = 'https://github.com/angular/material.git';
  var lineWidth      = 80;
  var lastMajorVer   = JSON.parse(exec('curl https://material.angularjs.org/docs.json')).latest;
  var newVersion;
  var dryRun;

  header();
  write('Is this a dry-run? {{"[yes/no]".cyan}} ');
  dryRun = prompt() !== 'no';

  if (dryRun) {
    write('What would you like the old version to be? (default: {{oldVersion.cyan}}) ');
    oldVersion = prompt() || oldVersion;
    build();
  } else if (validate()) {
    build();
  }

  function build () {
    newVersion = getNewVersion();

    line();

    checkoutVersionBranch();
    updateVersion();
    createChangelog();
    commitChanges();
    tagRelease();
    cloneRepo('bower-material');
    updateBowerVersion();
    cloneRepo('code.material.angularjs.org');
    updateSite();
    updateMaster();
    writeScript('abort', abortCmds.concat(cleanupCmds));
    if (!dryRun) writeScript('push', pushCmds.concat(cleanupCmds));

    line();
    log('Your repo is ready to be pushed.');
    log('Please look over {{"CHANGELOG.md".cyan}} and make any changes.');
    log('When you are ready, please run "{{"./push".cyan}}" to finish the process.');
    log('If you would like to cancel this release, please run "./abort"');
  }

  //-- utility methods

  function validate () {
    if (exec('npm whoami') !== 'angularcore') {
      err('You must be authenticated with npm as "angularcore" to perform a release.');
    } else if (exec('git rev-parse --abbrev-ref HEAD') !== 'staging') {
      err('Releases can only performed from "staging" at this time.');
    } else {
      return true;
    }
    function err (msg) {
      var str = 'Error: ' + msg;
      log(str.red);
    }
  }

  function checkoutVersionBranch () {
    exec('git checkout -q -b release/{{newVersion}}');
    abortCmds.push('git branch -D release/{{newVersion}}');
  }

  function updateVersion () {
    start('Updating {{"package.json".cyan}} version from {{oldVersion.cyan}} to {{newVersion.cyan}}...');
    pkg.version = newVersion;
    fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2));
    done();
    abortCmds.push('git checkout package.json');
    pushCmds.push('git add package.json');
  }

  function createChangelog () {
    start('Generating changelog from {{oldVersion.cyan}} to {{newVersion.cyan}}...');
    exec([
      'git fetch --tags',
      'git checkout v{{lastMajorVer}} -- CHANGELOG.md',
      'gulp changelog --sha=$(git merge-base v{{lastMajorVer}} HEAD)'
    ]);
    done();
    abortCmds.push('git checkout CHANGELOG.md');
    pushCmds.push('git add CHANGELOG.md');
  }

  function clear () {
    write("\u001b[2J\u001b[0;0H");
  }

  function getNewVersion () {
    header();
    var options = getVersionOptions(oldVersion), key, type, version;
    log('The current version is {{oldVersion.cyan}}.');
    log('');
    log('What should the next version be?');
    for (key in options) { log((+key + 1) + ') ' + options[key].cyan); }
    log('');
    write('Please select a new version: ');
    type = prompt();

    if (options[type - 1]) version = options[type - 1];
    else if (type.match(/^\d+\.\d+\.\d+(-rc\d+)?$/)) version = type;
    else throw new Error('Your entry was invalid.');

    log('');
    log('The new version will be ' + version.cyan + '.');
    write('Is this correct? {{"[yes/no]".cyan}} ');
    return prompt() === 'yes' ? version : getNewVersion();

    function getVersionOptions (version) {
      return version.match(/-rc\d+$/)
          ? [ increment(version, 'rc'),
              increment(version, 'minor') ]
          : [ increment(version, 'patch'),
              increment(version, 'minor'),
              increment(version, 'major') ].map(addRC);

      function increment (versionString, type) {
        var version = parseVersion(versionString);
        if (version.rc) {
          switch (type) {
            case 'minor': version.rc = 0; break;
            case 'rc': version.rc++; break;
          }
        } else {
          version[type]++;
          //-- reset any version numbers lower than the one changed
          switch (type) {
            case 'major': version.minor = 0;
            case 'minor': version.patch = 0;
            case 'patch': version.rc = 0;
          }
        }
        return getVersionString(version);

        function parseVersion (version) {
          var parts = version.split(/\.|\-rc/g);
          return { string: version, major: parts[0], minor: parts[1], patch: parts[2], rc: parts[3] || 0 };
        }

        function getVersionString(version) {
          var str = version.major + '.' + version.minor + '.' + version.patch;
          if (version.rc) str += '-rc' + version.rc;
          return str;
        }
      }

      function addRC (str) {
        return str + '-rc1';
      }
    }
  }

  function tagRelease () {
    pushCmds.push(
        'git tag v{{newVersion}}',
        'git push {{origin}} HEAD',
        'git push --tags'
    );
  }

  function commitChanges () {
    start('Committing changes...');
    exec('git commit -am "release: version {{newVersion}}"');
    done();
    pushCmds.push('git commit --amend --no-edit');
  }

  function cloneRepo (repo) {
    start('Cloning ' + repo.cyan + ' from Github...');
    exec('rm -rf ' + repo);
    exec('git clone https://github.com/angular/' + repo + '.git --depth=1');
    done();
    cleanupCmds.push('rm -rf ' + repo);
  }

  function fill(str) {
    return str.replace(/\{\{[^\}]+\}\}/g, function (match) {
      return eval(match.substr(2, match.length - 4));
    });
  }

  function writeScript (name, cmds) {
    fs.writeFileSync(name, '#!/usr/bin/env bash\n\n' + fill(cmds.join('\n')));
    exec('chmod +x ' + name);
  }

  function updateBowerVersion () {
    start('Updating bower version...');
    var options = { cwd: './bower-material' },
        bower = require(options.cwd + '/bower.json'),
        pkg = require(options.cwd + '/package.json');
    //-- update versions in config files
    bower.version = pkg.version = newVersion;
    fs.writeFileSync(options.cwd + '/package.json', JSON.stringify(pkg, null, 2));
    fs.writeFileSync(options.cwd + '/bower.json', JSON.stringify(bower, null, 2));
    done();
    start('Building bower files...');
    //-- build files for bower
    exec([
      'rm -rf dist',
      'gulp build',
      'gulp build-all-modules --mode=default',
      'gulp build-all-modules --mode=closure',
      'rm -rf dist/demos',
    ]);
    done();
    start('Copy files into bower repo...');
    //-- copy files over to bower repo
    exec([
      'cp -Rf ../dist/* ./',
      'git add -A',
      'git commit -m "release: version {{newVersion}}"',
      'rm -rf ../dist'
    ], options);
    done();
    //-- add steps to push script
    pushCmds.push(
        comment('push to bower (master and tag) and publish to npm'),
        'cd ' + options.cwd,
        'cp ../CHANGELOG.md .',
        'git add CHANGELOG.md',
        'git commit --amend --no-edit',
        'git tag -f v{{newVersion}}',
        'git pull --rebase',
        'git push',
        'git push --tags',
        ( newVersion.indexOf('rc') < 0 ? 'npm publish' : '# skipped npm publish due to RC version' ),
        'cd ..'
    );
  }

  function updateSite () {
    start('Adding new version of the docs site...');
    var options = { cwd: './code.material.angularjs.org' },
        config  = require(options.cwd + '/docs.json');
    config.versions = config.versions.filter(function (version) {
      return version.indexOf('rc') < 0;
    });
    config.versions.unshift(newVersion);
    //-- only set to default if not a release candidate
    if (newVersion.indexOf('rc') < 0) config.latest = newVersion;
    fs.writeFileSync(options.cwd + '/docs.json', JSON.stringify(config, null, 2));
    //-- build files for bower
    exec([
      'rm -rf dist',
      'gulp docs',
      'sed -i \'\' \'s,http:\\/\\/localhost:8080\\/angular-material,https:\\/\\/gitcdn.xyz/repo/angular/bower-material/v{{newVersion}}/angular-material,g\' dist/docs/docs.js'
    ]);

    //-- copy files over to site repo
    exec([
      'rm -rf ./*-rc*',
      'cp -Rf ../dist/docs {{newVersion}}',
      ( newVersion.indexOf('rc') < 0 ? 'rm -rf latest && cp -Rf ../dist/docs latest' : '# skipped latest because this is a release candidate' ),
      'git add -A',
      'git commit -m "release: version {{newVersion}}"',
      'rm -rf ../dist'
    ], options);
    done();
    //-- add steps to push script
    pushCmds.push(
        comment('push the site'),
        'cd ' + options.cwd,
        'git pull --rebase',
        'git push',
        'cd ..'
    );
  }

  function updateMaster () {
    pushCmds.push(
        comment('update package.json in master'),
        'git co master',
        'git pull --rebase {{origin}} master',
        'git checkout release/{{newVersion}} -- CHANGELOG.md',
        'node -e "' + stringifyFunction(buildCommand) + '"',
        'git add CHANGELOG.md',
        'git add package.json',
        'git commit -m "update version number in package.json to {{newVersion}}"',
        'git push'
    );
    function buildCommand () {
      require('fs').writeFileSync('package.json', JSON.stringify(getUpdatedJson(), null, 2));
      function getUpdatedJson () {
        var json = require('./package.json');
        json.version = '{{newVersion}}';
        return json;
      }
    }
    function stringifyFunction (method) {
      return method
          .toString()
          .split('\n')
          .slice(1, -1)
          .map(function (line) { return line.trim(); })
          .join(' ')
          .replace(/"/g, '\\"');
    }
  }

  function header () {
    clear();
    line();
    log(center('Angular Material Release'));
    line();
  }

  function center (msg) {
    msg = ' ' + msg.trim() + ' ';
    var length = msg.length;
    var spaces = Math.floor((lineWidth - length) / 2);
    return Array(spaces + 1).join('-') + msg.green + Array(lineWidth - msg.length - spaces + 1).join('-');
  }

  function done () {
    log('done'.green);
  }

  function exec (cmd, userOptions) {
    if (cmd instanceof Array) {
      return cmd.map(function (cmd) { return exec(cmd, userOptions); });
    }
    try {
      var options = Object.create(defaultOptions);
      for (var key in userOptions) options[key] = userOptions[key];
      return child_process.execSync(fill(cmd) + ' 2> /dev/null', options).trim();
    } catch (err) {
      return err;
    }
  }

  function comment (msg) {
    return '\n# ' + msg + '\n';
  }

  function start (msg) {
    var parsedMsg = fill(msg),
        msgLength = strip(parsedMsg).length,
        diff = lineWidth - 4 - msgLength;
    write(parsedMsg + Array(diff + 1).join(' '));
  }

  function log (msg) {
    msg = msg || '';
    console.log(fill(msg));
  }

  function write (msg) {
    process.stdout.write(fill(msg));
  }

  function line () {
    log(Array(lineWidth + 1).join('-'));
  }
})();
<|endoftext|>


<commit_before>var _view = module.exports = function() {
  var view = {},
      size = [0, 0],
      coordinateSize = [256, 256],
      center = [.5, .5, 0],
      angle = 0,
      angleCos = 1, // Math.cos(angle)
      angleSin = 0, // Math.sin(angle)
      angleCosi = 1, // Math.cos(-angle)
      angleSini = 0; // Math.sin(-angle)

  view.point = function(coordinate) {
    var kc = Math.pow(2, center[2] - (coordinate.length < 3 ? 0 : coordinate[2])),
        dx = (coordinate[0] * kc - center[0]) * coordinateSize[0],
        dy = (coordinate[1] * kc - center[1]) * coordinateSize[1];
    return [
      size[0] / 2 + angleCos * dx - angleSin * dy,
      size[1] / 2 + angleSin * dx + angleCos * dy
    ];
  };

  view.coordinate = function(point) {
    var dx = (point[0] - size[0] / 2);
        dy = (point[1] - size[1] / 2);
    return [
      center[0] + (angleCosi * dx - angleSini * dy) / coordinateSize[0],
      center[1] + (angleSini * dx + angleCosi * dy) / coordinateSize[1],
      center[2]
    ];
  };

  // The number of points in a coordinate at zoom level 0.
  view.coordinateSize = function(x) {
    if (!arguments.length) return coordinateSize;
    coordinateSize = x;
    return view;
  };

  view.size = function(x) {
    if (!arguments.length) return size;
    size = x;
    return view;
  };

  view.center = function(x) {
    if (!arguments.length) return center;
    center = x;
    if (center.length < 3) center[2] = 0;
    return view;
  };

  view.zoom = function(x) {
    if (!arguments.length) return center[2];
    return zoomBy(x - center[2]);
  };

  view.angle = function(x) {
    if (!arguments.length) return angle;
    angle = x;
    angleCos = Math.cos(angle);
    angleSin = Math.sin(angle);
    angleCosi = Math.cos(-angle);
    angleSini = Math.sin(-angle);
    return view;
  };

  view.panBy = function(x) {
    return view.center([
      center[0] - (angleSini * x[1] - angleCosi * x[0]) / coordinateSize[0],
      center[1] - (angleSini * x[0] + angleCosi * x[1]) / coordinateSize[1],
      center[2]
    ]);
  };

  function zoomBy(x) {
    var k = Math.pow(2, x);
    return view.center([
      center[0] * k,
      center[1] * k,
      center[2] + x
    ]);
  }

  view.zoomBy = function(x, point, coordinate) {
    if (arguments.length < 2) return zoomBy(x);

    // compute the coordinate of the center point
    if (arguments.length < 3) coordinate = view.coordinate(point);

    // compute the new point of the coordinate
    var point2 = zoomBy(x).point(coordinate);

    // pan so that the point and coordinate match after zoom
    return view.panBy([point[0] - point2[0], point[1] - point2[1]]);
  };

  view.rotateBy = function(x) {
    return view.angle(angle + x);
  };

  return view;
};
<commit_msg>Fix flip in pan-by x.<commit_after>var _view = module.exports = function() {
  var view = {},
      size = [0, 0],
      coordinateSize = [256, 256],
      center = [.5, .5, 0],
      angle = 0,
      angleCos = 1, // Math.cos(angle)
      angleSin = 0, // Math.sin(angle)
      angleCosi = 1, // Math.cos(-angle)
      angleSini = 0; // Math.sin(-angle)

  view.point = function(coordinate) {
    var kc = Math.pow(2, center[2] - (coordinate.length < 3 ? 0 : coordinate[2])),
        dx = (coordinate[0] * kc - center[0]) * coordinateSize[0],
        dy = (coordinate[1] * kc - center[1]) * coordinateSize[1];
    return [
      size[0] / 2 + angleCos * dx - angleSin * dy,
      size[1] / 2 + angleSin * dx + angleCos * dy
    ];
  };

  view.coordinate = function(point) {
    var dx = (point[0] - size[0] / 2);
        dy = (point[1] - size[1] / 2);
    return [
      center[0] + (angleCosi * dx - angleSini * dy) / coordinateSize[0],
      center[1] + (angleSini * dx + angleCosi * dy) / coordinateSize[1],
      center[2]
    ];
  };

  // The number of points in a coordinate at zoom level 0.
  view.coordinateSize = function(x) {
    if (!arguments.length) return coordinateSize;
    coordinateSize = x;
    return view;
  };

  view.size = function(x) {
    if (!arguments.length) return size;
    size = x;
    return view;
  };

  view.center = function(x) {
    if (!arguments.length) return center;
    center = x;
    if (center.length < 3) center[2] = 0;
    return view;
  };

  view.zoom = function(x) {
    if (!arguments.length) return center[2];
    return zoomBy(x - center[2]);
  };

  view.angle = function(x) {
    if (!arguments.length) return angle;
    angle = x;
    angleCos = Math.cos(angle);
    angleSin = Math.sin(angle);
    angleCosi = Math.cos(-angle);
    angleSini = Math.sin(-angle);
    return view;
  };

  view.panBy = function(x) {
    return view.center([
      center[0] - (angleSini * x[1] + angleCosi * x[0]) / coordinateSize[0],
      center[1] - (angleCosi * x[1] - angleSini * x[0]) / coordinateSize[1],
      center[2]
    ]);
  };

  function zoomBy(x) {
    var k = Math.pow(2, x);
    return view.center([
      center[0] * k,
      center[1] * k,
      center[2] + x
    ]);
  }

  view.zoomBy = function(x, point, coordinate) {
    if (arguments.length < 2) return zoomBy(x);

    // compute the coordinate of the center point
    if (arguments.length < 3) coordinate = view.coordinate(point);

    // compute the new point of the coordinate
    var point2 = zoomBy(x).point(coordinate);

    // pan so that the point and coordinate match after zoom
    return view.panBy([point[0] - point2[0], point[1] - point2[1]]);
  };

  view.rotateBy = function(x) {
    return view.angle(angle + x);
  };

  return view;
};
<|endoftext|>