<commit_before>var urlTools = require('urltools'),
    _ = require('lodash'),
    loadAndInstantiateAutoprefixer = require('../loadAndInstantiateAutoprefixer');

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

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

    var javaScriptSerializationOptions = _.extend({}, options.javaScriptSerializationOptions);

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

    if (options.browsers) {
        autoprefixer = loadAndInstantiateAutoprefixer(options.browsers, 'The \'browsers\' option requires the autoprefixer module to be available. Please install it in the containing project.');

        // Waiting for https://github.com/segmentio/myth/issues/94
        browsers = autoprefixer.prefixes.browsers;
        browsers.requirements.olderThan = {
            regexp: /^(\w+) (<=?)\s*([\d\.]+)/,
            select: function (browser, sign, version) {
                version = parseFloat(version);
                var data = this.byName(browser);
                return data.versions.filter(function (v) {
                    return sign === '<' ? v < version : v <= version;
                }).map(function(v) {
                    return '' + data.name + ' ' + v;
                });
            }
        };
        browsers.has = function (browserOrBrowsers) {
            try {
                return browsers.parse(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
        browsers.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;
        };

        if (browsers.minimum('ie') > 8) {
            javaScriptSerializationOptions.screw_ie8 = true;
        }
    }

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

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

        assetGraph.javaScriptSerializationOptions = javaScriptSerializationOptions;

        if (options.recursive) {
            assetGraph.followRelations =
                query.or({
                    to: {type: 'I18n'}
                },
                {
                    type: ['HtmlAnchor', 'HtmlMetaRefresh'],
                    to: /^file:/
                },
                {
                    type: query.not(['JavaScriptInclude', 'JavaScriptExtJsRequire', 'JavaScriptCommonJsRequire', 'SvgAnchor', 'JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl']),
                    to: {url: query.not(/^https?:/)}
                });
        } else {
            assetGraph.followRelations =
                query.or({
                    to: {type: 'I18n'}
                },
                {
                    type: query.not(['JavaScriptInclude', 'JavaScriptExtJsRequire', 'JavaScriptCommonJsRequire', 'SvgAnchor', 'JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl', 'HtmlAnchor', 'HtmlMetaRefresh']),
                    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.scss)
                .compileScssToCss({type: 'Scss', isLoaded: true})
            .endif()
            .compileJsxToJs()
            .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(autoprefixer)
                .autoprefixer(autoprefixer)
            .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')})
            .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.
            .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', {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()
            .if(options.angular)
              .inlineAngularJsTemplates()
            .endif()
            .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({from: {type: 'Html', isFragment: true, nonInlineAncestor: {type: ['Rss', 'Atom']}}}, {hrefType: 'absolute'})
            .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')
                    },
                    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
                        }),
                        {
                            type: 'Html',
                            isFragment: true
                        }
                    )), 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: '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
                        })
            .endif()
            .if(options.gzip)
                .gzip({isInline: false, isText: true, isLoaded: true, url: query.not(/\.t?gz([\?#]|$)/)})
            .endif()
            .run(cb);
    };
};
<commit_msg>Allow programmatic users of transforms/buildProduction to be more clever about population queries than the transform itself<commit_after>var urlTools = require('urltools'),
    _ = require('lodash'),
    loadAndInstantiateAutoprefixer = require('../loadAndInstantiateAutoprefixer');

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

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

    var javaScriptSerializationOptions = _.extend({}, options.javaScriptSerializationOptions);

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

    if (options.browsers) {
        autoprefixer = loadAndInstantiateAutoprefixer(options.browsers, 'The \'browsers\' option requires the autoprefixer module to be available. Please install it in the containing project.');

        // Waiting for https://github.com/segmentio/myth/issues/94
        browsers = autoprefixer.prefixes.browsers;
        browsers.requirements.olderThan = {
            regexp: /^(\w+) (<=?)\s*([\d\.]+)/,
            select: function (browser, sign, version) {
                version = parseFloat(version);
                var data = this.byName(browser);
                return data.versions.filter(function (v) {
                    return sign === '<' ? v < version : v <= version;
                }).map(function(v) {
                    return '' + data.name + ' ' + v;
                });
            }
        };
        browsers.has = function (browserOrBrowsers) {
            try {
                return browsers.parse(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
        browsers.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;
        };

        if (browsers.minimum('ie') > 8) {
            javaScriptSerializationOptions.screw_ie8 = true;
        }
    }

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

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

        assetGraph.javaScriptSerializationOptions = javaScriptSerializationOptions;

        if (!assetGraph.followRelations) {
            if (options.recursive) {
                assetGraph.followRelations =
                    query.or({
                        to: {type: 'I18n'}
                    },
                    {
                        type: ['HtmlAnchor', 'HtmlMetaRefresh'],
                        to: /^file:/
                    },
                    {
                        type: query.not(['JavaScriptInclude', 'JavaScriptExtJsRequire', 'JavaScriptCommonJsRequire', 'SvgAnchor', 'JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl']),
                        to: {url: query.not(/^https?:/)}
                    });
            } else {
                assetGraph.followRelations =
                    query.or({
                        to: {type: 'I18n'}
                    },
                    {
                        type: query.not(['JavaScriptInclude', 'JavaScriptExtJsRequire', 'JavaScriptCommonJsRequire', 'SvgAnchor', 'JavaScriptSourceMappingUrl', 'JavaScriptSourceUrl', 'HtmlAnchor', 'HtmlMetaRefresh']),
                        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.scss)
                .compileScssToCss({type: 'Scss', isLoaded: true})
            .endif()
            .compileJsxToJs()
            .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(autoprefixer)
                .autoprefixer(autoprefixer)
            .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')})
            .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.
            .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', {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()
            .if(options.angular)
              .inlineAngularJsTemplates()
            .endif()
            .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({from: {type: 'Html', isFragment: true, nonInlineAncestor: {type: ['Rss', 'Atom']}}}, {hrefType: 'absolute'})
            .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')
                    },
                    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
                        }),
                        {
                            type: 'Html',
                            isFragment: true
                        }
                    )), 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: '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
                        })
            .endif()
            .if(options.gzip)
                .gzip({isInline: false, isText: true, isLoaded: true, url: query.not(/\.t?gz([\?#]|$)/)})
            .endif()
            .run(cb);
    };
};
<|endoftext|>


<commit_before>import _flatten from 'lodash-es/flatten';
import _isEmpty from 'lodash-es/isEmpty';
import _reduce from 'lodash-es/reduce';
import _union from 'lodash-es/union';
import _throttle from 'lodash-es/throttle';

import {
    geoBounds as d3_geoBounds,
    geoPath as d3_geoPath
} from 'd3-geo';

import { text as d3_text } from 'd3-request';

import {
    event as d3_event,
    select as d3_select
} from 'd3-selection';

import stringify from 'fast-json-stable-stringify';
import toGeoJSON from '@mapbox/togeojson';

import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
import { services } from '../services';
import { svgPath } from './index';
import { utilDetect } from '../util/detect';
import { utilHashcode } from '../util';

var _initialized = false;
var _enabled = false;
var _geojson;

export function svgData(projection, context, dispatch) {
    var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
    var _showLabels = true;
    var detected = utilDetect();
    var layer = d3_select(null);
    var _vtService;
    var _fileList;
    var _template;
    var _src;

    function init() {
        if (_initialized) return;  // run once

        _geojson = {};
        _enabled = true;

        function over() {
            d3_event.stopPropagation();
            d3_event.preventDefault();
            d3_event.dataTransfer.dropEffect = 'copy';
        }

        d3_select('body')
            .attr('dropzone', 'copy')
            .on('drop.svgData', function() {
                d3_event.stopPropagation();
                d3_event.preventDefault();
                if (!detected.filedrop) return;
                drawData.fileList(d3_event.dataTransfer.files);
            })
            .on('dragenter.svgData', over)
            .on('dragexit.svgData', over)
            .on('dragover.svgData', over);

        _initialized = true;
    }

    function getService() {
        if (services.vectorTile && !_vtService) {
            _vtService = services.vectorTile;
            _vtService.event.on('loadedData', throttledRedraw);
        } else if (!services.vectorTile && _vtService) {
            _vtService = null;
        }

        return _vtService;
    }

    function showLayer() {
        layerOn();

        layer
            .style('opacity', 0)
            .transition()
            .duration(250)
            .style('opacity', 1)
            .on('end', function () { dispatch.call('change'); });
    }

    function hideLayer() {
        throttledRedraw.cancel();

        layer
            .transition()
            .duration(250)
            .style('opacity', 0)
            .on('end', layerOff);
    }

    function layerOn() {
        layer.style('display', 'block');
    }

    function layerOff() {
        layer.selectAll('.viewfield-group').remove();
        layer.style('display', 'none');
    }

    // ensure that all geojson features in a collection have IDs
    function ensureIDs(gj) {
        if (!gj) return null;

        if (gj.type === 'FeatureCollection') {
            for (var i = 0; i < gj.features.length; i++) {
                ensureFeatureID(gj.features[i]);
            }
        } else {
            ensureFeatureID(gj);
        }
        return gj;
    }

    // ensure that each single Feature object has a unique ID
    function ensureFeatureID(feature) {
        if (!feature) return;
        feature.__featurehash__ = utilHashcode(stringify(feature));
        return feature;
    }

    // Prefer an array of Features instead of a FeatureCollection
    function getFeatures(gj) {
        if (!gj) return [];

        if (gj.type === 'FeatureCollection') {
            return gj.features;
        } else {
            return [gj];
        }
    }

    function featureKey(d) {
        return d.__featurehash__;
    }

    function isPolygon(d) {
        return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
    }

    function clipPathID(d) {
        return 'data-' + d.__featurehash__ + '-clippath';
    }

    function featureClasses(d) {
        return [
            'data' + d.__featurehash__,
            d.geometry.type,
            isPolygon(d) ? 'area' : '',
            d.__layerID__ || ''
        ].filter(Boolean).join(' ');
    }

    function drawData(selection) {
        var vtService = getService();
        var getPath = svgPath(projection).geojson;
        var getAreaPath = svgPath(projection, null, true).geojson;
        var hasData = drawData.hasData();

        layer = selection.selectAll('.layer-mapdata')
            .data(_enabled && hasData ? [0] : []);

        layer.exit()
            .remove();

        layer = layer.enter()
            .append('g')
            .attr('class', 'layer-mapdata')
            .merge(layer);

        var surface = context.surface();
        if (!surface || surface.empty()) return;  // not ready to draw yet, starting up

        // Gather data
        var geoData, polygonData;
        if (_template && vtService) {   // fetch data from vector tile service
            var sourceID = _template;
            vtService.loadTiles(sourceID, _template, projection);
            geoData = vtService.data(sourceID, projection);
        } else {
            geoData = getFeatures(_geojson);
        }
        geoData = geoData.filter(getPath);
        polygonData = geoData.filter(isPolygon);

        // Draw clip paths for polygons
        var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
           .data(polygonData, featureKey);

        clipPaths.exit()
           .remove();

        var clipPathsEnter = clipPaths.enter()
           .append('clipPath')
           .attr('class', 'clipPath-data')
           .attr('id', clipPathID);

        clipPathsEnter
           .append('path');

        clipPaths.merge(clipPathsEnter)
           .selectAll('path')
           .attr('d', getAreaPath);

        // Draw fill, shadow, stroke layers
        var datagroups = layer
            .selectAll('g.datagroup')
            .data(['fill', 'shadow', 'stroke']);

        datagroups = datagroups.enter()
            .append('g')
            .attr('class', function(d) { return 'datagroup datagroup-' + d; })
            .merge(datagroups);

        // Draw paths
        var pathData = {
            fill: polygonData,
            shadow: geoData,
            stroke: geoData
        };

        var paths = datagroups
            .selectAll('path')
            .data(function(layer) { return pathData[layer]; }, featureKey);

        // exit
        paths.exit()
            .remove();

        // enter/update
        paths = paths.enter()
            .append('path')
            .attr('class', function(d) {
                var datagroup = this.parentNode.__data__;
                return 'pathdata ' + datagroup + ' ' + featureClasses(d);
            })
            .attr('clip-path', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
            })
            .merge(paths)
            .attr('d', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
            });

        // Draw labels
        layer
            .call(drawLabels, 'label-halo', geoData)
            .call(drawLabels, 'label', geoData);

        function drawLabels(selection, textClass, data) {
            var labelPath = d3_geoPath(projection);
            var labelData = data.filter(function(d) {
                return _showLabels && d.properties && (d.properties.desc || d.properties.name);
            });

            var labels = selection.selectAll('text.' + textClass)
                .data(labelData, featureKey);

            // exit
            labels.exit()
                .remove();

            // enter/update
            labels = labels.enter()
                .append('text')
                .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
                .merge(labels)
                .text(function(d) {
                    return d.properties.desc || d.properties.name;
                })
                .attr('x', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[0] + 11;
                })
                .attr('y', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[1];
                });
        }
    }

    function getExtension(fileName) {
        if (!fileName) return;

        var lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex < 0) return;

        return fileName.substr(lastDotIndex);
    }

    function xmlToDom(textdata) {
        return (new DOMParser()).parseFromString(textdata, 'text/xml');
    }

    drawData.setFile = function(extension, data, src) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        var gj;
        switch (extension) {
            case '.gpx':
                gj = toGeoJSON.gpx(xmlToDom(data));
                break;
            case '.kml':
                gj = toGeoJSON.kml(xmlToDom(data));
                break;
            case '.geojson':
            case '.json':
                gj = JSON.parse(data);
                break;
        }

        if (!_isEmpty(gj)) {
            _geojson = ensureIDs(gj);
            _src = extension + ' data file';
            this.fitZoom();
        }

        dispatch.call('change');
        return this;
    };

    drawData.showLabels = function(val) {
        if (!arguments.length) return _showLabels;

        _showLabels = val;
        return this;
    };

    drawData.enabled = function(val) {
        if (!arguments.length) return _enabled;

        _enabled = val;
        if (_enabled) {
            showLayer();
        } else {
            hideLayer();
        }

        dispatch.call('change');
        return this;
    };

    drawData.hasData = function() {
        return !!(_template || !_isEmpty(_geojson));
    };

    drawData.template = function(val, src) {
        if (!arguments.length) return _template;

        // test source against OSM imagery blacklists..
        var osm = context.connection();
        if (osm) {
            var blacklists = osm.imageryBlacklists();
            var fail = false;
            var tested = 0;
            var regex;

            for (var i = 0; i < blacklists.length; i++) {
                try {
                    regex = new RegExp(blacklists[i]);
                    fail = regex.test(val);
                    tested++;
                    if (fail) break;
                } catch (e) {
                    /* noop */
                }
            }

            // ensure at least one test was run.
            if (!tested) {
                regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
                fail = regex.test(val);
            }
        }

        _template = val;
        _fileList = null;
        _geojson = null;
        _src = src || 'vectortile:' + val;

        dispatch.call('change');
        return this;
    };

    drawData.geojson = function(gj, src) {
        if (!arguments.length) return _geojson;

        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        if (!_isEmpty(gj)) {
            _geojson = ensureIDs(gj);
            _src = src || 'unknown.geojson';
        }

        dispatch.call('change');
        return this;
    };

    drawData.fileList = function(fileList) {
        if (!arguments.length) return _fileList;

        _template = null;
        _fileList = fileList;
        _geojson = null;
        _src = null;

        if (!fileList || !fileList.length) return this;
        var f = fileList[0];
        var extension = getExtension(f.name);
        var reader = new FileReader();
        reader.onload = (function(file) {
            return function(e) {
                drawData.setFile(extension, e.target.result, file.name);
            };
        })(f);

        reader.readAsText(f);

        return this;
    };

    drawData.url = function(url) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        var extension = getExtension(url);
        var re = /\.(gpx|kml|(geo)?json)$/i;
        if (re.test(extension)) {
            _template = null;
            d3_text(url, function(err, data) {
                if (err) return;
                drawData.setFile(extension, data, url);
            });
        } else {
            drawData.template(url);
        }

        return this;
    };

    drawData.getSrc = function() {
        return _src || '';
    };

    drawData.fitZoom = function() {
        var features = getFeatures(_geojson);
        if (!features.length) return;

        var map = context.map();
        var viewport = map.trimmedExtent().polygon();
        var coords = _reduce(features, function(coords, feature) {
            var c = feature.geometry.coordinates;

            /* eslint-disable no-fallthrough */
            switch (feature.geometry.type) {
                case 'Point':
                    c = [c];
                case 'MultiPoint':
                case 'LineString':
                    break;

                case 'MultiPolygon':
                    c = _flatten(c);
                case 'Polygon':
                case 'MultiLineString':
                    c = _flatten(c);
                    break;
            }
            /* eslint-enable no-fallthrough */

            return _union(coords, c);
        }, []);

        if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
            var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
            map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
        }

        return this;
    };

    init();
    return drawData;
}
<commit_msg>Strip off querystring and hash from vector tile source string<commit_after>import _flatten from 'lodash-es/flatten';
import _isEmpty from 'lodash-es/isEmpty';
import _reduce from 'lodash-es/reduce';
import _union from 'lodash-es/union';
import _throttle from 'lodash-es/throttle';

