<commit_before>
Ink.requireModules(['Ink.UI.Carousel_1', 'Ink.UI.Pagination_1', 'Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Selector_1'], function (Carousel, Pagination, InkElement, InkEvent, Selector) {
    'use strict';

    function makeContainer(options) {
        options = options || {}
        var cont = InkElement.create('div', {
            className: 'ink-carousel',
            insertBottom: document.body
        });

        cont.style.width = '100px';

        var stage = cont.appendChild(InkElement.create('ul', {
            'class': 'unstyled stage'
        }));

        if (!options.animation) {
            stage.style.transitionDuration = '0s';
        }

        for (var i = 0; i < 5; i++) {
            var slide = stage.appendChild(InkElement.create('li', {
                'class': options.uneven ? 'slide all-60' : 'slide all-100',
                setTextContent: '' + i
            }));

            slide.style.minHeight = '50px';
        }

        return cont;
    }

    function testCarousel(name, testBack, options) {
        options = options || {};
        test(name, function ()  {
            var container = makeContainer(options);
            var stage = Ink.s('.stage', container);
            var slides = Ink.ss('.stage .slide', container);
            if (pag) {
                options.pagination = pag;
            }
            var carouselComponent = new Carousel(container, options);
            testBack(carouselComponent, container, stage, slides);
        });
    }

    test('(regression) creating a carousel doesn\'t call the onChange callback', function() {
        var onChange = sinon.spy()
        new Carousel(makeContainer(), { onChange: onChange });
        ok(onChange.notCalled, 'onChange not called')

        var onChange = sinon.spy()
        new Carousel(makeContainer(), { initialPage: 2, onChange: onChange });
        ok(onChange.notCalled, 'onChange not called even with an initialPage set')
    })

    module('_setPage()');

    testCarousel('Percentages are used to pull the slides left.', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-100%');
    });

    testCarousel('... even when the slide widths are uneven', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-60%');
    }, { uneven: true });

    var pagElm;
    var pag;
    module('Pagination integration', {
        setup: function () {
            pagElm = InkElement.create('div', { insertBottom: document.body });
            pag = new Pagination(pagElm, { size: 10 });
        },
        teardown: function () {
            pagElm = pag = null;
        }
    });

    testCarousel('Controlling the paginator sets the page on the Carousel', sinon.test(function (carousel) {
        this.stub(carousel, '_setPage');
        pag.setCurrent(2);
        equal(carousel._setPage.lastCall.args[0], 2);
    }));

    if ('ontouchstart' in document) {
        module('Touch');

        testCarousel('You can swipe the carousel sideways to trigger a page change', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 101, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            ok(carousel.setPage.notCalled, 'setPage() not called because finger is not up');
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 1);
        });

        testCarousel('You can swipe the carousel sideways to trigger a page change (part 2: swipe left)', function (carousel, _, stage) {
            carousel.setPage(1);
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 101, 10);
            utils.dispatchTouchEvent(stage, 'end', 101, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 0);
        });

        testCarousel('Smaller swipes don\'t count.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 98, 10);
            utils.dispatchTouchEvent(stage, 'end', 98, 10);
            ok(carousel.setPage.calledWith(0), 'Page hasn\'t changed');
        })

        testCarousel('But if you swipe about 1/3 of the way the page changes.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 66, 10);
            utils.dispatchTouchEvent(stage, 'end', 66, 10);
            ok(carousel.setPage.calledWith(1), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the left of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, '_setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 99, 10);
            utils.dispatchTouchEvent(stage, 'end', 99, 10);
            ok(carousel._setPage.calledOnce, 'Page changed');
            ok(carousel._setPage.calledWith(0), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the right of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, '_setPage');
            utils.dispatchTouchEvent(stage, 'start', 99, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel._setPage.calledOnce, 'Page changed');
            ok(carousel._setPage.calledWith(4), 'Page changed');
        }, { initialPage: 4 })
    }

    function testCarouselRefit(name, cb) {
        testCarousel(name, function (carousel, container, stage, slides) {
            var wrapper = InkElement.create('div', { style: 'width: 100px' });
            container.parentNode.appendChild(wrapper);
            wrapper.appendChild(container);
            cb(carousel, wrapper, container, stage, slides)
        })
    }
    module('refit()');

    testCarouselRefit('refit() won\'t call onChange unnecessarily', function (carousel, wrapper) {
        var onChange = sinon.stub()
        carousel.setOption('onChange', onChange);
        carousel.refit();
        ok(onChange.notCalled)
    });

    module('autoAdvance');

    test('autoAdvance', sinon.test(function () {
        var setTimeout = this.stub(window, 'setTimeout').returns('setTimeout handle');
        var clearTimeout = this.stub(window, 'clearTimeout');
        var carousel = new Carousel(makeContainer(), { autoAdvance: 3000 });
        ok(setTimeout.calledOnce,
            'called setTimeout');
        ok(clearTimeout.notCalled);
        ok(setTimeout.lastCall.args[1] === 3000,
            'setTimeout called with 3000 (from the autoAdvance option');
        setTimeout.reset();

        carousel.stopAutoAdvance();
        ok(clearTimeout.calledOnce,
            'clearTimeout was called');

        carousel.autoAdvance(2000);
        ok(setTimeout.calledOnce);
        ok(setTimeout.lastCall.args[1] === 2000);
    }));
});
<commit_msg>make carousel tests pass in oldIE<commit_after>
Ink.requireModules(['Ink.UI.Carousel_1', 'Ink.UI.Pagination_1', 'Ink.Dom.Element_1', 'Ink.Dom.Event_1', 'Ink.Dom.Selector_1'], function (Carousel, Pagination, InkElement, InkEvent, Selector) {
    'use strict';

    function makeContainer(options) {
        options = options || {}
        var cont = InkElement.create('div', {
            className: 'ink-carousel',
            insertBottom: document.body
        });

        cont.style.width = '100px';

        var stage = cont.appendChild(InkElement.create('ul', {
            'class': 'unstyled stage'
        }));

        if (!options.animation) {
            stage.style.transitionDuration = '0s';
        }

        for (var i = 0; i < 5; i++) {
            var slide = stage.appendChild(InkElement.create('li', {
                'class': options.uneven ? 'slide all-60' : 'slide all-100',
                setTextContent: '' + i
            }));

            slide.style.minHeight = '50px';
        }

        return cont;
    }

    function testCarousel(name, testBack, options) {
        options = options || {};
        test(name, function ()  {
            var container = makeContainer(options);
            var stage = Ink.s('.stage', container);
            var slides = Ink.ss('.stage .slide', container);
            if (pag) {
                options.pagination = pag;
            }
            var carouselComponent = new Carousel(container, options);
            testBack(carouselComponent, container, stage, slides);
        });
    }

    test('(regression) creating a carousel doesn\'t call the onChange callback', function() {
        var onChange = sinon.spy()
        new Carousel(makeContainer(), { onChange: onChange });
        ok(onChange.notCalled, 'onChange not called')

        var onChange = sinon.spy()
        new Carousel(makeContainer(), { initialPage: 2, onChange: onChange });
        ok(onChange.notCalled, 'onChange not called even with an initialPage set')
    })

    module('_setPage()');

    testCarousel('Percentages are used to pull the slides left.', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-100%');
    });

    testCarousel('... even when the slide widths are uneven', function (carousel, _, stage) {
        carousel._setPage(1);
        equal(stage.style.left, '-60%');
    }, { uneven: true });

    var pagElm;
    var pag;
    module('Pagination integration', {
        setup: function () {
            pagElm = InkElement.create('div', { insertBottom: document.body });
            pag = new Pagination(pagElm, { size: 10 });
        },
        teardown: function () {
            pagElm = pag = null;
        }
    });

    testCarousel('Controlling the paginator sets the page on the Carousel', sinon.test(function (carousel) {
        this.stub(carousel, '_setPage');
        pag.setCurrent(2);
        equal(carousel._setPage.lastCall.args[0], 2);
    }));

    if ('ontouchstart' in document) {
        module('Touch');

        testCarousel('You can swipe the carousel sideways to trigger a page change', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 101, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            ok(carousel.setPage.notCalled, 'setPage() not called because finger is not up');
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 1);
        });

        testCarousel('You can swipe the carousel sideways to trigger a page change (part 2: swipe left)', function (carousel, _, stage) {
            carousel.setPage(1);
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 101, 10);
            utils.dispatchTouchEvent(stage, 'end', 101, 10);
            ok(carousel.setPage.calledOnce);
            equal(carousel.setPage.lastCall.args[0], 0);
        });

        testCarousel('Smaller swipes don\'t count.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 98, 10);
            utils.dispatchTouchEvent(stage, 'end', 98, 10);
            ok(carousel.setPage.calledWith(0), 'Page hasn\'t changed');
        })

        testCarousel('But if you swipe about 1/3 of the way the page changes.', function (carousel, _, stage) {
            sinon.stub(carousel, 'setPage');
            utils.dispatchTouchEvent(stage, 'start', 100, 10);
            utils.dispatchTouchEvent(stage, 'move', 66, 10);
            utils.dispatchTouchEvent(stage, 'end', 66, 10);
            ok(carousel.setPage.calledWith(1), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the left of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, '_setPage');
            utils.dispatchTouchEvent(stage, 'start', 1, 10);
            utils.dispatchTouchEvent(stage, 'move', 99, 10);
            utils.dispatchTouchEvent(stage, 'end', 99, 10);
            ok(carousel._setPage.calledOnce, 'Page changed');
            ok(carousel._setPage.calledWith(0), 'Page changed');
        })

        testCarousel('Regression: Page changes even if you swipe off the right of the carousel.', function (carousel, _, stage) {
            sinon.stub(carousel, '_setPage');
            utils.dispatchTouchEvent(stage, 'start', 99, 10);
            utils.dispatchTouchEvent(stage, 'move', 1, 10);
            utils.dispatchTouchEvent(stage, 'end', 1, 10);
            ok(carousel._setPage.calledOnce, 'Page changed');
            ok(carousel._setPage.calledWith(4), 'Page changed');
        }, { initialPage: 4 })
    }

    function testCarouselRefit(name, cb) {
        testCarousel(name, function (carousel, container, stage, slides) {
            var wrapper = InkElement.create('div', { style: 'width: 100px' });
            container.parentNode.appendChild(wrapper);
            wrapper.appendChild(container);
            cb(carousel, wrapper, container, stage, slides)
        })
    }
    module('refit()');

    testCarouselRefit('refit() won\'t call onChange unnecessarily', function (carousel, wrapper) {
        var onChange = sinon.stub()
        carousel.setOption('onChange', onChange);
        carousel.refit();
        ok(onChange.notCalled)
    });

    module('autoAdvance');

    test('autoAdvance', function () {
        var carousel = new Carousel(makeContainer(), { autoAdvance: 3000 });

        equal(typeof carousel._autoAdvanceSto, 'number', '_autoAdvanceSto has a setTimeout handle');

        carousel.stopAutoAdvance();

        equal(carousel._autoAdvanceSto, null, '_autoAdvanceSto is now null');
    });
});
<|endoftext|>


