<commit_before>var urlModule = require('url');
var queryString = require('querystring');
var _ = require('lodash');
var memoizeSync = require('memoizesync');

var AssetGraph = require('../');

var getTextByFontProperties = require('../util/fonts/getTextByFontProperties');
var googleFontsReducer = require('../util/fonts/googleFontsReducer');
var getFontPropsFromGoogleId = require('../util/fonts/getFontPropsFromGoogleId');
var snapToAvailableFontProperties = require('../util/fonts/snapToAvailableFontProperties');
var cssListHelpers = require('css-list-helpers');
var unquote = require('../util/fonts/unquote');
var getCssRulesByProperty = require('../util/fonts/getCssRulesByProperty');

var googleFontsUrlRegex = /^(?:https?:)?\/\/fonts\.googleapis\.com\/css/;

function cssQuoteIfNecessary(value) {
    if (/^\w+$/.test(value)) {
        return value;
    } else {
        return '\'' + value.replace(/'/g, '\\\'') + '\'';
    }
}

function splitUpGoogleFontFamilyWeightCombo(url) {
    var parsedUrl = urlModule.parse(url, true);
    var fontString = parsedUrl.query.family;

    if (fontString) {
        var fonts = [].concat.apply([], fontString.split('|').map(function (familyStr) {
            var pair = familyStr.split(':');
            var family = pair[0].replace(' ', '+');
            var weights = pair[1];

            if (weights) {
                return weights.split(',')
                    .map(function (weight) {
                        return family + ':' + weight;
                    });
            } else {
                return [family + ':' + 400];
            }
        }));

        return fonts;
    }
}

function asyncCssLoadScriptRelation(relation) {
    var scriptText = [
        '(function () {',
        '  var el = document.createElement(\'link\');',
        '  el.href = \'' + relation.to.url + '\'.toString(\'url\');',
        '  el.rel = \'stylesheet\';',
        relation.media ? '  el.media = \'' + relation.media + '\';' : '',
        '  document.body.appendChild(el);',
        '}())'
    ].join('\n');

    var asset = new AssetGraph.JavaScript(relation.assetGraph.resolveAssetConfig({
        text: scriptText
    }));

    relation.assetGraph.addAsset(asset);

    var newRelation = new AssetGraph.HtmlScript({
        from: relation.from,
        to: asset,
        node: relation.from.parseTree.createElement('script')
    });

    return newRelation;
}

function asyncLoadStyleRelationWithFallback(htmlAsset, originalRelation) {
    var document = htmlAsset.parseTree;
    var injectionPointRelation = originalRelation;

    while (injectionPointRelation.from.isInline) {
        injectionPointRelation = injectionPointRelation.from.incomingRelations[0];
    }

    // Resource hint: prefetch google font stylesheet
    var prefetchRelation = new AssetGraph.HtmlPrefetchLink({
        hrefType: 'absolute',
        from: htmlAsset,
        to: originalRelation.to
    });

    prefetchRelation.attach(htmlAsset, 'before', injectionPointRelation);

    // Resource hint: prefetch google font stylesheet
    var preconnectRelation = new AssetGraph.HtmlPreconnectLink({
        hrefType: 'absolute',
        from: htmlAsset,
        to: { url: 'https://fonts.gstatic.com' }
    });

    preconnectRelation.attach(htmlAsset, 'before', injectionPointRelation);

    // Async load google font stylesheet
    var relation = new AssetGraph.HtmlStyle({
        from: htmlAsset,
        to: originalRelation.to
    });

    relation.attach(htmlAsset, 'last');

    relation.media = originalRelation.media;

    // Attach <noscript /> at bottom of <body> and put the <link> in it
    var noScriptNode = document.createElement('noscript');
    noScriptNode.appendChild(relation.node);
    document.body.appendChild(noScriptNode);

    // Insert async CSS loading <script> before <noscript>
    var asyncCssLoadingRelation = asyncCssLoadScriptRelation(relation);
    asyncCssLoadingRelation.attach(htmlAsset, 'before', relation);

    document.body.appendChild(asyncCssLoadingRelation.node);
    document.body.appendChild(noScriptNode);

    asyncCssLoadingRelation.inline();

    htmlAsset.markDirty();
}

var fontFormatUA = {
    woff: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0',
    woff2: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36'
};

module.exports = function (options) {
    options = options || {};
    var subsetPerPage = !!options.subsetPerPage;
    var inlineSubsets = !!options.inlineSubsets;
    var UA = fontFormatUA[options.format] || fontFormatUA.woff;

    return function subsetGoogleFonts(assetGraph, cb) {
        // Save AssetGraph User-Agent for later
        var assetGraphUA = assetGraph.teepee.headers['User-Agent'];
        assetGraph.teepee.headers['User-Agent'] = UA;

        var fontSubsetCssRelations = [];
        var googleFontSubsetNameMap = {};
        var htmlAssetTextByPropsMap = {};

        assetGraph
            .queue(function collectTextsByPage(assetGraph) {
                var memoizedGetCssRulesByProperty = memoizeSync(getCssRulesByProperty);
                var htmlAssets = assetGraph.findAssets({ type: 'Html', isInline: false });
                var traversalRelationQuery = assetGraph.query.or(
                    {
                        type: ['HtmlStyle', 'CssImport']
                    },
                    {
                        to: {
                            type: 'Html',
                            isInline: true
                        }
                    }
                );

                htmlAssets.forEach(function (htmlAsset) {
                    var accumulatedRelations = [];

                    assetGraph.eachAssetPreOrder(htmlAsset, traversalRelationQuery, function (asset) {
                        var googleFontRelations = assetGraph.findRelations({
                            from: asset,
                            to: { url: googleFontsUrlRegex }
                        }, true);

                        if (googleFontRelations.length > 0) {
                            Array.prototype.push.apply(accumulatedRelations, googleFontRelations);
                        }
                    });

                    if (accumulatedRelations.length) {
                        accumulatedRelations = _.uniq(accumulatedRelations);

                        var googleFontIds = Array.prototype.concat.apply([], accumulatedRelations
                            .map(function (googleFontRelation) {
                                return googleFontRelation.to.url;
                            })
                            .map(splitUpGoogleFontFamilyWeightCombo));

                        var availableFontProps = googleFontIds.map(getFontPropsFromGoogleId);

                        var textByProps = getTextByFontProperties(htmlAsset, memoizedGetCssRulesByProperty)
                            .map(function (textAndProps) {
                                return {
                                    text: textAndProps.text,
                                    props: snapToAvailableFontProperties(availableFontProps, textAndProps.props)
                                };
                            })
                            .filter(function (textByProps) {
                                return textByProps.props;
                            });
                        var uniqueTextByGoogleFontId = googleFontsReducer(textByProps);

                        if (options.debug) {
                            console.error(htmlAsset.urlOrDescription, uniqueTextByGoogleFontId);
                        }

                        var htmlAssetTextByProps = {
                            htmlAsset: htmlAsset,
                            googleFontRelations: accumulatedRelations,
                            textsByGoogleFontId: uniqueTextByGoogleFontId
                        };

                        htmlAssetTextByPropsMap[htmlAsset.url] = htmlAssetTextByProps;
                    }

                });

                if (Object.keys(htmlAssetTextByPropsMap).length <= 1) {
                    subsetPerPage = false;
                }
            })
            .if(!subsetPerPage)
                .queue(function unifySubsetsAcrossPages(assetGraph) {
                    var globalSubsets = {};

                    // Gather all subsets
                    Object.keys(htmlAssetTextByPropsMap).forEach(function (url) {
                        var textsByGoogleFontId = htmlAssetTextByPropsMap[url].textsByGoogleFontId;

                        Object.keys(textsByGoogleFontId).forEach(function (googleId) {
                            if (!globalSubsets[googleId]) {
                                globalSubsets[googleId] = [];
                            }

                            globalSubsets[googleId].push(textsByGoogleFontId[googleId]);
                        });
                    }, {});

                    // Merge subset values, unique glyphs, sort
                    Object.keys(globalSubsets).forEach(function (googleId) {
                        var texts = _.map(globalSubsets[googleId], 'text');

                        globalSubsets[googleId] = {
                            texts: texts,
                            props: globalSubsets[googleId][0].props,
                            text: _.uniq(texts.join(''))
                                .sort()
                                .join('')
                        };
                    });

                    // Assign the single global subset to all pages
                    Object.keys(htmlAssetTextByPropsMap).forEach(function (url) {
                        htmlAssetTextByPropsMap[url].textsByGoogleFontId = globalSubsets;
                    });

                })
            .endif()
            .queue(function insertSubsetRelations(assetGraph) {
                Object.keys(htmlAssetTextByPropsMap).forEach(function (assetUrl) {
                    var htmlAssetTextByProps = htmlAssetTextByPropsMap[assetUrl];
                    var htmlAsset = htmlAssetTextByProps.htmlAsset;
                    var textsByGoogleFontId = htmlAssetTextByProps.textsByGoogleFontId;
                    var googleFontCssUrl = htmlAssetTextByProps.googleFontRelations[0].to.url.split('?')[0];

                    Object.keys(textsByGoogleFontId).reverse().forEach(function (googleFontId) {
                        if (textsByGoogleFontId[googleFontId]) {
                            var cssAsset = new assetGraph.Css(assetGraph.resolveAssetConfig({
                                url: googleFontCssUrl + '?family=' + googleFontId + '&text=' + encodeURIComponent(textsByGoogleFontId[googleFontId].text)
                            }));
                            var individualRelation = new assetGraph.HtmlStyle({
                                to: cssAsset,
                                node: htmlAsset.parseTree.createElement('link')
                            });

                            var fontName = googleFontId.split(':')[0].replace(/\+/g, ' ');
                            googleFontSubsetNameMap[fontName] = fontName + '__subset';

                            fontSubsetCssRelations.push(individualRelation);

                            var firstHtmlStyleRelation = htmlAsset.outgoingRelations.find(function (relation) {
                                return relation.type === 'HtmlStyle';
                            });

                            individualRelation.attachNodeBeforeOrAfter('before', firstHtmlStyleRelation);
                        }
                    });
                });
            })
            .queue(function asyncLoadOriginalGoogleFontCss(assetGraph) {
                Object.keys(htmlAssetTextByPropsMap).forEach(function (assetUrl) {
                    var htmlAssetTextByProps = htmlAssetTextByPropsMap[assetUrl];
                    var htmlAsset = htmlAssetTextByProps.htmlAsset;
                    var googleFontRelations = htmlAssetTextByProps.googleFontRelations;

                    googleFontRelations.forEach(function (googleFontRelation) {
                        asyncLoadStyleRelationWithFallback(htmlAsset, googleFontRelation);
                    });
                });

                var allGoogleFontRelations = Object.keys(htmlAssetTextByPropsMap).reduce(function (result, assetUrl) {
                    result.push.apply(result, htmlAssetTextByPropsMap[assetUrl].googleFontRelations);

                    return result;
                }, []);

                _.uniq(allGoogleFontRelations).forEach(function (googleFontRelation) {
                    googleFontRelation.detach();
                });
            })
            .populate({
                from: { type: 'Html' },
                followRelations: assetGraph.query.or(
                    { to: { url: assetGraph.query.and(googleFontsUrlRegex, /&text=/) } },
                    {
                        from: { url: googleFontsUrlRegex },
                        type: 'CssFontFaceSrc'
                    }
                )
            })
            .queue(function moveSubsetAssetsToLocal(assetGraph) {
                Object.keys(htmlAssetTextByPropsMap).forEach(function (assetUrl) {
                    var htmlAsset = htmlAssetTextByPropsMap[assetUrl].htmlAsset;
                    var fontStyleSheetRelations = assetGraph.findRelations({ type: 'HtmlStyle', from: htmlAsset, to: { url: googleFontsUrlRegex } });

                    if (fontStyleSheetRelations.length === 0) {
                        return;
                    }

                    // Move web fonts to local domain
                    fontStyleSheetRelations.forEach(function (fontStyleSheetRelation) {
                        if (!fontStyleSheetRelation.to.isLoaded) {
                            return;
                        }

                        var parsedUrl = urlModule.parse(fontStyleSheetRelation.to.url);
                        var qs = queryString.parse(parsedUrl.query);

                        if (qs.family) {
                            var fileNamePreFix = qs.family.replace(' ', '+').replace(':', '_') + '-';

                            fontStyleSheetRelation.to.outgoingRelations.forEach(function (fontRelation) {
                                var fontAsset = fontRelation.to;

                                var fileName = fileNamePreFix + fontAsset.md5Hex.slice(0, 10) + '.' + fontAsset.contentType.split('/').pop();
                                fontAsset.url = assetGraph.root + 'google-font-subsets/' + fileName;

                                if (inlineSubsets) {
                                    fontRelation.inline();
                                } else {
                                    var htmlAsset = fontStyleSheetRelation.from;
                                    var preloadRelation = new AssetGraph.HtmlPreloadLink({
                                        hrefType: 'rootRelative',
                                        from: htmlAsset,
                                        to: fontAsset
                                    });

                                    preloadRelation.attach(htmlAsset, 'before', fontStyleSheetRelations[0]);
                                }
                            });
                        }
                    });

                    var source = fontStyleSheetRelations.map(function (relation) {
                        return relation.to.text;
                    }).join('\n');

                    var fontStyleBundle = assetGraph.findAssets({ type: 'Css', isLoaded: true, url: /google-font-subsets\/fonts-/, text: source })[0];

                    if (!fontStyleBundle) {
                        fontStyleBundle = new AssetGraph.Css(assetGraph.resolveAssetConfig({
                            text: source
                        }));
                        assetGraph.addAsset(fontStyleBundle);
                        fontStyleBundle.url = assetGraph.root + 'google-font-subsets/fonts-' + fontStyleBundle.md5Hex.slice(0, 10) + '.css';

                        fontStyleBundle.outgoingRelations.forEach(function (fontRelation) {
                            fontRelation.to = assetGraph.findAssets({ url: fontRelation.to.url })[0];

                            fontRelation.refreshHref();
                        });
                    }

                    var bundleRelation = new AssetGraph.HtmlStyle({
                        hrefType: 'rootRelative',
                        from: htmlAsset,
                        to: fontStyleBundle
                    });

                    bundleRelation.attach(htmlAsset, 'before', fontStyleSheetRelations[0]);

                    fontStyleSheetRelations.forEach(function (relation) {
                        relation.detach();
                    });
                });

                assetGraph.findAssets({ type: 'Css', url: /google-font-subsets\/fonts-/ }).forEach(function (CssBundle) {
                    CssBundle.outgoingRelations.forEach(function (fontFaceSrc) {
                        fontFaceSrc.hrefType = 'relative';
                    });
                });
            })
            .removeUnreferencedAssets({ type: 'Css', url: googleFontsUrlRegex })
            .queue(function renameSubsetFonts() {
                // Inject subset font name before original webfomt
                assetGraph.findAssets({type: 'Css', isLoaded: true }).forEach(function (cssAsset) {
                    var changesMade = false;
                    cssAsset.eachRuleInParseTree(function (cssRule) {
                        if (cssRule.type === 'decl' && cssRule.prop === 'font-family') {
                            if (cssRule.parent.type === 'rule') {
                                var fontFamilies = cssListHelpers.splitByCommas(cssRule.value).map(unquote);
                                var subsetFontFamily = googleFontSubsetNameMap[fontFamilies[0]];
                                if (subsetFontFamily && fontFamilies.indexOf(subsetFontFamily) === -1) {
                                    cssRule.value = cssQuoteIfNecessary(subsetFontFamily) + ', ' + cssRule.value;
                                    changesMade = true;
                                }
                            }

                            if (cssRule.parent.type === 'atrule' && cssRule.parent.name === 'font-face') {
                                cssRule.value = cssQuoteIfNecessary(googleFontSubsetNameMap[unquote(cssRule.value)] || cssRule.value);
                                changesMade = true;
                            }
                        }
                    });
                    if (changesMade) {
                        cssAsset.markDirty();
                    }
                });
            })
            .then(function (assetGraph) {
                // Undo User-Agent override
                assetGraph.teepee.headers['User-Agent'] = assetGraphUA;

                cb();
            });
    };
};
<commit_msg>transforms/subsetGoogleFonts: Make relation query for google subset CSS strict enough to not include relations to original google CSS<commit_after>var urlModule = require('url');
var queryString = require('querystring');
var _ = require('lodash');
var memoizeSync = require('memoizesync');