import {
    geoBounds as d3_geoBounds,
    geoPath as d3_geoPath
} from 'd3-geo';

import { text as d3_text } from 'd3-request';

import {
    event as d3_event,
    select as d3_select
} from 'd3-selection';

import stringify from 'fast-json-stable-stringify';
import toGeoJSON from '@mapbox/togeojson';

import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
import { services } from '../services';
import { svgPath } from './index';
import { utilDetect } from '../util/detect';
import { utilHashcode } from '../util';

var _initialized = false;
var _enabled = false;
var _geojson;

export function svgData(projection, context, dispatch) {
    var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
    var _showLabels = true;
    var detected = utilDetect();
    var layer = d3_select(null);
    var _vtService;
    var _fileList;
    var _template;
    var _src;

    function init() {
        if (_initialized) return;  // run once

        _geojson = {};
        _enabled = true;

        function over() {
            d3_event.stopPropagation();
            d3_event.preventDefault();
            d3_event.dataTransfer.dropEffect = 'copy';
        }

        d3_select('body')
            .attr('dropzone', 'copy')
            .on('drop.svgData', function() {
                d3_event.stopPropagation();
                d3_event.preventDefault();
                if (!detected.filedrop) return;
                drawData.fileList(d3_event.dataTransfer.files);
            })
            .on('dragenter.svgData', over)
            .on('dragexit.svgData', over)
            .on('dragover.svgData', over);

        _initialized = true;
    }

    function getService() {
        if (services.vectorTile && !_vtService) {
            _vtService = services.vectorTile;
            _vtService.event.on('loadedData', throttledRedraw);
        } else if (!services.vectorTile && _vtService) {
            _vtService = null;
        }

        return _vtService;
    }

    function showLayer() {
        layerOn();

        layer
            .style('opacity', 0)
            .transition()
            .duration(250)
            .style('opacity', 1)
            .on('end', function () { dispatch.call('change'); });
    }

    function hideLayer() {
        throttledRedraw.cancel();

        layer
            .transition()
            .duration(250)
            .style('opacity', 0)
            .on('end', layerOff);
    }

    function layerOn() {
        layer.style('display', 'block');
    }

    function layerOff() {
        layer.selectAll('.viewfield-group').remove();
        layer.style('display', 'none');
    }

    // ensure that all geojson features in a collection have IDs
    function ensureIDs(gj) {
        if (!gj) return null;

        if (gj.type === 'FeatureCollection') {
            for (var i = 0; i < gj.features.length; i++) {
                ensureFeatureID(gj.features[i]);
            }
        } else {
            ensureFeatureID(gj);
        }
        return gj;
    }

    // ensure that each single Feature object has a unique ID
    function ensureFeatureID(feature) {
        if (!feature) return;
        feature.__featurehash__ = utilHashcode(stringify(feature));
        return feature;
    }

    // Prefer an array of Features instead of a FeatureCollection
    function getFeatures(gj) {
        if (!gj) return [];

        if (gj.type === 'FeatureCollection') {
            return gj.features;
        } else {
            return [gj];
        }
    }

    function featureKey(d) {
        return d.__featurehash__;
    }

    function isPolygon(d) {
        return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
    }

    function clipPathID(d) {
        return 'data-' + d.__featurehash__ + '-clippath';
    }

    function featureClasses(d) {
        return [
            'data' + d.__featurehash__,
            d.geometry.type,
            isPolygon(d) ? 'area' : '',
            d.__layerID__ || ''
        ].filter(Boolean).join(' ');
    }

    function drawData(selection) {
        var vtService = getService();
        var getPath = svgPath(projection).geojson;
        var getAreaPath = svgPath(projection, null, true).geojson;
        var hasData = drawData.hasData();

        layer = selection.selectAll('.layer-mapdata')
            .data(_enabled && hasData ? [0] : []);

        layer.exit()
            .remove();

        layer = layer.enter()
            .append('g')
            .attr('class', 'layer-mapdata')
            .merge(layer);

        var surface = context.surface();
        if (!surface || surface.empty()) return;  // not ready to draw yet, starting up

        // Gather data
        var geoData, polygonData;
        if (_template && vtService) {   // fetch data from vector tile service
            var sourceID = _template;
            vtService.loadTiles(sourceID, _template, projection);
            geoData = vtService.data(sourceID, projection);
        } else {
            geoData = getFeatures(_geojson);
        }
        geoData = geoData.filter(getPath);
        polygonData = geoData.filter(isPolygon);

        // Draw clip paths for polygons
        var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
           .data(polygonData, featureKey);

        clipPaths.exit()
           .remove();

        var clipPathsEnter = clipPaths.enter()
           .append('clipPath')
           .attr('class', 'clipPath-data')
           .attr('id', clipPathID);

        clipPathsEnter
           .append('path');

        clipPaths.merge(clipPathsEnter)
           .selectAll('path')
           .attr('d', getAreaPath);

        // Draw fill, shadow, stroke layers
        var datagroups = layer
            .selectAll('g.datagroup')
            .data(['fill', 'shadow', 'stroke']);

        datagroups = datagroups.enter()
            .append('g')
            .attr('class', function(d) { return 'datagroup datagroup-' + d; })
            .merge(datagroups);

        // Draw paths
        var pathData = {
            fill: polygonData,
            shadow: geoData,
            stroke: geoData
        };

        var paths = datagroups
            .selectAll('path')
            .data(function(layer) { return pathData[layer]; }, featureKey);

        // exit
        paths.exit()
            .remove();

        // enter/update
        paths = paths.enter()
            .append('path')
            .attr('class', function(d) {
                var datagroup = this.parentNode.__data__;
                return 'pathdata ' + datagroup + ' ' + featureClasses(d);
            })
            .attr('clip-path', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
            })
            .merge(paths)
            .attr('d', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
            });

        // Draw labels
        layer
            .call(drawLabels, 'label-halo', geoData)
            .call(drawLabels, 'label', geoData);

        function drawLabels(selection, textClass, data) {
            var labelPath = d3_geoPath(projection);
            var labelData = data.filter(function(d) {
                return _showLabels && d.properties && (d.properties.desc || d.properties.name);
            });

            var labels = selection.selectAll('text.' + textClass)
                .data(labelData, featureKey);

            // exit
            labels.exit()
                .remove();

            // enter/update
            labels = labels.enter()
                .append('text')
                .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
                .merge(labels)
                .text(function(d) {
                    return d.properties.desc || d.properties.name;
                })
                .attr('x', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[0] + 11;
                })
                .attr('y', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[1];
                });
        }
    }

    function getExtension(fileName) {
        if (!fileName) return;

        var lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex < 0) return;

        return fileName.substr(lastDotIndex);
    }

    function xmlToDom(textdata) {
        return (new DOMParser()).parseFromString(textdata, 'text/xml');
    }

    drawData.setFile = function(extension, data) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        var gj;
        switch (extension) {
            case '.gpx':
                gj = toGeoJSON.gpx(xmlToDom(data));
                break;
            case '.kml':
                gj = toGeoJSON.kml(xmlToDom(data));
                break;
            case '.geojson':
            case '.json':
                gj = JSON.parse(data);
                break;
        }

        if (!_isEmpty(gj)) {
            _geojson = ensureIDs(gj);
            _src = extension + ' data file';
            this.fitZoom();
        }

        dispatch.call('change');
        return this;
    };

    drawData.showLabels = function(val) {
        if (!arguments.length) return _showLabels;

        _showLabels = val;
        return this;
    };

    drawData.enabled = function(val) {
        if (!arguments.length) return _enabled;

        _enabled = val;
        if (_enabled) {
            showLayer();
        } else {
            hideLayer();
        }

        dispatch.call('change');
        return this;
    };

    drawData.hasData = function() {
        return !!(_template || !_isEmpty(_geojson));
    };

    drawData.template = function(val, src) {
        if (!arguments.length) return _template;

        // test source against OSM imagery blacklists..
        var osm = context.connection();
        if (osm) {
            var blacklists = osm.imageryBlacklists();
            var fail = false;
            var tested = 0;
            var regex;

            for (var i = 0; i < blacklists.length; i++) {
                try {
                    regex = new RegExp(blacklists[i]);
                    fail = regex.test(val);
                    tested++;
                    if (fail) break;
                } catch (e) {
                    /* noop */
                }
            }

            // ensure at least one test was run.
            if (!tested) {
                regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
                fail = regex.test(val);
            }
        }

        _template = val;
        _fileList = null;
        _geojson = null;

        // strip off the querystring/hash from the template,
        // it often includes the access token
        _src = src || ('vectortile:' + val.split(/[?#]/)[0]);

        dispatch.call('change');
        return this;
    };

    drawData.geojson = function(gj, src) {
        if (!arguments.length) return _geojson;

        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        if (!_isEmpty(gj)) {
            _geojson = ensureIDs(gj);
            _src = src || 'unknown.geojson';
        }

        dispatch.call('change');
        return this;
    };

    drawData.fileList = function(fileList) {
        if (!arguments.length) return _fileList;

        _template = null;
        _fileList = fileList;
        _geojson = null;
        _src = null;

        if (!fileList || !fileList.length) return this;
        var f = fileList[0];
        var extension = getExtension(f.name);
        var reader = new FileReader();
        reader.onload = (function() {
            return function(e) {
                drawData.setFile(extension, e.target.result);
            };
        })(f);

        reader.readAsText(f);

        return this;
    };

    drawData.url = function(url) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        var extension = getExtension(url);
        var re = /\.(gpx|kml|(geo)?json)$/i;
        if (re.test(extension)) {
            _template = null;
            d3_text(url, function(err, data) {
                if (err) return;
                drawData.setFile(extension, data);
            });
        } else {
            drawData.template(url);
        }

        return this;
    };

    drawData.getSrc = function() {
        return _src || '';
    };

    drawData.fitZoom = function() {
        var features = getFeatures(_geojson);
        if (!features.length) return;

        var map = context.map();
        var viewport = map.trimmedExtent().polygon();
        var coords = _reduce(features, function(coords, feature) {
            var c = feature.geometry.coordinates;

            /* eslint-disable no-fallthrough */
            switch (feature.geometry.type) {
                case 'Point':
                    c = [c];
                case 'MultiPoint':
                case 'LineString':
                    break;

                case 'MultiPolygon':
                    c = _flatten(c);
                case 'Polygon':
                case 'MultiLineString':
                    c = _flatten(c);
                    break;
            }
            /* eslint-enable no-fallthrough */

            return _union(coords, c);
        }, []);

        if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
            var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
            map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
        }

        return this;
    };

    init();
    return drawData;
}
<|endoftext|>