<commit_before>"use strict";
var crc = require("../basic/crc");
var path = require("path");
var fs = require("fs");
var report = require("./report");
var setting = require("./setting");
var getArgs = require('./getArgs');
var strings = require("../basic/strings");
var r21 = "'()*+,-./0123456789:;"
var r29 = "?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[";
var r34 = "]^_`abcdefghijklmnopqrstuvwxyz{|}~";
var memory = require("../efront/memery");
var toComponent = require("./toComponent");
var codetemp = function (delta) {
    var temp = [];
    while (delta > 0) {
        var mode = delta % 84;
        if (mode < 21) {
            temp.push(r21[mode])
        }
        else if (mode < 50) {
            temp.push(r29[mode - 21]);
        }
        else {
            temp.push(r34[mode - 50]);
        }
        delta = delta / 84 | 0;
    }
    return temp;
}
var encrypt = function (text, efrontsign) {
    text = String(text);
    if (!encoded) return text;
    var start = parseInt(efrontsign, 36) % 128;
    var rest = [];
    for (var cx = 0, dx = text.length; cx < dx; cx++) {
        var code = text.charCodeAt(cx);
        var delta = code - start;
        if (delta < 0) {
            delta = -delta;
            if (delta < 5) {
                rest.push(r21[delta])
            } else if (delta < 34) {
                rest.push(r29[delta - 5]);
            } else {
                var temp = codetemp(delta - 33);
                rest.push(r21[10 + temp.length]);
                rest.push.apply(rest, temp);
            }
        }
        else {
            if (delta < 6) {
                rest.push(r21[delta + 5]);
            }
            else if (delta < 40) {
                rest.push(r34[delta - 6]);
            } else {
                var temp = codetemp(delta - 39);
                rest.push(r21[15 + temp.length]);
                rest.push.apply(rest, temp);
            }
        }
        start = code;
    }
    return rest.join("");
}
var encoded = memory.ENCRYPT;
var ReleaseTime = new Date();
ReleaseTime = String(ReleaseTime);
var buildHtml = function (html, code, outsideMain) {
    var isZimoliDetected = false;
    var poweredByComment;
    var html = html.toString()
        .replace(/^\s*(<!doctype[^>]*?>\s*)?<!--([\s\S]*?)--!?>/i, function (_, doctype, message) {
            // `${doctype}<!--${message}\r\n${efrontReloadVersionAttribute}-->`
            poweredByComment = _;
            return "";
        })
        .replace(/<!--[\s\S]*?--!?>/g, "")
        .replace(/<title>(.*?)<\/title>/i, `<title>${memory.TITLE || "$1"}</title>`)
        .replace(/<script\b[\s\S]*?<\/script>\s*/ig, function (script) {
            if (/(["'`])post\1\s*,\s*(['`"])comm\/main\2/i.test(script)) {
                isZimoliDetected = true;
                return "";
            }
            if (/<script\s[^>]*?(type\s*=\s*)?(["']|)efront\-?(?:hook|main|host|script|loader)\1[^>]*?>/i.test(script)) {
                isZimoliDetected = true;
            }
            if (/\b((delete|ignore)oncompile|efrontworker)\b/i.test(script)) {
                return "";
            }
            return script;
        });

    if (isZimoliDetected)
        html = html.replace(/(<\/body>)/i, (_, head) => `\r\n<script compiledinfo="${ReleaseTime} by efront ${require(path.join(__dirname, "../../package.json")).version}"${outsideMain ? ` src="${outsideMain}"` : ''}>${outsideMain ? "" : `\r\n<!--\r\n${code}\r\n-->\r\n`}</script>\r\n${head}`);
    if (memory.IN_WATCH_MODE) {
        let WATCH_PORT = memory.WATCH_PORT;
        let reloadVersion = memory.WATCH_PROJECT_VERSION;
        let efrontReloadVersionAttribute = "efront-reload-version";
        html = html.replace(/(<\/head>)/i, (_, head) => `\r\n<script ${efrontReloadVersionAttribute}=${reloadVersion}>
        -function(){
            "use strict";
            var load = function(url, onload, method){
                var xhr = new XMLHttpRequest;
                xhr.open(method, url);
                xhr.timeout = 0;
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4 && xhr.status === 200) onload(xhr);
                };
                xhr.send("${reloadVersion}");
            };
            var reloader = function(){
                load(location.pathname + "?time=" + +new Date(), function(xhr){
                    var version = xhr.responseText.match(/\\b${efrontReloadVersionAttribute}\\s*=\\s*(\\d+)\\b/);
                    console.info("当前版本:${reloadVersion},目标版本:" + targetVersion + "正在重新加载,..");
                    if( !version || +version[1] !== ${reloadVersion}) location.reload() | console.warn("reload..", new Date);
                    else setTimeout(reloader, 200);
                }, "get");
            };
            var targetVersion = ${reloadVersion};
            var checkUpdate = function(xhr){
                targetVersion = xhr.responseText||xhr.response;
                reloader();
            };
            load("http://localhost${WATCH_PORT ? ":" + WATCH_PORT : ""}/reload/${reloadVersion}", checkUpdate, "post");
        }();
        </script>\r\n${head}`);
    }
    if (poweredByComment) {
        html = html.replace(/^\s*(?:<!doctype[\s\S]*?>)?/i, poweredByComment);
    }
    return html;
};
var mixin = require("../efront/mixin")
var indexnames = memory.webindex;
var getTreeIndex = function (tree) {
    var names = mixin(["/", "@"], indexnames).map(a => a.join(''));
    for (var n of names) {
        if (tree[n]) return tree[n];
    }
};
function toApplication(responseTree) {
    var mainScript = responseTree.main || responseTree["main.js"] || null;
    var indexHtml = getTreeIndex(responseTree);
    if (!indexHtml) {
        var htmlPath = path.join(__dirname, "../../apps", "_index.html");
        indexHtml = {
            time: 0,
            needed: true,
            fullpath: htmlPath,
            data: fs.readFileSync(htmlPath),
            realpath: htmlPath,
            version: fs.statSync(htmlPath).mtime,
            destpath: path.join(indexnames[0]),
        };
        responseTree["/" + indexnames[0]] = indexHtml;
    }
    if (mainScript) {
        var outsideMain = mainScript.version ? "main-" + mainScript.version + ".js" : "";
        Object.keys(responseTree).forEach(function (key) {
            if (/\.(jsp|php|html|asp)$/i.test(key)) {
                var response = responseTree[key];
                if (response && response.data) {
                    response.data = buildHtml(response.data, mainScript.data, outsideMain);
                }
            }
        });
        if (outsideMain) {
            mainScript.destpath = outsideMain;
            responseTree[outsideMain] = mainScript;
        }
    }
    delete responseTree["main"];
    delete responseTree["main.js"];
    return responseTree;
}
var rebuildData = function (responseTree) {
    var keys = Object.keys(responseTree).sort();
    var keysmap = Object.create(null);
    var renmap = Object.create(null);
    keys.forEach(function (k) {
        var o = responseTree[k];
        if (!o.data || !o.destpath || !o.realpath) {
            return;
        }
        var k0 = k.toLowerCase().replace(/\d+$/, '');
        if (k0 in keysmap) {
            keysmap[k0]++;
            k0 = k0 + keysmap[k0];
            if (k0 !== k) renmap[k] = k0;
        } else {
            keysmap[k0] = 1;
        }
    });
    Object.keys(renmap).forEach(k => {
        var o = responseTree[k];
        if (o.type !== '') {
            delete renmap[k];
            console.warn("发现可能被覆盖的路径", o.destpath);
            return;
        }

        var k1 = renmap[k];
        delete responseTree[k];
        responseTree[k1] = o;
        o.name = k1;
        k1 = k1.replace(/^[\s\S]*?([^\/\\]*?)(\.[^\/\\\.]+)?$/, '$1');
        var m = /^([\s\S]*?)[^\/\\]*?(\.[^\/\\\.]+)?$/.exec(o.destpath);
        if (m[1]) {
            o.destpath = m[1] + k1 + m[2];
        }
    });

    Object.keys(responseTree).forEach(function (k) {
        var imageIndex = 0;
        var rep = function (e) {
            if (typeof e !== "string") return String(e);

            return strings.encode(e.replace(/(["`']|)data(\.\w+)\:([\w\+\/\=,;\-\.]+)\1/gi, function (_, quote, ext, data) {
                var match = /^([\w\-\.\/]+;base64)?,([\w\+\/\-\=]+)$/i.exec(data);
                if (!match) return _;
                do {
                    imageIndex++;
                    var name = k + "-" + imageIndex + ext;
                } while (name in responseTree);
                var destpath = response.destpath.replace(/\.[\w]+$/, '') + '-' + imageIndex + ext;
                responseTree[name] = { destpath, data: Buffer.from(match[2], "base64"), realpath: true, url: name };
                return quote + destpath.replace(/\\/g, '/') + quote;
            }));
        };
        var response = responseTree[k];
        if (!isEfrontCode(response)) return;
        var data = String(response.data);

        var { argNames, args, required, dependenceNamesOffset, strs, strend } = getArgs(data);
        if (strs && strs.length > 0) {
            strs = `[${strs.map(rep)}]`;
            data = response.data = data.slice(0, dependenceNamesOffset) + (strs.length * 2).toString(36) + strs + data.slice(strend);
        }
        if (!dependenceNamesOffset) return;
        if (args) args = args.map(a => {
            if (!(a in renmap)) return a;
            return renmap[a];
        });
        var argstr = args.concat(argNames, !required ? [] : required.split(";").map(a => {
            return a.replace(/\.(\w+)$/g, '').replace(/\-(\w)/g, (_, a) => a.toUpperCase());
        }).join(";")).join(",");
        var arglen = (argstr.length << 1).toString(36);
        while (arglen.length < 3) {
            arglen = "0" + arglen;
        }
        response.data = arglen + argstr + data.slice(dependenceNamesOffset);
    });
};
var isEfrontCode = function (response) {
    if (!response) return;
    if (/^[@\\]|^\/.*?\.[^\\\/]+$/.test(response.name) || !response.data) return;
    if (response.type === "@") return;
    return true;
}
module.exports = async function (responseTree) {
    if (encoded) encoded = setting.version_mark;
    rebuildData(responseTree);
    report(responseTree);
    if (!responseTree["main"] && !responseTree["main.js"]) {
        console.warn("在您所编译的项目中没有发现主程序");
        return responseTree;
    }
    var commbuilder = require("../efront/commbuilder");
    var mainScript = responseTree.main || responseTree["main.js"];
    var mainScriptData = mainScript.data;
    var versionTree = {};
    var array_map = responseTree["[]map"] || responseTree["[]map.js"];
    if (setting.is_file_target) {
        commbuilder.ignoreUse_reg = /#decrypt_?\.js/;
        Object.keys(responseTree).sort().forEach(function (k) {
            var v = responseTree[k];
            if (!isEfrontCode(v)) return;
            if (v.name !== "main") {
                versionTree[v.name] = String(v.data);
                delete responseTree[k];
            }
        });
    } else {
        Object.keys(responseTree).sort().forEach(function (k) {
            var v = responseTree[k];
            if (!isEfrontCode(v)) return;
            if (v.name !== "main") {
                v.data = encrypt(v.data, encoded);
                var responseVersion = crc([].map.call(v.data.toString(), e => e.charCodeAt(0))).toString(36) + (+v.data.length).toString(36);
                versionTree[v.name] = responseVersion;
            }
        });
    }
    commbuilder.loadonly = true;
    var mainScriptData = await commbuilder(mainScript.data, "main.js", mainScript.realpath, []);
    commbuilder.loadonly = false;
    var mainVersion = '';
    if (setting.is_file_target) {
        var xTreeName = /(?:\bresponseTree\s*|\[\s*(["'])responseTree\1\s*\])\s*[\:\=]\s*(.+?)\b/m.exec(mainScriptData);
        if (xTreeName) xTreeName = xTreeName[2];
        else xTreeName = "responseTree";
        commbuilder.prepare = false;
    } else {
        var xTreeName = /(?:\bversionTree\s*|\[\s*(["'])versionTree\1\s*\])\s*[\:\=]\s*(.+?)\b/m.exec(mainScriptData);
        if (xTreeName) xTreeName = xTreeName[2];
        else xTreeName = "versionTree";
        mainVersion = true;
    }
    var code = "{\r\n" + Object.keys(versionTree).map(k => `["${k}"]:${strings.encode(versionTree[k])}`).join(",\r\n\t") + "\r\n}";
    var versionVariableName;
    mainScriptData = mainScriptData.toString()
        .replace(/var\s+killCircle[\s\S]*?\}\);?\s*\}\s*\};/, 'var killCircle=function(){};')
        .replace(/(?:\.send|\[\s*(["'])send\1\s*\])\s*\((.*?)\)/g, (match, quote, data) => (versionVariableName = data || "", quote ? `[${quote}send${quote}]()` : ".send()"))
        .replace(/(['"])post\1\s*,\s*(.*?)\s*\)/ig, `$1get$1,$2${versionVariableName && `+"${memory.EXTT}?"+` + versionVariableName})`)
        .replace(
            new RegExp(/\b/.source + xTreeName + /(\s*)=(\s*)\{.*?\}/.source),
            function (m, s1, s2) {
                return xTreeName + `${s1}=${s2}${code}`;
            }
        );
    if (mainVersion) mainScript.version = crc(Buffer.from(mainScriptData)).toString(36).replace(/^\-/, ""), mainScriptData = mainScriptData
        .replace(/(['"`]|)efrontsign\1\s*\:\s*(['"`])\2/, `$1efrontsign$1:$2?${mainScript.version}$2`)
        .replace(/decrypt(\.sign|\[(['"`])sign\1\])/, `parseInt("${encoded}",36)%128`);
    commbuilder.compress = false;
    mainScriptData = await commbuilder(mainScriptData, "main.js", mainScript.realpath, []);
    memory.EXPORT_AS = '';
    memory.EXPORT_TO = "this";
    mainScript.data = toComponent({
        main: { url: "main", destpath: "main", data: mainScriptData },
        "[]map": array_map && {}
    }, true).main.data;
    return toApplication(responseTree);
};<commit_msg>还是放到头部效果好一些<commit_after>"use strict";
var crc = require("../basic/crc");
var path = require("path");
var fs = require("fs");
var report = require("./report");
var setting = require("./setting");
var getArgs = require('./getArgs');
var strings = require("../basic/strings");
var r21 = "'()*+,-./0123456789:;"
var r29 = "?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[";
var r34 = "]^_`abcdefghijklmnopqrstuvwxyz{|}~";
var memory = require("../efront/memery");
var toComponent = require("./toComponent");
var codetemp = function (delta) {
    var temp = [];
    while (delta > 0) {
        var mode = delta % 84;
        if (mode < 21) {
            temp.push(r21[mode])
        }
        else if (mode < 50) {
            temp.push(r29[mode - 21]);
        }
        else {
            temp.push(r34[mode - 50]);
        }
        delta = delta / 84 | 0;
    }
    return temp;
}
var encrypt = function (text, efrontsign) {
    text = String(text);
    if (!encoded) return text;
    var start = parseInt(efrontsign, 36) % 128;
    var rest = [];
    for (var cx = 0, dx = text.length; cx < dx; cx++) {
        var code = text.charCodeAt(cx);
        var delta = code - start;
        if (delta < 0) {
            delta = -delta;
            if (delta < 5) {
                rest.push(r21[delta])
            } else if (delta < 34) {
                rest.push(r29[delta - 5]);
            } else {
                var temp = codetemp(delta - 33);
                rest.push(r21[10 + temp.length]);
                rest.push.apply(rest, temp);
            }
        }
        else {
            if (delta < 6) {
                rest.push(r21[delta + 5]);
            }
            else if (delta < 40) {
                rest.push(r34[delta - 6]);
            } else {
                var temp = codetemp(delta - 39);
                rest.push(r21[15 + temp.length]);
                rest.push.apply(rest, temp);
            }
        }
        start = code;
    }
    return rest.join("");
}
var encoded = memory.ENCRYPT;
var ReleaseTime = new Date();
ReleaseTime = String(ReleaseTime);
var buildHtml = function (html, code, outsideMain) {
    var isZimoliDetected = false;
    var poweredByComment;
    var html = html.toString()
        .replace(/^\s*(<!doctype[^>]*?>\s*)?<!--([\s\S]*?)--!?>/i, function (_, doctype, message) {
            // `${doctype}<!--${message}\r\n${efrontReloadVersionAttribute}-->`
            poweredByComment = _;
            return "";
        })
        .replace(/<!--[\s\S]*?--!?>/g, "")
        .replace(/<title>(.*?)<\/title>/i, `<title>${memory.TITLE || "$1"}</title>`)
        .replace(/<script\b[\s\S]*?<\/script>\s*/ig, function (script) {
            if (/(["'`])post\1\s*,\s*(['`"])comm\/main\2/i.test(script)) {
                isZimoliDetected = true;
                return "";
            }
            if (/<script\s[^>]*?(type\s*=\s*)?(["']|)efront\-?(?:hook|main|host|script|loader)\1[^>]*?>/i.test(script)) {
                isZimoliDetected = true;
            }
            if (/\b((delete|ignore)oncompile|efrontworker)\b/i.test(script)) {
                return "";
            }
            return script;
        });

    if (isZimoliDetected)
        html = html.replace(/(<\/head>)/i, (_, head) => `\r\n<script compiledinfo="${ReleaseTime} by efront ${require(path.join(__dirname, "../../package.json")).version}"${outsideMain ? ` src="${outsideMain}"` : ''}>${outsideMain ? "" : `\r\n<!--\r\n${code}\r\n-->\r\n`}</script>\r\n${head}`);
    if (memory.IN_WATCH_MODE) {
        let WATCH_PORT = memory.WATCH_PORT;
        let reloadVersion = memory.WATCH_PROJECT_VERSION;
        let efrontReloadVersionAttribute = "efront-reload-version";
        html = html.replace(/(<\/head>)/i, (_, head) => `\r\n<script ${efrontReloadVersionAttribute}=${reloadVersion}>
        -function(){
            "use strict";
            var load = function(url, onload, method){
                var xhr = new XMLHttpRequest;
                xhr.open(method, url);
                xhr.timeout = 0;
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4 && xhr.status === 200) onload(xhr);
                };
                xhr.send("${reloadVersion}");
            };
            var reloader = function(){
                load(location.pathname + "?time=" + +new Date(), function(xhr){
                    var version = xhr.responseText.match(/\\b${efrontReloadVersionAttribute}\\s*=\\s*(\\d+)\\b/);
                    console.info("当前版本:${reloadVersion},目标版本:" + targetVersion + "正在重新加载,..");
                    if( !version || +version[1] !== ${reloadVersion}) location.reload() | console.warn("reload..", new Date);
                    else setTimeout(reloader, 200);
                }, "get");
            };
            var targetVersion = ${reloadVersion};
            var checkUpdate = function(xhr){
                targetVersion = xhr.responseText||xhr.response;
                reloader();
            };
            load("http://localhost${WATCH_PORT ? ":" + WATCH_PORT : ""}/reload/${reloadVersion}", checkUpdate, "post");
        }();
        </script>\r\n${head}`);
    }
    if (poweredByComment) {
        html = html.replace(/^\s*(?:<!doctype[\s\S]*?>)?/i, poweredByComment);
    }
    return html;
};
var mixin = require("../efront/mixin")
var indexnames = memory.webindex;
var getTreeIndex = function (tree) {
    var names = mixin(["/", "@"], indexnames).map(a => a.join(''));
    for (var n of names) {
        if (tree[n]) return tree[n];
    }
};
function toApplication(responseTree) {
    var mainScript = responseTree.main || responseTree["main.js"] || null;
    var indexHtml = getTreeIndex(responseTree);
    if (!indexHtml) {
        var htmlPath = path.join(__dirname, "../../apps", "_index.html");
        indexHtml = {
            time: 0,
            needed: true,
            fullpath: htmlPath,
            data: fs.readFileSync(htmlPath),
            realpath: htmlPath,
            version: fs.statSync(htmlPath).mtime,
            destpath: path.join(indexnames[0]),
        };
        responseTree["/" + indexnames[0]] = indexHtml;
    }
    if (mainScript) {
        var outsideMain = mainScript.version ? "main-" + mainScript.version + ".js" : "";
        Object.keys(responseTree).forEach(function (key) {
            if (/\.(jsp|php|html|asp)$/i.test(key)) {
                var response = responseTree[key];
                if (response && response.data) {
                    response.data = buildHtml(response.data, mainScript.data, outsideMain);
                }
            }
        });
        if (outsideMain) {
            mainScript.destpath = outsideMain;
            responseTree[outsideMain] = mainScript;
        }
    }
    delete responseTree["main"];
    delete responseTree["main.js"];
    return responseTree;
}
var rebuildData = function (responseTree) {
    var keys = Object.keys(responseTree).sort();
    var keysmap = Object.create(null);
    var renmap = Object.create(null);
    keys.forEach(function (k) {
        var o = responseTree[k];
        if (!o.data || !o.destpath || !o.realpath) {
            return;
        }
        var k0 = k.toLowerCase().replace(/\d+$/, '');
        if (k0 in keysmap) {
            keysmap[k0]++;
            k0 = k0 + keysmap[k0];
            if (k0 !== k) renmap[k] = k0;
        } else {
            keysmap[k0] = 1;
        }
    });
    Object.keys(renmap).forEach(k => {
        var o = responseTree[k];
        if (o.type !== '') {
            delete renmap[k];
            console.warn("发现可能被覆盖的路径", o.destpath);
            return;
        }

        var k1 = renmap[k];
        delete responseTree[k];
        responseTree[k1] = o;
        o.name = k1;
        k1 = k1.replace(/^[\s\S]*?([^\/\\]*?)(\.[^\/\\\.]+)?$/, '$1');
        var m = /^([\s\S]*?)[^\/\\]*?(\.[^\/\\\.]+)?$/.exec(o.destpath);
        if (m[1]) {
            o.destpath = m[1] + k1 + m[2];
        }
    });

    Object.keys(responseTree).forEach(function (k) {
        var imageIndex = 0;
        var rep = function (e) {
            if (typeof e !== "string") return String(e);

            return strings.encode(e.replace(/(["`']|)data(\.\w+)\:([\w\+\/\=,;\-\.]+)\1/gi, function (_, quote, ext, data) {
                var match = /^([\w\-\.\/]+;base64)?,([\w\+\/\-\=]+)$/i.exec(data);
                if (!match) return _;
                do {
                    imageIndex++;
                    var name = k + "-" + imageIndex + ext;
                } while (name in responseTree);
                var destpath = response.destpath.replace(/\.[\w]+$/, '') + '-' + imageIndex + ext;
                responseTree[name] = { destpath, data: Buffer.from(match[2], "base64"), realpath: true, url: name };
                return quote + destpath.replace(/\\/g, '/') + quote;
            }));
        };
        var response = responseTree[k];
        if (!isEfrontCode(response)) return;
        var data = String(response.data);

        var { argNames, args, required, dependenceNamesOffset, strs, strend } = getArgs(data);
        if (strs && strs.length > 0) {
            strs = `[${strs.map(rep)}]`;
            data = response.data = data.slice(0, dependenceNamesOffset) + (strs.length * 2).toString(36) + strs + data.slice(strend);
        }
        if (!dependenceNamesOffset) return;
        if (args) args = args.map(a => {
            if (!(a in renmap)) return a;
            return renmap[a];
        });
        var argstr = args.concat(argNames, !required ? [] : required.split(";").map(a => {
            return a.replace(/\.(\w+)$/g, '').replace(/\-(\w)/g, (_, a) => a.toUpperCase());
        }).join(";")).join(",");
        var arglen = (argstr.length << 1).toString(36);
        while (arglen.length < 3) {
            arglen = "0" + arglen;
        }
        response.data = arglen + argstr + data.slice(dependenceNamesOffset);
    });
};
var isEfrontCode = function (response) {
    if (!response) return;
    if (/^[@\\]|^\/.*?\.[^\\\/]+$/.test(response.name) || !response.data) return;
    if (response.type === "@") return;
    return true;
}
module.exports = async function (responseTree) {
    if (encoded) encoded = setting.version_mark;
    rebuildData(responseTree);
    report(responseTree);
    if (!responseTree["main"] && !responseTree["main.js"]) {
        console.warn("在您所编译的项目中没有发现主程序");
        return responseTree;
    }
    var commbuilder = require("../efront/commbuilder");
    var mainScript = responseTree.main || responseTree["main.js"];
    var mainScriptData = mainScript.data;
    var versionTree = {};
    var array_map = responseTree["[]map"] || responseTree["[]map.js"];
    if (setting.is_file_target) {
        commbuilder.ignoreUse_reg = /#decrypt_?\.js/;
        Object.keys(responseTree).sort().forEach(function (k) {
            var v = responseTree[k];
            if (!isEfrontCode(v)) return;
            if (v.name !== "main") {
                versionTree[v.name] = String(v.data);
                delete responseTree[k];
            }
        });
    } else {
        Object.keys(responseTree).sort().forEach(function (k) {
            var v = responseTree[k];
            if (!isEfrontCode(v)) return;
            if (v.name !== "main") {
                v.data = encrypt(v.data, encoded);
                var responseVersion = crc([].map.call(v.data.toString(), e => e.charCodeAt(0))).toString(36) + (+v.data.length).toString(36);
                versionTree[v.name] = responseVersion;
            }
        });
    }
    commbuilder.loadonly = true;
    var mainScriptData = await commbuilder(mainScript.data, "main.js", mainScript.realpath, []);
    commbuilder.loadonly = false;
    var mainVersion = '';
    if (setting.is_file_target) {
        var xTreeName = /(?:\bresponseTree\s*|\[\s*(["'])responseTree\1\s*\])\s*[\:\=]\s*(.+?)\b/m.exec(mainScriptData);
        if (xTreeName) xTreeName = xTreeName[2];
        else xTreeName = "responseTree";
        commbuilder.prepare = false;
    } else {
        var xTreeName = /(?:\bversionTree\s*|\[\s*(["'])versionTree\1\s*\])\s*[\:\=]\s*(.+?)\b/m.exec(mainScriptData);
        if (xTreeName) xTreeName = xTreeName[2];
        else xTreeName = "versionTree";
        mainVersion = true;
    }
    var code = "{\r\n" + Object.keys(versionTree).map(k => `["${k}"]:${strings.encode(versionTree[k])}`).join(",\r\n\t") + "\r\n}";
    var versionVariableName;
    mainScriptData = mainScriptData.toString()
        .replace(/var\s+killCircle[\s\S]*?\}\);?\s*\}\s*\};/, 'var killCircle=function(){};')
        .replace(/(?:\.send|\[\s*(["'])send\1\s*\])\s*\((.*?)\)/g, (match, quote, data) => (versionVariableName = data || "", quote ? `[${quote}send${quote}]()` : ".send()"))
        .replace(/(['"])post\1\s*,\s*(.*?)\s*\)/ig, `$1get$1,$2${versionVariableName && `+"${memory.EXTT}?"+` + versionVariableName})`)
        .replace(
            new RegExp(/\b/.source + xTreeName + /(\s*)=(\s*)\{.*?\}/.source),
            function (m, s1, s2) {
                return xTreeName + `${s1}=${s2}${code}`;
            }
        );
    if (mainVersion) mainScript.version = crc(Buffer.from(mainScriptData)).toString(36).replace(/^\-/, ""), mainScriptData = mainScriptData
        .replace(/(['"`]|)efrontsign\1\s*\:\s*(['"`])\2/, `$1efrontsign$1:$2?${mainScript.version}$2`)
        .replace(/decrypt(\.sign|\[(['"`])sign\1\])/, `parseInt("${encoded}",36)%128`);
    commbuilder.compress = false;
    mainScriptData = await commbuilder(mainScriptData, "main.js", mainScript.realpath, []);
    memory.EXPORT_AS = '';
    memory.EXPORT_TO = "this";
    mainScript.data = toComponent({
        main: { url: "main", destpath: "main", data: mainScriptData },
        "[]map": array_map && {}
    }, true).main.data;
    return toApplication(responseTree);
};<|endoftext|>


<commit_before>/**
 * @author jonobr1 / http://jonobr1.com/
 * Dependent on Three.js and underscore.js
 */

(function() {

  var root = this;
  var objects = [];

  var twos = [];

  /**
   * Constants
   */
  var PI = Math.PI,
    TWO_PI = Math.PI * 2.0,
    HALF_PI = Math.PI * 0.5;

  /**
   * Cross browser events.
   */
  var dom = {

    hasEventListeners: _.isFunction(document.body.addEventListener),

    bind: function(elem, event, func, bool) {
      if (this.hasEventListeners) {
        elem.addEventListener(event, func, !!bool);
      } else {
        elem.attachEvent('on' + event, func);
      }
      return this;
    },

    unbind: function(elem, event, func, bool) {
      if (this.hasEventListeners) {
        elem.removeEventListeners(event, func, !!bool);
      } else {
        elem.detachEvent('on' + event, func);
      }
      return this;
    }

  };

  /**
   * Two.js is a two-dimensional drawing api built on top of Three.js
   * meant for the browser.
   *
   * @class
   */
  var Two = function(params) {

    var params = _.defaults(params || {}, {
      type: Two.TYPES.webgl,
      autoplay: true,
      width: 640,
      height: 480,
      fullscreen: false
    });

    this.__playing = params.autoplay;

    this.scene = new THREE.Scene();
    this.camera = new THREE.OrthographicCamera(0, params.width, 0, params.height, 0);

    this.scene.add(this.camera);

    var canvas = document.createElement('canvas');
    if (params.types === Two.TYPES.webgl
      && canvas.getContext('webgl')
      || canvas.getContext('experimental-webgl')) {

      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        canvas: canvas
      });
      params.type = Two.TYPES.webgl;

    } else {

      this.renderer = new THREE.CanvasRenderer({
        canvas: canvas
      });
      params.type = Two.TYPES.canvas2d;

    }

    this.type = params.type;

    if (params.fullscreen) {

      var fitted = _.bind(fitToWindow, this);

      _.extend(document.body.style, {
        overflow: 'hidden',
        margin: 0,
        padding: 0,
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        position: 'fixed'
      });
      dom.bind(window, 'resize', fitted);
      fitted();

    } else {

      this.renderer.setSize(params.width, params.height);
      this.width = params.width;
      this.height = params.height;

    }

    twos.push(this);

  };

  _.extend(Two.prototype, {

    /**
     * DOM
     */

    appendTo: function(elem) {

      if (!_.isElement(elem)) {
        return this;
      }

      elem.appendChild(this.renderer.domElement);

      return this;

    },

    onResize: function(func) {

      this.__onResize = func;
      return this;

    },

    /**
     * Controls
     */

    play: function() {

      this.__playing = true;
      return this;

    },

    pause: function() {

      this.__playing = false;
      return this;

    },

    /**
     * Rendering
     */

    render: function() {

      if (!this.__playing) {
        return this;
      }

      _.each(objects, function(object) {
        if (_.isFunction(object.__onUpdate)) {
          object.__onUpdate();
        }
      });

      this.renderer.render(this.scene, this.camera);
      return this;

    },

    addRectangle: function(x, y, width, height) {

      var rect = new Two.Rectangle(x, y, width, height);
      this.scene.add(rect.mesh);
      return rect;

    },

    addCircle: function(x, y, radius) {

      var circle = new Two.Circle(x, y, radius);
      this.scene.add(circle.mesh);
      return circle;

    },

    addLine: function(x1, y1, x2, y2) {

      var line = new Two.Line(x1, y1, x2, y2);
      this.scene.add(line.mesh);
      return line;

    },

    addPolygon: function() {

      var points = [];
      for (var i = 0, l = arguments.length; i < l; i+=2) {
        points.push(new THREE.Vector3(arguments[i], arguments[i + 1], 0));
      }

      var poly = new Two.Polygon(points);
      this.scene.add(poly.mesh);
      return poly;

    }

  });

  _.extend(Two, {

    VERSION: 0.1,

    TYPES: {
      webgl: 'webgl',
      canvas2d: 'canvas2d'
    },

    RESOLUTION: 32,

    /**
     *
     */
    Rectangle: function(x, y, width, height) {

      this.geometry = new THREE.CubeGeometry(width, height, 0);
      this.material = new THREE.MeshBasicMaterial({ color: 0x000000 });
      this.mesh = new THREE.Mesh(this.geometry, this.material);

      this.mesh.position.x = x;
      this.mesh.position.y = y;

      var half_width = width / 2;
      var half_height = height / 2;

      this.outline = new Two.Line(
        - half_width, - half_height,
          half_width, - half_height,
          half_width,   half_height,
        - half_width,   half_height,
        - half_width, - half_height
      );
      this.mesh.add(this.outline.mesh);

      objects.push(this);

    },

    /**
     *
     */
    Circle: function(x, y, radius) {

      this.geometry = new THREE.CylinderGeometry(radius, radius, 0, Two.RESOLUTION);
      this.material = new THREE.MeshBasicMaterial({ color: 0x000000 });
      this.mesh = new THREE.Mesh(this.geometry, this.material);

      this.mesh.position.x = x;
      this.mesh.position.y = y;
      this.mesh.rotation.x = HALF_PI;

      var points = _.map(_.range(Two.RESOLUTION), function(i) {
        var p = (i / Two.RESOLUTION);
        var a = TWO_PI * p;
        return new THREE.Vector3(radius * Math.cos(a) - radius / 2, 0, radius * Math.sin(a));
      });

      points.push(points[0]);

      this.outline = new Two.Line(points);
      this.mesh.add(this.outline.mesh);

      objects.push(this);

    },
    /**
     *
     */
    Line: function(x1, y1, x2, y2) {

      var points = [];
      if (_.isArray(x1)) {
        points = x1;
        x1 = points[0].x;
        y1 = points[0].y;
      } else {
        for (var i = 0, l = arguments.length; i < l; i+=2) {
          points.push(new THREE.Vector3(arguments[i] - x1, arguments[i + 1] - y1, 0));
        }
      }

      this.geometry = new THREE.Geometry();
      this.geometry.vertices = points;

      this.material = new THREE.LineBasicMaterial({ color: 0x000000 });
      this.mesh = new THREE.Line(this.geometry, this.material);

      this.mesh.position.x = x1;
      this.mesh.position.y = y1;

      objects.push(this);

    },

    Polygon: function(points) {

      var first = points[0].clone();

      _.each(points, function(p) {
        p.subSelf(first);
      });

      var shape = new THREE.Shape(points);
      this.geometry = new THREE.ExtrudeGeometry(shape, { amount: 0 });
      this.material = new THREE.MeshBasicMaterial({ color: 0x000000 });

      this.mesh = new THREE.Mesh(this.geometry, this.material);
      this.mesh.rotation.x = PI;
      this.mesh.rotation.z = -HALF_PI;
      this.mesh.position.x = first.x;
      this.mesh.position.y = first.y;

      points.push(points[0]);

      this.outline = new Two.Line(points);
      this.mesh.add(this.outline.mesh);

      objects.push(this);

    }

  });

  var ShapeProto = {

    update: function(func) {
      this.__onUpdate = func;
      return this;
    },

    scale: function(amt) {

      this.mesh.scale.x = amt;
      this.mesh.scale.y = amt;
      return this;

    },

    rotate: function(radians) {

      this.mesh.rotation.z = radians;
      return this;

    },

    position: function(x, y) {

      this.mesh.position.x = x;
      this.mesh.position.y = y;

      return this;

    }

  };

  /**
   * Prototype for all objects have fill-like material
   * Two.Rectangle
   * Two.Circle
   * TWo.Polygon
   */
  var FillProto = {

    fill: function(r, g, b, a) {
      var length = arguments.length;
      if (length <= 1) {
        g = b = r;
        a = 1.0;
      } else if (length <= 3) {
        a = 1.0;
      }
      this.material.color.setRGB(r, g, b);
      this.material.opacity = a;
      return this;
    }

  };

  /**
   * Prototype for all objects that have stroke-like material
   * Two.Line
   */
  var StrokeProto = {

    stroke: function(r, g, b, a) {
      var length = arguments.length;
      if (length <= 1) {
        g = b = r;
        a = 1.0;
      } else if (length <= 3) {
        a = 1.0;
      }
      if (_.isObject(this.outline)) {
        this.outline.material.color.setRGB(r, g, b);
        this.outline.material.opacity = a;
      } else {
        this.material.color.setRGB(r, g, b);
        this.material.opacity = a;
      }
      return this;
    },

    strokeWeight: function(n) {
      if (_.isObject(this.outline)) {
        this.outline.material.linewidth = n;
      } else {
        this.material.linewidth = n;
      }
      return this;
    }

  };

  /**
   * Prototype for Two.Polygon
   */
  var PolyProto = {

  };

  _.extend(Two.Rectangle.prototype, ShapeProto, FillProto, StrokeProto);
  _.extend(Two.Circle.prototype, ShapeProto, FillProto, StrokeProto);
  _.extend(Two.Polygon.prototype, ShapeProto, FillProto, StrokeProto);
  _.extend(Two.Line.prototype, ShapeProto, StrokeProto);

  function fitToWindow() {

    var wr = document.body.getBoundingClientRect();

    var width = this.width = wr.width;
    var height = this.height = wr.height;

    this.renderer.setSize(width, height);

    this.camera.top = 0;
    this.camera.left = 0;
    this.camera.right = width;
    this.camera.bottom = height;

    this.camera.updateProjectionMatrix();

    if (_.isFunction(this.__onResize)) {
      this.__onResize(width, height);
    }

  }

  function loop() {
    _.each(twos, function(two) {
      two.render();
    });
    requestAnimationFrame(loop);
  }

  loop();

  /**
   * Export
   */
  root['Two'] = Two;

})();
<commit_msg>making moves to have only Two.Polygon.<commit_after>/**
 * @author jonobr1 / http://jonobr1.com/
 * Dependent on Three.js and underscore.js
 */

(function() {

  var root = this;
  var objects = [];

  var twos = [];

  /**
   * Constants
   */
  var PI = Math.PI,
    TWO_PI = Math.PI * 2.0,
    HALF_PI = Math.PI * 0.5;

  /**
   * Cross browser events.
   */
  var dom = {

    hasEventListeners: _.isFunction(document.body.addEventListener),

    bind: function(elem, event, func, bool) {
      if (this.hasEventListeners) {
        elem.addEventListener(event, func, !!bool);
      } else {
        elem.attachEvent('on' + event, func);
      }
      return this;
    },

    unbind: function(elem, event, func, bool) {
      if (this.hasEventListeners) {
        elem.removeEventListeners(event, func, !!bool);
      } else {
        elem.detachEvent('on' + event, func);
      }
      return this;
    }

  };

  /**
   * Two.js is a two-dimensional drawing api built on top of Three.js
   * meant for the browser.
   *
   * @class
   */
  var Two = function(params) {

    var params = _.defaults(params || {}, {
      type: Two.TYPES.webgl,
      autoplay: true,
      width: 640,
      height: 480,
      fullscreen: false
    });

    this.__playing = params.autoplay;

    this.scene = new THREE.Scene();
    this.camera = new THREE.OrthographicCamera(0, params.width, 0, params.height, 0);

    this.scene.add(this.camera);

    var canvas = document.createElement('canvas');
    if (params.types === Two.TYPES.webgl
      && canvas.getContext('webgl')
      || canvas.getContext('experimental-webgl')) {

      this.renderer = new THREE.WebGLRenderer({
        // antialias: true,
        canvas: canvas
      });
      params.type = Two.TYPES.webgl;

    } else {

      this.renderer = new THREE.CanvasRenderer({
        canvas: canvas
      });
      params.type = Two.TYPES.canvas2d;

    }

    this.type = params.type;

    if (params.fullscreen) {

      var fitted = _.bind(fitToWindow, this);

      _.extend(document.body.style, {
        overflow: 'hidden',
        margin: 0,
        padding: 0,
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        position: 'fixed'
      });
      dom.bind(window, 'resize', fitted);
      fitted();

    } else {

      this.renderer.setSize(params.width, params.height);
      this.width = params.width;
      this.height = params.height;

    }

    twos.push(this);

  };

  _.extend(Two.prototype, {

    /**
     * DOM
     */

    appendTo: function(elem) {

      if (!_.isElement(elem)) {
        return this;
      }

      elem.appendChild(this.renderer.domElement);

      return this;

    },

    onResize: function(func) {

      this.__onResize = func;
      return this;

    },

    /**
     * Controls
     */

    play: function() {

      this.__playing = true;
      return this;

    },

    pause: function() {

      this.__playing = false;
      return this;

    },

    /**
     * Rendering
     */

    render: function() {

      if (!this.__playing) {
        return this;
      }

      _.each(objects, function(object) {
        if (_.isFunction(object.__onUpdate)) {
          object.__onUpdate();
        }
      });

      this.renderer.render(this.scene, this.camera);
      return this;

    },

    addRectangle: function(x, y, width, height) {

      var rect = new Two.Rectangle(x, y, width, height);
      this.scene.add(rect.mesh);
      return rect;

    },

    addCircle: function(x, y, radius) {

      var circle = new Two.Circle(x, y, radius);
      this.scene.add(circle.mesh);
      return circle;

    },

    addLine: function(x1, y1, x2, y2) {

      var line = new Two.Line(x1, y1, x2, y2);
      this.scene.add(line.mesh);
      return line;

    },

    addPolygon: function() {

      var points = [];
      for (var i = 0, l = arguments.length; i < l; i+=2) {
        points.push(new THREE.Vector3(arguments[i], arguments[i + 1], 0));
      }

      var poly = new Two.Polygon(points);
      this.scene.add(poly.mesh);
      return poly;

    }

  });

  _.extend(Two, {

    VERSION: 0.1,

    TYPES: {
      webgl: 'webgl',
      canvas2d: 'canvas2d'
    },

    RESOLUTION: 32,

    /**
     *
     */
    Rectangle: function(x, y, width, height) {

      var hw = width / 2;
      var hh = height / 2;

      var a = x - hw;
      var b = y - hh;
      var c = x + hw;
      var d = y + hh;

      Two.Polygon.call(this, [
        new THREE.Vector3(a, b, 0),
        new THREE.Vector3(c, b, 0),
        new THREE.Vector3(c, d, 0),
        new THREE.Vector3(a, d, 0)
      ]);

    },

    /**
     *
     */
    Circle: function(x, y, radius) {

      var resolution = Two.RESOLUTION;

      Two.Polygon.call(this, _.map(_.range(resolution), function(i) {
        var pct = i / resolution;
        var angle = TWO_PI * pct;
        var xpos = radius * Math.cos(angle) + x;
        var ypos = radius * Math.sin(angle) + y;
        return new THREE.Vector3(xpos, ypos, 0);
      }));

    },
    /**
     *
     */
    Line: function(x1, y1, x2, y2) {

      var points = [];
      if (_.isArray(x1)) {
        points = x1;
        x1 = points[0].x;
        y1 = points[0].y;
      } else {
        for (var i = 0, l = arguments.length; i < l; i+=2) {
          points.push(new THREE.Vector3(arguments[i] - x1, arguments[i + 1] - y1, 0));
        }
      }

      this.geometry = new THREE.Geometry();
      this.geometry.vertices = points;

      this.material = new THREE.LineBasicMaterial({ color: 0x000000 });
      this.mesh = new THREE.Line(this.geometry, this.material);

      this.mesh.position.x = x1;
      this.mesh.position.y = y1;

      objects.push(this);

    },

    Polygon: function(points) {

      var first = points[0].clone();

      var shape = new THREE.Shape();

      _.each(points, function(p, i) {
        p.subSelf(first);
        if (i === 0) {
          shape.moveTo(p.x, p.y);
        } else {
          shape.lineTo(p.x, p.y);
        }
      });

      if (!_.isEqual(points[0], points[points.length - 1])) {
        points.push(points[0]);
      }

      // var bb = shape.getBoundingBox();
      //
      // var center = new THREE.Shape();
      //
      // _.each(points, function(p, i) {
      //   p.x -= bb.centroid.x;
      //   p.y -= bb.centroid.y;
      //   if (i === 0) {
      //     center.moveTo(p.x, p.y);
      //   } else {
      //     center.lineTo(p.x, p.y);
      //   }
      // });

      this.geometry = new THREE.ExtrudeGeometry(shape, { amount: 10 });
      this.material = new THREE.MeshBasicMaterial({ color: 0x000000 });

      this.mesh = new THREE.Mesh(this.geometry, this.material);
      this.mesh.rotation.x = PI;
      this.mesh.rotation.z = -HALF_PI;
      this.mesh.position.x = first.x;
      this.mesh.position.y = first.y;

      this.outline = new Two.Line(points);
      this.mesh.add(this.outline.mesh);

      objects.push(this);

    }

  });

  var ShapeProto = {

    update: function(func) {
      this.__onUpdate = func;
      return this;
    },

    scale: function(amt) {

      this.mesh.scale.x = amt;
      this.mesh.scale.y = amt;
      return this;

    },

    rotate: function(radians) {

      this.mesh.rotation.z = radians;
      return this;

    },

    position: function(x, y) {

      this.mesh.position.x = x;
      this.mesh.position.y = y;

      return this;

    }

  };

  /**
   * Prototype for all objects have fill-like material
   * Two.Rectangle
   * Two.Circle
   * TWo.Polygon
   */
  var FillProto = {

    fill: function(r, g, b, a) {
      var length = arguments.length;
      if (length <= 1) {
        g = b = r;
        a = 1.0;
      } else if (length <= 3) {
        a = 1.0;
      }
      this.material.color.setRGB(r, g, b);
      this.material.opacity = a;
      return this;
    }

  };

  /**
   * Prototype for all objects that have stroke-like material
   * Two.Line
   */
  var StrokeProto = {

    stroke: function(r, g, b, a) {
      var length = arguments.length;
      if (length <= 1) {
        g = b = r;
        a = 1.0;
      } else if (length <= 3) {
        a = 1.0;
      }
      if (_.isObject(this.outline)) {
        this.outline.material.color.setRGB(r, g, b);
        this.outline.material.opacity = a;
      } else {
        this.material.color.setRGB(r, g, b);
        this.material.opacity = a;
      }
      return this;
    },

    strokeWeight: function(n) {
      if (_.isObject(this.outline)) {
        this.outline.material.linewidth = n;
      } else {
        this.material.linewidth = n;
      }
      return this;
    }

  };

  /**
   * Prototype for Two.Polygon
   */
  var PolyProto = {

  };

  _.extend(Two.Rectangle.prototype, ShapeProto, FillProto, StrokeProto);
  _.extend(Two.Circle.prototype, ShapeProto, FillProto, StrokeProto);
  _.extend(Two.Polygon.prototype, ShapeProto, FillProto, StrokeProto);
  _.extend(Two.Line.prototype, ShapeProto, StrokeProto);

  function fitToWindow() {

    var wr = document.body.getBoundingClientRect();

    var width = this.width = wr.width;
    var height = this.height = wr.height;

    this.renderer.setSize(width, height);

    this.camera.top = 0;
    this.camera.left = 0;
    this.camera.right = width;
    this.camera.bottom = height;

    this.camera.updateProjectionMatrix();

    if (_.isFunction(this.__onResize)) {
      this.__onResize(width, height);
    }

  }

  function loop() {
    _.each(twos, function(two) {
      two.render();
    });
    requestAnimationFrame(loop);
  }

  loop();

  /**
   * Export
   */
  root['Two'] = Two;

})();
<|endoftext|>


<commit_before>javascript: (async function() {
	var api = new mw.Api();
	var textbox = document.getElementById('wpTextbox1');

	var myPrefix = textbox.value.substring(0, textbox.selectionStart);
	var selectedText = textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
	var mySuffix = textbox.value.substring(textbox.selectionEnd);
	const RVLIMIT = 50;

	var username = '',
		timestamp = '';
	if (selectedText) {
		await api
			.get({
				action: 'query',
				format: 'json',
				prop: 'revisions',
				titles: mw.config.get('wgPageName'),
				utf8: 1,
				formatversion: '2',
				rvprop: 'ids|timestamp|user|content',
				rvslots: 'main',
				rvlimit: RVLIMIT,
			})
			.done(function(res) {
				var found = false;
				var revisions = res.query.pages[0].revisions;
				if (revisions[revisions.length - 1].slots.main.content.includes(selectedText)) {
					mw.notify('The text existed before ' + revisions.length + ' revisions', { type: 'error' });
					return;
				}
				for (let i = revisions.length - 2; i >= 0; i--) {
					if (revisions[i].slots.main.content.includes(selectedText)) {
						console.log(revisions[i])
						found = true;
						username = revisions[i].user;
						timestamp = revisions[i].timestamp;
						break;
					}
				}
				if (found) {
					mw.notify('Added by ' + username + ' at ' + timestamp);
				} else {
					mw.notify('The text is not found', { type: 'error' });
				}
			});
	}

	if (!username) {
		username = prompt('Username');
	}
	if (username !== '' && username !== null) {
		username = username.trim();
		textbox.value =
			myPrefix +
			selectedText +
			'{{subst:unsigned|' +
			username +
			'|' +
			new Morebits.date(timestamp).format('YYYY年M月D日 (ddd) HH:mm', 'utc') +
			' (UTC)}}' +
			mySuffix;
		var summary = '[[Template:Unsigned|補簽名]]';
		if (mw.util.isIPAddress(username)) {
			summary +=
				'[[User:' +
				username +
				'|' +
				username +
				']]（[[User talk:' +
				username +
				'|對話]]｜[[Special:Contributions/' +
				username +
				'|貢獻]]）';
		} else {
			summary += '[[Special:Contributions/' + username + '|' + username + ']]（[[User talk:' + username + '|留言]]）';
		}
		document.getElementById('wpSummary').value = summary;
		document.getElementById('wpMinoredit').click();

		var finish = function() {
			if (confirm('Save?')) {
				document.getElementById('wpSave').click();
			}
		};

		if (confirm('Notify ' + username + '?')) {
			var talkmessage = '== 您忘了簽名 ==\n{{subst:Uw-tilde|' + mw.config.get('wgPageName') + '}}--~~~~';
			var talksummary = '單層級通知：沒有在討論頁上簽名，於[[' + mw.config.get('wgPageName') + ']]';
			var talkpage = 'User_talk:' + username;

			api
				.edit(talkpage, function(revision) {
					return {
						text: revision.content + '\n\n' + talkmessage,
						summary: talksummary,
						nocreate: false,
					};
				})
				.then(
					function() {
						mw.notify('已通知');
						finish();
					},
					function(e) {
						if (e == 'nocreate-missing') {
							api
								.create(
									talkpage,
									{
										summary: talksummary,
									},
									talkmessage
								)
								.then(
									function() {
										mw.notify('已通知');
										finish();
									},
									function(e) {
										mw.notify('建立討論頁發生錯誤：' + e);
										finish();
									}
								);
						} else {
							mw.notify('編輯討論頁發生錯誤：' + e);
							finish();
						}
					}
				);
		} else {
			finish();
		}
	}
})();
<commit_msg>Add revid to summary<commit_after>javascript: (async function() {
	var api = new mw.Api();
	var textbox = document.getElementById('wpTextbox1');

	var myPrefix = textbox.value.substring(0, textbox.selectionStart);
	var selectedText = textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
	var mySuffix = textbox.value.substring(textbox.selectionEnd);
	const RVLIMIT = 50;

	var username = '',
		timestamp = '',
		revid;
	if (selectedText) {
		await api
			.get({
				action: 'query',
				format: 'json',
				prop: 'revisions',
				titles: mw.config.get('wgPageName'),
				utf8: 1,
				formatversion: '2',
				rvprop: 'ids|timestamp|user|content',
				rvslots: 'main',
				rvlimit: RVLIMIT,
			})
			.done(function(res) {
				var found = false;
				var revisions = res.query.pages[0].revisions;
				if (revisions[revisions.length - 1].slots.main.content.includes(selectedText)) {
					mw.notify('The text existed before ' + revisions.length + ' revisions', { type: 'error' });
					return;
				}
				for (let i = revisions.length - 2; i >= 0; i--) {
					if (revisions[i].slots.main.content.includes(selectedText)) {
						found = true;
						username = revisions[i].user;
						timestamp = revisions[i].timestamp;
						revid = revisions[i].revid;
						break;
					}
				}
				if (found) {
					mw.notify('Added by ' + username + ' at ' + timestamp);
				} else {
					mw.notify('The text is not found', { type: 'error' });
				}
			});
	}

	if (!username) {
		username = prompt('Username');
	}
	if (username !== '' && username !== null) {
		username = username.trim();
		textbox.value =
			myPrefix +
			selectedText +
			'{{subst:unsigned|' +
			username +
			'|' +
			new Morebits.date(timestamp).format('YYYY年M月D日 (ddd) HH:mm', 'utc') +
			' (UTC)}}' +
			mySuffix;
		var summary = '[[Template:Unsigned|補簽名]]';
		if (mw.util.isIPAddress(username)) {
			summary +=
				'[[User:' +
				username +
				'|' +
				username +
				']]（[[User talk:' +
				username +
				'|對話]]｜[[Special:Contributions/' +
				username +
				'|貢獻]]）';
		} else {
			summary += '[[Special:Contributions/' + username + '|' + username + ']]（[[User talk:' + username + '|留言]]）';
		}
		if (revid) {
			summary += ' rev [[Special:Diff/' + revid + '|' + revid + ']]';
		}
		document.getElementById('wpSummary').value = summary;
		document.getElementById('wpMinoredit').click();
		document.getElementById('wpDiff').click();

		var finish = function() {
			if (confirm('Save?')) {
				document.getElementById('wpSave').click();
			}
		};

		if (confirm('Notify ' + username + '?')) {
			var talkmessage = '== 您忘了簽名 ==\n{{subst:Uw-tilde|' + mw.config.get('wgPageName') + '}}--~~~~';
			var talksummary = '單層級通知：沒有在討論頁上簽名，於[[' + mw.config.get('wgPageName') + ']]';
			var talkpage = 'User_talk:' + username;

			api
				.edit(talkpage, function(revision) {
					return {
						text: revision.content + '\n\n' + talkmessage,
						summary: talksummary,
						nocreate: false,
					};
				})
				.then(
					function() {
						mw.notify('已通知');
						finish();
					},
					function(e) {
						if (e == 'nocreate-missing') {
							api
								.create(
									talkpage,
									{
										summary: talksummary,
									},
									talkmessage
								)
								.then(
									function() {
										mw.notify('已通知');
										finish();
									},
									function(e) {
										mw.notify('建立討論頁發生錯誤：' + e);
										finish();
									}
								);
						} else {
							mw.notify('編輯討論頁發生錯誤：' + e);
							finish();
						}
					}
				);
		} else {
			finish();
		}
	}
})();
<|endoftext|>


<commit_before>/**
* Log requests for build HAR output
*
* @see: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
*/
'use strict';

exports.version = '0.1';

var fs = require('fs');

/**
 * From phantomHAR
 * @author: Christopher Van (@cvan)
 * @homepage: https://github.com/cvan/phantomHAR
 * @original: https://github.com/cvan/phantomHAR/blob/master/phantomhar.js
 */

function getErrorString(error) {
    // According to http://qt-project.org/doc/qt-4.8/qnetworkreply.html
    switch (error.errorCode) {
        case 1:
            return '(refused)';
        case 2:
            return '(closed)';
        case 3:
            return '(host not found)';
        case 4:
            return '(timeout)';
        case 5:
            return '(canceled)';
        case 6:
            return '(ssl failure)';
        case 7:
            return '(net failure)';
        default:
            return '(unknown error)';
    }
}

function createHAR(page, creator) {
    var address = page.address;
    var title = page.title;
    var startTime = page.startTime;
    var resources = page.resources;

    var entries = [];

    resources.forEach(function(resource) {
        var request = resource.request;
        var startReply = resource.startReply;
        var endReply = resource.endReply;
        var error = resource.error;

        if (!request || !startReply || !endReply) {
            return;
        }

        // Exclude data URIs from the HAR because they aren't
        // included in the spec.
        if (request.url.substring(0, 5).toLowerCase() === 'data:') {
            return;
        }

        if (error) {
            startReply.bodySize = 0;
            startReply.time = 0;
            endReply.time = 0;
            endReply.content = {};
            endReply.contentType = null;
            endReply.headers = [];
            endReply.statusText = getErrorString(error);
            endReply.status = null;
        }

        entries.push({
            cache: {},
            pageref: address,
            request: {
                // Accurate bodySize blocked on https://github.com/ariya/phantomjs/pull/11484
                // bodySize: -1,
                bodySize: startReply.contentLength,
                cookies: [],
                headers: request.headers,
                // Accurate headersSize blocked on https://github.com/ariya/phantomjs/pull/11484
                // headersSize: -1,
                headersSize: 0,
                httpVersion: 'HTTP/1.1',
                method: request.method,
                queryString: [],
                url: request.url,
            },
            response: {
                bodySize: startReply.contentLength,
                cookies: [],
                headers: endReply.headers,
                headersSize: -1,
                httpVersion: 'HTTP/1.1',
                redirectURL: '',
                status: endReply.status,
                statusText: endReply.statusText,
                content: {
                    mimeType: endReply.contentType || '',
                    size: startReply.bodySize, // uncompressed
                    text: startReply.content || ''
                }
            },
            startedDateTime: request.time.toISOString(),
            time: endReply.time - request.time,
            timings: {
                blocked: 0,
                dns: -1,
                connect: -1,
                send: 0,
                wait: startReply.time - request.time,
                receive: endReply.time - startReply.time,
                ssl: -1
            }
        });
    });

    return {
        log: {
            creator: creator,
            entries: entries,
            pages: [
                {
                    startedDateTime: startTime.toISOString(),
                    id: address,
                    title: title,
                    pageTimings: {
                        onLoad: page.windowOnLoadTime || -1,
                        onContentLoad: page.onDOMReadyTime || -1
                    }
                }
            ],
            version: '1.2',
        }
    };
}
/** End **/

exports.module = function(phantomas) {

    var param = phantomas.getParam('har'),
        path = '';

    var page = {
        origin: undefined,
        resources: [],
        title: undefined,
        address: undefined,
        startTime: undefined,
        endTime: undefined,
        onDOMReadyTime: undefined,
        windowOnLoadTime: undefined
    };

    var creator = {
        name: "Phantomas - (using phantomHAR)",
        version: phantomas.getVersion()
    };

    if (typeof param === 'undefined') {
        phantomas.log('No HAR path specified, use --har <path>');
        return;
    }

    // --har
    if (param === true) {
        // defaults to "2013-12-07T20:15:01.521Z.har"
        path = (new Date()).toJSON() + '.har';
    }
    // --har [file name]
    else {
        path = param;
    }

    phantomas.log('HAR path: %s', path);

    phantomas.on('pageBeforeOpen', function(p) {
        page.origin = p;
    });

    phantomas.on('pageOpen', function() {
        page.startTime = new Date();
    });

    phantomas.on('loadFinished', function() {
        page.endTime = new Date();
    });

    phantomas.on('onResourceRequested', function(res, req) {
        page.resources[res.id] = {
            request: res,
            startReply: null,
            endReply: null
        };
    });

    phantomas.on('onResourceReceived', function(res) {
        switch (res.stage) {
            case 'start':
                page.resources[res.id].startReply = res;
                break;
            case 'end':
                page.resources[res.id].endReply = res;
                break;
        }
    });

    phantomas.on('metric', function(name, value) {
        switch (name) {
            case 'onDOMReadyTime':
                page.onDOMReadyTime = value;
                break;
            case 'windowOnLoadTime':
                page.windowOnLoadTime = value;
                break;
        }
    });

    phantomas.on('report', function() {
        // Set endTime if page was not finished correctly
        if (! page.endTime)
            page.endTime = new Date();

        // If metric 'windowOnLoadTime' hasn't been fired, compute it
        if (! page.windowOnLoadTime)
            page.windowOnLoadTime = page.endTime.getTime() - page.startTime.getTime();

        page.address = page.origin.url;
        page.title = page.origin.title;

        phantomas.log('Create HAR:', page.address, page.title);

        var har,
            dump;

        try {
            har = createHAR(page, creator);
        } catch (e) {
            phantomas.log('Impossible to build HAR: %s', e);
            return;
        }

        phantomas.log('Convert HAR to JSON');
        try {
            dump = JSON.stringify(har);
        } catch (e) {
            phantomas.log('Impossible stringify HAR on JSON format: %s', e);
            return;
        }

        phantomas.log("Write HAR in '%s'", path);
        try {
            fs.write(path, dump);
        } catch (e) {
            phantomas.log('Impossible write HAR file: %s', e);
            return;
        }

        phantomas.log('HAR Done !');
    });
};
<commit_msg>fix: output message<commit_after>/**
* Log requests for build HAR output
*
* @see: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
*/
'use strict';

exports.version = '0.1';

var fs = require('fs');

/**
 * From phantomHAR
 * @author: Christopher Van (@cvan)
 * @homepage: https://github.com/cvan/phantomHAR
 * @original: https://github.com/cvan/phantomHAR/blob/master/phantomhar.js
 */

function getErrorString(error) {
    // According to http://qt-project.org/doc/qt-4.8/qnetworkreply.html
    switch (error.errorCode) {
        case 1:
            return '(refused)';
        case 2:
            return '(closed)';
        case 3:
            return '(host not found)';
        case 4:
            return '(timeout)';
        case 5:
            return '(canceled)';
        case 6:
            return '(ssl failure)';
        case 7:
            return '(net failure)';
        default:
            return '(unknown error)';
    }
}

function createHAR(page, creator) {
    var address = page.address;
    var title = page.title;
    var startTime = page.startTime;
    var resources = page.resources;

    var entries = [];

    resources.forEach(function(resource) {
        var request = resource.request;
        var startReply = resource.startReply;
        var endReply = resource.endReply;
        var error = resource.error;

        if (!request || !startReply || !endReply) {
            return;
        }

        // Exclude data URIs from the HAR because they aren't
        // included in the spec.
        if (request.url.substring(0, 5).toLowerCase() === 'data:') {
            return;
        }

        if (error) {
            startReply.bodySize = 0;
            startReply.time = 0;
            endReply.time = 0;
            endReply.content = {};
            endReply.contentType = null;
            endReply.headers = [];
            endReply.statusText = getErrorString(error);
            endReply.status = null;
        }

        entries.push({
            cache: {},
            pageref: address,
            request: {
                // Accurate bodySize blocked on https://github.com/ariya/phantomjs/pull/11484
                // bodySize: -1,
                bodySize: startReply.contentLength,
                cookies: [],
                headers: request.headers,
                // Accurate headersSize blocked on https://github.com/ariya/phantomjs/pull/11484
                // headersSize: -1,
                headersSize: 0,
                httpVersion: 'HTTP/1.1',
                method: request.method,
                queryString: [],
                url: request.url,
            },
            response: {
                bodySize: startReply.contentLength,
                cookies: [],
                headers: endReply.headers,
                headersSize: -1,
                httpVersion: 'HTTP/1.1',
                redirectURL: '',
                status: endReply.status,
                statusText: endReply.statusText,
                content: {
                    mimeType: endReply.contentType || '',
                    size: startReply.bodySize, // uncompressed
                    text: startReply.content || ''
                }
            },
            startedDateTime: request.time.toISOString(),
            time: endReply.time - request.time,
            timings: {
                blocked: 0,
                dns: -1,
                connect: -1,
                send: 0,
                wait: startReply.time - request.time,
                receive: endReply.time - startReply.time,
                ssl: -1
            }
        });
    });

    return {
        log: {
            creator: creator,
            entries: entries,
            pages: [
                {
                    startedDateTime: startTime.toISOString(),
                    id: address,
                    title: title,
                    pageTimings: {
                        onLoad: page.windowOnLoadTime || -1,
                        onContentLoad: page.onDOMReadyTime || -1
                    }
                }
            ],
            version: '1.2',
        }
    };
}
/** End **/

exports.module = function(phantomas) {

    var param = phantomas.getParam('har'),
        path = '';

    var page = {
        origin: undefined,
        resources: [],
        title: undefined,
        address: undefined,
        startTime: undefined,
        endTime: undefined,
        onDOMReadyTime: undefined,
        windowOnLoadTime: undefined
    };

    var creator = {
        name: "Phantomas - (using phantomHAR)",
        version: phantomas.getVersion()
    };

    if (typeof param === 'undefined') {
        phantomas.log('No HAR path specified, use --har <path>');
        return;
    }

    // --har
    if (param === true) {
        // defaults to "2013-12-07T20:15:01.521Z.har"
        path = (new Date()).toJSON() + '.har';
    }
    // --har [file name]
    else {
        path = param;
    }

    phantomas.log('HAR path: %s', path);

    phantomas.on('pageBeforeOpen', function(p) {
        page.origin = p;
    });

    phantomas.on('pageOpen', function() {
        page.startTime = new Date();
    });

    phantomas.on('loadFinished', function() {
        page.endTime = new Date();
    });

    phantomas.on('onResourceRequested', function(res, req) {
        page.resources[res.id] = {
            request: res,
            startReply: null,
            endReply: null
        };
    });

    phantomas.on('onResourceReceived', function(res) {
        switch (res.stage) {
            case 'start':
                page.resources[res.id].startReply = res;
                break;
            case 'end':
                page.resources[res.id].endReply = res;
                break;
        }
    });

    phantomas.on('metric', function(name, value) {
        switch (name) {
            case 'onDOMReadyTime':
                page.onDOMReadyTime = value;
                break;
            case 'windowOnLoadTime':
                page.windowOnLoadTime = value;
                break;
        }
    });

    phantomas.on('report', function() {
        // Set endTime if page was not finished correctly
        if (! page.endTime)
            page.endTime = new Date();

        // If metric 'windowOnLoadTime' hasn't been fired, compute it
        if (! page.windowOnLoadTime)
            page.windowOnLoadTime = page.endTime.getTime() - page.startTime.getTime();

        page.address = page.origin.url;
        page.title = page.origin.title;

        phantomas.log('Create HAR: %s ("%s")', page.address, page.title);

        var har,
            dump;

        try {
            har = createHAR(page, creator);
        } catch (e) {
            phantomas.log('Impossible to build HAR: %s', e);
            return;
        }

        phantomas.log('Convert HAR to JSON');
        try {
            dump = JSON.stringify(har);
        } catch (e) {
            phantomas.log('Impossible stringify HAR on JSON format: %s', e);
            return;
        }

        phantomas.log("Write HAR in '%s'", path);
        try {
            fs.write(path, dump);
        } catch (e) {
            phantomas.log('Impossible write HAR file: %s', e);
            return;
        }

        phantomas.log('HAR Done !');
    });
};
<|endoftext|>