var AssetGraph = require('../');

var getTextByFontProperties = require('../util/fonts/getTextByFontProperties');
var googleFontsReducer = require('../util/fonts/googleFontsReducer');
var getFontPropsFromGoogleId = require('../util/fonts/getFontPropsFromGoogleId');
var snapToAvailableFontProperties = require('../util/fonts/snapToAvailableFontProperties');
var cssListHelpers = require('css-list-helpers');
var unquote = require('../util/fonts/unquote');
var getCssRulesByProperty = require('../util/fonts/getCssRulesByProperty');

var googleFontsUrlRegex = /^(?:https?:)?\/\/fonts\.googleapis\.com\/css/;

function cssQuoteIfNecessary(value) {
    if (/^\w+$/.test(value)) {
        return value;
    } else {
        return '\'' + value.replace(/'/g, '\\\'') + '\'';
    }
}

function splitUpGoogleFontFamilyWeightCombo(url) {
    var parsedUrl = urlModule.parse(url, true);
    var fontString = parsedUrl.query.family;

    if (fontString) {
        var fonts = [].concat.apply([], fontString.split('|').map(function (familyStr) {
            var pair = familyStr.split(':');
            var family = pair[0].replace(' ', '+');
            var weights = pair[1];

            if (weights) {
                return weights.split(',')
                    .map(function (weight) {
                        return family + ':' + weight;
                    });
            } else {
                return [family + ':' + 400];
            }
        }));

        return fonts;
    }
}

function asyncCssLoadScriptRelation(relation) {
    var scriptText = [
        '(function () {',
        '  var el = document.createElement(\'link\');',
        '  el.href = \'' + relation.to.url + '\'.toString(\'url\');',
        '  el.rel = \'stylesheet\';',
        relation.media ? '  el.media = \'' + relation.media + '\';' : '',
        '  document.body.appendChild(el);',
        '}())'
    ].join('\n');

    var asset = new AssetGraph.JavaScript(relation.assetGraph.resolveAssetConfig({
        text: scriptText
    }));

    relation.assetGraph.addAsset(asset);

    var newRelation = new AssetGraph.HtmlScript({
        from: relation.from,
        to: asset,
        node: relation.from.parseTree.createElement('script')
    });

    return newRelation;
}

function asyncLoadStyleRelationWithFallback(htmlAsset, originalRelation) {
    var document = htmlAsset.parseTree;
    var injectionPointRelation = originalRelation;

    while (injectionPointRelation.from.isInline) {
        injectionPointRelation = injectionPointRelation.from.incomingRelations[0];
    }

    // Resource hint: prefetch google font stylesheet
    var prefetchRelation = new AssetGraph.HtmlPrefetchLink({
        hrefType: 'absolute',
        from: htmlAsset,
        to: originalRelation.to
    });

    prefetchRelation.attach(htmlAsset, 'before', injectionPointRelation);

    // Resource hint: prefetch google font stylesheet
    var preconnectRelation = new AssetGraph.HtmlPreconnectLink({
        hrefType: 'absolute',
        from: htmlAsset,
        to: { url: 'https://fonts.gstatic.com' }
    });

    preconnectRelation.attach(htmlAsset, 'before', injectionPointRelation);

    // Async load google font stylesheet
    var relation = new AssetGraph.HtmlStyle({
        from: htmlAsset,
        to: originalRelation.to
    });

    relation.attach(htmlAsset, 'last');

    relation.media = originalRelation.media;

    // Attach <noscript /> at bottom of <body> and put the <link> in it
    var noScriptNode = document.createElement('noscript');
    noScriptNode.appendChild(relation.node);
    document.body.appendChild(noScriptNode);

    // Insert async CSS loading <script> before <noscript>
    var asyncCssLoadingRelation = asyncCssLoadScriptRelation(relation);
    asyncCssLoadingRelation.attach(htmlAsset, 'before', relation);

    document.body.appendChild(asyncCssLoadingRelation.node);
    document.body.appendChild(noScriptNode);

    asyncCssLoadingRelation.inline();

    htmlAsset.markDirty();
}

var fontFormatUA = {
    woff: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0',
    woff2: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36'
};