<commit_before>import _debounce from 'lodash-es/debounce';

import { select as d3_select } from 'd3-selection';

import {
    modeAddArea,
    modeAddLine,
    modeAddPoint,
    modeAddNote,
    modeBrowse
} from '../modes';

import { svgIcon } from '../svg';
import { t } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { uiPresetIcon } from './preset_icon';
import { uiTooltipHtml } from './tooltipHtml';

export function uiModes(context) {
    var modes = [
        modeAddPoint(context),
        modeAddLine(context),
        modeAddArea(context),
        modeAddNote(context)
    ];

    function enabled(d) {
        if (d.id === 'add-note') {
            return notesEnabled() && notesEditable();
        } else {
            return osmEditable();
        }
    }

    function osmEditable() {
        var mode = context.mode();
        return context.editable() && mode && mode.id !== 'save';
    }

    function notesEnabled() {
        var noteLayer = context.layers().layer('notes');
        return noteLayer && noteLayer.enabled();
    }

    function notesEditable() {
        var mode = context.mode();
        return context.map().notesEditable() && mode && mode.id !== 'save';
    }

    return function(selection) {
        context
            .on('enter.editor', function(entered) {
                selection.selectAll('button.add-button')
                    .classed('active', function(mode) { return entered.button === mode.button; });
                context.container()
                    .classed('mode-' + entered.id, true);
            });

        context
            .on('exit.editor', function(exited) {
                context.container()
                    .classed('mode-' + exited.id, false);
            });

        modes.forEach(function(mode) {
            context.keybinding().on(mode.key, function() {
                if (!enabled(mode)) return;

                if (mode.id === context.mode().id) {
                    context.enter(modeBrowse(context));
                } else {
                    context.enter(mode);
                }
            });
        });

        var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true });

        context.map()
            .on('move.modes', debouncedUpdate)
            .on('drawn.modes', debouncedUpdate);

        context
            .on('enter.modes', update)
            .on('favoritePreset.modes', update);

        update();

        function update() {
            var showNotes = notesEnabled();
            var data = showNotes ? modes : modes.slice(0, 3);

            // add favorite presets to modes
            var favoritePresets = context.getFavoritePresets();
            var favoriteModes = favoritePresets.map(function(d) {
                var preset = context.presets().item(d.id);
                var isMaki = /^maki-/.test(preset.icon);
                var icon = '#' + preset.icon + (isMaki ? '-11' : '');
                var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + preset.name()
                    .replace(/\s+/g, '_')
                    + '-' + d.geom; //replace spaces with underscores to avoid css interpretation
                var presetName = t('presets.presets.' + preset.id + '.name');
                var relevantMatchingGeometry = preset.geometry.filter(function(geometry) {
                    return ['point', 'line', 'area'].indexOf(geometry) !== -1;
                });
                var tooltipTitleID = 'modes.add_preset.title';
                if (relevantMatchingGeometry.length !== 1) {
                    tooltipTitleID = 'modes.add_preset.' + d.geom + '.title'
                }
                var favoriteMode = {
                    id: markerClass,
                    button: markerClass,
                    title: presetName,
                    description: t(tooltipTitleID, { feature: presetName }),
                    key: '',
                    icon: icon,
                    preset: preset,
                    geometry: d.geom
                };
                switch (d.geom) {
                    case 'point':
                    case 'vertex':
                        return modeAddPoint(context, favoriteMode);
                    case 'line':
                        return modeAddLine(context, favoriteMode);
                    case 'area':
                        return modeAddArea(context, favoriteMode);
                }
            });

            data = data.concat(favoriteModes);

            var buttons = selection.selectAll('button.add-button')
                .data(data, function(d) { return d.id; });

            // exit
            buttons.exit()
                .remove();

            // enter
            var buttonsEnter = buttons.enter()
                .append('button')
                .attr('tabindex', -1)
                .attr('class', function(d) { return d.id + ' add-button'; })
                .on('click.mode-buttons', function(d) {
                    if (!enabled(d)) return;

                    // When drawing, ignore accidental clicks on mode buttons - #4042
                    var currMode = context.mode().id;
                    if (/^draw/.test(currMode)) return;

                    if (d.id === currMode) {
                        context.enter(modeBrowse(context));
                    } else {
                        context.enter(d);
                    }
                })
                .call(tooltip()
                    .placement('bottom')
                    .html(true)
                    .title(function(d) { return uiTooltipHtml(d.description, d.key); })
                );

            buttonsEnter
                .each(function(d) {
                    if (d.preset) {
                        d3_select(this)
                            .call(uiPresetIcon()
                                .geometry(d.geometry)
                                .preset(d.preset)
                                .sizeClass('small')
                            )
                    } else {
                        d3_select(this)
                            .call(svgIcon(d.icon || '#iD-icon-' + d.button));
                    }
                });

            buttonsEnter
                .append('span')
                .attr('class', 'label')
                .text(function(mode) { return mode.title; });

            // if we are adding/removing the buttons, check if toolbar has overflowed
            if (buttons.enter().size() || buttons.exit().size()) {
                context.ui().checkOverflow('#bar', true);
            }

            // update
            buttons = buttons
                .merge(buttonsEnter)
                .classed('disabled', function(d) { return !enabled(d); });
        }
    };
}
<commit_msg>Fix lint errors<commit_after>import _debounce from 'lodash-es/debounce';

