PageSourceSearch

https://ncmec.org/etc.clientlibs/clientlibs/granite/utils.js

js ncmec.org collected 2026-09-24 06:44:44 UTC 50,522 bytes, 1,482 lines download raw bytes

1/*
2 * ADOBE CONFIDENTIAL
3 *
4 * Copyright 2012 Adobe Systems Incorporated
5 * All Rights Reserved.
6 *
7 * NOTICE:  All information contained herein is, and remains
8 * the property of Adobe Systems Incorporated and its suppliers,
9 * if any.  The intellectual and technical concepts contained
10 * herein are proprietary to Adobe Systems Incorporated and its
11 * suppliers and may be covered by U.S. and Foreign Patents,
12 * patents in process, and are protected by trade secret or copyright law.
13 * Dissemination of this information or reproduction of this material
14 * is strictly forbidden unless prior written permission is obtained
15 * from Adobe Systems Incorporated.
16 *
17 */
18(function(factory) {
19    "use strict";
20
21    if (typeof module === "object" && module.exports) {
22        module.exports = factory();
23    } else {
24        var g = window.Granite = window.Granite || {};
25        g.Sling = factory();
26    }
27}(function() {
28    "use strict";
29
30    /**
31     * A helper class providing a set of Sling-related utilities.
32     * @static
33     * @singleton
34     * @class Granite.Sling
35     * @deprecated Using the constants is no longer needed and actually is not a best practice as it is not RESTful,
36     *             where the server should drive the client via hypermedia and the client should not make any
37     *             assumption about the URL.
38     */
39    return {
40        /**
41         * The selector for infinite hierarchy depth when retrieving repository content.
42         * @static
43         * @final
44         * @type String
45         */
46        SELECTOR_INFINITY: ".infinity",
47
48        /**
49         * The parameter name for the used character set.
50         * @static
51         * @final
52         * @type String
53         */
54        CHARSET: "_charset_",
55
56        /**
57         * The parameter name for the status.
58         * @static
59         * @final
60         * @type String
61         */
62        STATUS: ":status",
63
64        /**
65         * The parameter value for the status type "browser".
66         * @static
67         * @final
68         * @type String
69         */
70        STATUS_BROWSER: "browser",
71
72        /**
73         * The parameter name for the operation.
74         * @static
75         * @final
76         * @type String
77         */
78        OPERATION: ":operation",
79
80        /**
81         * The parameter value for the delete operation.
82         * @static
83         * @final
84         * @type String
85         */
86        OPERATION_DELETE: "delete",
87
88        /**
89         * The parameter value for the move operation.
90         * @static
91         * @final
92         * @type String
93         */
94        OPERATION_MOVE: "move",
95
96        /**
97         * The parameter name suffix for deleting.
98         * @static
99         * @final
100         * @type String
101         */
102        DELETE_SUFFIX: "@Delete",
103
104        /**
105         * The parameter name suffix for setting a type hint.
106         * @static
107         * @final
108         * @type String
109         */
110        TYPEHINT_SUFFIX: "@TypeHint",
111
112        /**
113         * The parameter name suffix for copying.
114         * @static
115         * @final
116         * @type String
117         */
118        COPY_SUFFIX: "@CopyFrom",
119
120        /**
121         * The parameter name suffix for moving.
122         * @static
123         * @final
124         * @type String
125         */
126        MOVE_SUFFIX: "@MoveFrom",
127
128        /**
129         * The parameter name for the ordering.
130         * @static
131         * @final
132         * @type String
133         */
134        ORDER: ":order",
135
136        /**
137         * The parameter name for the replace flag.
138         * @static
139         * @final
140         * @type String
141         */
142        REPLACE: ":replace",
143
144        /**
145         * The parameter name for the destination flag.
146         * @static
147         * @final
148         * @type String
149         */
150        DESTINATION: ":dest",
151
152        /**
153         * The parameter name for the save parameter prefix.
154         * @static
155         * @final
156         * @type String
157         */
158        SAVE_PARAM_PREFIX: ":saveParamPrefix",
159
160        /**
161         * The parameter name for input fields that should be ignored by Sling.
162         * @static
163         * @final
164         * @type String
165         */
166        IGNORE_PARAM: ":ignore",
167
168        /**
169         * The parameter name for login requests.
170         * @static
171         * @final
172         * @type String
173         */
174        REQUEST_LOGIN_PARAM: "sling:authRequestLogin",
175
176        /**
177         * The login URL.
178         * @static
179         * @final
180         * @type String
181         */
182        LOGIN_URL: "/system/sling/login.html",
183
184        /**
185         * The logout URL.
186         * @static
187         * @final
188         * @type String
189         */
190        LOGOUT_URL: "/system/sling/logout.html"
191    };
192}));
193
194/*
195 * ADOBE CONFIDENTIAL
196 *
197 * Copyright 2012 Adobe Systems Incorporated
198 * All Rights Reserved.
199 *
200 * NOTICE:  All information contained herein is, and remains
201 * the property of Adobe Systems Incorporated and its suppliers,
202 * if any.  The intellectual and technical concepts contained
203 * herein are proprietary to Adobe Systems Incorporated and its
204 * suppliers and may be covered by U.S. and Foreign Patents,
205 * patents in process, and are protected by trade secret or copyright law.
206 * Dissemination of this information or reproduction of this material
207 * is strictly forbidden unless prior written permission is obtained
208 * from Adobe Systems Incorporated.
209 *
210 */
211(function(factory) {
212    "use strict";
213
214    if (typeof module === "object" && module.exports) {
215        module.exports = factory();
216    } else {
217        var g = window.Granite = window.Granite || {};
218        g.Util = factory();
219    }
220}(function() {
221    "use strict";
222
223    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Polyfill
224    var isArray = function(arg) {
225        return Object.prototype.toString.call(arg) === "[object Array]";
226    };
227
228    /**
229     * A helper class providing a set of general utilities.
230     * @static
231     * @singleton
232     * @class Granite.Util
233     */
234    return {
235        /**
236         * Replaces occurrences of <code>{n}</code> in the specified text with the texts from the snippets.
237         *
238         * @example
239         * var text = Granite.Util.patchText("{0} has signed in.", "Jack");
240         * // text = "Jack has signed in."
241         * var text2 = Granite.Util.patchText("{0} {1} has signed in from {2}.", ["Jack", "McFarland", "x.x.x.x"]);
242         * // text2 = "Jack McFarland has signed in from x.x.x.x."
243         *
244         * @param {String} text The text.
245         * @param {String|String[]} snippets The text(s) replacing <code>{n}</code>.
246         * @returns {String} The patched text.
247         */
248        patchText: function(text, snippets) {
249            if (snippets) {
250                if (!isArray(snippets)) {
251                    text = text.replace("{0}", snippets);
252                } else {
253                    for (var i = 0; i < snippets.length; i++) {
254                        text = text.replace(("{" + i + "}"), snippets[i]);
255                    }
256                }
257            }
258            return text;
259        },
260
261        /**
262         * Returns the top most accessible window.
263         * Check {@link .setIFrameMode} to avoid security exception message on WebKit browsers
264         * if this method is called in an iFrame included in a window from different domain.
265         *
266         * @returns {Window} The top window.
267         */
268        getTopWindow: function() {
269            var win = window;
270            if (this.iFrameTopWindow) {
271                return this.iFrameTopWindow;
272            }
273            try {
274                // try to access parent
275                // win.parent.location.href throws an exception if not authorized (e.g. different location in a portlet)
276                while (win.parent && win !== win.parent && win.parent.location.href) {
277                    win = win.parent;
278                }
279            } catch (error) {
280                // ignored
281            }
282            return win;
283        },
284
285        /**
286         * Allows to define if Granite.Util is running in an iFrame and parent window is in another domain
287         * (and optionally define what would be the top window in that case.
288         * This is necessary to use {@link .getTopWindow} in a iFrame on WebKit based browsers because
289         * {@link .getTopWindow} iterates on parent windows to find the top one which triggers a security exception
290         * if one parent window is in a different domain. Exception cannot be caught but is not breaking the JS
291         * execution.
292         *
293         * @param {Window} [topWindow=window] The iFrame top window. Must be running on the same host to avoid
294         * security exception.
295         */
296        setIFrameMode: function(topWindow) {
297            this.iFrameTopWindow = topWindow || window;
298        },
299
300        /**
301         * Applies default properties if non-existent into the base object.
302         * Child objects are merged recursively.
303         * REMARK:
304         *   - objects are recursively merged
305         *   - simple type object properties are copied over the base
306         *   - arrays are cloned and override the base (no value merging)
307         *
308         * @param {Object} base The object.
309         * @param {...Object} pass The objects to be copied onto the base.
310         * @returns {Object} The base object with defaults.
311         */
312        applyDefaults: function() {
313            var override;
314            var base = arguments[0] || {};
315
316            for (var i = 1; i < arguments.length; i++) {
317                override = arguments[i];
318
319                for (var name in override) {
320                    var value = override[name];
321
322                    if (override.hasOwnProperty(name) && value !== undefined) {
323                        if (value !== null && typeof value === "object" && !(value instanceof Array)) {
324                            // nested object
325                            base[name] = this.applyDefaults(base[name], value);
326                        } else if (value instanceof Array) {
327                            // override array
328                            base[name] = value.slice(0);
329                        } else {
330                            // simple type
331                            base[name] = value;
332                        }
333                    }
334                }
335            }
336
337            return base;
338        },
339
340        /**
341         * Returns the keycode from the given event.
342         * It is a normalized value over variation of browsers' inconsistencies.
343         *
344         * @param {UIEvent} event The event.
345         * @returns {Number} The keycode.
346         */
347        getKeyCode: function(event) {
348            return event.keyCode ? event.keyCode : event.which;
349        }
350    };
351}));
352
353/*
354 * ADOBE CONFIDENTIAL
355 *
356 * Copyright 2012 Adobe Systems Incorporated
357 * All Rights Reserved.
358 *
359 * NOTICE:  All information contained herein is, and remains
360 * the property of Adobe Systems Incorporated and its suppliers,
361 * if any.  The intellectual and technical concepts contained
362 * herein are proprietary to Adobe Systems Incorporated and its
363 * suppliers and may be covered by U.S. and Foreign Patents,
364 * patents in process, and are protected by trade secret or copyright law.
365 * Dissemination of this information or reproduction of this material
366 * is strictly forbidden unless prior written permission is obtained
367 * from Adobe Systems Incorporated.
368 *
369 */
370/* global CQURLInfo:false, G_XHR_HOOK:false */
371/* eslint strict: 0 */
372(function(factory) {
373    "use strict";
374
375    if (typeof module === "object" && module.exports) {
376        module.exports = factory(require("@granite/util"), require("jquery"));
377    } else {
378        window.Granite.HTTP = factory(Granite.Util, jQuery);
379    }
380}(function(util, $) {
381    /**
382     * A helper class providing a set of HTTP-related utilities.
383     * @static
384     * @singleton
385     * @class Granite.HTTP
386     */
387    return (function() {
388        /**
389         * The context path used on the server.
390         * May only be set by {@link #detectContextPath}.
391         * @type String
392         */
393        var contextPath = null;
394
395        /**
396         * The regular expression to detect the context path used
397         * on the server using the URL of this script.
398         * @readonly
399         * @type RegExp
400         */
401        // eslint-disable-next-line max-len
402        var SCRIPT_URL_REGEXP = /^(?:http|https):\/\/[^/]+(\/.*)\/(?:etc\.clientlibs|etc(\/.*)*\/clientlibs|libs(\/.*)*\/clientlibs|apps(\/.*)*\/clientlibs|etc\/designs).*\.js(\?.*)?$/;
403
404        /**
405         * The regular expression to match `#` and other non-ASCII characters in a URI.
406         * @readonly
407         * @type RegExp
408         */
409        var ENCODE_PATH_REGEXP = /[^\w-.~%:/?[\]@!$&'()*+,;=]/;
410
411        /**
412         * The regular expression to parse URI.
413         * @readonly
414         * @type RegExp
415         * @see https://tools.ietf.org/html/rfc3986#appendix-B
416         */
417        var URI_REGEXP = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
418
419        /**
420         * Indicates after a session timeout if a refresh has already been triggered
421         * in order to avoid multiple alerts.
422         * @type String
423         */
424        var loginRedirected = false;
425
426        var self = {};
427
428        /**
429         * Returns the scheme and authority (userinfo, host, port) components of the given URI;
430         * or an empty string if the URI does not have the components.
431         *
432         * This method assumes the URI is valid.
433         *
434         * e.g. `scheme://userinfo@host:80/path?query#fragment` -> `scheme://userinfo@host:80`
435         *
436         * @param {String} uri The URI
437         * @returns {String} The scheme and authority components
438         */
439        self.getSchemeAndAuthority = function(uri) {
440            if (!uri) {
441                return "";
442            }
443
444            var result = URI_REGEXP.exec(uri);
445
446            if (result === null) {
447                return "";
448            }
449
450            return [ result[1], result[3] ].join("");
451        };
452
453        /**
454         * Returns the context path used on the server.
455         *
456         * @returns {String} The context path
457         */
458        self.getContextPath = function() {
459            // Keep cache of calculated path.
460            if (contextPath === null) {
461                contextPath = self.detectContextPath();
462            }
463            return contextPath;
464        };
465
466        /**
467         * Detects the context path used on the server.
468         *
469         * @returns {String} The context path
470         * @private
471         */
472        self.detectContextPath = function() {
473            try {
474                if (window.CQURLInfo) {
475                    contextPath = CQURLInfo.contextPath || "";
476                } else {
477                    var scripts = document.getElementsByTagName("script");
478                    for (var i = 0; i < scripts.length; i++) {
479                        var result = SCRIPT_URL_REGEXP.exec(scripts[i].src);
480                        if (result) {
481                            contextPath = result[1];
482                            return contextPath;
483                        }
484                    }
485                    contextPath = "";
486                }
487            } catch (e) {
488                // ignored
489            }
490
491            return contextPath;
492        };
493
494        /**
495         * Makes sure the specified relative URL starts with the context path
496         * used on the server. If an absolute URL is passed, it will be returned
497         * as-is.
498         *
499         * @param {String} url The URL
500         * @returns {String} The externalized URL
501         */
502        self.externalize = function(url) {
503            try {
504                if (url.indexOf("/") === 0 && self.getContextPath() && url.indexOf(self.getContextPath() + "/") !== 0) {
505                    url = self.getContextPath() + url;
506                }
507            } catch (e) {
508                // ignored
509            }
510            return url;
511        };
512
513        /**
514         * Removes scheme, authority and context path from the specified
515         * absolute URL if it has the same scheme and authority as the
516         * specified document (or the current one). If a relative URL is passed,
517         * the context path will be stripped if present.
518         *
519         * @param {String} url The URL
520         * @param {String} doc (optional) The document
521         * @returns {String} The internalized URL
522         */
523        self.internalize = function(url, doc) {
524            if (url.charAt(0) === "/") {
525                if (contextPath === url) {
526                    return "";
527                } else if (contextPath && url.indexOf(contextPath + "/") === 0) {
528                    return url.substring(contextPath.length);
529                } else {
530                    return url;
531                }
532            }
533
534            if (!doc) {
535                doc = document;
536            }
537            var docHost = self.getSchemeAndAuthority(doc.location.href);
538            var urlHost = self.getSchemeAndAuthority(url);
539            if (docHost === urlHost) {
540                return url.substring(urlHost.length + (contextPath ? contextPath.length : 0));
541            } else {
542                return url;
543            }
544        };
545
546        /**
547         * Removes all parts but the path from the specified URL.
548         * <p>Examples:<pre><code>
549         /x/y.sel.html?param=abc => /x/y
550         </code></pre>
551         * <pre><code>
552         http://www.day.com/foo/bar.html => /foo/bar
553         </code></pre><p>
554         *
555         * @param {String} url The URL, may be empty. If empty <code>window.location.href</code> is taken.
556         * @returns {String} The path
557         */
558        self.getPath = function(url) {
559            if (!url) {
560                if (window.CQURLInfo && CQURLInfo.requestPath) {
561                    return CQURLInfo.requestPath;
562                } else {
563                    url = window.location.pathname;
564                }
565            } else {
566                url = self.removeParameters(url);
567                url = self.removeAnchor(url);
568            }
569
570            url = self.internalize(url);
571            var i = url.indexOf(".", url.lastIndexOf("/"));
572            if (i !== -1) {
573                url = url.substring(0, i);
574            }
575            return url;
576        };
577
578        /**
579         * Removes the fragment component from the given URI.
580         *
581         * This method assumes the URI is valid.
582         *
583         * e.g. `scheme://userinfo@host:80/path?query#fragment` -> `scheme://userinfo@host:80/path?query`
584         *
585         * @param {String} uri The URI
586         * @returns {String} The URI without fragment component
587         */
588        self.removeAnchor = function(uri) {
589            var fragmentIndex = uri.indexOf("#");
590            if (fragmentIndex >= 0) {
591                return uri.substring(0, fragmentIndex);
592            } else {
593                return uri;
594            }
595        };
596
597        /**
598         * Removes the query component and its subsequent fragment component from the given URI.
599         * i.e. When query component exists, the subsequent fragment component is also removed.
600         * However, when query component doesn't exist, fragment component is not removed.
601         *
602         * The assumption here is that the usages of `#` before the `?` are intended as part of the path component
603         * that need to be encoded separately.
604         * This assumption is made because `c.d.cq.commons.jcr.JcrUtil#isValidName` allows `#`.
605         *
606         * e.g. `scheme://userinfo@host:80/path#with#hash?query#fragment` -> `scheme://userinfo@host:80/path#with#hash`
607         *
608         * @param {String} uri The URL
609         * @returns {String} The URI without the query component and its subsequent fragment component
610         */
611        self.removeParameters = function(uri) {
612            var queryIndex = uri.indexOf("?");
613            if (queryIndex >= 0) {
614                return uri.substring(0, queryIndex);
615            } else {
616                return uri;
617            }
618        };
619
620        /**
621         * Encodes the path component of the given URI if it is not already encoded.
622         * See {@link #encodePath} for the details of the encoding.
623         *
624         * e.g. `scheme://userinfo@host:80/path#with#hash?query#fragment`
625         * -> `scheme://userinfo@host:80/path%23with%23hash?query#fragment`
626         *
627         * @param {String} uri The URI to encode
628         * @returns {String} The encoded URI
629         */
630        self.encodePathOfURI = function(uri) {
631            var DELIMS = [ "?", "#" ];
632
633            var parts = [ uri ];
634            var delim;
635            for (var i = 0, ln = DELIMS.length; i < ln; i++) {
636                delim = DELIMS[i];
637                if (uri.indexOf(delim) >= 0) {
638                    parts = uri.split(delim);
639                    break;
640                }
641            }
642
643            if (ENCODE_PATH_REGEXP.test(parts[0])) {
644                parts[0] = self.encodePath(parts[0]);
645            }
646
647            return parts.join(delim);
648        };
649
650        /**
651         * Encodes the given URI using `encodeURI`.
652         *
653         * This method is used to encode URI components from the scheme component up to the path component (inclusive).
654         * Therefore, `?` and `#` are also encoded in addition.
655         *
656         * However `[` and `]` are not encoded.
657         * The assumption here is that the usages of `[` and `]` are only at the host component (for IPv6),
658         * not at the path component.
659         * This assumption is made because `c.d.cq.commons.jcr.JcrUtil#isValidName` disallows `[` and `]`.
660         *
661         * Examples
662         *
663         * * `scheme://userinfo@host:80/path?query#fragment` -> `scheme://userinfo@host:80/path%3Fquery%23fragment`
664         * * `http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/` -> `http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/`
665         *
666         * @param {String} uri The URI to encode
667         * @returns {String} The encoded URI
668         */
669        self.encodePath = function(uri) {
670            uri = encodeURI(uri);
671
672            // Decode back `%5B` and `%5D`.
673            // The `[` and `]` are not valid characters at the path component and need to be encoded,
674            // which `encodeURI` does correctly.
675            // However as mentioned in the doc, they are assumed to be used for authority component only.
676            uri = uri.replace(/%5B/g, "[").replace(/%5D/g, "]");
677
678            uri = uri.replace(/\?/g, "%3F");
679            uri = uri.replace(/#/g, "%23");
680
681            return uri;
682        };
683
684        /**
685         * Handles login redirection if needed.
686         */
687        self.handleLoginRedirect = function() {
688            if (!loginRedirected) {
689                loginRedirected = true;
690                alert(Granite.I18n.get("Your request could not be completed because you have been signed out."));
691
692                var l = util.getTopWindow().document.location;
693                l.href = self.externalize("/") + "?resource=" + encodeURIComponent(l.pathname + l.search + l.hash);
694            }
695        };
696
697        /**
698         * Gets the XHR hooked URL if called in a portlet context
699         *
700         * @param {String} url The URL to get
701         * @param {String} method The method to use to retrieve the XHR hooked URL
702         * @param {Object} params The parameters
703         * @returns {String} The XHR hooked URL if available, the provided URL otherwise
704         */
705        self.getXhrHook = function(url, method, params) {
706            method = method || "GET";
707            if (window.G_XHR_HOOK && typeof G_XHR_HOOK === "function") {
708                var p = {
709                    "url": url,
710                    "method": method
711                };
712                if (params) {
713                    p["params"] = params;
714                }
715                return G_XHR_HOOK(p);
716            }
717            return null;
718        };
719
720        /**
721         * Evaluates and returns the body of the specified response object.
722         * Alternatively, a URL can be specified, in which case it will be
723         * requested using a synchronous {@link #get} in order to acquire
724         * the response object.
725         *
726         * @param {Object|String} response The response object or URL
727         * @returns {Object} The evaluated response body
728         * @since 5.3
729         */
730        self.eval = function(response) {
731            if (typeof response !== "object") {
732                response = $.ajax({
733                    url: response,
734                    type: "get",
735                    async: false
736                });
737            }
738            try {
739                // support responseText for backward compatibility (pre 5.3)
740                var text = response.body ? response.body : response.responseText;
741                return JSON.parse(text);
742            } catch (e) {
743                // ignored
744            }
745            return null;
746        };
747
748        return self;
749    }());
750}));
751
752/*
753 * ADOBE CONFIDENTIAL
754 * ___________________
755 *
756 * Copyright 2012 Adobe
757 * All Rights Reserved.
758 *
759 * NOTICE: All information contained herein is, and remains
760 * the property of Adobe and its suppliers, if any. The intellectual
761 * and technical concepts contained herein are proprietary to Adobe
762 * and its suppliers and are protected by all applicable intellectual
763 * property laws, including trade secret and copyright laws.
764 * Dissemination of this information or reproduction of this material
765 * is strictly forbidden unless prior written permission is obtained
766 * from Adobe.
767 */
768(function(factory) {
769    "use strict";
770
771    if (typeof module === "object" && module.exports) {
772        module.exports = factory(require("@granite/http"));
773    } else {
774        window.Granite.I18n = factory(window.Granite.HTTP);
775    }
776}(function(HTTP) {
777    "use strict";
778
779    /**
780     * A helper class providing a set of utilities related to internationalization (i18n).
781     *
782     * <h3>Locale Priorities</h3>
783     * <p>The locale is read based on the following priorities:</p>
784     * <ol>
785     *   <li>manually specified locale</li>
786     *   <li><code>document.documentElement.lang</code></li>
787     *   <li><code>Granite.I18n.LOCALE_DEFAULT</code></li>
788     * </ol>
789     *
790     * <h3>Dictionary Priorities</h3>
791     * <p>The dictionary URL is read based on the following priorities:</p>
792     * <ol>
793     *   <li>manually specified URL (<code>urlPrefix</code, <code>urlSuffix</code>)</li>
794     *   <li><code>data-i18n-dictionary-src</code> attribute at &lt;html&gt; element,
795     *       which has the type of <a href="http://tools.ietf.org/html/rfc6570">URI Template</a> string</li>
796     *   <li>The URL resolved from default <code>urlPrefix</code> and <code>urlSuffix</code></li>
797     * </ol>
798     *
799     * <h3>URI Template of data-i18n-dictionary-src</h3>
800     * <p>It expects the variable named <code>locale</code>,
801     * which will be fetched from the locale (based on priorities above).
802     * E.g. <code>&lt;html lang="en" data-i18n-dictionary-src="/libs/cq/i18n/dict.{+locale}.json"&gt;</code>.</p>
803     *
804     * @static
805     * @class Granite.I18n
806     */
807    return (function() {
808        /**
809         * The map where the dictionaries are stored under their locale.
810         * @type Object
811         */
812        var dicts = {};
813
814        /**
815         * The prefix for the URL used to request dictionaries from the server.
816         * @type String
817         */
818        var urlPrefix = "/libs/cq/i18n/dict.";
819
820        /**
821         * The suffix for the URL used to request dictionaries from the server.
822         * @type String
823         */
824        var urlSuffix = ".json";
825
826        /**
827         * The manually specified locale as a String or a function that returns the locale as a string.
828         * @type String
829         */
830        var manualLocale = undefined;
831
832        /**
833         * If the current locale represents pseudo translations.
834         * In that case the dictionary is expected to provide just a special
835         * translation pattern to automatically convert all original strings.
836         */
837        var pseudoTranslations = false;
838
839        var languages = null;
840
841        var self = {};
842
843        /**
844         * Indicates if the dictionary parameters are specified manually.
845         */
846        var manualDictionary = false;
847
848        var getDictionaryUrl = function(locale) {
849            if (manualDictionary) {
850                return urlPrefix + locale + urlSuffix;
851            }
852
853            var dictionarySrc;
854            var htmlEl = document.querySelector("html");
855            if (htmlEl) {
856                dictionarySrc = htmlEl.getAttribute("data-i18n-dictionary-src");
857            }
858
859            if (!dictionarySrc) {
860                return urlPrefix + locale + urlSuffix;
861            }
862
863            // dictionarySrc is a URITemplate
864            // Use simple string replacement for now; for more complicated scenario, please use Granite.URITemplate
865            return dictionarySrc.replace("{locale}", encodeURIComponent(locale)).replace("{+locale}", locale);
866        };
867
868        var patchText = function(text, snippets) {
869            if (snippets) {
870                if (Array.isArray(snippets)) {
871                    for (var i = 0; i < snippets.length; i++) {
872                        text = text.replace("{" + i + "}", snippets[i]);
873                    }
874                } else {
875                    text = text.replace("{0}", snippets);
876                }
877            }
878            return text;
879        };
880
881        /**
882         * The default locale (en).
883         * @readonly
884         * @type String
885         */
886        self.LOCALE_DEFAULT = "en";
887
888        /**
889         * The language code for pseudo translations.
890         * @readonly
891         * @type String
892         */
893        self.PSEUDO_LANGUAGE = "zz";
894
895        /**
896         * The dictionary key for pseudo translation pattern.
897         * @readonly
898         * @type String
899         */
900        self.PSEUDO_PATTERN_KEY = "_pseudoPattern_";
901
902        /**
903         * Initializes I18n with the given config options:
904         * <ul>
905         * <li>locale: the current locale (defaults to "en")</li>
906         * <li>urlPrefix: the prefix for the URL used to request dictionaries from
907         * the server (defaults to "/libs/cq/i18n/dict.")</li>
908         * <li>urlSuffix: the suffix for the URL used to request dictionaries from
909         * the server (defaults to ".json")</li>
910         * </ul>
911         * Sample config. The dictionary would be requested from
912         * "/apps/i18n/dict.fr.json":
913         <code><pre>{
914         "locale": "fr",
915         "urlPrefix": "/apps/i18n/dict.",
916         "urlSuffix": ".json"
917         }</pre></code>
918         *
919         * @param {Object} config The config
920         */
921        self.init = function(config) {
922            config = config || {};
923
924            this.setLocale(config.locale);
925            this.setUrlPrefix(config.urlPrefix);
926            this.setUrlSuffix(config.urlSuffix);
927        };
928
929        /**
930         * Sets the current locale.
931         *
932         * @param {String|Function} locale The locale or a function that returns the locale as a string
933         */
934        self.setLocale = function(locale) {
935            if (!locale) {
936                return;
937            }
938            manualLocale = locale;
939        };
940
941        /**
942         * Returns the current locale based on the priorities.
943         *
944         * @returns {String} The locale
945         */
946        self.getLocale = function() {
947            if (typeof manualLocale === "function") {
948                // execute function first time only and store result in currentLocale
949                manualLocale = manualLocale();
950            }
951            return manualLocale || document.documentElement.lang || self.LOCALE_DEFAULT;
952        };
953
954        /**
955         * Sets the prefix for the URL used to request dictionaries from
956         * the server. The locale and URL suffix will be appended.
957         *
958         * @param {String} prefix The URL prefix
959         */
960        self.setUrlPrefix = function(prefix) {
961            if (!prefix) {
962                return;
963            }
964            urlPrefix = prefix;
965            manualDictionary = true;
966        };
967
968        /**
969         * Sets the suffix for the URL used to request dictionaries from
970         * the server. It will be appended to the URL prefix and locale.
971         *
972         * @param {String} suffix The URL suffix
973         */
974        self.setUrlSuffix = function(suffix) {
975            if (!suffix) {
976                return;
977            }
978            urlSuffix = suffix;
979            manualDictionary = true;
980        };
981
982        /**
983         * Returns the dictionary for the specified locale. This method
984         * will request the dictionary using the URL prefix, the locale,
985         * and the URL suffix. If no locale is specified, the current
986         * locale is used.
987         *
988         * @param {String} locale (optional) The locale
989         * @returns {Object} The dictionary
990         */
991        self.getDictionary = function(locale) {
992            locale = locale || self.getLocale();
993
994            if (!dicts[locale]) {
995                pseudoTranslations = locale.indexOf(self.PSEUDO_LANGUAGE) === 0;
996
997                try {
998                    var xhr = new XMLHttpRequest();
999                    xhr.open("GET", HTTP.externalize(getDictionaryUrl(locale)), false);
1000                    xhr.send();
1001
1002                    dicts[locale] = JSON.parse(xhr.responseText);
1003                } catch (e) {
1004                    // ignored
1005                }
1006                if (!dicts[locale]) {
1007                    dicts[locale] = {};
1008                }
1009            }
1010            return dicts[locale];
1011        };
1012
1013        /**
1014         * Translates the specified text into the current language.
1015         *
1016         * @param {String} text The text to translate
1017         * @param {String[]} snippets The snippets replacing <code>{n}</code> (optional)
1018         * @param {String} note A hint for translators (optional)
1019         * @returns {String} The translated text
1020         */
1021        self.get = function(text, snippets, note) {
1022            var dict;
1023            var newText;
1024            var lookupText;
1025
1026            dict = self.getDictionary();
1027
1028            // note that pseudoTranslations is initialized in the getDictionary() call above
1029            lookupText = pseudoTranslations ? self.PSEUDO_PATTERN_KEY
1030                : note ? text + " ((" + note + "))"
1031                    : text;
1032            if (dict) {
1033                newText = dict[lookupText];
1034            }
1035            if (!newText) {
1036                newText = text;
1037            }
1038            if (pseudoTranslations) {
1039                newText = newText.replace("{string}", text).replace("{comment}", note ? note : "");
1040            }
1041            return patchText(newText, snippets);
1042        };
1043
1044        /**
1045         * Translates the specified text into the current language. Use this
1046         * method to translate String variables, e.g. data from the server.
1047         *
1048         * @param {String} text The text to translate
1049         * @param {String} note A hint for translators (optional)
1050         * @returns {String} The translated text
1051         */
1052        self.getVar = function(text, note) {
1053            if (!text) {
1054                return null;
1055            }
1056            return self.get(text, null, note);
1057        };
1058
1059        /**
1060         * Returns the available languages, including a "title" property with a display name:
1061         * for instance "German" for "de" or "German (Switzerland)" for "de_ch".
1062         *
1063         * @returns {Object} An object with language codes as keys and an object with "title",
1064         *                  "language", "country" and "defaultCountry" members.
1065         */
1066        self.getLanguages = function() {
1067            if (!languages) {
1068                try {
1069                    // use overlay servlet so customers can define /apps/wcm/core/resources/languages
1070                    // TODO: broken!!!
1071                    var url = HTTP.externalize("/libs/wcm/core/resources/languages.overlay.infinity.json");
1072                    var xhr = new XMLHttpRequest();
1073                    xhr.open("GET", url, false);
1074                    xhr.send();
1075
1076                    var json = JSON.parse(xhr.responseText);
1077
1078                    Object.keys(json).forEach(function(prop) {
1079                        var lang = json[prop];
1080                        if (lang.language) {
1081                            lang.title = self.getVar(lang.language);
1082                        }
1083                        if (lang.title && lang.country && lang.country !== "*") {
1084                            lang.title += " (" + self.getVar(lang.country) + ")";
1085                        }
1086                    });
1087                    languages = json;
1088                } catch (e) {
1089                    languages = {};
1090                }
1091            }
1092            return languages;
1093        };
1094
1095        /**
1096         * Parses a language code string such as "de_CH" and returns an object with
1097         * language and country extracted. The delimiter can be "_" or "-".
1098         *
1099         * @param {String} langCode a language code such as "de" or "de_CH" or "de-ch"
1100         * @returns {Object} an object with "code" ("de_CH"), "language" ("de") and "country" ("CH")
1101         *                  (or null if langCode was null)
1102         */
1103        self.parseLocale = function(langCode) {
1104            if (!langCode) {
1105                return null;
1106            }
1107            var pos = langCode.indexOf("_");
1108            if (pos < 0) {
1109                pos = langCode.indexOf("-");
1110            }
1111
1112            var language;
1113            var country;
1114            if (pos < 0) {
1115                language = langCode;
1116                country = null;
1117            } else {
1118                language = langCode.substring(0, pos);
1119                country = langCode.substring(pos + 1);
1120            }
1121            return {
1122                code: langCode,
1123                language: language,
1124                country: country
1125            };
1126        };
1127
1128        return self;
1129    }());
1130}));
1131
1132/*
1133 * ADOBE CONFIDENTIAL
1134 *
1135 * Copyright 2012 Adobe Systems Incorporated
1136 * All Rights Reserved.
1137 *
1138 * NOTICE:  All information contained herein is, and remains
1139 * the property of Adobe Systems Incorporated and its suppliers,
1140 * if any.  The intellectual and technical concepts contained
1141 * herein are proprietary to Adobe Systems Incorporated and its
1142 * suppliers and may be covered by U.S. and Foreign Patents,
1143 * patents in process, and are protected by trade secret or copyright law.
1144 * Dissemination of this information or reproduction of this material
1145 * is strictly forbidden unless prior written permission is obtained
1146 * from Adobe Systems Incorporated.
1147 *
1148 */
1149(function(factory) {
1150    "use strict";
1151
1152    if (typeof module === "object" && module.exports) {
1153        module.exports = factory();
1154    } else {
1155        var g = window.Granite = window.Granite || {};
1156        g.TouchIndicator = factory();
1157    }
1158}(function() {
1159    "use strict";
1160
1161    function createIndicator() {
1162        var el = document.createElement("div");
1163        el.style.visibility = "hidden";
1164        // fixed would be better, but flickers on ipad while scrolling
1165        el.style.position = "absolute";
1166        el.style.width = "30px";
1167        el.style.height = "30px";
1168        el.style.borderRadius = "20px";
1169        el.style.border = "5px solid orange";
1170        el.style.userSelect = "none";
1171        el.style.opacity = "0.5";
1172        el.style.zIndex = "2000";
1173        el.style.pointerEvents = "none";
1174        return el;
1175    }
1176
1177    var used = {};
1178
1179    var unused = [];
1180
1181    /**
1182     * Implements the "Adobe Dynamic Touch Indicator" that tracks touch events and displays a visual indicator for
1183     * screen sharing and presentation purposes.
1184     *
1185     * To enable it, call <code>Granite.TouchIndicator.init()</code> e.g. on document ready:
1186     * <pre><code>
1187     * Granite.$(document).ready(function() {
1188     *     Granite.TouchIndicator.init();
1189     * });
1190     * </code></pre>
1191     *
1192     * AdobePatentID="2631US01"
1193     */
1194    return {
1195        debugWithMouse: false,
1196
1197        init: function() {
1198            var self = this;
1199
1200            var update = function(e) {
1201                self.update(e.touches);
1202                return true;
1203            };
1204            document.addEventListener("touchstart", update);
1205            document.addEventListener("touchmove", update);
1206            document.addEventListener("touchend", update);
1207
1208            if (this.debugWithMouse) {
1209                document.addEventListener("mousemove", function(e) {
1210                    e.identifer = "fake";
1211                    self.update([ e ]);
1212                    return true;
1213                });
1214            }
1215        },
1216
1217        update: function(touches) {
1218            // go over all touch events present in the array
1219            var retained = {};
1220            for (var i = 0; i < touches.length; i++) {
1221                var touch = touches[i];
1222                var id = touch.identifier;
1223
1224                // check if we already have a indicator with the correct id
1225                var indicator = used[id];
1226                if (!indicator) {
1227                    // if not, check if we have an unused one
1228                    indicator = unused.pop();
1229
1230                    // if not, create a new one and append it to the dom
1231                    if (!indicator) {
1232                        indicator = createIndicator();
1233                        document.body.appendChild(indicator);
1234                    }
1235                }
1236
1237                retained[id] = indicator;
1238                indicator.style.left = (touch.pageX - 20) + "px";
1239                indicator.style.top = (touch.pageY - 20) + "px";
1240                indicator.style.visibility = "visible";
1241            }
1242
1243            // now hide all unused ones and stuff them in the unused array
1244            for (id in used) {
1245                if (used.hasOwnProperty(id) && !retained[id]) {
1246                    indicator = used[id];
1247                    indicator.style.visibility = "hidden";
1248                    unused.push(indicator);
1249                }
1250            }
1251            used = retained;
1252        }
1253    };
1254}));
1255
1256/*
1257 * ADOBE CONFIDENTIAL
1258 *
1259 * Copyright 2012 Adobe Systems Incorporated
1260 * All Rights Reserved.
1261 *
1262 * NOTICE:  All information contained herein is, and remains
1263 * the property of Adobe Systems Incorporated and its suppliers,
1264 * if any.  The intellectual and technical concepts contained
1265 * herein are proprietary to Adobe Systems Incorporated and its
1266 * suppliers and may be covered by U.S. and Foreign Patents,
1267 * patents in process, and are protected by trade secret or copyright law.
1268 * Dissemination of this information or reproduction of this material
1269 * is strictly forbidden unless prior written permission is obtained
1270 * from Adobe Systems Incorporated.
1271 *
1272 */
1273(function(factory) {
1274    "use strict";
1275
1276    if (typeof module === "object" && module.exports) {
1277        module.exports = factory();
1278    } else {
1279        var g = window.Granite = window.Granite || {};
1280        g.OptOutUtil = factory();
1281    }
1282}(function($) {
1283    "use strict";
1284
1285    function trim(s) {
1286        if (String.prototype.trim) {
1287            return s.trim();
1288        }
1289        return s.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
1290    }
1291
1292    /**
1293     * A library to determine whether any opt-out cookie is set and whether a given cookie name is white-listed.
1294     *
1295     * The opt-out and white-list cookie names are determined by a server-side configuration
1296     * (<code>com.adobe.granite.security.commons.OptOutService</code>) and provided to this tool by an optionally
1297     * included component (<code>/libs/granite/security/components/optout</code>) which provides a global JSON object
1298     * named <code>GraniteOptOutConfig</code>.
1299     *
1300     * @static
1301     * @class Granite.OptOutUtil
1302     */
1303    return (function() {
1304        var self = {};
1305
1306        /**
1307         * The names of cookies the presence of which indicates the user has opted out.
1308         * @type String[]
1309         */
1310        var optOutCookieNames = [];
1311
1312        /**
1313         * The names of cookies which may still be set in spite of the user having opted out.
1314         * @type String[]
1315         */
1316        var whitelistedCookieNames = [];
1317
1318        /**
1319         * Initializes this tool with an opt-out configuration.
1320         *
1321         * The following options are supported:
1322         * <ul>
1323         *     <li>cookieNames: an array of cookie names representing opt-out cookies. Defaults to empty.</li>
1324         *     <li>whitelistCookieNames: an array of cookies representing white-listed cookies. Defaults to empty.</li>
1325         * </ul>
1326         *
1327         * @param {Object} config The opt-out configuration.
1328         *
1329         * @example
1330         * {
1331         *     "cookieNames": ["omniture_optout","cq-opt-out"],
1332         *     "whitelistCookieNames": ["someAppCookie", "anotherImportantAppCookie"]
1333         * }
1334         */
1335        self.init = function(config) {
1336            if (config) {
1337                optOutCookieNames = config.cookieNames || [];
1338                whitelistedCookieNames = config.whitelistCookieNames || [];
1339            } else {
1340                optOutCookieNames = [];
1341                whitelistedCookieNames = [];
1342            }
1343        };
1344
1345        /**
1346         * Returns the array of configured cookie names representing opt-out cookies.
1347         *
1348         * @returns {String[]} The cookie names.
1349         */
1350        self.getCookieNames = function() {
1351            return optOutCookieNames;
1352        };
1353
1354        /**
1355         * Returns the array of configured cookie names representing white-listed cookies.
1356         *
1357         * @returns {String[]} The cookie names.
1358         */
1359        self.getWhitelistCookieNames = function() {
1360            return whitelistedCookieNames;
1361        };
1362
1363        /**
1364         * Determines whether the user (browser) has elected to opt-out.
1365         * This is indicated by the presence of one of the cookies retrieved through {@link #getCookieNames()}.
1366         *
1367         * @returns {Boolean} <code>true</code> if an opt-cookie was found in the browser's cookies;
1368         *     <code>false</code> otherwise.
1369         */
1370        self.isOptedOut = function() {
1371            var browserCookies = document.cookie.split(";");
1372            for (var i = 0; i < browserCookies.length; i++) {
1373                var cookie = browserCookies[i];
1374                var cookieName = trim(cookie.split("=")[0]);
1375                if (self.getCookieNames().indexOf(cookieName) >= 0) {
1376                    return true;
1377                }
1378            }
1379            return false;
1380        };
1381
1382        /**
1383         * Determines whether the given <code>cookieName</code> may be used to set a cookie.
1384         * This is the case if either opt-out is inactive (<code>{@link #isOptedOut()} === false</code>) or it is
1385         * active and the give cookie name was found in the white-list ({@link #getWhitelistCookieNames()}).
1386         *
1387         * @param {String} cookieName The name of the cookie to check.
1388         * @returns {Boolean} <code>true</code> if a cookie of this name may be used with respect to the opt-out status;
1389         *     <code>false</code> otherwise.
1390         */
1391        self.maySetCookie = function(cookieName) {
1392            return !(self.isOptedOut() && self.getWhitelistCookieNames().indexOf(cookieName) === -1);
1393        };
1394
1395        return self;
1396    }());
1397}));
1398
1399/*************************************************************************
1400 * ADOBE CONFIDENTIAL
1401 * ___________________
1402 *
1403 * Copyright 2019 Adobe
1404 * All Rights Reserved.
1405 *
1406 * NOTICE: All information contained herein is, and remains
1407 * the property of Adobe and its suppliers, if any. The intellectual
1408 * and technical concepts contained herein are proprietary to Adobe
1409 * and its suppliers and are protected by all applicable intellectual
1410 * property laws, including trade secret and copyright laws.
1411 * Dissemination of this information or reproduction of this material
1412 * is strictly forbidden unless prior written permission is obtained
1413 * from Adobe.
1414 **************************************************************************/
1415(function(factory) {
1416    "use strict";
1417
1418    if (typeof module === "object" && module.exports) {
1419        module.exports = factory();
1420    } else {
1421        var g = window.Granite = window.Granite || {};
1422        g.Toggles = factory();
1423    }
1424}(function() {
1425    "use strict";
1426
1427    var toggles = null;
1428
1429    /**
1430     * Requests toggle status from the toggle router servlet.
1431     *
1432     * To use it, call <code>Granite.Toggles.isEnabled("toggle-name")</code> e.g. on document ready:
1433     * <pre><code>
1434     * Granite.$(document).ready(function() {
1435     *     Granite.Toggles.isEnabled("sprint7-ft21");
1436     * });
1437     * </code></pre>
1438     */
1439    return {
1440        isEnabled: function(toggleName) {
1441            toggles = toggles || fetchToggles();
1442            return (toggles || { enabled: [] }).enabled.includes(toggleName);
1443
1444            function fetchToggles() {
1445                var request = new XMLHttpRequest();
1446                request.open("GET", Granite.HTTP.externalize("/etc.clientlibs/toggles.json"), false);
1447                request.send(null);
1448                if (request.status === 200) {
1449                    return JSON.parse(request.responseText);
1450                } else {
1451                    return null;
1452                }
1453            }
1454        }
1455    };
1456}));
1457
1458/*
1459 * ADOBE CONFIDENTIAL
1460 *
1461 * Copyright 2012 Adobe Systems Incorporated
1462 * All Rights Reserved.
1463 *
1464 * NOTICE:  All information contained herein is, and remains
1465 * the property of Adobe Systems Incorporated and its suppliers,
1466 * if any.  The intellectual and technical concepts contained
1467 * herein are proprietary to Adobe Systems Incorporated and its
1468 * suppliers and may be covered by U.S. and Foreign Patents,
1469 * patents in process, and are protected by trade secret or copyright law.
1470 * Dissemination of this information or reproduction of this material
1471 * is strictly forbidden unless prior written permission is obtained
1472 * from Adobe Systems Incorporated.
1473 *
1474 */
1475
1476
1477//------------------------------------------------------------------------------
1478// Initialize the Granite utils library
1479
1480Granite.OptOutUtil.init(window.GraniteOptOutConfig);
1481Granite.HTTP.detectContextPath();
1482

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.