module.exports = function (options) {
    options = options || {};
    var subsetPerPage = !!options.subsetPerPage;
    var inlineSubsets = !!options.inlineSubsets;
    var UA = fontFormatUA[options.format] || fontFormatUA.woff;

    return function subsetGoogleFonts(assetGraph, cb) {
        // Save AssetGraph User-Agent for later
        var assetGraphUA = assetGraph.teepee.headers['User-Agent'];
        assetGraph.teepee.headers['User-Agent'] = UA;

        var fontSubsetCssRelations = [];
        var googleFontSubsetNameMap = {};
        var htmlAssetTextByPropsMap = {};

        assetGraph
            .queue(function collectTextsByPage(assetGraph) {
                var memoizedGetCssRulesByProperty = memoizeSync(getCssRulesByProperty);
                var htmlAssets = assetGraph.findAssets({ type: 'Html', isInline: false });
                var traversalRelationQuery = assetGraph.query.or(
                    {
                        type: ['HtmlStyle', 'CssImport']
                    },
                    {
                        to: {
                            type: 'Html',
                            isInline: true
                        }
                    }
                );

                htmlAssets.forEach(function (htmlAsset) {
                    var accumulatedRelations = [];

                    assetGraph.eachAssetPreOrder(htmlAsset, traversalRelationQuery, function (asset) {
                        var googleFontRelations = assetGraph.findRelations({
                            from: asset,
                            to: { url: googleFontsUrlRegex }
                        }, true);

                        if (googleFontRelations.length > 0) {
                            Array.prototype.push.apply(accumulatedRelations, googleFontRelations);
                        }
                    });

                    if (accumulatedRelations.length) {
                        accumulatedRelations = _.uniq(accumulatedRelations);

                        var googleFontIds = Array.prototype.concat.apply([], accumulatedRelations
                            .map(function (googleFontRelation) {
                                return googleFontRelation.to.url;
                            })
                            .map(splitUpGoogleFontFamilyWeightCombo));

                        var availableFontProps = googleFontIds.map(getFontPropsFromGoogleId);

                        var textByProps = getTextByFontProperties(htmlAsset, memoizedGetCssRulesByProperty)
                            .map(function (textAndProps) {
                                return {
                                    text: textAndProps.text,
                                    props: snapToAvailableFontProperties(availableFontProps, textAndProps.props)
                                };
                            })
                            .filter(function (textByProps) {
                                return textByProps.props;
                            });
                        var uniqueTextByGoogleFontId = googleFontsReducer(textByProps);

                        if (options.debug) {
                            console.error(htmlAsset.urlOrDescription, uniqueTextByGoogleFontId);
                        }

                        var htmlAssetTextByProps = {
                            htmlAsset: htmlAsset,
                            googleFontRelations: accumulatedRelations,
                            textsByGoogleFontId: uniqueTextByGoogleFontId
                        };

                        htmlAssetTextByPropsMap[htmlAsset.url] = htmlAssetTextByProps;
                    }

                });

                if (Object.keys(htmlAssetTextByPropsMap).length <= 1) {
                    subsetPerPage = false;
                }
            })
            .if(!subsetPerPage)
                .queue(function unifySubsetsAcrossPages(assetGraph) {
                    var globalSubsets = {};

                    // Gather all subsets
                    Object.keys(htmlAssetTextByPropsMap).forEach(function (url) {
                        var textsByGoogleFontId = htmlAssetTextByPropsMap[url].textsByGoogleFontId;

                        Object.keys(textsByGoogleFontId).forEach(function (googleId) {
                            if (!globalSubsets[googleId]) {
                                globalSubsets[googleId] = [];
                            }

                            globalSubsets[googleId].push(textsByGoogleFontId[googleId]);
                        });
                    }, {});

                    // Merge subset values, unique glyphs, sort
                    Object.keys(globalSubsets).forEach(function (googleId) {
                        var texts = _.map(globalSubsets[googleId], 'text');

                        globalSubsets[googleId] = {
                            texts: texts,
                            props: globalSubsets[googleId][0].props,
                            text: _.uniq(texts.join(''))
                                .sort()
                                .join('')
                        };
                    });

                    // Assign the single global subset to all pages
                    Object.keys(htmlAssetTextByPropsMap).forEach(function (url) {
                        htmlAssetTextByPropsMap[url].textsByGoogleFontId = globalSubsets;
                    });

                })
            .endif()
            .queue(function insertSubsetRelations(assetGraph) {
                Object.keys(htmlAssetTextByPropsMap).forEach(function (assetUrl) {
                    var htmlAssetTextByProps = htmlAssetTextByPropsMap[assetUrl];
                    var htmlAsset = htmlAssetTextByProps.htmlAsset;
                    var textsByGoogleFontId = htmlAssetTextByProps.textsByGoogleFontId;
                    var googleFontCssUrl = htmlAssetTextByProps.googleFontRelations[0].to.url.split('?')[0];

                    Object.keys(textsByGoogleFontId).reverse().forEach(function (googleFontId) {
                        if (textsByGoogleFontId[googleFontId]) {
                            var cssAsset = new assetGraph.Css(assetGraph.resolveAssetConfig({
                                url: googleFontCssUrl + '?family=' + googleFontId + '&text=' + encodeURIComponent(textsByGoogleFontId[googleFontId].text)
                            }));
                            var individualRelation = new assetGraph.HtmlStyle({
                                to: cssAsset,
                                node: htmlAsset.parseTree.createElement('link')
                            });

                            var fontName = googleFontId.split(':')[0].replace(/\+/g, ' ');
                            googleFontSubsetNameMap[fontName] = fontName + '__subset';

                            fontSubsetCssRelations.push(individualRelation);

                            var firstHtmlStyleRelation = htmlAsset.outgoingRelations.find(function (relation) {
                                return relation.type === 'HtmlStyle';
                            });

                            individualRelation.attachNodeBeforeOrAfter('before', firstHtmlStyleRelation);
                        }
                    });
                });
            })
            .queue(function asyncLoadOriginalGoogleFontCss(assetGraph) {
                Object.keys(htmlAssetTextByPropsMap).forEach(function (assetUrl) {
                    var htmlAssetTextByProps = htmlAssetTextByPropsMap[assetUrl];
                    var htmlAsset = htmlAssetTextByProps.htmlAsset;
                    var googleFontRelations = htmlAssetTextByProps.googleFontRelations;

                    googleFontRelations.forEach(function (googleFontRelation) {
                        asyncLoadStyleRelationWithFallback(htmlAsset, googleFontRelation);
                    });
                });

                var allGoogleFontRelations = Object.keys(htmlAssetTextByPropsMap).reduce(function (result, assetUrl) {
                    result.push.apply(result, htmlAssetTextByPropsMap[assetUrl].googleFontRelations);

                    return result;
                }, []);

                _.uniq(allGoogleFontRelations).forEach(function (googleFontRelation) {
                    googleFontRelation.detach();
                });
            })
            .populate({
                from: { type: 'Html' },
                followRelations: assetGraph.query.or(
                    { to: { url: assetGraph.query.and(googleFontsUrlRegex, /&text=/) } },
                    {
                        from: { url: googleFontsUrlRegex },
                        type: 'CssFontFaceSrc'
                    }
                )
            })
            .queue(function moveSubsetAssetsToLocal(assetGraph) {
                Object.keys(htmlAssetTextByPropsMap).forEach(function (assetUrl) {
                    var htmlAsset = htmlAssetTextByPropsMap[assetUrl].htmlAsset;
                    var fontStyleSheetRelations = assetGraph.findRelations({ type: 'HtmlStyle', from: htmlAsset, to: { url: assetGraph.query.and(googleFontsUrlRegex, /&text=/) } });

                    if (fontStyleSheetRelations.length === 0) {
                        return;
                    }

                    // Move web fonts to local domain
                    fontStyleSheetRelations.forEach(function (fontStyleSheetRelation) {
                        if (!fontStyleSheetRelation.to.isLoaded) {
                            return;
                        }

                        var parsedUrl = urlModule.parse(fontStyleSheetRelation.to.url);
                        var qs = queryString.parse(parsedUrl.query);

                        if (qs.family) {
                            var fileNamePreFix = qs.family.replace(' ', '+').replace(':', '_') + '-';

                            fontStyleSheetRelation.to.outgoingRelations.forEach(function (fontRelation) {
                                var fontAsset = fontRelation.to;

                                var fileName = fileNamePreFix + fontAsset.md5Hex.slice(0, 10) + '.' + fontAsset.contentType.split('/').pop();
                                fontAsset.url = assetGraph.root + 'google-font-subsets/' + fileName;

                                if (inlineSubsets) {
                                    fontRelation.inline();
                                } else {
                                    var htmlAsset = fontStyleSheetRelation.from;
                                    var preloadRelation = new AssetGraph.HtmlPreloadLink({
                                        hrefType: 'rootRelative',
                                        from: htmlAsset,
                                        to: fontAsset
                                    });

                                    preloadRelation.attach(htmlAsset, 'before', fontStyleSheetRelations[0]);
                                }
                            });
                        }
                    });

                    var source = fontStyleSheetRelations.map(function (relation) {
                        return relation.to.text;
                    }).join('\n');

                    var fontStyleBundle = assetGraph.findAssets({ type: 'Css', isLoaded: true, url: /google-font-subsets\/fonts-/, text: source })[0];

                    if (!fontStyleBundle) {
                        fontStyleBundle = new AssetGraph.Css(assetGraph.resolveAssetConfig({
                            text: source
                        }));
                        assetGraph.addAsset(fontStyleBundle);
                        fontStyleBundle.url = assetGraph.root + 'google-font-subsets/fonts-' + fontStyleBundle.md5Hex.slice(0, 10) + '.css';

                        fontStyleBundle.outgoingRelations.forEach(function (fontRelation) {
                            fontRelation.to = assetGraph.findAssets({ url: fontRelation.to.url })[0];

                            fontRelation.refreshHref();
                        });
                    }

                    var bundleRelation = new AssetGraph.HtmlStyle({
                        hrefType: 'rootRelative',
                        from: htmlAsset,
                        to: fontStyleBundle
                    });

                    bundleRelation.attach(htmlAsset, 'before', fontStyleSheetRelations[0]);

                    fontStyleSheetRelations.forEach(function (relation) {
                        relation.detach();
                    });
                });

                assetGraph.findAssets({ type: 'Css', url: /google-font-subsets\/fonts-/ }).forEach(function (CssBundle) {
                    CssBundle.outgoingRelations.forEach(function (fontFaceSrc) {
                        fontFaceSrc.hrefType = 'relative';
                    });
                });
            })
            .removeUnreferencedAssets({ type: 'Css', url: googleFontsUrlRegex })
            .queue(function renameSubsetFonts() {
                // Inject subset font name before original webfomt
                assetGraph.findAssets({type: 'Css', isLoaded: true }).forEach(function (cssAsset) {
                    var changesMade = false;
                    cssAsset.eachRuleInParseTree(function (cssRule) {
                        if (cssRule.type === 'decl' && cssRule.prop === 'font-family') {
                            if (cssRule.parent.type === 'rule') {
                                var fontFamilies = cssListHelpers.splitByCommas(cssRule.value).map(unquote);
                                var subsetFontFamily = googleFontSubsetNameMap[fontFamilies[0]];
                                if (subsetFontFamily && fontFamilies.indexOf(subsetFontFamily) === -1) {
                                    cssRule.value = cssQuoteIfNecessary(subsetFontFamily) + ', ' + cssRule.value;
                                    changesMade = true;
                                }
                            }

                            if (cssRule.parent.type === 'atrule' && cssRule.parent.name === 'font-face') {
                                cssRule.value = cssQuoteIfNecessary(googleFontSubsetNameMap[unquote(cssRule.value)] || cssRule.value);
                                changesMade = true;
                            }
                        }
                    });
                    if (changesMade) {
                        cssAsset.markDirty();
                    }
                });
            })
            .then(function (assetGraph) {
                // Undo User-Agent override
                assetGraph.teepee.headers['User-Agent'] = assetGraphUA;

                cb();
            });
    };
};
<|endoftext|>