import { select as d3_select } from 'd3-selection';

import {
    modeAddArea,
    modeAddLine,
    modeAddPoint,
    modeAddNote,
    modeBrowse
} from '../modes';

import { svgIcon } from '../svg';
import { t } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { uiPresetIcon } from './preset_icon';
import { uiTooltipHtml } from './tooltipHtml';

export function uiModes(context) {
    var modes = [
        modeAddPoint(context),
        modeAddLine(context),
        modeAddArea(context),
        modeAddNote(context)
    ];

    function enabled(d) {
        if (d.id === 'add-note') {
            return notesEnabled() && notesEditable();
        } else {
            return osmEditable();
        }
    }

    function osmEditable() {
        var mode = context.mode();
        return context.editable() && mode && mode.id !== 'save';
    }

    function notesEnabled() {
        var noteLayer = context.layers().layer('notes');
        return noteLayer && noteLayer.enabled();
    }

    function notesEditable() {
        var mode = context.mode();
        return context.map().notesEditable() && mode && mode.id !== 'save';
    }

    return function(selection) {
        context
            .on('enter.editor', function(entered) {
                selection.selectAll('button.add-button')
                    .classed('active', function(mode) { return entered.button === mode.button; });
                context.container()
                    .classed('mode-' + entered.id, true);
            });

        context
            .on('exit.editor', function(exited) {
                context.container()
                    .classed('mode-' + exited.id, false);
            });

        modes.forEach(function(mode) {
            context.keybinding().on(mode.key, function() {
                if (!enabled(mode)) return;

                if (mode.id === context.mode().id) {
                    context.enter(modeBrowse(context));
                } else {
                    context.enter(mode);
                }
            });
        });

        var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true });

        context.map()
            .on('move.modes', debouncedUpdate)
            .on('drawn.modes', debouncedUpdate);

        context
            .on('enter.modes', update)
            .on('favoritePreset.modes', update);

        update();

        function update() {
            var showNotes = notesEnabled();
            var data = showNotes ? modes : modes.slice(0, 3);

            // add favorite presets to modes
            var favoritePresets = context.getFavoritePresets();
            var favoriteModes = favoritePresets.map(function(d) {
                var preset = context.presets().item(d.id);
                var isMaki = /^maki-/.test(preset.icon);
                var icon = '#' + preset.icon + (isMaki ? '-11' : '');
                var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + preset.name()
                    .replace(/\s+/g, '_')
                    + '-' + d.geom; //replace spaces with underscores to avoid css interpretation
                var presetName = t('presets.presets.' + preset.id + '.name');
                var relevantMatchingGeometry = preset.geometry.filter(function(geometry) {
                    return ['point', 'line', 'area'].indexOf(geometry) !== -1;
                });
                var tooltipTitleID = 'modes.add_preset.title';
                if (relevantMatchingGeometry.length !== 1) {
                    tooltipTitleID = 'modes.add_preset.' + d.geom + '.title';
                }
                var favoriteMode = {
                    id: markerClass,
                    button: markerClass,
                    title: presetName,
                    description: t(tooltipTitleID, { feature: presetName }),
                    key: '',
                    icon: icon,
                    preset: preset,
                    geometry: d.geom
                };
                switch (d.geom) {
                    case 'point':
                    case 'vertex':
                        return modeAddPoint(context, favoriteMode);
                    case 'line':
                        return modeAddLine(context, favoriteMode);
                    case 'area':
                        return modeAddArea(context, favoriteMode);
                }
            });

            data = data.concat(favoriteModes);

            var buttons = selection.selectAll('button.add-button')
                .data(data, function(d) { return d.id; });

            // exit
            buttons.exit()
                .remove();

            // enter
            var buttonsEnter = buttons.enter()
                .append('button')
                .attr('tabindex', -1)
                .attr('class', function(d) { return d.id + ' add-button'; })
                .on('click.mode-buttons', function(d) {
                    if (!enabled(d)) return;

                    // When drawing, ignore accidental clicks on mode buttons - #4042
                    var currMode = context.mode().id;
                    if (/^draw/.test(currMode)) return;

                    if (d.id === currMode) {
                        context.enter(modeBrowse(context));
                    } else {
                        context.enter(d);
                    }
                })
                .call(tooltip()
                    .placement('bottom')
                    .html(true)
                    .title(function(d) { return uiTooltipHtml(d.description, d.key); })
                );

            buttonsEnter
                .each(function(d) {
                    if (d.preset) {
                        d3_select(this)
                            .call(uiPresetIcon()
                                .geometry(d.geometry)
                                .preset(d.preset)
                                .sizeClass('small')
                            );
                    } else {
                        d3_select(this)
                            .call(svgIcon(d.icon || '#iD-icon-' + d.button));
                    }
                });

            buttonsEnter
                .append('span')
                .attr('class', 'label')
                .text(function(mode) { return mode.title; });

            // if we are adding/removing the buttons, check if toolbar has overflowed
            if (buttons.enter().size() || buttons.exit().size()) {
                context.ui().checkOverflow('#bar', true);
            }

            // update
            buttons = buttons
                .merge(buttonsEnter)
                .classed('disabled', function(d) { return !enabled(d); });
        }
    };
}
<|endoftext|>


