Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static/js/framework/framework-ajax.js b/static/js/framework/framework-ajax.js
new file mode 100644
index 0000000..038c4c3
--- /dev/null
+++ b/static/js/framework/framework-ajax.js
@@ -0,0 +1,153 @@
+/* Copyright 2016 The Chromium Authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+
+/**
+ * @fileoverview AJAX-related helper functions.
+ */
+
+
+var DEBOUNCE_THRESH_MS = 2000;
+
+
+/**
+ * Simple debouncer to handle text input. Don't try to hit the server
+ * until the user has stopped typing for a few seconds. E.g.,
+ * var debouncedKeyHandler = debounce(keyHandler);
+ * el.addEventListener('keyup', debouncedKeyHandler);
+ */
+function debounce(func, opt_threshold_ms) {
+ let timeout;
+ return function() {
+ let context = this, args = arguments;
+ let later = function() {
+ timeout = null;
+ func.apply(context, args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, opt_threshold_ms || DEBOUNCE_THRESH_MS);
+ };
+}
+
+
+/**
+ * Builds a POST string from a parameter dictionary.
+ * @param {Array|Object} args: parameters to encode. Either an object
+ * mapping names to values or an Array of doubles containing [key, value].
+ * @return {string} encoded POST data.
+ */
+function CS_postData(args) {
+ let params = [];
+
+ if (args instanceof Array) {
+ for (var key in args) {
+ let inputValue = args[key];
+ let name = inputValue[0];
+ let value = inputValue[1];
+ if (value !== undefined) {
+ params.push(name + '=' + encodeURIComponent(String(value)));
+ }
+ }
+ } else {
+ for (var key in args) {
+ params.push(key + '=' + encodeURIComponent(String(args[key])));
+ }
+ }
+
+ params.push('token=' + encodeURIComponent(window.prpcClient.token));
+
+ return params.join('&');
+}
+
+/**
+ * Helper for an extremely common kind of XHR: a POST with an XHRF token
+ * where we silently ignore server or connectivity errors. If the token
+ * has expired, get a new one and retry the original request with the new
+ * token.
+ * @param {string} url request destination.
+ * @param {function(event)} callback function to be called
+ * upon successful completion of the request.
+ * @param {Object} args parameters to encode as POST data.
+ */
+function CS_doPost(url, callback, args) {
+ window.prpcClient.ensureTokenIsValid().then(() => {
+ let xh = XH_XmlHttpCreate();
+ XH_XmlHttpPOST(xh, url, CS_postData(args), callback);
+ });
+}
+
+
+/**
+ * Helper function to strip leading junk characters from a JSON response
+ * and then parse it into a JS constant.
+ *
+ * The reason that "}])'\n" is prepended to the response text is that
+ * it makes it impossible for a hacker to hit one of our JSON servlets
+ * via a <script src="..."> tag and do anything with the result. Even
+ * though a JSON response is just a constant, it could be passed into
+ * hacker code by tricks such as overriding the array constructor.
+ */
+function CS_parseJSON(xhr) {
+ return JSON.parse(xhr.responseText.substr(5));
+}
+
+
+/**
+ * Promise-based version of CS_parseJSON using the fetch API.
+ *
+ * Sends a GET request to a JSON endpoint then strips the XSSI prefix off
+ * of the response before resolving the promise.
+ *
+ * Args:
+ * url (string): The URL to fetch.
+ * Returns:
+ * A promise, resolved when the request returns. Also be sure to call
+ * .catch() on the promise (or wrap in a try/catch if using async/await)
+ * if you don't want errors to halt script execution.
+ */
+function CS_fetch(url) {
+ return fetch(url, {credentials: 'same-origin'})
+ .then((res) => res.text())
+ .then((rawResponse) => JSON.parse(rawResponse.substr(5)));
+}
+
+
+/**
+ * After we refresh the form token, we need to actually submit the form.
+ * formToSubmit keeps track of which form the user was trying to submit.
+ */
+var formToSubmit = null;
+
+/**
+ * If the form token that was generated when the page was served has
+ * now expired, then request a refreshed token from the server, and
+ * don't submit the form until after it arrives.
+ */
+function refreshTokens(event, formToken, formTokenPath, tokenExpiresSec) {
+ if (!window.prpcClient.constructor.isTokenExpired(tokenExpiresSec)) {
+ return;
+ }
+
+ formToSubmit = event.target;
+ event.preventDefault();
+ const message = {
+ token: formToken,
+ tokenPath: formTokenPath,
+ };
+ const refreshTokenPromise = window.prpcClient.call(
+ 'monorail.Sitewide', 'RefreshToken', message);
+
+ refreshTokenPromise.then((freshToken) => {
+ let tokenFields = document.querySelectorAll('input[name=token]');
+ for (let i = 0; i < tokenFields.length; ++i) {
+ tokenFields[i].value = freshToken.token;
+ }
+ if (formToSubmit) {
+ formToSubmit.submit();
+ }
+ });
+}