<commit_before>/*! wpt - v4.0.3 - 2013-09-09\n* Copyright (c) 2013 Billy Vong <billy@woopra.com>; Licensed  */(function(t,i){"use strict";var e,n,o,a={},s=[],r=!0,c=!0;a.CONSTANTS={VERSION:11,ENDPOINT:"//www.woopra.com/track/"},a.extend=function(t,i){for(var e in i)t[e]=i[e]},a.cookie=function(t,e,n){if(void 0!==e){var o=n||{};if("number"==typeof o.expires){var a=o.expires,s=o.expires=new Date;s.setDate(s.getDate()+a)}return i.cookie=[encodeURIComponent(t),"=",encodeURIComponent(e),o.expires?"; expires="+o.expires.toUTCString():"",o.path?"; path="+o.path:"",o.domain?"; domain="+o.domain:"",o.secure?"; secure":""].join("")}for(var r=function(t){return decodeURIComponent(t.replace(/\+/g," "))},c=function c(t){0===t.indexOf('"')&&(t=t.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return t}catch(i){}},h=i.cookie.split("; "),p=t?void 0:{},l=0,u=h.length;u>l;l++){var g=h[l].split("="),d=r(g.shift()),f=r(g.join("="));if(t&&t===d){p=c(f);break}t||(p[d]=c(f))}return p},a.getCampaignData=function(){for(var t,i,e=a.getUrlParams(),n={},o=["source","medium","content","campaign","term"],s=0;o.length>s;s++)t=o[s],i=e["utm_"+t]||e["woo_"+t],i!==void 0&&(n["campaign_"+("campaign"===t?"name":t)]=i);return n},a.getCustomData=function(t,i){var e,n,o,s=a.getUrlParams(),r=i||"wv_";for(e in s)s.hasOwnProperty(e)&&(o=s[e],e.substring(0,r.length)===r&&(n=e.substring(r.length),t.call(this,n,o)))},a.getVisitorUrlData=function(t){a.getCustomData.call(t,t.identify,"wv_")},a.hideCampaignData=function(){var i=t.location.search.replace(/[?&]+((?:wv_|woo_|utm_)[^=&]+)=([^&]*)/gi,"");"?"!==i.substring(0,1)&&""!==i&&(i="?"+i),t.history&&t.history.replaceState&&t.history.replaceState(null,null,t.location.pathname+i)},a.getUrlParams=function(){var i={};return t.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(t,e,n){i[e]=decodeURIComponent(n.split("+").join(" "))}),i},a.buildUrlParams=function(t,i){var e,n=i||"",o=[];if(t===void 0)return t;for(e in t)t.hasOwnProperty(e)&&o.push(n+encodeURIComponent(e)+"="+encodeURIComponent(t[e]));return o.join("&")},a.randomString=function(){var t,i,e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",n="";for(t=0;12>t;t++)i=Math.floor(Math.random()*e.length),n+=e.substring(i,i+1);return n},a.loadScript=function(t,e){var n,o,s=i.createElement("script");s.type="text/javascript",s.src=t,s.async=!0,e&&"function"==typeof e&&(o=e),s.onreadystatechange!==void 0?s.onreadystatechange=function(){(4===this.readyState||"complete"===this.readyState||"loaded"===this.readyState)&&o&&o(),a.removeScript(s)}:(s.onload=function(){o&&o(),a.removeScript(s)},s.onerror=function(){a.removeScript(s)}),n=i.getElementsByTagName("script")[0],n.parentNode.insertBefore(s,n)},a.removeScript=function(t){t&&t.parentNode&&t.parentNode.removeChild(t)},a.getHost=function(){return t.location.host.replace("www.","")},a.endsWith=function(t,i){return-1!==t.indexOf(i,t.length-i.length)},a.sleep=function(t){for(var i=new Date,e=new Date;t>e-i;)e=new Date},e=a._on=function(t,i){s[t]||(s[t]=[]),s[t].push(i)},a._fire=function(t){var i;if(s[t])for(i=0;s[t].length>i;i++)s[t][i].apply(this,Array.prototype.slice.call(arguments,1))},a.attachEvent=function(i,e,n){var o,a,s;t.document.attachEvent!==void 0?(o="attachEvent",a="on"+e):(o="addEventListener",a=e,s=!1),i[o](a,n,s)},a.leftClick=function(i){i=i||t.event;var e=i.which||i.button!==void 0&&0===i.button;return e&&!i.metaKey&&!i.altKey&&!i.ctrlKey&&!i.shiftKey},function(e,s){e(i,"mousedown",function(t){s("mousemove",t,new Date)}),e(i,"click",function(i){var e,h,p,l,u="_blank";if(e=i.srcElement||i.target,r||c){for(;e!==void 0&&null!==e&&(!e.tagName||"a"!==e.tagName.toLowerCase());)e=e.parentNode;e!==void 0&&null!==e&&(h=e,p=h.pathname.match(/(?:doc|dmg|eps|svg|xls|ppt|pdf|xls|zip|txt|vsd|vxd|js|css|rar|exe|wma|mov|avi|wmv|mp3|mp4|m4v)($|\&)/),l=!1,r&&p&&(s("download",h.href),h.target!==u&&a.leftClick(i)&&t.setTimeout(function(){a.sleep(n)},0)),c&&!p&&h.hostname!==t.location.hostname&&-1===h.hostname.indexOf("javascript")&&""!==h.hostname&&(s("outgoing",h.href),h.target!==u&&a.leftClick(i)&&t.setTimeout(function(){a.sleep(o)},0)))}}),e(i,"mousemove",function(t){s("mousemove",t,new Date)}),e(i,"keydown",function(){s("keydown")})}(a.attachEvent,a._fire);var h=function(i){this.visitorData={},this.sessionData={},this.options={},this.instanceName=i,this.idle=0,this.cookie="",this.last_activity=new Date,this.loaded=!1,this.dirtyCookie=!1,this.version=a.CONSTANTS.VERSION,i&&""!==i&&(t[i]=this)};if(h.prototype={init:function(){this._setOptions(),this._processQueue("config"),this._setupCookie(),this._bindEvents(),this._processQueue(),this.loaded=!0},_setOptions:function(){var t=new Date;t.setDate(t.getDate()+365),this.config({domain:a.getHost(),use_cookies:!0,cookie_name:"wooTracker",cookie_domain:null,cookie_path:"/",cookie_expire:t,ping:!0,ping_interval:12e3,idle_timeout:3e5,idle_threshold:1e4,download_pause:n||200,outgoing_pause:o||200,download_tracking:!0,outgoing_tracking:!0,hide_campaign:!1,ignore_query_url:!0})},_processQueue:function(i){var e,n,o,a=t._w[this.instanceName];if(a&&a._e)for(o=a._e,e=0;o.length>e;e++)n=o[e],void 0===n||!this[n[0]]||void 0!==i&&i!==n[0]||this[n[0]].apply(this,Array.prototype.slice.call(n,1))},_setupCookie:function(){this.cookie=a.cookie(this.config("cookie_name")),this.cookie&&this.cookie.length>0||(this.cookie=a.randomString()),null===this.config("cookie_domain")&&(a.endsWith(t.location.host,"."+this.config("domain"))?this.config("cookie_domain",this.config("domain")):this.config("cookie_domain",a.getHost())),a.cookie(this.config("cookie_name"),this.cookie,{expires:this.config("cookie_expire"),path:this.config("cookie_path"),domain:this.config("cookie_domain")}),this.dirtyCookie=!0},_bindEvents:function(){var t=this;e("mousemove",function(){t.moved.apply(t,arguments)}),e("keydown",function(){t.typed.apply(t,arguments)}),e("download",function(){t.downloaded.apply(t,arguments)}),e("outgoing",function(){t.outgoing.apply(t,arguments)})},_dataSetter:function(t,i,e){var n;if(i===void 0)return t;if(e===void 0){if("string"==typeof i)return t[i];if("object"==typeof i)for(n in i)i.hasOwnProperty(n)&&("cookie_"===n.substring(0,7)&&(this.dirtyCookie=!0),t[n]=i[n])}else"cookie_"===i.substring(0,7)&&(this.dirtyCookie=!0),t[i]=e;return this},_push:function(t){var i,e,n,o,s,r=t||{},c=this.config("protocol"),h=c&&""!==c?c+":":"",p=h+a.CONSTANTS.ENDPOINT+r.endpoint+"/",l="ra="+a.randomString(),u=[["visitorData","cv_"],["eventData","ce_"],["sessionData","cs_"]],g=[];a.getVisitorUrlData(this),this.config("hide_campaign")&&a.hideCampaignData(),g.push(l),g.push(a.buildUrlParams(this.getOptionParams()));for(s in u)u.hasOwnProperty(s)&&(o=u[s],r[o[0]]&&(e=a.buildUrlParams(r[o[0]],o[1]),e&&g.push(e)));i="?"+g.join("&"),n=p+i,a.loadScript(n,r.callback)},config:function(t,i){var e=this._dataSetter(this.options,t,i);return e===this&&(6e3>this.options.ping_interval?this.options.ping_interval=6e3:this.options.ping_interval>6e4&&(this.options.ping_interval=6e4),c=c&&this.options.outgoing_tracking,o=this.options.outgoing_pause,r=r&&this.options.download_tracking,n=this.options.download_pause,this.dirtyCookie&&this.loaded&&this._setupCookie()),e},visit:function(t,i){return this._dataSetter(this.sessionData,t,i)},identify:function(t,i){return this._dataSetter(this.visitorData,t,i)},call:function(t){this[t]&&"function"==typeof this[t]&&this[t].apply(this,Array.prototype.slice.call(arguments,1))},track:function(t,i){var e,n={},o=arguments[arguments.length-1];a.extend(n,a.getCampaignData()),"function"==typeof o&&(e=o),t===void 0||t===e?n.name="pv":i===void 0||i===e?("string"==typeof t&&(n.name=t),"object"==typeof t&&this._dataSetter(n,t)):(this._dataSetter(n,i),n.name=t),"pv"===n.name&&(n.url=n.url||this.getPageUrl(),n.title=n.title||this.getPageTitle()),this._push({endpoint:"ce",visitorData:this.visitorData,sessionData:this.sessionData,eventData:n,callback:e}),this.startPing()},startPing:function(){var i=this;this.pingInterval===void 0&&(this.pingInterval=t.setInterval(function(){i.ping()},this.config("ping_interval")))},stopPing:function(){this.pingInterval!==void 0&&(t.clearInterval(this.pingInterval),delete this.pingInterval)},ping:function(){var t;return this.config("ping")&&this.idle<this.config("idle_timeout")?this._push({endpoint:"ping"}):this.stopPing(),t=new Date,t-this.last_activity>this.config("idle_threshold")&&(this.idle=t-this.last_activity),this},push:function(t){return this._push({endpoint:"identify",visitorData:this.visitorData,sessionData:this.sessionData,callback:t}),this},sleep:function(t){a.sleep(t)},moved:function(t,i){this.last_activity=i,this.idle=0},typed:function(){this.vs=2},downloaded:function(t){this.track("download",{url:t})},outgoing:function(t){this.track("outgoing",{url:t})},getPageUrl:function(){return this.options.ignore_query_url?t.location.pathname:t.location.pathname+t.location.search},getPageTitle:function(){return 0===i.getElementsByTagName("title").length?"":i.getElementsByTagName("title")[0].innerHTML},getOptionParams:function(){var e={alias:this.config("domain"),instance:this.instanceName,ka:this.config("keep_alive")||2*this.config("ping_interval"),meta:a.cookie("wooMeta")||"",screen:t.screen.width+"x"+t.screen.height,language:t.navigator.browserLanguage||t.navigator.language||"",referer:i.referrer,idle:""+parseInt(this.idle/1e3,10),vs:"i"};return this.config("use_cookies")&&(e.cookie=a.cookie(this.config("cookie_name"))),this.config("ip")&&(e.ip=this.config("ip")),2===this.vs?(e.vs="w",this.vs=0):0===this.idle&&(e.vs="r"),e},dispose:function(){this.stopPing(),t[this.instanceName]!==void 0&&delete t[this.instanceName]}},t._w!==void 0)for(var p in t._w)if(t._w.hasOwnProperty(p)){var l=new h(p);l.init(),t.woopraTracker===void 0&&(t.woopraTracker=l)}t.WoopraTracker=h,t.WoopraLoadScript=a.loadScript,t.exports!==void 0&&(a.Tracker=h,t.exports.Woopra=a)})(window,document);<commit_msg>Update build<commit_after>/*! wpt - v4.0.3 - 2013-09-09\n* Copyright (c) 2013 Billy Vong <billy@woopra.com>; Licensed  */(function(t,i){"use strict";var e,n,o,a={},s=[],r=!0,c=!0;a.CONSTANTS={VERSION:11,ENDPOINT:"//www.woopra.com/track/"},a.extend=function(t,i){for(var e in i)t[e]=i[e]},a.cookie=function(t,e,n){if(void 0!==e){var o=n||{};if("number"==typeof o.expires){var a=o.expires,s=o.expires=new Date;s.setDate(s.getDate()+a)}return i.cookie=[encodeURIComponent(t),"=",encodeURIComponent(e),o.expires?"; expires="+o.expires.toUTCString():"",o.path?"; path="+o.path:"",o.domain?"; domain="+o.domain:"",o.secure?"; secure":""].join("")}for(var r=function(t){return decodeURIComponent(t.replace(/\+/g," "))},c=function c(t){0===t.indexOf('"')&&(t=t.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return t}catch(i){}},h=i.cookie.split("; "),p=t?void 0:{},l=0,u=h.length;u>l;l++){var g=h[l].split("="),d=r(g.shift()),f=r(g.join("="));if(t&&t===d){p=c(f);break}t||(p[d]=c(f))}return p},a.getCampaignData=function(){for(var t,i,e=a.getUrlParams(),n={},o=["source","medium","content","campaign","term"],s=0;o.length>s;s++)t=o[s],i=e["utm_"+t]||e["woo_"+t],i!==void 0&&(n["campaign_"+("campaign"===t?"name":t)]=i);return n},a.getCustomData=function(t,i){var e,n,o,s=a.getUrlParams(),r=i||"wv_";for(e in s)s.hasOwnProperty(e)&&(o=s[e],e.substring(0,r.length)===r&&(n=e.substring(r.length),t.call(this,n,o)))},a.getVisitorUrlData=function(t){a.getCustomData.call(t,t.identify,"wv_")},a.hideCampaignData=function(){var i=t.location.search.replace(/[?&]+((?:wv_|woo_|utm_)[^=&]+)=([^&]*)/gi,"");"?"!==i.substring(0,1)&&""!==i&&(i="?"+i),t.history&&t.history.replaceState&&t.history.replaceState(null,null,t.location.pathname+i)},a.getUrlParams=function(){var i={};return t.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(t,e,n){i[e]=decodeURIComponent(n.split("+").join(" "))}),i},a.buildUrlParams=function(t,i){var e,n=i||"",o=[];if(t===void 0)return t;for(e in t)t.hasOwnProperty(e)&&o.push(n+encodeURIComponent(e)+"="+encodeURIComponent(t[e]));return o.join("&")},a.randomString=function(){var t,i,e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",n="";for(t=0;12>t;t++)i=Math.floor(Math.random()*e.length),n+=e.substring(i,i+1);return n},a.loadScript=function(t,e){var n,o,s=i.createElement("script");s.type="text/javascript",s.src=t,s.async=!0,e&&"function"==typeof e&&(o=e),s.onreadystatechange!==void 0?s.onreadystatechange=function(){(4===this.readyState||"complete"===this.readyState||"loaded"===this.readyState)&&o&&o(),a.removeScript(s)}:(s.onload=function(){o&&o(),a.removeScript(s)},s.onerror=function(){a.removeScript(s)}),n=i.getElementsByTagName("script")[0],n.parentNode.insertBefore(s,n)},a.removeScript=function(t){t&&t.parentNode&&t.parentNode.removeChild(t)},a.getHost=function(){return t.location.host.replace("www.","")},a.endsWith=function(t,i){return-1!==t.indexOf(i,t.length-i.length)},a.sleep=function(t){for(var i=new Date,e=new Date;t>e-i;)e=new Date},e=a._on=function(t,i){s[t]||(s[t]=[]),s[t].push(i)},a._fire=function(t){var i;if(s[t])for(i=0;s[t].length>i;i++)s[t][i].apply(this,Array.prototype.slice.call(arguments,1))},a.attachEvent=function(i,e,n){var o,a,s;t.document.attachEvent!==void 0?(o="attachEvent",a="on"+e):(o="addEventListener",a=e,s=!1),i[o](a,n,s)},a.leftClick=function(i){i=i||t.event;var e=i.which||i.button!==void 0&&0===i.button;return e&&!i.metaKey&&!i.altKey&&!i.ctrlKey&&!i.shiftKey},function(e,s){e(i,"mousedown",function(t){s("mousemove",t,new Date)}),e(i,"click",function(i){var e,h,p,l,u="_blank";if(e=i.srcElement||i.target,r||c){for(;e!==void 0&&null!==e&&(!e.tagName||"a"!==e.tagName.toLowerCase());)e=e.parentNode;e!==void 0&&null!==e&&(h=e,p=h.pathname.match(/(?:doc|dmg|eps|svg|xls|ppt|pdf|xls|zip|txt|vsd|vxd|js|css|rar|exe|wma|mov|avi|wmv|mp3|mp4|m4v)($|\&)/),l=!1,r&&p&&(s("download",h.href),h.target!==u&&a.leftClick(i)&&t.setTimeout(function(){t.location.href=h.href},n)),c&&!p&&h.hostname!==t.location.hostname&&-1===h.hostname.indexOf("javascript")&&""!==h.hostname&&(s("outgoing",h.href),h.target!==u&&a.leftClick(i)&&t.setTimeout(function(){t.location.href=h.href},o)))}}),e(i,"mousemove",function(t){s("mousemove",t,new Date)}),e(i,"keydown",function(){s("keydown")})}(a.attachEvent,a._fire);var h=function(i){this.visitorData={},this.sessionData={},this.options={},this.instanceName=i,this.idle=0,this.cookie="",this.last_activity=new Date,this.loaded=!1,this.dirtyCookie=!1,this.version=a.CONSTANTS.VERSION,i&&""!==i&&(t[i]=this)};if(h.prototype={init:function(){this._setOptions(),this._processQueue("config"),this._setupCookie(),this._bindEvents(),this._processQueue(),this.loaded=!0},_setOptions:function(){var t=new Date;t.setDate(t.getDate()+365),this.config({domain:a.getHost(),use_cookies:!0,cookie_name:"wooTracker",cookie_domain:null,cookie_path:"/",cookie_expire:t,ping:!0,ping_interval:12e3,idle_timeout:3e5,idle_threshold:1e4,download_pause:n||200,outgoing_pause:o||200,download_tracking:!0,outgoing_tracking:!0,hide_campaign:!1,ignore_query_url:!0})},_processQueue:function(i){var e,n,o,a=t._w[this.instanceName];if(a&&a._e)for(o=a._e,e=0;o.length>e;e++)n=o[e],void 0===n||!this[n[0]]||void 0!==i&&i!==n[0]||this[n[0]].apply(this,Array.prototype.slice.call(n,1))},_setupCookie:function(){this.cookie=a.cookie(this.config("cookie_name")),this.cookie&&this.cookie.length>0||(this.cookie=a.randomString()),null===this.config("cookie_domain")&&(a.endsWith(t.location.host,"."+this.config("domain"))?this.config("cookie_domain",this.config("domain")):this.config("cookie_domain",a.getHost())),a.cookie(this.config("cookie_name"),this.cookie,{expires:this.config("cookie_expire"),path:this.config("cookie_path"),domain:this.config("cookie_domain")}),this.dirtyCookie=!0},_bindEvents:function(){var t=this;e("mousemove",function(){t.moved.apply(t,arguments)}),e("keydown",function(){t.typed.apply(t,arguments)}),e("download",function(){t.downloaded.apply(t,arguments)}),e("outgoing",function(){t.outgoing.apply(t,arguments)})},_dataSetter:function(t,i,e){var n;if(i===void 0)return t;if(e===void 0){if("string"==typeof i)return t[i];if("object"==typeof i)for(n in i)i.hasOwnProperty(n)&&("cookie_"===n.substring(0,7)&&(this.dirtyCookie=!0),t[n]=i[n])}else"cookie_"===i.substring(0,7)&&(this.dirtyCookie=!0),t[i]=e;return this},_push:function(t){var i,e,n,o,s,r=t||{},c=this.config("protocol"),h=c&&""!==c?c+":":"",p=h+a.CONSTANTS.ENDPOINT+r.endpoint+"/",l="ra="+a.randomString(),u=[["visitorData","cv_"],["eventData","ce_"],["sessionData","cs_"]],g=[];a.getVisitorUrlData(this),this.config("hide_campaign")&&a.hideCampaignData(),g.push(l),g.push(a.buildUrlParams(this.getOptionParams()));for(s in u)u.hasOwnProperty(s)&&(o=u[s],r[o[0]]&&(e=a.buildUrlParams(r[o[0]],o[1]),e&&g.push(e)));i="?"+g.join("&"),n=p+i,a.loadScript(n,r.callback)},config:function(t,i){var e=this._dataSetter(this.options,t,i);return e===this&&(6e3>this.options.ping_interval?this.options.ping_interval=6e3:this.options.ping_interval>6e4&&(this.options.ping_interval=6e4),c=c&&this.options.outgoing_tracking,o=this.options.outgoing_pause,r=r&&this.options.download_tracking,n=this.options.download_pause,this.dirtyCookie&&this.loaded&&this._setupCookie()),e},visit:function(t,i){return this._dataSetter(this.sessionData,t,i)},identify:function(t,i){return this._dataSetter(this.visitorData,t,i)},call:function(t){this[t]&&"function"==typeof this[t]&&this[t].apply(this,Array.prototype.slice.call(arguments,1))},track:function(t,i){var e,n={},o=arguments[arguments.length-1];a.extend(n,a.getCampaignData()),"function"==typeof o&&(e=o),t===void 0||t===e?n.name="pv":i===void 0||i===e?("string"==typeof t&&(n.name=t),"object"==typeof t&&this._dataSetter(n,t)):(this._dataSetter(n,i),n.name=t),"pv"===n.name&&(n.url=n.url||this.getPageUrl(),n.title=n.title||this.getPageTitle()),this._push({endpoint:"ce",visitorData:this.visitorData,sessionData:this.sessionData,eventData:n,callback:e}),this.startPing()},startPing:function(){var i=this;this.pingInterval===void 0&&(this.pingInterval=t.setInterval(function(){i.ping()},this.config("ping_interval")))},stopPing:function(){this.pingInterval!==void 0&&(t.clearInterval(this.pingInterval),delete this.pingInterval)},ping:function(){var t;return this.config("ping")&&this.idle<this.config("idle_timeout")?this._push({endpoint:"ping"}):this.stopPing(),t=new Date,t-this.last_activity>this.config("idle_threshold")&&(this.idle=t-this.last_activity),this},push:function(t){return this._push({endpoint:"identify",visitorData:this.visitorData,sessionData:this.sessionData,callback:t}),this},sleep:function(t){a.sleep(t)},moved:function(t,i){this.last_activity=i,this.idle=0},typed:function(){this.vs=2},downloaded:function(t){this.track("download",{url:t})},outgoing:function(t){this.track("outgoing",{url:t})},getPageUrl:function(){return this.options.ignore_query_url?t.location.pathname:t.location.pathname+t.location.search},getPageTitle:function(){return 0===i.getElementsByTagName("title").length?"":i.getElementsByTagName("title")[0].innerHTML},getOptionParams:function(){var e={alias:this.config("domain"),instance:this.instanceName,ka:this.config("keep_alive")||2*this.config("ping_interval"),meta:a.cookie("wooMeta")||"",screen:t.screen.width+"x"+t.screen.height,language:t.navigator.browserLanguage||t.navigator.language||"",referer:i.referrer,idle:""+parseInt(this.idle/1e3,10),vs:"i"};return this.config("use_cookies")&&(e.cookie=a.cookie(this.config("cookie_name"))),this.config("ip")&&(e.ip=this.config("ip")),2===this.vs?(e.vs="w",this.vs=0):0===this.idle&&(e.vs="r"),e},dispose:function(){this.stopPing(),t[this.instanceName]!==void 0&&delete t[this.instanceName]}},t._w!==void 0)for(var p in t._w)if(t._w.hasOwnProperty(p)){var l=new h(p);l.init(),t.woopraTracker===void 0&&(t.woopraTracker=l)}t.WoopraTracker=h,t.WoopraLoadScript=a.loadScript,t.exports!==void 0&&(a.Tracker=h,t.exports.Woopra=a)})(window,document);<|endoftext|>


<commit_before>/**
 * @properties={typeid:35,uuid:"2AFDC806-15C2-4F4C-B003-D71264F8119B",variableType:-4}
 */
var _skipSelect = true;

/**
 * @properties={typeid:35,uuid:"4FDACEFD-6F26-46F7-827B-375E25824AD6"}
 */
var _license_dsa_mosaic_WEB_cms = 'Module: _dsa_mosaic_WEB_cms \
									Copyright (C) 2011 Data Mosaic \
									MIT Licensed';

/**
 * @param	{Object}	obj Data object passed to all markup methods
 * @properties={typeid:24,uuid:"F20BDBF7-8CB2-4035-A50B-0785CE98FC00"}
 */
function VIEW_default(obj) {

	// The main CMS page object gets passed to block VIEW methods.
	// So include "obj" in the parameter slot for these methods
	// The objective of VIEW methods is to return markup back to the page request.

	return "Hello world!"
}

/**
 * @properties={typeid:24,uuid:"4E0834F2-0DDC-4364-8CEB-EFC70BEB339E"}
 */
function BLOCK_sample_method() {
	// method of type BLOCK
	// BLOCK type methods are used in the CMS GUI
	// not required
	plugins.dialogs.showInfoDialog( "Block action demo", "Block action just occurred" )

}

/**
 * Update display as needed when block selected.
 *
 * @param 	{JSEvent}	event The event that triggered the action.
 * @param	{Boolean}	[alwaysRun] Force the on select method to refire.
 *
 * @properties={typeid:24,uuid:"D0B09ECA-F3FA-4035-A0DD-62166343DF4E"}
 */
function REC_on_select(event,alwaysRun) {
	//run on select only when it is 'enabled'
	if (alwaysRun || globals.WEBc_block_enable(event)) {

	}
}

/**
 * Build a data object that defines this block. Called when creating a new block.
 * Used to create all meta data associated with this block (register a block in the CMS)
 *
 * @returns Data object that is used to register a block
 * @type object
 *
 * @properties={typeid:24,uuid:"B1A1F180-3F8A-47FE-8D44-F3FFC0CBEE9A"}
 */
function INIT_block() {

	// main data object to build
	var block = {}

	// block record data
	block.record = {
	        block_name			: 'Starter block',
			block_description	: 'Boiler template for creating a custom block',
			form_name			: 'WEB_0F___starter_block'//,
//			form_name_display	: 'WEB_0F___starter_block'	//this line only required when form_name_display different than form_name
		}

	// block views
	block.views = globals.WEBc_block_type_getMethods(controller.getName(),"VIEW")

	// block client actions - Block
	block.clientActionsBlock = globals.WEBc_block_type_getMethods(controller.getName(),"BLOCK")

	// block client actions - Page
	block.clientActionsPage = globals.WEBc_block_type_getMethods(controller.getName(),"PAGE")

	// block web actions
	block.webActions = globals.WEBc_block_type_getMethods(controller.getName(),"WEB")

	// block data points
	block.data = {
		sample : 'TEXT'
	}

	// block configure data points
	block.blockConfigure = {

	}

	// block response data points
	block.blockResponse = {

	}

	return block

}

/**
 * @properties={typeid:24,uuid:"7E3894A3-27A8-4435-AEAA-36891B21E483"}
 */
function PAGE_sample_method() {
	plugins.dialogs.showInfoDialog( "Page action demo", "Block page actions added to this menu")
}

/**
 * @properties={typeid:24,uuid:"CA0EC453-6093-41F1-BB67-534534B10636"}
 */
function BLOCK_save() {
	// your code goes here

	// exit edit mode
	globals.WEBc_block_save()
}

/**
 * @properties={typeid:24,uuid:"7122605B-CB57-4365-852C-5DC97AADE5F1"}
 */
function BLOCK_cancel() {
	// your code goes here

	// exit edit mode
	globals.WEBc_block_cancel()
}
<commit_msg>deprecate block cancel/save global methods<commit_after>/**
 * @properties={typeid:35,uuid:"2AFDC806-15C2-4F4C-B003-D71264F8119B",variableType:-4}
 */
var _skipSelect = true;

/**
 * @properties={typeid:35,uuid:"4FDACEFD-6F26-46F7-827B-375E25824AD6"}
 */
var _license_dsa_mosaic_WEB_cms = 'Module: _dsa_mosaic_WEB_cms \
									Copyright (C) 2011 Data Mosaic \
									MIT Licensed';

/**
 * @param	{Object}	obj Data object passed to all markup methods
 * @properties={typeid:24,uuid:"F20BDBF7-8CB2-4035-A50B-0785CE98FC00"}
 */
function VIEW_default(obj) {

	// The main CMS page object gets passed to block VIEW methods.
	// So include "obj" in the parameter slot for these methods
	// The objective of VIEW methods is to return markup back to the page request.

	return "Hello world!"
}

/**
 * @properties={typeid:24,uuid:"4E0834F2-0DDC-4364-8CEB-EFC70BEB339E"}
 */
function BLOCK_sample_method() {
	// method of type BLOCK
	// BLOCK type methods are used in the CMS GUI
	// not required
	plugins.dialogs.showInfoDialog( "Block action demo", "Block action just occurred" )

}

/**
 * Update display as needed when block selected.
 *
 * @param 	{JSEvent}	event The event that triggered the action.
 * @param	{Boolean}	[alwaysRun] Force the on select method to refire.
 *
 * @properties={typeid:24,uuid:"D0B09ECA-F3FA-4035-A0DD-62166343DF4E"}
 */
function REC_on_select(event,alwaysRun) {
	//run on select only when it is 'enabled'
	if (alwaysRun || globals.WEBc_block_enable(event)) {

	}
}

/**
 * Build a data object that defines this block. Called when creating a new block.
 * Used to create all meta data associated with this block (register a block in the CMS)
 *
 * @returns Data object that is used to register a block
 * @type object
 *
 * @properties={typeid:24,uuid:"B1A1F180-3F8A-47FE-8D44-F3FFC0CBEE9A"}
 */
function INIT_block() {

	// main data object to build
	var block = {}

	// block record data
	block.record = {
	        block_name			: 'Starter block',
			block_description	: 'Boiler template for creating a custom block',
			form_name			: 'WEB_0F___starter_block'//,
//			form_name_display	: 'WEB_0F___starter_block'	//this line only required when form_name_display different than form_name
		}

	// block views
	block.views = globals.WEBc_block_type_getMethods(controller.getName(),"VIEW")

	// block client actions - Block
	block.clientActionsBlock = globals.WEBc_block_type_getMethods(controller.getName(),"BLOCK")

	// block client actions - Page
	block.clientActionsPage = globals.WEBc_block_type_getMethods(controller.getName(),"PAGE")

	// block web actions
	block.webActions = globals.WEBc_block_type_getMethods(controller.getName(),"WEB")

	// block data points
	block.data = {
		sample : 'TEXT'
	}

	// block configure data points
	block.blockConfigure = {

	}

	// block response data points
	block.blockResponse = {

	}

	return block

}

/**
 * @properties={typeid:24,uuid:"7E3894A3-27A8-4435-AEAA-36891B21E483"}
 */
function PAGE_sample_method() {
	plugins.dialogs.showInfoDialog( "Page action demo", "Block page actions added to this menu")
}

/**
 * @properties={typeid:24,uuid:"CA0EC453-6093-41F1-BB67-534534B10636"}
 */
function BLOCK_save() {
	// your code goes here
}

/**
 * @properties={typeid:24,uuid:"7122605B-CB57-4365-852C-5DC97AADE5F1"}
 */
function BLOCK_cancel() {
	// your code goes here
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var express  = require('express'),
	routes   = require('./routes'),
	config   = require('./config'),
	http     = require('http'),
	path     = require('path'),
	Instagram = require('instagram-node-lib'),
	io        = require('socket.io');

// ---
// Setup Instagram

Instagram.set( 'client_id', config.instagram.id );
Instagram.set( 'client_secret', config.instagram.secret );

// ---
// Setup Express

var app = express();

app.configure(function() {
	app.set('port', process.env.PORT || 3000);
	app.set('views', __dirname + '/views');
	app.set('view engine', 'hjs');
	app.use(express.favicon());
	app.use(express.logger('dev'));
	app.use(express.cookieParser());
	app.use(express.bodyParser());
	app.use(express.methodOverride());
	app.use(app.router);
	app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
	app.use(express.errorHandler());
});

// ---
// Request Handlers

app.get('/newphoto', routes.auth.get);
app.post('/newphoto', routes.newphoto.post);

app.get('/', routes.index);

// ---
// Launch server

var server = http.createServer(app).listen(app.get('port'), function(){
	console.log("Express server listening on port " + app.get('port'));
});

// ---
// Sockets

io.listen( server ).sockets.on('connection', function( socket ) {
	setTimeout(function() {
		socket.emit('newphoto', {
			"img":{
				"url":"http://distilleryimage7.s3.amazonaws.com/b97986ac3fb711e2890a22000a1fbc9a_7.jpg",
				"width":612,
				"height":612
			},
			"description":"La pompe #bonparty",
			"author":"Ressac"
		});
	}, 3000);
});

// ---
// Subscribe to instagram

Instagram.tags.subscribe({ object_id: 'bonparty' });
<commit_msg>Typo in router<commit_after>/**
 * Module dependencies.
 */

var express  = require('express'),
	routes   = require('./routes'),
	config   = require('./config'),
	http     = require('http'),
	path     = require('path'),
	Instagram = require('instagram-node-lib'),
	io        = require('socket.io');

// ---
// Setup Instagram

Instagram.set( 'client_id', config.instagram.id );
Instagram.set( 'client_secret', config.instagram.secret );

// ---
// Setup Express

var app = express();

app.configure(function() {
	app.set('port', process.env.PORT || 3000);
	app.set('views', __dirname + '/views');
	app.set('view engine', 'hjs');
	app.use(express.favicon());
	app.use(express.logger('dev'));
	app.use(express.cookieParser());
	app.use(express.bodyParser());
	app.use(express.methodOverride());
	app.use(app.router);
	app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
	app.use(express.errorHandler());
});

// ---
// Request Handlers

app.get('/newphoto', routes.newphoto.get);
app.post('/newphoto', routes.newphoto.post);

app.get('/', routes.index);

// ---
// Launch server

var server = http.createServer(app).listen(app.get('port'), function(){
	console.log("Express server listening on port " + app.get('port'));
});

// ---
// Sockets

io.listen( server ).sockets.on('connection', function( socket ) {
	setTimeout(function() {
		socket.emit('newphoto', {
			"img":{
				"url":"http://distilleryimage7.s3.amazonaws.com/b97986ac3fb711e2890a22000a1fbc9a_7.jpg",
				"width":612,
				"height":612
			},
			"description":"La pompe #bonparty",
			"author":"Ressac"
		});
	}, 3000);
});

// ---
// Subscribe to instagram

Instagram.tags.subscribe({ object_id: 'bonparty' });
<|endoftext|>


<commit_before>function generatePartitions(partitions){

    var rowTemplate =
            '
'+
                'Disk #%s'+
                    '
'+
                    '
'+
                '	'+
                '%sGB of %sGB	'+
                ''+
                    '
'+
                        '
'+
                        '
'+
                    '
'+
                    '
%s%% in use
'+
                '	'+
            '
';

    $(".partition-info").remove();
    var lastElement = $('#partitions');
    for (var i=0; i<partitions.length; i++){
        var spaceUsed = partitions[i].totalSpace-partitions[i].totalFreeSpace;
        var totalSpace = partitions[i].totalSpace;
        var percUsed = sprintf("%01.1f", spaceUsed/totalSpace*100);

        var spaceUsedGb = sprintf("%01.1f", spaceUsed/Math.pow(10, 9));
        var totalSpaceGb = sprintf("%01.1f", totalSpace/Math.pow(10, 9));

        var row = sprintf(rowTemplate, i, i, spaceUsedGb, totalSpaceGb, percUsed, percUsed);
        var tr = $(row);
        lastElement.after(tr);

        var watched_dirs_ul = $('#watched-dir-list-'+i);
        for (var j=0; j<partitions[i].dirs.length; j++){
            watched_dirs_ul.append('
'+partitions[i].dirs[j]+'
');
        }
        lastElement = tr;
    }

}

function success(data, textStatus, jqXHR){
    var services = data.status.services;

    console.log(data.status);

    for (var key in services){
        var s = services[key];
        var children = $("#"+s.name).children();
        $(children[0]).text(s.name);
        $($(children[1]).children()[0]).attr("class", s.status ? "checked-icon": "not-available-icon");
        $(children[2]).text(sprintf('%(days)sd %(hours)sh %(minutes)sm %(seconds)ss', convertSecondsToDaysHoursMinutesSeconds(s.uptime_seconds)));
        $(children[3]).text(s.cpu_perc);
        $(children[4]).text(sprintf('%01.1fMB (%s)', parseInt(s.memory_kb)/1000, s.memory_perc));
    }

    generatePartitions(data.status.partitions);
}

function updateStatus(){
    $.getJSON( "api/status/format/json", null, success);

}

$(document).ready(function() {
    updateStatus();
    setInterval(updateStatus, 5000);
});
<commit_msg>CC-2363: Diagnostic screen in Web UI<commit_after>function generatePartitions(partitions){

    var rowTemplate =
            '
'+
                'Disk #%s'+
                    '
'+
                    '
'+
                '	'+
                '%sGB of %sGB	'+
                ''+
                    '
'+
                        '
'+
                        '
'+
                    '
'+
                    '
%s%% in use
'+
                '	'+
            '
';

    $(".partition-info").remove();
    var lastElement = $('#partitions');
    for (var i=0; i<partitions.length; i++){
        var spaceUsed = partitions[i].totalSpace-partitions[i].totalFreeSpace;
        var totalSpace = partitions[i].totalSpace;
        var percUsed = sprintf("%01.1f", spaceUsed/totalSpace*100);

        var spaceUsedGb = sprintf("%01.1f", spaceUsed/Math.pow(10, 9));
        var totalSpaceGb = sprintf("%01.1f", totalSpace/Math.pow(10, 9));

        var row = sprintf(rowTemplate, i, i, spaceUsedGb, totalSpaceGb, percUsed, percUsed);
        var tr = $(row);
        lastElement.after(tr);

        var watched_dirs_ul = $('#watched-dir-list-'+i);
        for (var j=0; j<partitions[i].dirs.length; j++){
            watched_dirs_ul.append('
'+partitions[i].dirs[j]+'
');
        }
        lastElement = tr;
    }

}

function success(data, textStatus, jqXHR){
    var services = data.status.services;

    for (var key in services){
        var s = services[key];
        var children = $("#"+s.name).children();
        $(children[0]).text(s.name);
        $($(children[1]).children()[0]).attr("class", s.status ? "checked-icon": "not-available-icon");
        $(children[2]).text(sprintf('%(days)sd %(hours)sh %(minutes)sm %(seconds)ss', convertSecondsToDaysHoursMinutesSeconds(s.uptime_seconds)));
        $(children[3]).text(s.cpu_perc);
        $(children[4]).text(sprintf('%01.1fMB (%s)', parseInt(s.memory_kb)/1000, s.memory_perc));
    }

    generatePartitions(data.status.partitions);
}

function updateStatus(){
    $.getJSON( "api/status/format/json", null, success);

}

$(document).ready(function() {
    updateStatus();
    setInterval(updateStatus, 5000);
});
<|endoftext|>


<commit_before>const fetchVideoInfo = require('youtube-info');
const querystring = require('querystring');
const config = require('./config');
const util = require('./util');
const cmd = require('node-cmd');

function Youtube(url, email, anhao) {
    if (/youtu.be/.test(url)) {
        url = url.split('be/');
        let id = url[1];
        dealWithUrl(id, email, anhao);
    } else {
        url = url.split('?')[1];
        url = querystring.parse(url);
        let id = url.v;
        dealWithUrl(id, email, anhao);
    }
}

function dealWithUrl(videoId, email, anhao) {
    fetchVideoInfo(videoId)
        .then((videoInfo) => {
            cmdDown(videoInfo.title, email, videoId, anhao);
        });
}

function makeid() {
    let text = "";
    const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for (var i = 0; i < 7; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}

function makeHtml(filename, url) {
    let eStr = ['
'];
    eStr.push('您要的' + filename);
    eStr.push('
');
    eStr.push('' + url + '');
    eStr.push('
');
    return eStr.join('');
}

function cmdDown(filename, email, id, anhao) {
    const url = `https://www.youtube.com/watch?v=${id}`;
    const randomname = makeid();
    let _cmd = `
            cd /home/download
            youtube-dl -f 18 ${url} -o ${randomname}.mp4
            `;
    if (anhao === 'you-get') {
        _cmd = `
            cd /home/download
            you-get ${url} -O ${randomname}
        `;
    }

    cmd.get(
        _cmd,
        function (err, data, stderr) {
            let url = config.baseUrl + randomname + '.mp4';
            url = url.replace(/\.download/, '');
            const html = makeHtml(filename, url);
            util.sendMail({
                "to": email,
                "subject": "主人，视频已经帮你下载好了。",
                "html": html
            });
        }
    );
}
module.exports = Youtube;
<commit_msg>fix url.<commit_after>const fetchVideoInfo = require('youtube-info');
const querystring = require('querystring');
const config = require('./config');
const util = require('./util');
const cmd = require('node-cmd');

function Youtube(url, email, anhao) {
    if (/youtu.be/.test(url)) {
        url = url.split('be/');
        let id = url[1];
        dealWithUrl(id, email, anhao);
    } else {
        url = url.split('?')[1];
        url = querystring.parse(url);
        let id = url.v;
        dealWithUrl(id, email, anhao);
    }
}

function dealWithUrl(videoId, email, anhao) {
    fetchVideoInfo(videoId)
        .then((videoInfo) => {
            cmdDown(videoInfo.title, email, videoId, anhao);
        });
}

function makeid() {
    let text = "";
    const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for (var i = 0; i < 7; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}

function makeHtml(filename, url) {
    let eStr = ['
'];
    eStr.push('您要的' + filename);
    eStr.push('
');
    eStr.push('' + url + '');
    eStr.push('
');
    return eStr.join('');
}

function cmdDown(filename, email, id, anhao) {
    const url = `https://www.youtube.com/watch?v=${id}`;
    const randomname = makeid();
    let _cmd = `
            cd /home/download
            youtube-dl -f 18 ${url} -o ${randomname}.mp4
            `;
    if (anhao === 'you-get') {
        _cmd = `
            cd /home/download
            you-get ${url} -O ${randomname}
        `;
    }

    cmd.get(
        _cmd,
        function (err, data, stderr) {
            let url = config.baseUrl + randomname + '.mp4';
            const html = makeHtml(filename, url);
            util.sendMail({
                "to": email,
                "subject": "主人，视频已经帮你下载好了。",
                "html": html
            });
        }
    );
}
module.exports = Youtube;
<|endoftext|>


<commit_before>Object.isNullOrUndefined = function (value) {
	return (typeof (value) === "undefined" || value === null);
};

String.isNullOrEmpty = function (value) {
	return (Object.isNullOrUndefined(value) || value === "");
};

String.toBoolean = function(value) {
    if (typeof value === "string") {
        var str = value.toLowerCase();
        return str === "true" || str === "yes" || str === "on" || str === "1";
    }
    return value === true || value === 1;
},

String.Empty = "";

Object.IsArray = function (obj) {
    if (obj && typeof obj === 'object' && obj.constructor === Array) {
        return true;
    }
    return false;
};

if (typeof Object.getOwnPropertyNames === "undefined") {
    Object.getOwnPropertyNames = function(obj) {
        var ownProperties = [];
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                ownProperties.push(key);
            }
        }
        return ownProperties;
    };
};

if (typeof String.toCamel === "undefined") {
	String.toCamel = function (text) {
		return String.isNullOrEmpty(text) ? text : (text.charAt(0).toLowerCase() + text.substr(1));
	};
};

if (typeof Object.ToCamel === "undefined") {
    Object.ToCamel = function(o) {
        var camelizedObject;
        if (Object.IsArray(o)) {
            camelizedObject = [];
            for (var i = 0, size = o.length; i < size; i++) {
                camelizedObject.push(Object.ToCamel(o[i]));
            }
        }
        else {
            camelizedObject = {};
            var propertyValue;
            for (var propertyName in o) {
                if (Object.prototype.hasOwnProperty.call(o, propertyName) && typeof(propertyName) === "string") {
                    propertyValue = o[propertyName];
                    if (typeof(propertyValue) === "object" && propertyValue !== null) {
                        propertyValue = Object.ToCamel(propertyValue);
                    }
                    camelizedObject[String.toCamel(propertyName)] = propertyValue;
                }
            }
        }
        return camelizedObject;
    };
};

String.prototype.append = function (stringToAppend, separator) {
	if (String.isNullOrEmpty(stringToAppend)) {
		return this.toString();
	}
	else if (String.isNullOrEmpty(this.toString())) {
		return stringToAppend;
	}
	else {
		var result = [];
		result[0] = this;
		result[1] = stringToAppend;
		return result.join(separator);
	}
};

if (typeof String.prototype.trim !== 'function') {
	String.prototype.trim = function() { return this.replace( /^\s\s*/ , '').replace( /\s\s*$/ , ''); };
}

if (typeof String.prototype.ltrim !== 'function') {
	String.prototype.ltrim = function() { return this.replace( /^\s+/ , ''); };
}

if (typeof String.prototype.rtrim !== 'function') {
	String.prototype.rtrim = function() { return this.replace( /\s+$/ , ''); };
}

if (typeof String.prototype.fulltrim !== 'function') {
	String.prototype.fulltrim = function() { return this.replace( /(?:(?:^|\n)\s+|\s+(?:$|\n))/g , '').replace( /\s+/g , ' '); };
}

if (typeof String.prototype.startsWith !== 'function') {
    String.prototype.startsWith = function (str) {
        return this.slice(0, str.length) === str;
    };
}

if (typeof Array.prototype.insertAt !== "function") {
    Array.prototype.insertAt = function (index) {
        this.splice.apply(this, [index, 0].concat(
            Array.prototype.slice.call(arguments, 1)));
        return this;
    };
}

if (typeof Array.prototype.peek !== "function") {
    Array.prototype.peek = function () {
        return this[this.length - 1];
    };
}

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(searchElement /*, fromIndex */) {
        if (this == null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (len === 0) {
            return -1;
        }
        var n = 0;
        if (arguments.length > 1) {
            n = Number(arguments[1]);
            if (n != n) { // shortcut for verifying if it's NaN
                n = 0;
            } else if (n != 0 && n != Infinity && n != -Infinity) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) {
            return -1;
        }
        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
        for (; k < len; k++) {
            if (k in t && t[k] === searchElement) {
                return k;
            }
        }
        return -1;
    };
}

if (typeof dnn === "undefined" || dnn === null) { dnn = {}; }; //var dnn = dnn || {};

dnn.Enum = function (keyValuePairs) {
    for (var i in keyValuePairs) {
        if (Object.prototype.hasOwnProperty.call(keyValuePairs, i)) {
            this[keyValuePairs[i].value] = keyValuePairs[i].key;
        }
    }
};

dnn.Enum.prototype.getEnumName = function(enumValue) {
    for (var propertyName in this) {
        if (Object.prototype.hasOwnProperty.call(this, propertyName) && enumValue === this[propertyName]) {
            return propertyName;
        }
    }
    return "";
};

dnn.SortOrder = new dnn.Enum([{ key: 0, value: "unspecified" }, { key: 1, value: "ascending" }, { key: 2, value: "descending" }]);

dnn.executeFunctionByName = function(functionName, context /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for (var i = 0, size = namespaces.length; i < size; i++) {
        context = context[namespaces[i]];
    }
    if (typeof context[func] === "function") {
        return context[func].apply(context, args);
    }
    return null;
};

dnn.singletonify = function(constructorFunc /*, args */) {
    var instance = null;
    var args = Array.prototype.slice.call(arguments, 1);

    return new function () {
        this.getInstance = function () {
            var instanceArguments;
            var f;
            if (instance == null) {
                instanceArguments = Array.prototype.slice.call(arguments);
                f = function() {
                    return constructorFunc.apply(this, [].concat(args, instanceArguments));
                };
                f.prototype = constructorFunc.prototype;
                instance = new f();
            }
            return instance;
        };
    };
};

dnn.extend = function(child, parent) {
    var tempConstructor = function() {};
    tempConstructor.prototype = parent.prototype;
    child.prototype = new tempConstructor();
    child.prototype.constructor = child;
    child.superclass = parent.prototype;
};

(function ($, window, document, undefined) {
    "use strict";

    var KeyValueConverter = this.KeyValueConverter = function () {
    };

    KeyValueConverter.arrayToDictionary = function (pairs, keyProp, valProp) {
        var dictionary = new dnn.Dictionary();
        if (pairs && pairs.length > 0) {
            for (var i = 0, size = pairs.length; i < size; i++) {
                dictionary.set(pairs[i][keyProp], pairs[i][valProp]);
            }
        }
        return dictionary;
    };

    KeyValueConverter.dictionaryToArray = function (dictionary, keyProp, valProp) {
        var pairs = [];
        if (dictionary) {
            for (var key in dictionary) {
                var pair = {};
                pair[keyProp] = key;
                pair[valProp] = dictionary[key];
                pairs.push(pair);
            }
        }
        return pairs;
    };

    KeyValueConverter.getKeyValuePairByKey = function (pairs, key) {
        if (!pairs) {
            return null;
        }
        for (var i = 0, size = pairs.length; i < size; i++) {
            if (pairs[i].key === key) {
                return pairs[i];
            }
        }
        return null;
    };

}).apply(dnn, [jQuery, window, document]);

if (typeof Date.prototype.format === "undefined") {
    Date.prototype.format = function(pattern) {
        var hours = this.getHours();
        var ttime = "AM";
        if (pattern.indexOf("t") > -1 && hours > 12) {
            hours = hours - 12;
            ttime = "PM";
        }

        var o = {
            "M+": this.getMonth() + 1, //month
            "d+": this.getDate(), //day
            "h+": hours, //hour
            "m+": this.getMinutes(), //minute
            "s+": this.getSeconds(), //second
            "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
            "S": this.getMilliseconds(), //millisecond,
            "t+": ttime
        };

        if (/(y+)/.test(pattern)) {
            pattern = pattern.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (var k in o) {
            if (new RegExp("(" + k + ")").test(pattern)) {
                pattern = pattern.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
            }
        }
        return pattern;
    };
}

/* Compare the current date against another date.
     *
     * @param b  {Date} the other date
     * @returns   -1 : if this < b
     *             0 : if this === b
     *             1 : if this > b
     *            NaN : if a or b is an illegal date
    */
if (typeof Date.prototype.compare === "undefined") {
    Date.prototype.compare = function (b) {
        if (b.constructor !== Date) {
            throw "invalid_date";
        }
        return (isFinite(this.valueOf()) && isFinite(b.valueOf()) ?
                    (this > b) - (this < b) : NaN);
    };
}

//Returns true if it is a DOM element
if (typeof Object.isElement === "undefined") {
    Object.isElement = function (o) {
        return typeof HTMLElement === "object" ?
                o instanceof HTMLElement : //DOM2
                o && typeof o === "object" && o.nodeType === 1 && typeof o.nodeName === "string";
    };
}

dnn.removeElement = function (element) {
    if (element && element.parentNode) {
        element.parentNode.removeChild(element);
    }
};

/*
 * Generates a GUID string, according to RFC4122 standards.
 * @returns {String} The generated GUID like "af8a8416-6e18-a307-bd9c-f2c947bbb3aa"
 */
dnn.guid = (function() {
    var partOf8 = function (dashed) {
        var part = (Math.random().toString(16) + "000000000").substr(2, 8);
        return dashed ? "-" + part.substr(0, 4) + "-" + part.substr(4, 4) : part;
    };
    return function () { return partOf8(false) + partOf8(true) + partOf8(true) + partOf8(false); };
})();

dnn.uid = (function () {
    var id = (new Date()).getTime();
    return function (prefix) {
        return (prefix || "id") + (id++);
    };
})();

dnn.isUrl = function(url) {
    var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    return regexp.test(url);
};
<commit_msg>DNN-4267 DNNMenuProvider is broken fixed issue with clashing javascript name collison<commit_after>Object.isNullOrUndefined = function (value) {
	return (typeof (value) === "undefined" || value === null);
};

String.isNullOrEmpty = function (value) {
	return (Object.isNullOrUndefined(value) || value === "");
};

String.toBoolean = function(value) {
    if (typeof value === "string") {
        var str = value.toLowerCase();
        return str === "true" || str === "yes" || str === "on" || str === "1";
    }
    return value === true || value === 1;
},

String.Empty = "";

Object.IsArray = function (obj) {
    if (obj && typeof obj === 'object' && obj.constructor === Array) {
        return true;
    }
    return false;
};

if (typeof Object.getOwnPropertyNames === "undefined") {
    Object.getOwnPropertyNames = function(obj) {
        var ownProperties = [];
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                ownProperties.push(key);
            }
        }
        return ownProperties;
    };
};

if (typeof String.toCamel === "undefined") {
	String.toCamel = function (text) {
		return String.isNullOrEmpty(text) ? text : (text.charAt(0).toLowerCase() + text.substr(1));
	};
};

if (typeof Object.ToCamel === "undefined") {
    Object.ToCamel = function(o) {
        var camelizedObject;
        if (Object.IsArray(o)) {
            camelizedObject = [];
            for (var i = 0, size = o.length; i < size; i++) {
                camelizedObject.push(Object.ToCamel(o[i]));
            }
        }
        else {
            camelizedObject = {};
            var propertyValue;
            for (var propertyName in o) {
                if (Object.prototype.hasOwnProperty.call(o, propertyName) && typeof(propertyName) === "string") {
                    propertyValue = o[propertyName];
                    if (typeof(propertyValue) === "object" && propertyValue !== null) {
                        propertyValue = Object.ToCamel(propertyValue);
                    }
                    camelizedObject[String.toCamel(propertyName)] = propertyValue;
                }
            }
        }
        return camelizedObject;
    };
};

String.prototype.append = function (stringToAppend, separator) {
	if (String.isNullOrEmpty(stringToAppend)) {
		return this.toString();
	}
	else if (String.isNullOrEmpty(this.toString())) {
		return stringToAppend;
	}
	else {
		var result = [];
		result[0] = this;
		result[1] = stringToAppend;
		return result.join(separator);
	}
};

if (typeof String.prototype.trim !== 'function') {
	String.prototype.trim = function() { return this.replace( /^\s\s*/ , '').replace( /\s\s*$/ , ''); };
}

if (typeof String.prototype.ltrim !== 'function') {
	String.prototype.ltrim = function() { return this.replace( /^\s+/ , ''); };
}

if (typeof String.prototype.rtrim !== 'function') {
	String.prototype.rtrim = function() { return this.replace( /\s+$/ , ''); };
}

if (typeof String.prototype.fulltrim !== 'function') {
	String.prototype.fulltrim = function() { return this.replace( /(?:(?:^|\n)\s+|\s+(?:$|\n))/g , '').replace( /\s+/g , ' '); };
}

if (typeof String.prototype.startsWith !== 'function') {
    String.prototype.startsWith = function (str) {
        return this.slice(0, str.length) === str;
    };
}

if (typeof Array.prototype.insertAt !== "function") {
    Array.prototype.insertAt = function (index) {
        this.splice.apply(this, [index, 0].concat(
            Array.prototype.slice.call(arguments, 1)));
        return this;
    };
}

if (typeof Array.prototype.peek !== "function") {
    Array.prototype.peek = function () {
        return this[this.length - 1];
    };
}

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(searchElement /*, fromIndex */) {
        if (this == null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (len === 0) {
            return -1;
        }
        var n = 0;
        if (arguments.length > 1) {
            n = Number(arguments[1]);
            if (n != n) { // shortcut for verifying if it's NaN
                n = 0;
            } else if (n != 0 && n != Infinity && n != -Infinity) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) {
            return -1;
        }
        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
        for (; k < len; k++) {
            if (k in t && t[k] === searchElement) {
                return k;
            }
        }
        return -1;
    };
}

if (typeof dnn === "undefined" || dnn === null) { dnn = {}; }; //var dnn = dnn || {};

dnn.Enum = function (keyValuePairs) {
    for (var i in keyValuePairs) {
        if (Object.prototype.hasOwnProperty.call(keyValuePairs, i)) {
            this[keyValuePairs[i].value] = keyValuePairs[i].key;
        }
    }
};

dnn.Enum.prototype.getEnumName = function(enumValue) {
    for (var propertyName in this) {
        if (Object.prototype.hasOwnProperty.call(this, propertyName) && enumValue === this[propertyName]) {
            return propertyName;
        }
    }
    return "";
};

dnn.SortOrder = new dnn.Enum([{ key: 0, value: "unspecified" }, { key: 1, value: "ascending" }, { key: 2, value: "descending" }]);

dnn.executeFunctionByName = function(functionName, context /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for (var i = 0, size = namespaces.length; i < size; i++) {
        context = context[namespaces[i]];
    }
    if (typeof context[func] === "function") {
        return context[func].apply(context, args);
    }
    return null;
};

dnn.singletonify = function(constructorFunc /*, args */) {
    var instance = null;
    var args = Array.prototype.slice.call(arguments, 1);

    return new function () {
        this.getInstance = function () {
            var instanceArguments;
            var f;
            if (instance == null) {
                instanceArguments = Array.prototype.slice.call(arguments);
                f = function() {
                    return constructorFunc.apply(this, [].concat(args, instanceArguments));
                };
                f.prototype = constructorFunc.prototype;
                instance = new f();
            }
            return instance;
        };
    };
};

dnn.derive = function(child, parent) {
    var tempConstructor = function() {};
    tempConstructor.prototype = parent.prototype;
    child.prototype = new tempConstructor();
    child.prototype.constructor = child;
    child.superclass = parent.prototype;
};

(function ($, window, document, undefined) {
    "use strict";

    var KeyValueConverter = this.KeyValueConverter = function () {
    };

    KeyValueConverter.arrayToDictionary = function (pairs, keyProp, valProp) {
        var dictionary = new dnn.Dictionary();
        if (pairs && pairs.length > 0) {
            for (var i = 0, size = pairs.length; i < size; i++) {
                dictionary.set(pairs[i][keyProp], pairs[i][valProp]);
            }
        }
        return dictionary;
    };

    KeyValueConverter.dictionaryToArray = function (dictionary, keyProp, valProp) {
        var pairs = [];
        if (dictionary) {
            for (var key in dictionary) {
                var pair = {};
                pair[keyProp] = key;
                pair[valProp] = dictionary[key];
                pairs.push(pair);
            }
        }
        return pairs;
    };

    KeyValueConverter.getKeyValuePairByKey = function (pairs, key) {
        if (!pairs) {
            return null;
        }
        for (var i = 0, size = pairs.length; i < size; i++) {
            if (pairs[i].key === key) {
                return pairs[i];
            }
        }
        return null;
    };

}).apply(dnn, [jQuery, window, document]);

if (typeof Date.prototype.format === "undefined") {
    Date.prototype.format = function(pattern) {
        var hours = this.getHours();
        var ttime = "AM";
        if (pattern.indexOf("t") > -1 && hours > 12) {
            hours = hours - 12;
            ttime = "PM";
        }

        var o = {
            "M+": this.getMonth() + 1, //month
            "d+": this.getDate(), //day
            "h+": hours, //hour
            "m+": this.getMinutes(), //minute
            "s+": this.getSeconds(), //second
            "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
            "S": this.getMilliseconds(), //millisecond,
            "t+": ttime
        };

        if (/(y+)/.test(pattern)) {
            pattern = pattern.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (var k in o) {
            if (new RegExp("(" + k + ")").test(pattern)) {
                pattern = pattern.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
            }
        }
        return pattern;
    };
}

/* Compare the current date against another date.
     *
     * @param b  {Date} the other date
     * @returns   -1 : if this < b
     *             0 : if this === b
     *             1 : if this > b
     *            NaN : if a or b is an illegal date
    */
if (typeof Date.prototype.compare === "undefined") {
    Date.prototype.compare = function (b) {
        if (b.constructor !== Date) {
            throw "invalid_date";
        }
        return (isFinite(this.valueOf()) && isFinite(b.valueOf()) ?
                    (this > b) - (this < b) : NaN);
    };
}

//Returns true if it is a DOM element
if (typeof Object.isElement === "undefined") {
    Object.isElement = function (o) {
        return typeof HTMLElement === "object" ?
                o instanceof HTMLElement : //DOM2
                o && typeof o === "object" && o.nodeType === 1 && typeof o.nodeName === "string";
    };
}

dnn.removeElement = function (element) {
    if (element && element.parentNode) {
        element.parentNode.removeChild(element);
    }
};

/*
 * Generates a GUID string, according to RFC4122 standards.
 * @returns {String} The generated GUID like "af8a8416-6e18-a307-bd9c-f2c947bbb3aa"
 */
dnn.guid = (function() {
    var partOf8 = function (dashed) {
        var part = (Math.random().toString(16) + "000000000").substr(2, 8);
        return dashed ? "-" + part.substr(0, 4) + "-" + part.substr(4, 4) : part;
    };
    return function () { return partOf8(false) + partOf8(true) + partOf8(true) + partOf8(false); };
})();

dnn.uid = (function () {
    var id = (new Date()).getTime();
    return function (prefix) {
        return (prefix || "id") + (id++);
    };
})();

dnn.isUrl = function(url) {
    var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    return regexp.test(url);
};
<|endoftext|>


<commit_before>const { NODE_ENV } = process.env
const modules = NODE_ENV === 'production' ? false : 'commonjs'
const loose = true

module.exports = {
  presets: [
    [
      'env',
      {
        loose,
        modules
      }
    ],
    'react'
  ],
  plugins: [
    'react-hot-loader/babel',
    'transform-object-rest-spread',
    ['transform-class-properties', { loose }]
  ]
}
<commit_msg>Fix modules in lib<commit_after>const loose = true

module.exports = {
  presets: [
    [
      'env',
      {
        loose
      }
    ],
    'react'
  ],
  plugins: [
    'react-hot-loader/babel',
    'transform-object-rest-spread',
    ['transform-class-properties', { loose }]
  ]
}
<|endoftext|>