<commit_before>import React from 'react';
import Preview from './preview';
import keycode from 'keycode';
import { EventEmitter } from 'events';
import parseKeyEvent from '../../src/libs/key_events';
import { Provider } from '../../src';

const id = 0;

const style = {
  flex: 1,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
};

export default class ReactProvider extends Provider {
  constructor() {
    super();
    this.globalState = new EventEmitter();
  }

  getPanels() {
    const panels = {
      test1: {
        title: 'Test 1',
        render: () => {
          let inp;
          return (

              <input
                ref={i => {
                  inp = i;
                }}
                value={
                  this.api.getQueryParam('text') === undefined
                    ? 'ONE'
                    : this.api.getQueryParam('text')
                }
                onChange={() => {
                  this.api.setQueryParams({ text: inp.value });
                }}
              />

          );
        },
      },
      test2: {
        title: 'Test 2',
        render: () =>
II
,
      },
      test3: {
        title: 'Test 3',
        render: () =>
III
,
      },
      test4: {
        title: 'Test 4',
        render: () =>
IV
,
      },
    };
    return panels;
  }

  // You must implement this public API.
  renderPreview(selectedKind, selectedStory) {
    // We need to do this here to avoid memory leaks in the globalState.
    // That's because renderPreview can be called multiple times.
    this._handlePreviewEvents();

    // create preview React component.
    const preview = new Preview(this.globalState);
    this.globalState.emit('change', selectedKind, selectedStory);
    return preview;
  }

