<commit_before>var urlTools = require('urltools'),
    browsersList = require('browserslist');

module.exports = function (options) {
    options = options || {};

    var minify = typeof options.minify === 'undefined' ? true : options.minify;

    // Default to try to support all browsers
    var browsers = {
        has: function () { return true; },
        minimum: function () { return 1; }
    };

    if (options.browsers) {
        browsers = {
            selected: browsersList(options.browsers),
            has: function (browserOrBrowsers) {
                try {
                    return browsersList(browserOrBrowsers).some(function (browser) {
                        return browsers.selected.some(function (selectedBrowser) {
                            return selectedBrowser === browser || selectedBrowser.indexOf(browser + ' ') === 0;
                        });
                    });
                } catch (e) {
                    // Parse error, try to match it as a browser name (any version) so that
                    // browsers.has('ie') does the expected.
                    return browsers.selected.some(function (selectedBrowser) {
                        return selectedBrowser.indexOf(browserOrBrowsers + ' ') === 0;
                    });
                }
            },
            // Find the minimum version of a given browser that is to be supported.
            // For example: browsers.minimum('ie'); // 10
            minimum: function (browser) {
                var minimumVersion = null;
                browsers.selected.forEach(function (selectedBrowser) {
                    if (selectedBrowser.indexOf(browser + ' ') === 0) {
                        var version = parseFloat(selectedBrowser.substr(browser.length + 1));
                        if (minimumVersion === null || version < minimumVersion) {
                            minimumVersion = version;
                        }
                    }
                });
                return minimumVersion;
            }
        };
    }

    var bundleStrategyName = options.sharedBundles ? 'sharedBundles' : 'oneBundlePerIncludingAsset',
        inlineByRelationType = options.inlineByRelationType || {HtmlScript: 4096, HtmlStyle: 4096};

    return function buildProduction(assetGraph, cb) {
        assetGraph.sourceMaps = !!options.sourceMaps;
        var query = assetGraph.query;

        assetGraph.javaScriptSerializationOptions = options.javaScriptSerializationOptions;

        if (!assetGraph.followRelations) {
            var excludeRelationTypes = ['JavaScriptInclude', 'JavaScriptExtJsRequire', 'SvgAnchor', 'SourceMapSource', 'SourceMapFile'];
            if (!options.sourceMaps) {
                excludeRelationTypes.push('JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl', 'CssSourceMappingUrl', 'CssSourceUrl');
            }
            if (options.recursive) {
                assetGraph.followRelations =
                    query.or({
                        to: {type: 'I18n'}
                    }, {
                        type: ['HtmlAnchor', 'HtmlMetaRefresh'],
                        to: /^file:/
                    }, {
                        type: query.not(excludeRelationTypes),
                        to: {url: query.not(/^https?:/)}
                    });
            } else {
                excludeRelationTypes.push('HtmlAnchor', 'HtmlMetaRefresh');
                assetGraph.followRelations =
                    query.or({
                        to: {type: 'I18n'}
                    }, {
                        type: query.not(excludeRelationTypes),
                        to: {url: query.not(/^https?:/)}
                    });
            }
        }

        assetGraph
            .populate({from: {type: 'Html'}, followRelations: {type: 'HtmlScript', to: {url: /^file:/}}})
            .bundleSystemJs({
                polyfill: true // FIXME: Check caniuse Promise vs. window.URL
            })
            .bundleRequireJs()
            .populate()
            .populate({startAssets: {type: 'JavaScript'}})
            .if(options.sourceMaps)
                .applySourceMaps()
            .endif()
            .removeRelations({ type: [ 'SourceMapSource', 'SourceMapFile' ] }, { unresolved: true })
            .populate()
            .populate({startAssets: {type: 'Html'}})
            // Remove bootstrapper scripts injected by buildDevelopment:
            .removeRelations({type: 'HtmlScript', node: {id: 'bootstrapper'}, from: {type: 'Html'}}, {detach: true, removeOrphan: true})
            .addDataVersionAttributeToHtmlElement({type: 'Html', isInitial: true}, options.version)
            .if(options.localeIds)
                .checkLanguageKeys({
                    supportedLocaleIds: options.localeIds,
                    defaultLocaleId: options.defaultLocaleId,
                    ignoreMessageTypes: 'unused'
                })
            .endif()
            .if(options.stripDebug)
                .stripDebug({type: 'JavaScript', isLoaded: true})
            .endif()
            .removeRelations({type: 'JavaScriptInclude', to: {type: ['Css', 'JavaScript', 'Html']}}, {detach: true, unresolved: true})
            .externalizeRelations({from: {type: query.not('Htc')}, type: ['HtmlStyle', 'HtmlScript'], to: {isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}})
            .mergeIdenticalAssets({
                isImage: true,
                isLoaded: true,
                url: /^[^\?]$/ // Skip images with a query string in the url (might contain processImage instructions)
            })
            // First execute explicit instructions in the query strings for images that are to be sprited:
            .processImages({isImage: true, isLoaded: true, url: /\?(?:|.*&)sprite(?:[&=#]|$)/})
            .spriteBackgroundImages()
            // Execute explicit and automatic optimizations for all images, including the generated sprite images:
            .processImages({isImage: true, isLoaded: true}, {autoLossless: options.optimizeImages})
            .minifySvgAssetsWithSvgo({isLoaded: true})
            // Remove orphan assets, except I18n which might originally have been referenced from the no longer existing bootstrapper:
            .inlineHtmlTemplates()
            .removeUnreferencedAssets({type: query.not('I18n'), isInitial: query.not(true)})
            .flattenStaticIncludes()
            .convertCssImportsToHtmlStyles()
            .removeDuplicateHtmlStyles({type: 'Html', isInitial: true})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']})
            .if(options.browsers)
                .autoprefixer(options.browsers, { sourceMaps: options.sourceMaps, sourcesContent: options.sourcesContent })
            .endif()
            .bundleRelations({type: 'HtmlStyle', to: {type: 'Css', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .splitCssIfIeLimitIsReached({type: 'Css'}, {minimumIeVersion: browsers.minimum('ie')})
            .bundleRelations({type: 'HtmlScript', to: {type: 'JavaScript', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .bundleRelations({type: 'JavaScriptImportScripts', to: {type: 'JavaScript', isLoaded: true}}, {strategyName: bundleStrategyName})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']}) // The bundling might produce several identical files, especially the 'oneBundlePerIncludingAsset' strategy.
            .if(options.angular)
                .angularAnnotations()
            .endif()
            .removeNobundleAttribute({type: ['HtmlScript', 'HtmlStyle']})
            .if(inlineByRelationType.CssImage)
                .inlineCssImagesWithLegacyFallback({
                    type: 'Html',
                    isInline: false,
                    isFragment: false
                }, {
                    sizeThreshold: typeof inlineByRelationType.CssImage === 'boolean' ? (inlineByRelationType.CssImage ? Infinity : 0) : inlineByRelationType.CssImage,
                    minimumIeVersion: browsers.minimum('ie')
                })
                .mergeIdenticalAssets({isLoaded: true, isInline: false, type: 'Css'})
            .endif()
            .if(minify)
                .minifyAssets({isLoaded: true})
            .endif()
            .if(options.addInitialHtmlExtension)
                .queue(function addInitialHtmlExtension() {
                    assetGraph.findAssets({isInitial: true, type: 'Html'}).forEach(function (asset) {
                        asset.fileName = asset.fileName.replace(/(?=\.|$)/, '.' + String(options.addInitialHtmlExtension).replace(/^\./, ''));
                    });
                })
            .endif()
            .if(options.localeIds)
                .cloneForEachLocale({type: 'Html', isInitial: true}, {
                    localeIds: options.localeIds,
                    supportedLocaleIds: options.localeIds,
                    localeCookieName: options.localeCookieName,
                    defaultLocaleId: options.defaultLocaleId
                })
                .runJavaScriptConditionalBlocks({isInitial: true}, 'LOCALIZE', true)
                .removeRelations({type: query.not('JavaScriptGetText'), to: {type: 'I18n'}}, {detach: true})
                .removeUnreferencedAssets({type: 'I18n'})
            .endif()
            .if(options.defines)
                .replaceSymbolsInJavaScript({type: 'JavaScript', isLoaded: true}, options.defines)
            .endif()
            .if(options.removeDeadIfs)
                .removeDeadIfsInJavaScript({isLoaded: true})
            .endif()
            .if(!options.noCompress)
                .compressJavaScript({type: 'JavaScript', isLoaded: true}, 'uglifyJs', { sourceMaps: options.sourceMaps, mangleOptions: { except: options.reservedNames || [] } })
            .endif()
            .removeEmptyJavaScripts()
            .removeEmptyStylesheets()
            .queue(function inlineRelations(assetGraph) {
                return assetGraph.findRelations({
                    to: {isLoaded: true, isInline: false},
                    from: {isInline: false} // Excludes relations occurring in conditional comments
                }).filter(function (relation) {
                    if (relation.type === 'CssImage' && !options.noInlineCssImagesWithLegacyFallback) {
                        return false; // Already handled by the inlineCssImagesWithLegacyFallback transform above
                    }
                    var sizeThreshold = inlineByRelationType[relation.type],
                        isStarRule = false;
                    if (typeof sizeThreshold === 'undefined') {
                        sizeThreshold = inlineByRelationType['*'];
                        isStarRule = true;
                    }
                    if (typeof sizeThreshold !== 'undefined' && sizeThreshold === true || relation.to.lastKnownByteLength < sizeThreshold) {
                        if (isStarRule) {
                            // Some relation types don't support inlining, and the inline method will be missing or throw an exception.
                            // Wrap it in a try...catch so the * rule means "all relation types that support inlining".
                            try {
                                relation.inline();
                            } catch (e) {}
                        } else {
                            relation.inline();
                        }
                    }
                });
            })
            .if(options.pretty)
                .prettyPrintAssets({type: 'Css', isLoaded: true})
                .prettyPrintAssets(function (asset) {
                    return asset.isLoaded && asset.type === 'JavaScript' && (!asset.isInline || asset.incomingRelations.every(function (incomingRelation) {
                        return incomingRelation.type === 'HtmlScript';
                    }));
                })
            .endif()
            .if(options.angular)
                .inlineAngularJsTemplates()
            .endif()
            .duplicateFavicon()
            .setAsyncOrDeferOnHtmlScripts({to: {isInline: false, url: /^file:/}}, options.asyncScripts, options.deferScripts)
            .queue(function omitFunctionCalls(assetGraph) {
                assetGraph.findRelations({type: ['JavaScriptGetStaticUrl', 'JavaScriptTrHtml'], to: {isLoaded: true}}).forEach(function (relation) {
                    relation.omitFunctionCall();
                });
            })
            .if(options.manifest)
                .addCacheManifest({isInitial: true})
                .if(options.localeIds && options.negotiateManifest)
                    .queue(function stripLocaleIdFromHtmlCacheManifestRelations(assetGraph) {
                        // This would be much less fragile if an asset could have a canonical url as well as an url (under consideration):
                        assetGraph.findRelations({type: 'HtmlCacheManifest'}).forEach(function (htmlCacheManifest) {
                            htmlCacheManifest.href = htmlCacheManifest.href.replace(/\.\w+\.appcache$/, '.appcache');
                        });
                    })
                .endif()
            .endif()
            .if(options.canonicalUrl)
                // Maybe this should be the effect of updating assetGraph.root?
                .moveAssets({isLoaded: true, isInline: false}, function (asset, assetGraph) {
                    if (asset.url.indexOf(assetGraph.root) === 0) {
                        return assetGraph.resolveUrl(options.canonicalUrl, urlTools.buildRelativeUrl(assetGraph.root, asset.url));
                    }
                })
                .updateRelations({from: {type: 'Html', isFragment: true, nonInlineAncestor: {type: ['Rss', 'Atom']}}}, {hrefType: 'absolute'})
            .endif()
            .if(options.sourceMaps)
                .serializeSourceMaps({ sourcesContent: options.sourcesContent })
                .updateRelations({ type: 'SourceMapSource' }, { hrefType: 'rootRelative' }, { includeUnresolved: true })
                .removeRelations({ type: [ 'SourceMapSource', 'SourceMapFile' ] }, { unresolved: true })
            .endif()
            .if(!options.noFileRev)
                .moveAssetsInOrder(query.and(
                    {
                        isLoaded: true,
                        isInline: false,
                        type: query.not(['CacheManifest', 'Rss', 'Atom']),
                        fileName: query.not(['.htaccess', 'humans.txt', 'robots.txt'])
                    },
                    {
                        url: query.not(assetGraph.root + 'favicon.ico')
                    },

                    // Rule for service worker scripts:
                    // Must be served from the root domain: https://www.w3.org/TR/service-workers/#origin-relativity
                    // Must keep its file name across builds: https://twitter.com/jaffathecake/status/748123748969095168
                    // Exclude service worrkers from file revisioning.
                    query.not({
                        type: 'JavaScript',
                        incomingRelations: function (relations) {
                            return relations.some(function (rel) {
                                return rel.type === 'JavaScriptServiceWorkerRegistration' || rel.type === 'JavaScriptWebWorker';
                            });
                        }
                    }),

                    query.not({
                        type: 'Html',
                        incomingRelations: function (relations) {
                            return relations.some(function (rel) {
                                return rel.type === 'HtmlAnchor' || rel.type === 'HtmlMetaRefresh';
                            });
                        }
                    }),
                    query.or(
                        query.not({
                            isInitial: true
                        }),
                        // Assume that non-inline HTML assets without an <html> element, but with incoming relations
                        // are templates that can safely be moved to /static/ even though they're initial
                        // (probably the result of loading **/*.html)
                        {
                            type: 'Html',
                            isFragment: true,
                            incomingRelations: function (incomingRelations) {
                                return incomingRelations.length > 0;
                            }
                        }
                    )), function (asset, assetGraph) {
                        var targetUrl = (options.canonicalUrl || '/') + 'static/';
                        // Conservatively assume that all GETSTATICURL relations pointing at non-images are intended to be fetched via XHR
                        // and thus cannot be put on a CDN because of same origin restrictions:
                        if (options.cdnRoot && asset.type !== 'Htc' && asset.extension !== '.jar' && (asset.type !== 'Html' || options.cdnHtml) && (asset.isImage || assetGraph.findRelations({to: asset, type: 'JavaScriptGetStaticUrl'}).length === 0) || (options.cdnRoot && options.cdnFlash && asset.type === 'Flash')) {
                            targetUrl = options.cdnRoot;
                            assetGraph.findRelations({to: asset}).forEach(function (incomingRelation) {
                                if (/^\/\//.test(options.cdnRoot)) {
                                    incomingRelation.hrefType = 'protocolRelative';
                                }
                                // Set crossorigin=anonymous on <script> tags pointing at CDN JavaScript.
                                // See http://blog.errorception.com/2012/12/catching-cross-domain-js-errors.html'
                                if ((asset.type === 'JavaScript' && incomingRelation.type === 'HtmlScript') ||
                                    (asset.type === 'Css' && incomingRelation.type === 'HtmlStyle')) {

                                    incomingRelation.node.setAttribute('crossorigin', 'anonymous');
                                    incomingRelation.from.markDirty();
                                }
                            });
                        }
                        return targetUrl + asset.fileName.split('.').shift() + '.' + asset.md5Hex.substr(0, 10) + asset.extension + asset.url.replace(/^[^#\?]*(?:)/, ''); // Preserve query string and fragment identifier
                    })
            .endif()
            .reviewContentSecurityPolicy({type: 'Html', isInline: false, isFragment: false, isLoaded: true}, {update: options.contentSecurityPolicy})
            .reviewSubResourceIntegrity({type: 'Html', isInline: false, isFragment: false, isLoaded: true}, {update: options.subResourceIntegrity})
            .if(options.gzip)
                .gzip({isInline: false, isText: true, isLoaded: true, url: query.not(/\.t?gz([\?#]|$)/)})
            .endif()
            .run(cb);
    };
};
<commit_msg>buildProduction: Minify assets after cloning for each locale. Prevents isMinified flag from getting lost from inline assets as a result of the cloning.<commit_after>var urlTools = require('urltools'),
    browsersList = require('browserslist');

module.exports = function (options) {
    options = options || {};

    var minify = typeof options.minify === 'undefined' ? true : options.minify;

    // Default to try to support all browsers
    var browsers = {
        has: function () { return true; },
        minimum: function () { return 1; }
    };

    if (options.browsers) {
        browsers = {
            selected: browsersList(options.browsers),
            has: function (browserOrBrowsers) {
                try {
                    return browsersList(browserOrBrowsers).some(function (browser) {
                        return browsers.selected.some(function (selectedBrowser) {
                            return selectedBrowser === browser || selectedBrowser.indexOf(browser + ' ') === 0;
                        });
                    });
                } catch (e) {
                    // Parse error, try to match it as a browser name (any version) so that
                    // browsers.has('ie') does the expected.
                    return browsers.selected.some(function (selectedBrowser) {
                        return selectedBrowser.indexOf(browserOrBrowsers + ' ') === 0;
                    });
                }
            },
            // Find the minimum version of a given browser that is to be supported.
            // For example: browsers.minimum('ie'); // 10
            minimum: function (browser) {
                var minimumVersion = null;
                browsers.selected.forEach(function (selectedBrowser) {
                    if (selectedBrowser.indexOf(browser + ' ') === 0) {
                        var version = parseFloat(selectedBrowser.substr(browser.length + 1));
                        if (minimumVersion === null || version < minimumVersion) {
                            minimumVersion = version;
                        }
                    }
                });
                return minimumVersion;
            }
        };
    }

    var bundleStrategyName = options.sharedBundles ? 'sharedBundles' : 'oneBundlePerIncludingAsset',
        inlineByRelationType = options.inlineByRelationType || {HtmlScript: 4096, HtmlStyle: 4096};

    return function buildProduction(assetGraph, cb) {
        assetGraph.sourceMaps = !!options.sourceMaps;
        var query = assetGraph.query;

        assetGraph.javaScriptSerializationOptions = options.javaScriptSerializationOptions;

        if (!assetGraph.followRelations) {
            var excludeRelationTypes = ['JavaScriptInclude', 'JavaScriptExtJsRequire', 'SvgAnchor', 'SourceMapSource', 'SourceMapFile'];
            if (!options.sourceMaps) {
                excludeRelationTypes.push('JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl', 'CssSourceMappingUrl', 'CssSourceUrl');
            }
            if (options.recursive) {
                assetGraph.followRelations =
                    query.or({
                        to: {type: 'I18n'}
                    }, {
                        type: ['HtmlAnchor', 'HtmlMetaRefresh'],
                        to: /^file:/
                    }, {
                        type: query.not(excludeRelationTypes),
                        to: {url: query.not(/^https?:/)}
                    });
            } else {
                excludeRelationTypes.push('HtmlAnchor', 'HtmlMetaRefresh');
                assetGraph.followRelations =
                    query.or({
                        to: {type: 'I18n'}
                    }, {
                        type: query.not(excludeRelationTypes),
                        to: {url: query.not(/^https?:/)}
                    });
            }
        }

        assetGraph
            .populate({from: {type: 'Html'}, followRelations: {type: 'HtmlScript', to: {url: /^file:/}}})
            .bundleSystemJs({
                polyfill: true // FIXME: Check caniuse Promise vs. window.URL
            })
            .bundleRequireJs()
            .populate()
            .populate({startAssets: {type: 'JavaScript'}})
            .if(options.sourceMaps)
                .applySourceMaps()
            .endif()
            .removeRelations({ type: [ 'SourceMapSource', 'SourceMapFile' ] }, { unresolved: true })
            .populate()
            .populate({startAssets: {type: 'Html'}})
            // Remove bootstrapper scripts injected by buildDevelopment:
            .removeRelations({type: 'HtmlScript', node: {id: 'bootstrapper'}, from: {type: 'Html'}}, {detach: true, removeOrphan: true})
            .addDataVersionAttributeToHtmlElement({type: 'Html', isInitial: true}, options.version)
            .if(options.localeIds)
                .checkLanguageKeys({
                    supportedLocaleIds: options.localeIds,
                    defaultLocaleId: options.defaultLocaleId,
                    ignoreMessageTypes: 'unused'
                })
            .endif()
            .if(options.stripDebug)
                .stripDebug({type: 'JavaScript', isLoaded: true})
            .endif()
            .removeRelations({type: 'JavaScriptInclude', to: {type: ['Css', 'JavaScript', 'Html']}}, {detach: true, unresolved: true})
            .externalizeRelations({from: {type: query.not('Htc')}, type: ['HtmlStyle', 'HtmlScript'], to: {isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}})
            .mergeIdenticalAssets({
                isImage: true,
                isLoaded: true,
                url: /^[^\?]$/ // Skip images with a query string in the url (might contain processImage instructions)
            })
            // First execute explicit instructions in the query strings for images that are to be sprited:
            .processImages({isImage: true, isLoaded: true, url: /\?(?:|.*&)sprite(?:[&=#]|$)/})
            .spriteBackgroundImages()
            // Execute explicit and automatic optimizations for all images, including the generated sprite images:
            .processImages({isImage: true, isLoaded: true}, {autoLossless: options.optimizeImages})
            .minifySvgAssetsWithSvgo({isLoaded: true})
            // Remove orphan assets, except I18n which might originally have been referenced from the no longer existing bootstrapper:
            .inlineHtmlTemplates()
            .removeUnreferencedAssets({type: query.not('I18n'), isInitial: query.not(true)})
            .flattenStaticIncludes()
            .convertCssImportsToHtmlStyles()
            .removeDuplicateHtmlStyles({type: 'Html', isInitial: true})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']})
            .if(options.browsers)
                .autoprefixer(options.browsers, { sourceMaps: options.sourceMaps, sourcesContent: options.sourcesContent })
            .endif()
            .bundleRelations({type: 'HtmlStyle', to: {type: 'Css', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .splitCssIfIeLimitIsReached({type: 'Css'}, {minimumIeVersion: browsers.minimum('ie')})
            .bundleRelations({type: 'HtmlScript', to: {type: 'JavaScript', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .bundleRelations({type: 'JavaScriptImportScripts', to: {type: 'JavaScript', isLoaded: true}}, {strategyName: bundleStrategyName})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']}) // The bundling might produce several identical files, especially the 'oneBundlePerIncludingAsset' strategy.
            .if(options.angular)
                .angularAnnotations()
            .endif()
            .removeNobundleAttribute({type: ['HtmlScript', 'HtmlStyle']})
            .if(inlineByRelationType.CssImage)
                .inlineCssImagesWithLegacyFallback({
                    type: 'Html',
                    isInline: false,
                    isFragment: false
                }, {
                    sizeThreshold: typeof inlineByRelationType.CssImage === 'boolean' ? (inlineByRelationType.CssImage ? Infinity : 0) : inlineByRelationType.CssImage,
                    minimumIeVersion: browsers.minimum('ie')
                })
                .mergeIdenticalAssets({isLoaded: true, isInline: false, type: 'Css'})
            .endif()
            .if(options.addInitialHtmlExtension)
                .queue(function addInitialHtmlExtension() {
                    assetGraph.findAssets({isInitial: true, type: 'Html'}).forEach(function (asset) {
                        asset.fileName = asset.fileName.replace(/(?=\.|$)/, '.' + String(options.addInitialHtmlExtension).replace(/^\./, ''));
                    });
                })
            .endif()
            .if(options.localeIds)
                .cloneForEachLocale({type: 'Html', isInitial: true}, {
                    localeIds: options.localeIds,
                    supportedLocaleIds: options.localeIds,
                    localeCookieName: options.localeCookieName,
                    defaultLocaleId: options.defaultLocaleId
                })
                .runJavaScriptConditionalBlocks({isInitial: true}, 'LOCALIZE', true)
                .removeRelations({type: query.not('JavaScriptGetText'), to: {type: 'I18n'}}, {detach: true})
                .removeUnreferencedAssets({type: 'I18n'})
            .endif()
            .if(minify)
                .minifyAssets({isLoaded: true})
            .endif()
            .if(options.defines)
                .replaceSymbolsInJavaScript({type: 'JavaScript', isLoaded: true}, options.defines)
            .endif()
            .if(options.removeDeadIfs)
                .removeDeadIfsInJavaScript({isLoaded: true})
            .endif()
            .if(!options.noCompress)
                .compressJavaScript({type: 'JavaScript', isLoaded: true}, 'uglifyJs', { sourceMaps: options.sourceMaps, mangleOptions: { except: options.reservedNames || [] } })
            .endif()
            .removeEmptyJavaScripts()
            .removeEmptyStylesheets()
            .queue(function inlineRelations(assetGraph) {
                return assetGraph.findRelations({
                    to: {isLoaded: true, isInline: false},
                    from: {isInline: false} // Excludes relations occurring in conditional comments
                }).filter(function (relation) {
                    if (relation.type === 'CssImage' && !options.noInlineCssImagesWithLegacyFallback) {
                        return false; // Already handled by the inlineCssImagesWithLegacyFallback transform above
                    }
                    var sizeThreshold = inlineByRelationType[relation.type],
                        isStarRule = false;
                    if (typeof sizeThreshold === 'undefined') {
                        sizeThreshold = inlineByRelationType['*'];
                        isStarRule = true;
                    }
                    if (typeof sizeThreshold !== 'undefined' && sizeThreshold === true || relation.to.lastKnownByteLength < sizeThreshold) {
                        if (isStarRule) {
                            // Some relation types don't support inlining, and the inline method will be missing or throw an exception.
                            // Wrap it in a try...catch so the * rule means "all relation types that support inlining".
                            try {
                                relation.inline();
                            } catch (e) {}
                        } else {
                            relation.inline();
                        }
                    }
                });
            })
            .if(options.pretty)
                .prettyPrintAssets({type: 'Css', isLoaded: true})
                .prettyPrintAssets(function (asset) {
                    return asset.isLoaded && asset.type === 'JavaScript' && (!asset.isInline || asset.incomingRelations.every(function (incomingRelation) {
                        return incomingRelation.type === 'HtmlScript';
                    }));
                })
            .endif()
            .if(options.angular)
                .inlineAngularJsTemplates()
            .endif()
            .duplicateFavicon()
            .setAsyncOrDeferOnHtmlScripts({to: {isInline: false, url: /^file:/}}, options.asyncScripts, options.deferScripts)
            .queue(function omitFunctionCalls(assetGraph) {
                assetGraph.findRelations({type: ['JavaScriptGetStaticUrl', 'JavaScriptTrHtml'], to: {isLoaded: true}}).forEach(function (relation) {
                    relation.omitFunctionCall();
                });
            })
            .if(options.manifest)
                .addCacheManifest({isInitial: true})
                .if(options.localeIds && options.negotiateManifest)
                    .queue(function stripLocaleIdFromHtmlCacheManifestRelations(assetGraph) {
                        // This would be much less fragile if an asset could have a canonical url as well as an url (under consideration):
                        assetGraph.findRelations({type: 'HtmlCacheManifest'}).forEach(function (htmlCacheManifest) {
                            htmlCacheManifest.href = htmlCacheManifest.href.replace(/\.\w+\.appcache$/, '.appcache');
                        });
                    })
                .endif()
            .endif()
            .if(options.canonicalUrl)
                // Maybe this should be the effect of updating assetGraph.root?
                .moveAssets({isLoaded: true, isInline: false}, function (asset, assetGraph) {
                    if (asset.url.indexOf(assetGraph.root) === 0) {
                        return assetGraph.resolveUrl(options.canonicalUrl, urlTools.buildRelativeUrl(assetGraph.root, asset.url));
                    }
                })
                .updateRelations({from: {type: 'Html', isFragment: true, nonInlineAncestor: {type: ['Rss', 'Atom']}}}, {hrefType: 'absolute'})
            .endif()
            .if(options.sourceMaps)
                .serializeSourceMaps({ sourcesContent: options.sourcesContent })
                .updateRelations({ type: 'SourceMapSource' }, { hrefType: 'rootRelative' }, { includeUnresolved: true })
                .removeRelations({ type: [ 'SourceMapSource', 'SourceMapFile' ] }, { unresolved: true })
            .endif()
            .if(!options.noFileRev)
                .moveAssetsInOrder(query.and(
                    {
                        isLoaded: true,
                        isInline: false,
                        type: query.not(['CacheManifest', 'Rss', 'Atom']),
                        fileName: query.not(['.htaccess', 'humans.txt', 'robots.txt'])
                    },
                    {
                        url: query.not(assetGraph.root + 'favicon.ico')
                    },

                    // Rule for service worker scripts:
                    // Must be served from the root domain: https://www.w3.org/TR/service-workers/#origin-relativity
                    // Must keep its file name across builds: https://twitter.com/jaffathecake/status/748123748969095168
                    // Exclude service worrkers from file revisioning.
                    query.not({
                        type: 'JavaScript',
                        incomingRelations: function (relations) {
                            return relations.some(function (rel) {
                                return rel.type === 'JavaScriptServiceWorkerRegistration' || rel.type === 'JavaScriptWebWorker';
                            });
                        }
                    }),

                    query.not({
                        type: 'Html',
                        incomingRelations: function (relations) {
                            return relations.some(function (rel) {
                                return rel.type === 'HtmlAnchor' || rel.type === 'HtmlMetaRefresh';
                            });
                        }
                    }),
                    query.or(
                        query.not({
                            isInitial: true
                        }),
                        // Assume that non-inline HTML assets without an <html> element, but with incoming relations
                        // are templates that can safely be moved to /static/ even though they're initial
                        // (probably the result of loading **/*.html)
                        {
                            type: 'Html',
                            isFragment: true,
                            incomingRelations: function (incomingRelations) {
                                return incomingRelations.length > 0;
                            }
                        }
                    )), function (asset, assetGraph) {
                        var targetUrl = (options.canonicalUrl || '/') + 'static/';
                        // Conservatively assume that all GETSTATICURL relations pointing at non-images are intended to be fetched via XHR
                        // and thus cannot be put on a CDN because of same origin restrictions:
                        if (options.cdnRoot && asset.type !== 'Htc' && asset.extension !== '.jar' && (asset.type !== 'Html' || options.cdnHtml) && (asset.isImage || assetGraph.findRelations({to: asset, type: 'JavaScriptGetStaticUrl'}).length === 0) || (options.cdnRoot && options.cdnFlash && asset.type === 'Flash')) {
                            targetUrl = options.cdnRoot;
                            assetGraph.findRelations({to: asset}).forEach(function (incomingRelation) {
                                if (/^\/\//.test(options.cdnRoot)) {
                                    incomingRelation.hrefType = 'protocolRelative';
                                }
                                // Set crossorigin=anonymous on <script> tags pointing at CDN JavaScript.
                                // See http://blog.errorception.com/2012/12/catching-cross-domain-js-errors.html'
                                if ((asset.type === 'JavaScript' && incomingRelation.type === 'HtmlScript') ||
                                    (asset.type === 'Css' && incomingRelation.type === 'HtmlStyle')) {

                                    incomingRelation.node.setAttribute('crossorigin', 'anonymous');
                                    incomingRelation.from.markDirty();
                                }
                            });
                        }
                        return targetUrl + asset.fileName.split('.').shift() + '.' + asset.md5Hex.substr(0, 10) + asset.extension + asset.url.replace(/^[^#\?]*(?:)/, ''); // Preserve query string and fragment identifier
                    })
            .endif()
            .reviewContentSecurityPolicy({type: 'Html', isInline: false, isFragment: false, isLoaded: true}, {update: options.contentSecurityPolicy})
            .reviewSubResourceIntegrity({type: 'Html', isInline: false, isFragment: false, isLoaded: true}, {update: options.subResourceIntegrity})
            .if(options.gzip)
                .gzip({isInline: false, isText: true, isLoaded: true, url: query.not(/\.t?gz([\?#]|$)/)})
            .endif()
            .run(cb);
    };
};
<|endoftext|>


<commit_before> /**
 *  Copyright (C) 2010 Cloud.com, Inc.  All rights reserved.
 *
 * This software is licensed under the GNU General Public License v3 or later.
 *
 * It is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

function afterLoadClusterJSP($midmenuItem1) {
    hideMiddleMenu();
    initDialog("dialog_add_host", 400);

    //add pool dialog
    initDialog("dialog_add_pool", 400);
    bindEventHandlerToDialogAddPool($("#dialog_add_pool"));
}

function clusterToRightPanel($midmenuItem1) {
    $("#right_panel_content").data("$midmenuItem1", $midmenuItem1);
    clusterJsonToDetailsTab();
}

function clusterClearRightPanel() {
    clusterClearDetailsTab();
}

function clusterJsonToDetailsTab() {
    var $midmenuItem1 = $("#right_panel_content").data("$midmenuItem1");
    if($midmenuItem1 == null) {
        clusterClearDetailsTab();
        return;
    }

    var jsonObj = $midmenuItem1.data("jsonObj");
    if(jsonObj == null) {
        clusterClearDetailsTab();
	    return;
	}

    bindAddHostButton($midmenuItem1);
    bindAddPrimaryStorageButton($midmenuItem1);

    $.ajax({
        data: createURL("command=listClusters&id="+jsonObj.id),
        dataType: "json",
        async: false,
        success: function(json) {
            var items = json.listclustersresponse.cluster;
			if(items != null && items.length > 0) {
                jsonObj = items[0];
                $midmenuItem1.data("jsonObj", jsonObj);
            }
        }
    });

    var $thisTab = $("#right_panel_content").find("#tab_content_details");
    $thisTab.find("#grid_header_title").text(fromdb(jsonObj.name));
    $thisTab.find("#id").text(fromdb(jsonObj.id));
    $thisTab.find("#name").text(fromdb(jsonObj.name));
    $thisTab.find("#zonename").text(fromdb(jsonObj.zonename));
    $thisTab.find("#podname").text(fromdb(jsonObj.podname));
    $thisTab.find("#hypervisortype").text(fromdb(jsonObj.hypervisortype));
    $thisTab.find("#clustertype").text(fromdb(jsonObj.clustertype));
    $thisTab.find("#allocationstate").text(fromdb(jsonObj.allocationstate));
    $thisTab.find("#managedstate").text(fromdb(jsonObj.managedstate));

    //actions ***
    clusterBuildActionMenu(jsonObj);
}

function clusterBuildActionMenu(jsonObj) {
	var $midmenuItem1 = $("#right_panel_content").data("$midmenuItem1");
	var $thisTab = $("#right_panel_content").find("#tab_content_details");

    var $actionLink = $thisTab.find("#action_link");
    bindActionLink($actionLink);

    var $actionMenu = $actionLink.find("#action_menu");
    $actionMenu.find("#action_list").empty();

    if(jsonObj.allocationstate == "Disabled")
        buildActionLinkForTab("label.action.enable.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
    else if(jsonObj.allocationstate == "Enabled")
        buildActionLinkForTab("label.action.disable.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);

    if(jsonObj.managedstate == "Managed")
    	buildActionLinkForTab("label.action.unmanage.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
    else //PrepareUnmanaged , PrepareUnmanagedError, Unmanaged
    	buildActionLinkForTab("label.action.manage.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);

    buildActionLinkForTab("label.action.delete.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
}

function clusterClearDetailsTab() {
    var $thisTab = $("#right_panel_content").find("#tab_content_details");
    $thisTab.find("#grid_header_title").text("");
    $thisTab.find("#id").text("");
    $thisTab.find("#name").text("");
    $thisTab.find("#zonename").text("");
    $thisTab.find("#podname").text("");
    $thisTab.find("#hypervisortype").text("");
    $thisTab.find("#clustertype").text("");
    $thisTab.find("#allocationstate").text("");
    $thisTab.find("#managedstate").text("");

    //actions ***
    var $actionMenu = $thisTab.find("#action_link #action_menu");
    $actionMenu.find("#action_list").empty();
	$actionMenu.find("#action_list").append($("#no_available_actions").clone().show());
}

var clusterActionMap = {
	"label.action.enable.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doEnableCluster,
	    inProcessText: "label.action.enable.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#allocationstate").text(fromdb(jsonObj.allocationstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.disable.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doDisableCluster,
	    inProcessText: "label.action.disable.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#allocationstate").text(fromdb(jsonObj.allocationstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.manage.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doManageCluster,
	    inProcessText: "label.action.manage.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#managedstate").text(fromdb(jsonObj.managedstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.unmanage.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doUnmanageCluster,
	    inProcessText: "label.action.unmanage.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#managedstate").text(fromdb(jsonObj.managedstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.delete.cluster": {
        api: "deleteCluster",
        isAsyncJob: false,
        dialogBeforeActionFn : doDeleteCluster,
        inProcessText: "label.action.delete.cluster.processing",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            $midmenuItem1.slideUp("slow", function() {
                $(this).remove();
                if(id.toString() == $("#right_panel_content").find("#tab_content_details").find("#id").text()) {
                    clearRightPanel();
                    clusterClearRightPanel();
                }
            });
        }
    }
}

function doEnableCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.enable.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&allocationstate=Enabled";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doDisableCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.disable.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&allocationstate=Disabled";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doManageCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.manage.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&managedstate=Managed";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doUnmanageCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.unmanage.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&managedstate=Unmanaged";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doDeleteCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.delete.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=deleteCluster&id="+id;
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}<commit_msg>bug 11065: cloudStack - cluster page - remove action "Manage Cluster" and "Unmanage Cluster".<commit_after> /**
 *  Copyright (C) 2010 Cloud.com, Inc.  All rights reserved.
 *
 * This software is licensed under the GNU General Public License v3 or later.
 *
 * It is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

function afterLoadClusterJSP($midmenuItem1) {
    hideMiddleMenu();
    initDialog("dialog_add_host", 400);

    //add pool dialog
    initDialog("dialog_add_pool", 400);
    bindEventHandlerToDialogAddPool($("#dialog_add_pool"));
}

function clusterToRightPanel($midmenuItem1) {
    $("#right_panel_content").data("$midmenuItem1", $midmenuItem1);
    clusterJsonToDetailsTab();
}

function clusterClearRightPanel() {
    clusterClearDetailsTab();
}

function clusterJsonToDetailsTab() {
    var $midmenuItem1 = $("#right_panel_content").data("$midmenuItem1");
    if($midmenuItem1 == null) {
        clusterClearDetailsTab();
        return;
    }

    var jsonObj = $midmenuItem1.data("jsonObj");
    if(jsonObj == null) {
        clusterClearDetailsTab();
	    return;
	}

    bindAddHostButton($midmenuItem1);
    bindAddPrimaryStorageButton($midmenuItem1);

    $.ajax({
        data: createURL("command=listClusters&id="+jsonObj.id),
        dataType: "json",
        async: false,
        success: function(json) {
            var items = json.listclustersresponse.cluster;
			if(items != null && items.length > 0) {
                jsonObj = items[0];
                $midmenuItem1.data("jsonObj", jsonObj);
            }
        }
    });

    var $thisTab = $("#right_panel_content").find("#tab_content_details");
    $thisTab.find("#grid_header_title").text(fromdb(jsonObj.name));
    $thisTab.find("#id").text(fromdb(jsonObj.id));
    $thisTab.find("#name").text(fromdb(jsonObj.name));
    $thisTab.find("#zonename").text(fromdb(jsonObj.zonename));
    $thisTab.find("#podname").text(fromdb(jsonObj.podname));
    $thisTab.find("#hypervisortype").text(fromdb(jsonObj.hypervisortype));
    $thisTab.find("#clustertype").text(fromdb(jsonObj.clustertype));
    $thisTab.find("#allocationstate").text(fromdb(jsonObj.allocationstate));
    $thisTab.find("#managedstate").text(fromdb(jsonObj.managedstate));

    //actions ***
    clusterBuildActionMenu(jsonObj);
}

function clusterBuildActionMenu(jsonObj) {
	var $midmenuItem1 = $("#right_panel_content").data("$midmenuItem1");
	var $thisTab = $("#right_panel_content").find("#tab_content_details");

    var $actionLink = $thisTab.find("#action_link");
    bindActionLink($actionLink);

    var $actionMenu = $actionLink.find("#action_menu");
    $actionMenu.find("#action_list").empty();

    if(jsonObj.allocationstate == "Disabled")
        buildActionLinkForTab("label.action.enable.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
    else if(jsonObj.allocationstate == "Enabled")
        buildActionLinkForTab("label.action.disable.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);

    /*
    if(jsonObj.managedstate == "Managed")
    	buildActionLinkForTab("label.action.unmanage.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
    else //PrepareUnmanaged , PrepareUnmanagedError, Unmanaged
    	buildActionLinkForTab("label.action.manage.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
    */

    buildActionLinkForTab("label.action.delete.cluster", clusterActionMap, $actionMenu, $midmenuItem1, $thisTab);
}

function clusterClearDetailsTab() {
    var $thisTab = $("#right_panel_content").find("#tab_content_details");
    $thisTab.find("#grid_header_title").text("");
    $thisTab.find("#id").text("");
    $thisTab.find("#name").text("");
    $thisTab.find("#zonename").text("");
    $thisTab.find("#podname").text("");
    $thisTab.find("#hypervisortype").text("");
    $thisTab.find("#clustertype").text("");
    $thisTab.find("#allocationstate").text("");
    $thisTab.find("#managedstate").text("");

    //actions ***
    var $actionMenu = $thisTab.find("#action_link #action_menu");
    $actionMenu.find("#action_list").empty();
	$actionMenu.find("#action_list").append($("#no_available_actions").clone().show());
}

var clusterActionMap = {
	"label.action.enable.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doEnableCluster,
	    inProcessText: "label.action.enable.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#allocationstate").text(fromdb(jsonObj.allocationstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.disable.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doDisableCluster,
	    inProcessText: "label.action.disable.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#allocationstate").text(fromdb(jsonObj.allocationstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.manage.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doManageCluster,
	    inProcessText: "label.action.manage.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#managedstate").text(fromdb(jsonObj.managedstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.unmanage.cluster": {
	    isAsyncJob: false,
	    dialogBeforeActionFn : doUnmanageCluster,
	    inProcessText: "label.action.unmanage.cluster.processing",
	    afterActionSeccessFn: function(json, $midmenuItem1, id) {
			var jsonObj = json.updateclusterresponse.cluster;
			$("#right_panel_content").find("#tab_content_details").find("#managedstate").text(fromdb(jsonObj.managedstate));
			clusterBuildActionMenu(jsonObj);
	    }
	}
	,
	"label.action.delete.cluster": {
        api: "deleteCluster",
        isAsyncJob: false,
        dialogBeforeActionFn : doDeleteCluster,
        inProcessText: "label.action.delete.cluster.processing",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            $midmenuItem1.slideUp("slow", function() {
                $(this).remove();
                if(id.toString() == $("#right_panel_content").find("#tab_content_details").find("#id").text()) {
                    clearRightPanel();
                    clusterClearRightPanel();
                }
            });
        }
    }
}

function doEnableCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.enable.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&allocationstate=Enabled";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doDisableCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.disable.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&allocationstate=Disabled";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doManageCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.manage.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&managedstate=Managed";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doUnmanageCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.unmanage.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=updateCluster&id="+id+"&managedstate=Unmanaged";
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}

function doDeleteCluster($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
	var id = jsonObj.id;

	$("#dialog_confirmation")
	.text(dictionary["message.action.delete.cluster"])
	.dialog('option', 'buttons', {
		"Confirm": function() {
			$(this).dialog("close");
			var apiCommand = "command=deleteCluster&id="+id;
            doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
		},
		"Cancel": function() {
			$(this).dialog("close");
		}
	}).dialog("open");
}<|endoftext|>


<commit_before>/*
The files in this directory are part of node-ansible-test, a javascript-based library.
Copyright (C) 2016 Daniel Bush
This program is distributed under the terms of the BSD-2-Clause License.
*/
// File for using cucumber-mink.
// Cucumber-mink will run webdriverio and give you a bunch of pre-defined step definitions.
// You just need something like phantomjs --webdriver or similar.
// See 'npm run test:acceptance'.

var Mink = require('cucumber-mink');
var username  = process.env.SAUCE_USERNAME;
var accessKey = process.env.SAUCE_ACCESS_KEY;

// Phantomjs - GhostDriver
var parameters = {
  driver: {
    desiredCapabilities: {
      browserName: 'internet explorer',
      platform: 'Windows 10',
      version: '11.103',
      username: username,
      accessKey: accessKey
    },
    //host: username + ':' + accessKey + '@ondemand.saucelabs.com',
    //host: username + ':' + accessKey + '@localhost',
    port: 4445,
    path: '/wd/hub'
//"http://" + username + ":" + accessKey + "@ondemand.saucelabs.com:80/wd/hub"
  }
};

module.exports = function () {
  Mink.init(this, parameters);
};
<commit_msg>Trying to add saucelabs.<commit_after>/*
The files in this directory are part of node-ansible-test, a javascript-based library.
Copyright (C) 2016 Daniel Bush
This program is distributed under the terms of the BSD-2-Clause License.
*/
// File for using cucumber-mink.
// Cucumber-mink will run webdriverio and give you a bunch of pre-defined step definitions.
// You just need something like phantomjs --webdriver or similar.
// See 'npm run test:acceptance'.

var Mink = require('cucumber-mink');
var username  = process.env.SAUCE_USERNAME;
var accessKey = process.env.SAUCE_ACCESS_KEY;

// Phantomjs - GhostDriver
var parameters = {
  driver: {
    desiredCapabilities: {
      browserName: 'internet explorer',
      platform: 'Windows 10',
      version: '11.103'
      //username: username,
      //accessKey: accessKey
    },
    host: username + ':' + accessKey + '@ondemand.saucelabs.com/wd/hub'
    //host: username + ':' + accessKey + '@ondemand.saucelabs.com',
    //host: username + ':' + accessKey + '@localhost',
    //port: 4445,
    //path: '/wd/hub'
//"http://" + username + ":" + accessKey + "@ondemand.saucelabs.com:80/wd/hub"
  }
};

module.exports = function () {
  Mink.init(this, parameters);
};
<|endoftext|>


<commit_before>var urlTools = require('urltools');

module.exports = function (options) {
    options = options || {};

    var bundleStrategyName = options.sharedBundles ? 'sharedBundles' : 'oneBundlePerIncludingAsset',
        inlineByRelationType = options.inlineByRelationType || {HtmlScript: 4096, HtmlStyle: 4096};

    return function buildProduction(assetGraph, cb) {
        var query = assetGraph.query,
            followRelationsQueryObj = query.or({
                to: {type: 'I18n'}
            },
            {
                type: query.not(['JavaScriptInclude', 'JavaScriptExtJsRequire', 'JavaScriptCommonJsRequire', 'HtmlAnchor', 'SvgAnchor', 'JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl']),
                to: {url: query.not(/^https?:/)}
            });

        assetGraph
            .populate({from: {type: 'Html'}, followRelations: {type: 'HtmlScript', to: {url: /^file:/}}})
            .assumeRequireJsConfigHasBeenFound()
            .populate({followRelations: followRelationsQueryObj})
            .fixBaseAssetsOfUnresolvedOutgoingRelationsFromHtmlFragments({isInitial: true})
            .assumeThatAllHtmlFragmentAssetsWithoutIncomingRelationsAreNotTemplates()
            .populate({followRelations: followRelationsQueryObj, startAssets: {type: 'Html'}})
            // Remove bootstrapper scripts injected by buildDevelopment:
            .removeRelations({type: 'HtmlScript', node: {id: 'bootstrapper'}, from: {type: 'Html'}}, {detach: true, removeOrphan: true})
            .addDataVersionAttributeToHtmlElement({type: 'Html', isInitial: true}, options.version)
            .replaceDartWithJavaScript()
            .populate({followRelations: followRelationsQueryObj, startAssets: {type: 'JavaScript', url: /\.dart\.js$/}})
            .if(options.localeIds)
                .checkLanguageKeys({
                    supportedLocaleIds: options.localeIds,
                    defaultLocaleId: options.defaultLocaleId,
                    ignoreMessageTypes: 'unused'
                })
            .endif()
            .if(options.less)
                // Replace Less assets with their Css counterparts:
                .compileLessToCss({type: 'Less', isLoaded: true})

                // Remove the in-browser less compiler and its incoming relations,
                // even if it's included from a CDN and thus hasn't been populated.
                // FIXME: Turns out there's a bunch of less.js files (eg. in the ACE editor)
                // that we don't want to be remove, so this is probably a bit too magical.
                .removeRelations({type: 'HtmlScript', to: {url: /\/less(?:-\d+\.\d+\.\d+)?(?:\.min)?\.js$/}}, {unresolved: true, detach: true, removeOrphan: true})

                // Find and populate CssImage relations from the compiled Less assets:
                .populate({from: {type: 'Css'}, followRelations: followRelationsQueryObj})
            .endif()
            .if(options.stripDebug)
                .stripDebug({type: 'JavaScript', isLoaded: true})
            .endif()
            .removeRelations({type: 'JavaScriptInclude', to: {type: ['Css', 'JavaScript', 'Html', 'Less']}}, {detach: true, unresolved: true})
            .externalizeRelations({from: {type: query.not('Htc')}, type: ['HtmlStyle', 'HtmlScript'], to: {isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}})
            .mergeIdenticalAssets({
                isImage: true,
                isLoaded: true,
                url: /^[^\?]$/ // Skip images with a query string in the url (might contain processImage instructions)
            })
            // First execute explicit instructions in the query strings for images that are to be sprited:
            .processImages({isImage: true, isLoaded: true, url: /\?(?:|.*&)sprite(?:[&=#]|$)/})
            .spriteBackgroundImages()
            // Execute explicit and automatic optimizations for all images, including the generated sprite images:
            .processImages({isImage: true, isLoaded: true}, {autoLossless: options.optimizeImages})
            .minifySvgAssetsWithSvgo({isLoaded: true})
            .inlineKnockoutJsTemplates()
            .liftUpJavaScriptRequireJsCommonJsCompatibilityRequire()
            .flattenRequireJs({type: 'Html', isFragment: false})
            // Remove orphan assets, except I18n which might originally have been referenced from the no longer existing bootstrapper:
            .removeUnreferencedAssets({type: query.not('I18n'), isInitial: query.not(true)})
            .convertCssImportsToHtmlStyles()
            .removeDuplicateHtmlStyles({type: 'Html', isInitial: true})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']})
            .if(options.autoprefix)
                .autoprefixer(options.autoprefix)
            .endif()
            .bundleRelations({type: 'HtmlStyle', to: {type: 'Css', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .splitCssIfIeLimitIsReached()
            .replaceRequireJsWithAlmond()
            .bundleRelations({type: 'HtmlScript', to: {type: 'JavaScript', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']}) // The bundling might produce several identical files, especially the 'oneBundlePerIncludingAsset' strategy.
            .queue(function ngmin(assetGraph) {
                var annotate,
                    angular = assetGraph.findAssets({
                        type: 'Html',
                        isInline: false,
                        isFragment: false
                    }).some(function (asset) {
                        var document = asset.parseTree;

                        return document.body && (document.querySelector('[ng-app]') || document.querySelector('[class~="ng-app:"]'));
                    });

                if (angular) {
                    annotate = require('ngmin').annotate;

                    assetGraph.findAssets({
                        type: 'JavaScript'
                    }).forEach(function (asset) {
                        asset.text = annotate(asset.text);
                    });
                }

                return assetGraph;
            })
            .removeNobundleAttribute({type: ['HtmlScript', 'HtmlStyle']})
            .if(inlineByRelationType.CssImage && !options.noInlineCssImagesWithLegacyFallback)
                .inlineCssImagesWithLegacyFallback({type: 'Html', isInline: false, isFragment: false}, inlineByRelationType.CssImage)
            .endif()
            .minifyAssets({isLoaded: true})
            .if(options.addInitialHtmlExtension)
                .queue(function addInitialHtmlExtension() {
                    assetGraph.findAssets({isInitial: true, type: 'Html'}).forEach(function (asset) {
                        asset.fileName = asset.fileName.replace(/(?=\.|$)/, '.' + String(options.addInitialHtmlExtension).replace(/^\./, ''));
                    });
                })
            .endif()
            .if(options.localeIds)
                .cloneForEachLocale({type: 'Html', isInitial: true}, {
                    localeIds: options.localeIds,
                    supportedLocaleIds: options.localeIds,
                    localeCookieName: options.localeCookieName,
                    defaultLocaleId: options.defaultLocaleId
                })
                .runJavaScriptConditionalBlocks({isInitial: true}, 'LOCALIZE', true)
                .removeRelations({type: query.not('JavaScriptGetText'), to: {type: 'I18n'}}, {detach: true})
                .removeUnreferencedAssets({type: 'I18n'})
            .endif()
            .if(options.defines)
                .replaceSymbolsInJavaScript({type: 'JavaScript', isLoaded: true}, options.defines)
            .endif()
            .if(!options.noCompress)
                .compressJavaScript({type: 'JavaScript', isLoaded: true}, 'uglifyJs', {mangleOptions: {except: options.reservedNames || []}})
            .endif()
            .removeEmptyJavaScripts()
            .removeEmptyStylesheets()
            .queue(function inlineRelations(assetGraph) {
                return assetGraph.findRelations({
                    to: {isLoaded: true, isInline: false},
                    from: {isInline: false} // Excludes relations occurring in conditional comments
                }).filter(function (relation) {
                    if (relation.type === 'CssImage' && !options.noInlineCssImagesWithLegacyFallback) {
                        return false; // Already handled by the inlineCssImagesWithLegacyFallback transform above
                    }
                    var sizeThreshold = inlineByRelationType[relation.type],
                        isStarRule = false;
                    if (typeof sizeThreshold === 'undefined') {
                        sizeThreshold = inlineByRelationType['*'];
                        isStarRule = true;
                    }
                    if (typeof sizeThreshold !== 'undefined' && sizeThreshold === true || relation.to.lastKnownByteLength < sizeThreshold) {
                        if (isStarRule) {
                            // Some relation types don't support inlining, and the inline method will be missing or throw an exception.
                            // Wrap it in a try...catch so the * rule means "all relation types that support inlining".
                            try {
                                relation.inline();
                            } catch (e) {}
                        } else {
                            relation.inline();
                        }
                    }
                });
            })
            .if(options.noCompress)
                .prettyPrintAssets({type: 'Css', isLoaded: true})
                .prettyPrintAssets(function (asset) {
                    return asset.isLoaded && asset.type === 'JavaScript' && (!asset.isInline || asset.incomingRelations.every(function (incomingRelation) {
                        return incomingRelation.type === 'HtmlScript';
                    }));
                })
            .endif()
            .inlineAngularJsTemplates()
            .duplicateFavicon()
            .setAsyncOrDeferOnHtmlScripts({to: {isInline: false, url: /^file:/}}, options.asyncScripts, options.deferScripts)
            .omitFunctionCall({type: ['JavaScriptGetStaticUrl', 'JavaScriptTrHtml'], to: {isLoaded: true}})
            .inlineRelations({type: ['JavaScriptGetText', 'JavaScriptTrHtml'], to: {isLoaded: true}})
            .if(options.manifest)
                .addCacheManifest({isInitial: true})
                .if(options.localeIds && options.negotiateManifest)
                    .queue(function stripLocaleIdFromHtmlCacheManifestRelations(assetGraph) {
                        // This would be much less fragile if an asset could have a canonical url as well as an url (under consideration):
                        assetGraph.findRelations({type: 'HtmlCacheManifest'}).forEach(function (htmlCacheManifest) {
                            htmlCacheManifest.href = htmlCacheManifest.href.replace(/\.\w+\.appcache$/, '.appcache');
                        });
                    })
                .endif()
            .endif()
            .if(options.canonicalUrl)
                // Maybe this should be the effect of updating assetGraph.root?
                .moveAssets({isLoaded: true, isInline: false}, function (asset, assetGraph) {
                    if (asset.url.indexOf(assetGraph.root) === 0) {
                        return assetGraph.resolveUrl(options.canonicalUrl, urlTools.buildRelativeUrl(assetGraph.root, asset.url));
                    }
                })
                .updateRelations({type: 'RssChannelLink'}, {hrefType: 'absolute'})
            .endif()
            .moveAssetsInOrder({isLoaded: true, isInline: false, type: query.not(['CacheManifest', 'Rss'])}, function (asset, assetGraph) {
                // Don't rename Html assets that are linked to:
                if (asset.type === 'Html' && asset.incomingRelations.some(function (incomingRelation) {
                    return incomingRelation.type === 'HtmlAnchor';
                })) {
                    return;
                }
                var fileName = asset.fileName;
                if (fileName === '.htaccess' || fileName === 'robots.txt' || fileName === 'humans.txt' || asset.url === assetGraph.root + 'favicon.ico') {
                    return;
                }

                if (!asset.isInitial || (asset.type === 'Html' && asset.isFragment)) {
                    var targetUrl = (options.canonicalUrl || '/') + 'static/';
                    // Conservatively assume that all GETSTATICURL relations pointing at non-images are intended to be fetched via XHR
                    // and thus cannot be put on a CDN because of same origin restrictions:
                    if (options.cdnRoot && asset.type !== 'Htc' && asset.extension !== '.jar' && (asset.type !== 'Html' || options.cdnHtml) && (asset.isImage || assetGraph.findRelations({to: asset, type: 'StaticUrlMapEntry'}).length === 0) || (options.cdnRoot && options.cdnFlash && asset.type === 'Flash')) {
                        targetUrl = options.cdnRoot;
                        if (/^\/\//.test(options.cdnRoot)) {
                            assetGraph.findRelations({to: asset}).forEach(function (incomingRelation) {
                                incomingRelation.hrefType = 'protocolRelative';

                                // Set crossorigin=anonymous on <script> tags pointing at CDN JavaScript.
                                // See http://blog.errorception.com/2012/12/catching-cross-domain-js-errors.html'
                                if (asset.type === 'JavaScript' && incomingRelation.type === 'HtmlScript') {
                                    incomingRelation.node.setAttribute('crossorigin', 'anonymous');
                                    incomingRelation.from.markDirty();
                                }
                            });
                        }
                    }
                    return targetUrl + asset.fileName.split('.').shift() + '.' + asset.md5Hex.substr(0, 10) + asset.extension + asset.url.replace(/^[^#\?]*(?:)/, ''); // Preserve query string and fragment identifier
                }
            })
            .if(options.gzip)
                .gzip({isInline: false, isText: true, isLoaded: true, url: query.not(/\.t?gz([\?#]|$)/)})
            .endif()
            .run(cb);
    };
};
<commit_msg>buildProduction: Use the followRelations option.<commit_after>var urlTools = require('urltools');

module.exports = function (options) {
    options = options || {};

    var bundleStrategyName = options.sharedBundles ? 'sharedBundles' : 'oneBundlePerIncludingAsset',
        inlineByRelationType = options.inlineByRelationType || {HtmlScript: 4096, HtmlStyle: 4096};

    return function buildProduction(assetGraph, cb) {
        var query = assetGraph.query;

        assetGraph.followRelations =
            query.or({
                to: {type: 'I18n'}
            },
            {
                type: query.not(['JavaScriptInclude', 'JavaScriptExtJsRequire', 'JavaScriptCommonJsRequire', 'HtmlAnchor', 'SvgAnchor', 'JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl']),
                to: {url: query.not(/^https?:/)}
            });

        assetGraph
            .populate({from: {type: 'Html'}, followRelations: {type: 'HtmlScript', to: {url: /^file:/}}})
            .assumeRequireJsConfigHasBeenFound()
            .populate()
            .fixBaseAssetsOfUnresolvedOutgoingRelationsFromHtmlFragments({isInitial: true})
            .assumeThatAllHtmlFragmentAssetsWithoutIncomingRelationsAreNotTemplates()
            .populate({startAssets: {type: 'Html'}})
            // Remove bootstrapper scripts injected by buildDevelopment:
            .removeRelations({type: 'HtmlScript', node: {id: 'bootstrapper'}, from: {type: 'Html'}}, {detach: true, removeOrphan: true})
            .addDataVersionAttributeToHtmlElement({type: 'Html', isInitial: true}, options.version)
            .replaceDartWithJavaScript()
            .populate({startAssets: {type: 'JavaScript', url: /\.dart\.js$/}})
            .if(options.localeIds)
                .checkLanguageKeys({
                    supportedLocaleIds: options.localeIds,
                    defaultLocaleId: options.defaultLocaleId,
                    ignoreMessageTypes: 'unused'
                })
            .endif()
            .if(options.less)
                // Replace Less assets with their Css counterparts:
                .compileLessToCss({type: 'Less', isLoaded: true})

                // Remove the in-browser less compiler and its incoming relations,
                // even if it's included from a CDN and thus hasn't been populated.
                // FIXME: Turns out there's a bunch of less.js files (eg. in the ACE editor)
                // that we don't want to be remove, so this is probably a bit too magical.
                .removeRelations({type: 'HtmlScript', to: {url: /\/less(?:-\d+\.\d+\.\d+)?(?:\.min)?\.js$/}}, {unresolved: true, detach: true, removeOrphan: true})
            .endif()
            .if(options.stripDebug)
                .stripDebug({type: 'JavaScript', isLoaded: true})
            .endif()
            .removeRelations({type: 'JavaScriptInclude', to: {type: ['Css', 'JavaScript', 'Html', 'Less']}}, {detach: true, unresolved: true})
            .externalizeRelations({from: {type: query.not('Htc')}, type: ['HtmlStyle', 'HtmlScript'], to: {isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}})
            .mergeIdenticalAssets({
                isImage: true,
                isLoaded: true,
                url: /^[^\?]$/ // Skip images with a query string in the url (might contain processImage instructions)
            })
            // First execute explicit instructions in the query strings for images that are to be sprited:
            .processImages({isImage: true, isLoaded: true, url: /\?(?:|.*&)sprite(?:[&=#]|$)/})
            .spriteBackgroundImages()
            // Execute explicit and automatic optimizations for all images, including the generated sprite images:
            .processImages({isImage: true, isLoaded: true}, {autoLossless: options.optimizeImages})
            .minifySvgAssetsWithSvgo({isLoaded: true})
            .inlineKnockoutJsTemplates()
            .liftUpJavaScriptRequireJsCommonJsCompatibilityRequire()
            .flattenRequireJs({type: 'Html', isFragment: false})
            // Remove orphan assets, except I18n which might originally have been referenced from the no longer existing bootstrapper:
            .removeUnreferencedAssets({type: query.not('I18n'), isInitial: query.not(true)})
            .convertCssImportsToHtmlStyles()
            .removeDuplicateHtmlStyles({type: 'Html', isInitial: true})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']})
            .if(options.autoprefix)
                .autoprefixer(options.autoprefix)
            .endif()
            .bundleRelations({type: 'HtmlStyle', to: {type: 'Css', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .splitCssIfIeLimitIsReached()
            .replaceRequireJsWithAlmond()
            .bundleRelations({type: 'HtmlScript', to: {type: 'JavaScript', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
            .mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']}) // The bundling might produce several identical files, especially the 'oneBundlePerIncludingAsset' strategy.
            .queue(function ngmin(assetGraph) {
                var annotate,
                    angular = assetGraph.findAssets({
                        type: 'Html',
                        isInline: false,
                        isFragment: false
                    }).some(function (asset) {
                        var document = asset.parseTree;

                        return document.body && (document.querySelector('[ng-app]') || document.querySelector('[class~="ng-app:"]'));
                    });

                if (angular) {
                    annotate = require('ngmin').annotate;

                    assetGraph.findAssets({
                        type: 'JavaScript'
                    }).forEach(function (asset) {
                        asset.text = annotate(asset.text);
                    });
                }

                return assetGraph;
            })
            .removeNobundleAttribute({type: ['HtmlScript', 'HtmlStyle']})
            .if(inlineByRelationType.CssImage && !options.noInlineCssImagesWithLegacyFallback)
                .inlineCssImagesWithLegacyFallback({type: 'Html', isInline: false, isFragment: false}, inlineByRelationType.CssImage)
            .endif()
            .minifyAssets({isLoaded: true})
            .if(options.addInitialHtmlExtension)
                .queue(function addInitialHtmlExtension() {
                    assetGraph.findAssets({isInitial: true, type: 'Html'}).forEach(function (asset) {
                        asset.fileName = asset.fileName.replace(/(?=\.|$)/, '.' + String(options.addInitialHtmlExtension).replace(/^\./, ''));
                    });
                })
            .endif()
            .if(options.localeIds)
                .cloneForEachLocale({type: 'Html', isInitial: true}, {
                    localeIds: options.localeIds,
                    supportedLocaleIds: options.localeIds,
                    localeCookieName: options.localeCookieName,
                    defaultLocaleId: options.defaultLocaleId
                })
                .runJavaScriptConditionalBlocks({isInitial: true}, 'LOCALIZE', true)
                .removeRelations({type: query.not('JavaScriptGetText'), to: {type: 'I18n'}}, {detach: true})
                .removeUnreferencedAssets({type: 'I18n'})
            .endif()
            .if(options.defines)
                .replaceSymbolsInJavaScript({type: 'JavaScript', isLoaded: true}, options.defines)
            .endif()
            .if(!options.noCompress)
                .compressJavaScript({type: 'JavaScript', isLoaded: true}, 'uglifyJs', {mangleOptions: {except: options.reservedNames || []}})
            .endif()
            .removeEmptyJavaScripts()
            .removeEmptyStylesheets()
            .queue(function inlineRelations(assetGraph) {
                return assetGraph.findRelations({
                    to: {isLoaded: true, isInline: false},
                    from: {isInline: false} // Excludes relations occurring in conditional comments
                }).filter(function (relation) {
                    if (relation.type === 'CssImage' && !options.noInlineCssImagesWithLegacyFallback) {
                        return false; // Already handled by the inlineCssImagesWithLegacyFallback transform above
                    }
                    var sizeThreshold = inlineByRelationType[relation.type],
                        isStarRule = false;
                    if (typeof sizeThreshold === 'undefined') {
                        sizeThreshold = inlineByRelationType['*'];
                        isStarRule = true;
                    }
                    if (typeof sizeThreshold !== 'undefined' && sizeThreshold === true || relation.to.lastKnownByteLength < sizeThreshold) {
                        if (isStarRule) {
                            // Some relation types don't support inlining, and the inline method will be missing or throw an exception.
                            // Wrap it in a try...catch so the * rule means "all relation types that support inlining".
                            try {
                                relation.inline();
                            } catch (e) {}
                        } else {
                            relation.inline();
                        }
                    }
                });
            })
            .if(options.noCompress)
                .prettyPrintAssets({type: 'Css', isLoaded: true})
                .prettyPrintAssets(function (asset) {
                    return asset.isLoaded && asset.type === 'JavaScript' && (!asset.isInline || asset.incomingRelations.every(function (incomingRelation) {
                        return incomingRelation.type === 'HtmlScript';
                    }));
                })
            .endif()
            .inlineAngularJsTemplates()
            .duplicateFavicon()
            .setAsyncOrDeferOnHtmlScripts({to: {isInline: false, url: /^file:/}}, options.asyncScripts, options.deferScripts)
            .omitFunctionCall({type: ['JavaScriptGetStaticUrl', 'JavaScriptTrHtml'], to: {isLoaded: true}})
            .inlineRelations({type: ['JavaScriptGetText', 'JavaScriptTrHtml'], to: {isLoaded: true}})
            .if(options.manifest)
                .addCacheManifest({isInitial: true})
                .if(options.localeIds && options.negotiateManifest)
                    .queue(function stripLocaleIdFromHtmlCacheManifestRelations(assetGraph) {
                        // This would be much less fragile if an asset could have a canonical url as well as an url (under consideration):
                        assetGraph.findRelations({type: 'HtmlCacheManifest'}).forEach(function (htmlCacheManifest) {
                            htmlCacheManifest.href = htmlCacheManifest.href.replace(/\.\w+\.appcache$/, '.appcache');
                        });
                    })
                .endif()
            .endif()
            .if(options.canonicalUrl)
                // Maybe this should be the effect of updating assetGraph.root?
                .moveAssets({isLoaded: true, isInline: false}, function (asset, assetGraph) {
                    if (asset.url.indexOf(assetGraph.root) === 0) {
                        return assetGraph.resolveUrl(options.canonicalUrl, urlTools.buildRelativeUrl(assetGraph.root, asset.url));
                    }
                })
                .updateRelations({type: 'RssChannelLink'}, {hrefType: 'absolute'})
            .endif()
            .moveAssetsInOrder({isLoaded: true, isInline: false, type: query.not(['CacheManifest', 'Rss'])}, function (asset, assetGraph) {
                // Don't rename Html assets that are linked to:
                if (asset.type === 'Html' && asset.incomingRelations.some(function (incomingRelation) {
                    return incomingRelation.type === 'HtmlAnchor';
                })) {
                    return;
                }
                var fileName = asset.fileName;
                if (fileName === '.htaccess' || fileName === 'robots.txt' || fileName === 'humans.txt' || asset.url === assetGraph.root + 'favicon.ico') {
                    return;
                }

                if (!asset.isInitial || (asset.type === 'Html' && asset.isFragment)) {
                    var targetUrl = (options.canonicalUrl || '/') + 'static/';
                    // Conservatively assume that all GETSTATICURL relations pointing at non-images are intended to be fetched via XHR
                    // and thus cannot be put on a CDN because of same origin restrictions:
                    if (options.cdnRoot && asset.type !== 'Htc' && asset.extension !== '.jar' && (asset.type !== 'Html' || options.cdnHtml) && (asset.isImage || assetGraph.findRelations({to: asset, type: 'StaticUrlMapEntry'}).length === 0) || (options.cdnRoot && options.cdnFlash && asset.type === 'Flash')) {
                        targetUrl = options.cdnRoot;
                        if (/^\/\//.test(options.cdnRoot)) {
                            assetGraph.findRelations({to: asset}).forEach(function (incomingRelation) {
                                incomingRelation.hrefType = 'protocolRelative';

                                // Set crossorigin=anonymous on <script> tags pointing at CDN JavaScript.
                                // See http://blog.errorception.com/2012/12/catching-cross-domain-js-errors.html'
                                if (asset.type === 'JavaScript' && incomingRelation.type === 'HtmlScript') {
                                    incomingRelation.node.setAttribute('crossorigin', 'anonymous');
                                    incomingRelation.from.markDirty();
                                }
                            });
                        }
                    }
                    return targetUrl + asset.fileName.split('.').shift() + '.' + asset.md5Hex.substr(0, 10) + asset.extension + asset.url.replace(/^[^#\?]*(?:)/, ''); // Preserve query string and fragment identifier
                }
            })
            .if(options.gzip)
                .gzip({isInline: false, isText: true, isLoaded: true, url: query.not(/\.t?gz([\?#]|$)/)})
            .endif()
            .run(cb);
    };
};
<|endoftext|>