  // You must implement this public API.
  handleAPI(api) {
    this.api = api;
    this.api.setOptions({
      name: 'REACT-STORYBOOK',
      sortStoriesByKind: true,
      hierarchySeparator: '/'
    });

    // set stories
    this.api.setStories(this.createStories());

    // listen to the story change and update the preview.
    this.api.onStory((kind, story) => {
      this.globalState.emit('change', kind, story);
    });
  }

  createStories() {
    return [
      {
        kind: 'some/name/Component 1',
        stories: ['State 1', 'State 2'],
      },
      {
        kind: 'some/name/Component 2',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'some/name2/Component 3',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'some/name2',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'some/name2/Component 4',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'another/name3/Component 5',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'another/name3/Component 6',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'Bla 1',
        stories: ['State 1', 'State 2'],
      },
      {
        kind: 'Bla 2',
        stories: ['State 1', 'State 2'],
      },
      {
        kind: 'anotherComponent in the middle',
        stories: ['State a', 'State b'],
      },
    ]
  }

  _handlePreviewEvents() {
    this.globalState.removeAllListeners();

    // jumping to an story.
    this.globalState.on('jump', (kind, story) => {
      this.api.selectStory(kind, story);
    });

    // calling a shortcut functionality.
    this.globalState.on('toggleFullscreen', () => {
      const event = {
        ctrlKey: true,
        shiftKey: true,
        keyCode: keycode('F'),
        preventDefault() {},
      };
      const parsedEvent = parseKeyEvent(event);
      this.api.handleShortcut(parsedEvent);
    });
  }
}
<commit_msg>Remove '/' from ui\example\client\provider since it's a default value<commit_after>import React from 'react';
import Preview from './preview';
import keycode from 'keycode';
import { EventEmitter } from 'events';
import parseKeyEvent from '../../src/libs/key_events';
import { Provider } from '../../src';

const id = 0;

const style = {
  flex: 1,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
};

export default class ReactProvider extends Provider {
  constructor() {
    super();
    this.globalState = new EventEmitter();
  }

  getPanels() {
    const panels = {
      test1: {
        title: 'Test 1',
        render: () => {
          let inp;
          return (

              <input
                ref={i => {
                  inp = i;
                }}
                value={
                  this.api.getQueryParam('text') === undefined
                    ? 'ONE'
                    : this.api.getQueryParam('text')
                }
                onChange={() => {
                  this.api.setQueryParams({ text: inp.value });
                }}
              />

          );
        },
      },
      test2: {
        title: 'Test 2',
        render: () =>
II
,
      },
      test3: {
        title: 'Test 3',
        render: () =>
III
,
      },
      test4: {
        title: 'Test 4',
        render: () =>
IV
,
      },
    };
    return panels;
  }

  // You must implement this public API.
  renderPreview(selectedKind, selectedStory) {
    // We need to do this here to avoid memory leaks in the globalState.
    // That's because renderPreview can be called multiple times.
    this._handlePreviewEvents();

    // create preview React component.
    const preview = new Preview(this.globalState);
    this.globalState.emit('change', selectedKind, selectedStory);
    return preview;
  }

  // You must implement this public API.
  handleAPI(api) {
    this.api = api;
    this.api.setOptions({
      name: 'REACT-STORYBOOK',
      sortStoriesByKind: true,
    });

    // set stories
    this.api.setStories(this.createStories());

    // listen to the story change and update the preview.
    this.api.onStory((kind, story) => {
      this.globalState.emit('change', kind, story);
    });
  }

  createStories() {
    return [
      {
        kind: 'some/name/Component 1',
        stories: ['State 1', 'State 2'],
      },
      {
        kind: 'some/name/Component 2',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'some/name2/Component 3',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'some/name2',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'some/name2/Component 4',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'another/name3/Component 5',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'another/name3/Component 6',
        stories: ['State a', 'State b'],
      },
      {
        kind: 'Bla 1',
        stories: ['State 1', 'State 2'],
      },
      {
        kind: 'Bla 2',
        stories: ['State 1', 'State 2'],
      },
      {
        kind: 'anotherComponent in the middle',
        stories: ['State a', 'State b'],
      },
    ]
  }

  _handlePreviewEvents() {
    this.globalState.removeAllListeners();

    // jumping to an story.
    this.globalState.on('jump', (kind, story) => {
      this.api.selectStory(kind, story);
    });

    // calling a shortcut functionality.
    this.globalState.on('toggleFullscreen', () => {
      const event = {
        ctrlKey: true,
        shiftKey: true,
        keyCode: keycode('F'),
        preventDefault() {},
      };
      const parsedEvent = parseKeyEvent(event);
      this.api.handleShortcut(parsedEvent);
    });
  }
}
<|endoftext|>


<commit_before>/**
 * generate SOAP error message with the error
 */

/**
 * SOAP 1.1 fault message
 * @param error message string
 */

exports.getSOAP11Fault = function(error){
	return
	  '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'+
            '<soap:Body>'+
              '<soap:Fault>'+
                '<faultcode>soap:Client</faultcode>'+
                '<faultstring>'+error+'</faultstring>'+
              '</soap:Fault>'+
            '</soap:Body>'+
          '</soap:Envelope>';
}
<commit_msg>modified method<commit_after>/**
 * generate SOAP error message with the error
 */

/**
 * SOAP 1.1 fault message
 * @param error message string
 */

exports.getSOAP11Fault = function(error){
	var msg =
	  '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'+
            '<soap:Body>'+
              '<soap:Fault>'+
                '<faultcode>soap:Client</faultcode>'+
                '<faultstring>'+error+'</faultstring>'+
              '</soap:Fault>'+
            '</soap:Body>'+
          '</soap:Envelope>';
	return msg;
}

<|endoftext|>


<commit_before>var socket = io.connect('http://soundspaces.herokuapp.com/');

// retrieve saved volume from localstorage
var localVol = localStorage['volume'] || 1,
    slider = document.getElementById('fader');

if (localVol != 1) {
  slider.value = localVol;
}

var saveVolume = function() {
  console.log('save', slider.value);
  localStorage['volume'] = slider.value;
};

slider.addEventListener('change', saveVolume);

socket.on('newsound', function (data) {
  if (document.getElementById('loading') != null) {
    document.body.removeChild(document.getElementById('loading'));
  }

  // create the audio element and metadata children
  var audio = document.createElement('audio');
  audio.setAttribute('src', data.play.url);
  audio.setAttribute('controls', '');
  audio.setAttribute('autoplay', '');
  audio.volume = slider.value;

  var p = document.createElement('p');
  p.innerHTML = data.play.sound + ' — ';

  var time = document.createElement('time');
  time.innerHTML = data.play.date;

  var li = document.createElement('li');

  // append everything to the dom
  li.appendChild(audio);
  p.appendChild(time);
  li.appendChild(p);

  var ul = document.getElementById('sounds');
  var first = ul.firstChild;
  ul.insertBefore(li, first);

  // gray out old sounds
  if (first != null) {
    first.className = 'grayed';
  }
});
<commit_msg>Changing the socket.io endpoint back to //<commit_after>var socket = io.connect('//');

// retrieve saved volume from localstorage
var localVol = localStorage['volume'] || 1,
    slider = document.getElementById('fader');

if (localVol != 1) {
  slider.value = localVol;
}

var saveVolume = function() {
  console.log('save', slider.value);
  localStorage['volume'] = slider.value;
};

slider.addEventListener('change', saveVolume);

socket.on('newsound', function (data) {
  if (document.getElementById('loading') != null) {
    document.body.removeChild(document.getElementById('loading'));
  }

  // create the audio element and metadata children
  var audio = document.createElement('audio');
  audio.setAttribute('src', data.play.url);
  audio.setAttribute('controls', '');
  audio.setAttribute('autoplay', '');
  audio.volume = slider.value;

  var p = document.createElement('p');
  p.innerHTML = data.play.sound + ' — ';

  var time = document.createElement('time');
  time.innerHTML = data.play.date;

  var li = document.createElement('li');

  // append everything to the dom
  li.appendChild(audio);
  p.appendChild(time);
  li.appendChild(p);

  var ul = document.getElementById('sounds');
  var first = ul.firstChild;
  ul.insertBefore(li, first);

  // gray out old sounds
  if (first != null) {
    first.className = 'grayed';
  }
});
<|endoftext|>


<commit_before>/**
 * Dependencies
 */

var _ = require('lodash'),
    extend = require('../utils/extend');

// Various Pieces
var Core = require('../core'),
    Query = require('../query');

/**
 * Collection
 *
 * A prototype for managing a collection of database
 * records.
 *
 * This file is the prototype for collections defined using Waterline.
 * It contains the entry point for all ORM methods (e.g. User.find())
 *
 * Methods in this file defer to the adapter for their true implementation:
 * the implementation here just validates and normalizes the parameters.
 *
 * @param {Object} waterline, reference to parent
 * @param {Object} options
 * @param {Function} callback
 */

var Collection = module.exports = function(waterline, options, cb) {

  // Allow options object to be optional
  if(typeof options === 'function') {
    cb = options;
    options = {};
  }

  var self = this;

  // Throw Error if no Tablename/Identity is set
  if(!this.tableName && !this.identity && !options.tableName && !options.identity) {
    return cb(new Error('A tableName property must be set.'));
  }

  // Normalize tableName to identity
  if(options.tableName) this.identity = options.tableName;

  // Cache reference to the parent
  this.waterline = waterline;

  // Default Attributes
  this.attributes = this.attributes || {};

  // Instantiate Base Collection
  Core.call(this, options);

  // Instantiate Query Language
  Query.call(this);

  // Register Collection
  this._adapter.registerCollection(function() {

    // Sync the schema to the database
    self.sync(function(err) {
      if(err) return cb(err);

      // Call Local Initialize (Made to be overridden)
      self.initialize.call(this, options);

      cb(null, self);
    });
  });
};

_.extend(Collection.prototype, Core.prototype, Query.prototype, {

  /**
   * Empty No-Op function to override in a Model
   */
  initialize: function() {}

});

// Make Extendable
Collection.extend = extend;
<commit_msg>ensure identity is always lowercased<commit_after>/**
 * Dependencies
 */

var _ = require('lodash'),
    extend = require('../utils/extend');

// Various Pieces
var Core = require('../core'),
    Query = require('../query');

/**
 * Collection
 *
 * A prototype for managing a collection of database
 * records.
 *
 * This file is the prototype for collections defined using Waterline.
 * It contains the entry point for all ORM methods (e.g. User.find())
 *
 * Methods in this file defer to the adapter for their true implementation:
 * the implementation here just validates and normalizes the parameters.
 *
 * @param {Object} waterline, reference to parent
 * @param {Object} options
 * @param {Function} callback
 */

var Collection = module.exports = function(waterline, options, cb) {

  // Allow options object to be optional
  if(typeof options === 'function') {
    cb = options;
    options = {};
  }

  var self = this;

  // Throw Error if no Tablename/Identity is set
  if(!this.tableName && !this.identity && !options.tableName && !options.identity) {
    return cb(new Error('A tableName property must be set.'));
  }

  // Normalize tableName to identity
  if(options.tableName) this.identity = options.tableName;

  // Ensure identity is lowercased
  this.identity = this.identity.toLowerCase();

  // Cache reference to the parent
  this.waterline = waterline;

  // Default Attributes
  this.attributes = this.attributes || {};

  // Instantiate Base Collection
  Core.call(this, options);

  // Instantiate Query Language
  Query.call(this);

  // Register Collection
  this._adapter.registerCollection(function() {

    // Sync the schema to the database
    self.sync(function(err) {
      if(err) return cb(err);

      // Call Local Initialize (Made to be overridden)
      self.initialize.call(this, options);

      cb(null, self);
    });
  });
};

_.extend(Collection.prototype, Core.prototype, Query.prototype, {

  /**
   * Empty No-Op function to override in a Model
   */
  initialize: function() {}

});

// Make Extendable
Collection.extend = extend;
<|endoftext|>