| Index: appengine/monorail/static/js/tracker/tracker-ac.js
|
| diff --git a/appengine/monorail/static/js/tracker/tracker-ac.js b/appengine/monorail/static/js/tracker/tracker-ac.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ab986129a1ade069a2e5123dba800243d8a8e81a
|
| --- /dev/null
|
| +++ b/appengine/monorail/static/js/tracker/tracker-ac.js
|
| @@ -0,0 +1,1135 @@
|
| +/* 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
|
| + */
|
| +
|
| +/**
|
| + * This file contains the autocomplete configuration logic that is
|
| + * specific to the issue fields of Monorail. It depends on ac.js, our
|
| + * modified version of the autocomplete library.
|
| + */
|
| +
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds well-known issue label
|
| + * values for the current project.
|
| + */
|
| +var TKR_labelStore;
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds issue components.
|
| + */
|
| +var TKR_componentListStore;
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds many different kinds of
|
| + * items that can be shown in the artifact search autocomplete.
|
| + */
|
| +var TKR_searchStore;
|
| +
|
| +/**
|
| + * This is similar to TKR_searchStore, but does not include any suggestions
|
| + * to use the "me" keyword. Using "me" is not a good idea for project canned
|
| + * queries and filter rules.
|
| + */
|
| +var TKR_projectQueryStore;
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds items for the quick edit
|
| + * autocomplete.
|
| + */
|
| +// TODO(jrobbins): add options for fields and components.
|
| +var TKR_quickEditStore;
|
| +
|
| +/**
|
| + * This is a list of label prefixes that each issue should only use once.
|
| + * E.g., each issue should only have one Priority-* label. We do not prevent
|
| + * the user from using multiple such labels, we just warn the user before
|
| + * he/she submits.
|
| + */
|
| +var TKR_exclPrefixes = [];
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds custom permission names that
|
| + * have already been used in this project.
|
| + */
|
| +var TKR_customPermissionsStore;
|
| +
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds well-known issue status
|
| + * values for the current project.
|
| + */
|
| +var TKR_statusStore;
|
| +
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds the usernames of all the
|
| + * members of the current project. This is used for autocomplete in
|
| + * the cc-list of an issue, where many user names can entered with
|
| + * commas between them.
|
| + */
|
| +var TKR_memberListStore;
|
| +
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds the projects that the current
|
| + * user is contributor/member/owner of.
|
| + */
|
| +var TKR_projectStore;
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds the usernames of possible
|
| + * issue owners in the current project. The list of possible issue
|
| + * owners is the same as the list of project members, but the behavior
|
| + * of this autocompete store is different because the issue owner text
|
| + * field can only accept one value.
|
| + */
|
| +var TKR_ownerStore;
|
| +
|
| +
|
| +/**
|
| + * This is an autocomplete store that holds any list of string for choices.
|
| + */
|
| +var TKR_autoCompleteStore;
|
| +
|
| +
|
| +/**
|
| + * An array of autocomplete stores used for user-type custom fields.
|
| + */
|
| +var TKR_userAutocompleteStores = [];
|
| +
|
| +
|
| +/**
|
| + * This boolean controls whether odd-ball status and labels are treated as
|
| + * a warning or an error. Normally, it is False.
|
| + */
|
| +// TODO(jrobbins): split this into one option for statuses and one for labels.
|
| +var TKR_restrict_to_known;
|
| +
|
| +/**
|
| + * This keeps track of the type of autocomplete feed that will be displayed.
|
| + * The type determines which search operators are offered to the user. E.g.,
|
| + * "filename:" only makes sense for downloads.
|
| + */
|
| +// TODO(jrobbins): remove, this seems unneeded now.
|
| +var TKR_autoCompleteFeedName;
|
| +
|
| +/**
|
| + * This substitute function should be used for multi-valued autocomplete fields
|
| + * that are delimited by commas. When we insert an autocomplete value, replace
|
| + * an entire search term. Add a comma and a space after it if it is a complete
|
| + * search term.
|
| + */
|
| +function TKR_acSubstituteWithComma(inputValue, caret, completable, completion) {
|
| + var nextTerm = caret;
|
| + while (inputValue.charAt(nextTerm) != ' ' && nextTerm < inputValue.length) {
|
| + nextTerm++;
|
| + }
|
| + while (inputValue.charAt(nextTerm) == ' ' && nextTerm < inputValue.length) {
|
| + nextTerm++;
|
| + }
|
| + return inputValue.substring(0, caret - completable.length) +
|
| + completion.value + ', ' + inputValue.substring(nextTerm);
|
| +}
|
| +
|
| +/**
|
| + * When the prefix starts with '*', return the complete set of all
|
| + * possible completions.
|
| + * @param {string} prefix If this starts with '*', return all possible
|
| + * completions. Otherwise return null.
|
| + * @param {Array} labelDefs The array of label names and docstrings.
|
| + * @returns Array of new _AC_Completions for each possible completion, or null.
|
| + */
|
| +function TKR_fullComplete(prefix, labelDefs) {
|
| + if (!prefix.startsWith('*')) return null;
|
| + var out = [];
|
| + for (var i = 0; i < labelDefs.length; i++) {
|
| + out.push(new _AC_Completion(labelDefs[i].name,
|
| + labelDefs[i].name,
|
| + labelDefs[i].doc));
|
| + }
|
| + return out;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Constucts a list of all completions for both open and closed
|
| + * statuses, with a header for each group.
|
| + * @param {string} prefix If starts with '*', return all possible completions,
|
| + * else return null.
|
| + * @param {Array} openStatusDefs The array of open status values and
|
| + * docstrings.
|
| + * @param {Array} closedStatusDefs The array of closed status values
|
| + * and docstrings.
|
| + * @returns Array of new _AC_Completions for each possible completion, or null.
|
| + */
|
| +function TKR_openClosedComplete(prefix, openStatusDefs, closedStatusDefs) {
|
| + if (!prefix.startsWith('*')) return null;
|
| + var out = [];
|
| + out.push({heading:'Open Statuses:'}); // TODO: i18n
|
| + for (var i = 0; i < openStatusDefs.length; i++) {
|
| + out.push(new _AC_Completion(openStatusDefs[i].name,
|
| + openStatusDefs[i].name,
|
| + openStatusDefs[i].doc));
|
| + }
|
| + out.push({heading:'Closed Statuses:'}); // TODO: i18n
|
| + for (var i = 0; i < closedStatusDefs.length; i++) {
|
| + out.push(new _AC_Completion(closedStatusDefs[i].name,
|
| + closedStatusDefs[i].name,
|
| + closedStatusDefs[i].doc));
|
| + }
|
| + return out;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * An array of definitions of all well-known issue statuses. Each
|
| + * definition has the name of the status value, and a docstring that
|
| + * describes its meaning.
|
| + */
|
| +var TKR_statusWords = [];
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with all the well-known issue
|
| + * status values. The store has some DIT-specific methods.
|
| + * TODO(jrobbins): would it be easier to define my own class to use
|
| + * instead of _AC_Simple_Store?
|
| + * @param {Array} openStatusDefs An array of definitions of the
|
| + * well-known open status values. Each definition has a name and
|
| + * docstring.
|
| + * @param {Array} closedStatusDefs An array of definitions of the
|
| + * well-known closed status values. Each definition has a name and
|
| + * docstring.
|
| + */
|
| +function TKR_setUpStatusStore(openStatusDefs, closedStatusDefs) {
|
| + var docdict = {};
|
| + TKR_statusWords = [];
|
| + for (var i = 0; i < openStatusDefs.length; i++) {
|
| + var status = openStatusDefs[i];
|
| + TKR_statusWords.push(status.name);
|
| + docdict[status.name] = status.doc;
|
| + }
|
| + for (var i = 0; i < closedStatusDefs.length; i++) {
|
| + var status = closedStatusDefs[i];
|
| + TKR_statusWords.push(status.name);
|
| + docdict[status.name] = status.doc;
|
| + }
|
| +
|
| + TKR_statusStore = new _AC_SimpleStore(TKR_statusWords);
|
| + TKR_statusStore.docstrings = docdict;
|
| +
|
| + TKR_statusStore.commaCompletes = false;
|
| +
|
| + TKR_statusStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| +
|
| + TKR_statusStore.completable = function(inputValue, cursor) {
|
| + if (!ac_everTyped) return '*status';
|
| + return inputValue;
|
| + }
|
| +
|
| + TKR_statusStore.completions = function(prefix, tofilter) {
|
| + var fullList = TKR_openClosedComplete(prefix,
|
| + openStatusDefs,
|
| + closedStatusDefs);
|
| + if (fullList) return fullList;
|
| + return _AC_SimpleStore.prototype.completions.call(this, prefix, tofilter);
|
| + }
|
| +
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Simple function to add a given item to the list of items used to construct
|
| + * an "autocomplete store", and also update the docstring that describes
|
| + * that item. They are stored separately for backward compatability with
|
| + * autocomplete store logic that preceeded the introduction of descriptions.
|
| + */
|
| +function TKR_addACItem(items, docDict, item, docStr) {
|
| + items.push(item);
|
| + docDict[item] = docStr;
|
| +}
|
| +
|
| +/**
|
| + * Add several autocomplete items to a word list that will be used to construct
|
| + * an autocomplete store. Also, keep track of description strings for each
|
| + * item. A search operator is prepended to the name of each item. The opt_old
|
| + * and opt_new parameters are used to transform Key-Value labels into Key=Value
|
| + * search terms.
|
| + */
|
| +function TKR_addACItemList(
|
| + items, docDict, searchOp, acDefs, opt_old, opt_new) {
|
| + var item;
|
| + for (var i = 0; i < acDefs.length; i++) {
|
| + var nameAndDoc = acDefs[i];
|
| + item = searchOp + nameAndDoc.name;
|
| + if (opt_old) {
|
| + // Preserve any leading minus-sign.
|
| + item = item.slice(0, 1) + item.slice(1).replace(opt_old, opt_new);
|
| + }
|
| + TKR_addACItem(items, docDict, item, nameAndDoc.doc)
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Use information from an options feed to populate the artifact search
|
| + * autocomplete menu. The order of sections is: custom fields, labels,
|
| + * components, people, status, special, dates. Within each section,
|
| + * options are ordered semantically where possible, or alphabetically
|
| + * if there is no semantic ordering. Negated options all come after
|
| + * all normal options.
|
| + */
|
| +function TKR_setUpSearchStore(
|
| + labelDefs, memberDefs, openDefs, closedDefs, componentDefs, fieldDefs,
|
| + indMemberDefs) {
|
| + var searchWords = [];
|
| + var searchWordsNeg = [];
|
| + var docDict = {};
|
| +
|
| + // Treat Key-Value and OneWord labels separately.
|
| + var keyValueLabelDefs = [];
|
| + var oneWordLabelDefs = [];
|
| + for (var i = 0; i < labelDefs.length; i++) {
|
| + var nameAndDoc = labelDefs[i];
|
| + if (nameAndDoc.name.indexOf('-') == -1) {
|
| + oneWordLabelDefs.push(nameAndDoc)
|
| + } else {
|
| + keyValueLabelDefs.push(nameAndDoc)
|
| + }
|
| + }
|
| +
|
| + // Autocomplete for custom fields.
|
| + for (i = 0; i < fieldDefs.length; i++) {
|
| + var fieldName = fieldDefs[i]['field_name'];
|
| + var fieldType = fieldDefs[i]['field_type'];
|
| + if (fieldType == '1') { // enum type
|
| + var choices = fieldDefs[i]['choices'];
|
| + TKR_addACItemList(searchWords, docDict, fieldName + '=', choices);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-' + fieldName + '=', choices);
|
| + } else if (fieldType == '3') { // string types
|
| + TKR_addACItem(searchWords, docDict, fieldName + ':',
|
| + fieldDefs[i]['docstring']);
|
| + } else {
|
| + TKR_addACItem(searchWords, docDict, fieldName + '=',
|
| + fieldDefs[i]['docstring']);
|
| + }
|
| + TKR_addACItem(searchWords, docDict, 'has:' + fieldName,
|
| + 'Issues with any ' + fieldName + ' value');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:' + fieldName,
|
| + 'Issues with no ' + fieldName + ' value');
|
| + }
|
| +
|
| + // Add suggestions with "me" first, because otherwise they may be impossible
|
| + // to reach in a project that has a lot of members with emails starting with
|
| + // "me".
|
| + TKR_addACItem(searchWords, docDict, 'owner:me', 'Issues owned by me');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-owner:me', 'Issues not owned by me');
|
| + TKR_addACItem(searchWords, docDict, 'cc:me', 'Issues that CC me');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-cc:me', 'Issues that don\'t CC me');
|
| + TKR_addACItem(searchWords, docDict, 'reporter:me', 'Issues I reported');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-reporter:me', 'Issues reported by others');
|
| + TKR_addACItem(searchWords, docDict, 'commentby:me',
|
| + 'Issues that I commented on');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-commentby:me',
|
| + 'Issues that I didn\'t comment on');
|
| +
|
| + TKR_addACItemList(searchWords, docDict, '', keyValueLabelDefs, '-', '=');
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-', keyValueLabelDefs, '-', '=');
|
| + TKR_addACItemList(searchWords, docDict, 'label:', oneWordLabelDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-label:', oneWordLabelDefs);
|
| +
|
| + TKR_addACItemList(searchWords, docDict, 'component:', componentDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-component:', componentDefs);
|
| + TKR_addACItem(searchWords, docDict, 'has:component',
|
| + 'Issues with any components specified');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:component',
|
| + 'Issues with no components specified');
|
| +
|
| + TKR_addACItemList(searchWords, docDict, 'owner:', indMemberDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-owner:', indMemberDefs);
|
| + TKR_addACItemList(searchWords, docDict, 'cc:', memberDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-cc:', memberDefs);
|
| + TKR_addACItem(searchWords, docDict, 'has:cc',
|
| + 'Issues with any cc\'d users');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:cc',
|
| + 'Issues with no cc\'d users');
|
| + TKR_addACItemList(searchWords, docDict, 'reporter:', memberDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-reporter:', memberDefs);
|
| + TKR_addACItemList(searchWords, docDict, 'status:', openDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-status:', openDefs);
|
| + TKR_addACItemList(searchWords, docDict, 'status:', closedDefs);
|
| + TKR_addACItemList(searchWordsNeg, docDict, '-status:', closedDefs);
|
| + TKR_addACItem(searchWords, docDict, 'has:status',
|
| + 'Issues with any status');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:status',
|
| + 'Issues with no status');
|
| +
|
| + TKR_addACItem(searchWords, docDict, 'is:blocked',
|
| + 'Issues that are blocked');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-is:blocked',
|
| + 'Issues that are not blocked');
|
| + TKR_addACItem(searchWords, docDict, 'has:blockedon',
|
| + 'Issues that are blocked');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:blockedon',
|
| + 'Issues that are not blocked');
|
| + TKR_addACItem(searchWords, docDict, 'has:blocking',
|
| + 'Issues that are blocking other issues');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:blocking',
|
| + 'Issues that are not blocking other issues');
|
| +
|
| + TKR_addACItem(searchWords, docDict, 'is:starred',
|
| + 'Starred by me');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-is:starred',
|
| + 'Not starred by me');
|
| + TKR_addACItem(searchWords, docDict, 'stars>10',
|
| + 'More than 10 stars');
|
| + TKR_addACItem(searchWords, docDict, 'stars>100',
|
| + 'More than 100 stars');
|
| + TKR_addACItem(searchWords, docDict, 'summary:',
|
| + 'Search within the summary field');
|
| +
|
| + TKR_addACItemList(searchWords, docDict, 'commentby:', memberDefs);
|
| + TKR_addACItem(searchWords, docDict, 'attachment:',
|
| + 'Search within attachment names');
|
| + TKR_addACItem(searchWords, docDict, 'attachments>5',
|
| + 'Has more than 5 attachments');
|
| + TKR_addACItem(searchWords, docDict, 'is:open', 'Issues that are open');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-is:open', 'Issues that are closed');
|
| + TKR_addACItem(searchWords, docDict, 'has:owner',
|
| + 'Issues with some owner');
|
| + TKR_addACItem(searchWordsNeg, docDict, '-has:owner',
|
| + 'Issues with no owner');
|
| + TKR_addACItem(searchWords, docDict, 'has:attachment',
|
| + 'Issues with some attachments');
|
| + TKR_addACItem(searchWords, docDict, 'id:1,2,3',
|
| + 'Match only the specified issues');
|
| + TKR_addACItem(searchWords, docDict, 'id<100000',
|
| + 'Issues with IDs under 100,000');
|
| + TKR_addACItem(searchWords, docDict, 'blockedon:1',
|
| + 'Blocked on the specified issues');
|
| + TKR_addACItem(searchWords, docDict, 'blocking:1',
|
| + 'Blocking the specified issues');
|
| + TKR_addACItem(searchWords, docDict, 'is:spam', 'Issues classified as spam');
|
| + // We do not suggest -is:spam because it is implicit.
|
| +
|
| + var today = new Date();
|
| + var todayStr = (today.getFullYear() + '/' + (today.getMonth() + 1) + '/' +
|
| + today.getDate());
|
| + TKR_addACItem(searchWords, docDict, 'opened>today-1',
|
| + 'Opened within the last N days');
|
| + TKR_addACItem(searchWords, docDict, 'opened>' + todayStr,
|
| + 'Opened after the specified date');
|
| + TKR_addACItem(searchWords, docDict, 'opened<today-1',
|
| + 'Opened more than N days ago');
|
| + TKR_addACItem(searchWords, docDict, 'opened<' + todayStr,
|
| + 'Opened before the specified date');
|
| + TKR_addACItem(searchWords, docDict, 'modified>today-1',
|
| + 'Modified within the last N days');
|
| + TKR_addACItem(searchWords, docDict, 'modified>' + todayStr,
|
| + 'Modified after the specified date');
|
| + TKR_addACItem(searchWords, docDict, 'modified<today-1',
|
| + 'Modified more than N days ago');
|
| + TKR_addACItem(searchWords, docDict, 'modified<' + todayStr,
|
| + 'Modified before the specified date');
|
| + TKR_addACItem(searchWords, docDict, 'closed>today-1',
|
| + 'Closed within the last N days');
|
| + TKR_addACItem(searchWords, docDict, 'closed>' + todayStr,
|
| + 'Closed after the specified date');
|
| + TKR_addACItem(searchWords, docDict, 'closed<today-1',
|
| + 'Closed more than N days ago');
|
| + TKR_addACItem(searchWords, docDict, 'closed<' + todayStr,
|
| + 'Closed before the specified date');
|
| +
|
| + TKR_projectQueryStore = new _AC_SimpleStore(searchWords);
|
| + TKR_projectQueryStore.docstrings = docDict;
|
| +
|
| + searchWords = searchWords.concat(searchWordsNeg);
|
| +
|
| + TKR_searchStore = new _AC_SimpleStore(searchWords);
|
| + TKR_searchStore.docstrings = docDict;
|
| +
|
| + // When we insert an autocomplete value, replace an entire search term.
|
| + // Add just a space after it (not a comma) if it is a complete search term,
|
| + // or leave the caret immediately after the completion if we are just helping
|
| + // the user with the search operator.
|
| + TKR_searchStore.substitute =
|
| + function(inputValue, caret, completable, completion) {
|
| + var nextTerm = caret;
|
| + while (inputValue.charAt(nextTerm) != ' ' &&
|
| + nextTerm < inputValue.length) {
|
| + nextTerm++;
|
| + }
|
| + while (inputValue.charAt(nextTerm) == ' ' &&
|
| + nextTerm < inputValue.length) {
|
| + nextTerm++;
|
| + }
|
| + return inputValue.substring(0, caret - completable.length) +
|
| + completion.value + ' ' + inputValue.substring(nextTerm);
|
| + };
|
| + TKR_searchStore.autoselectFirstRow =
|
| + function() {
|
| + return false;
|
| + };
|
| +
|
| + TKR_projectQueryStore.substitute = TKR_searchStore.substitute;
|
| + TKR_projectQueryStore.autoselectFirstRow = TKR_searchStore.autoselectFirstRow;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Use information from an options feed to populate the issue quick edit
|
| + * autocomplete menu.
|
| + */
|
| +function TKR_setUpQuickEditStore(
|
| + labelDefs, memberDefs, openDefs, closedDefs, indMemberDefs) {
|
| + var qeWords = [];
|
| + var docDict = {};
|
| +
|
| + // Treat Key-Value and OneWord labels separately.
|
| + var keyValueLabelDefs = [];
|
| + var oneWordLabelDefs = [];
|
| + for (var i = 0; i < labelDefs.length; i++) {
|
| + var nameAndDoc = labelDefs[i];
|
| + if (nameAndDoc.name.indexOf('-') == -1) {
|
| + oneWordLabelDefs.push(nameAndDoc)
|
| + } else {
|
| + keyValueLabelDefs.push(nameAndDoc)
|
| + }
|
| + }
|
| + TKR_addACItemList(qeWords, docDict, '', keyValueLabelDefs, '-', '=');
|
| + TKR_addACItemList(qeWords, docDict, '-', keyValueLabelDefs, '-', '=');
|
| + TKR_addACItemList(qeWords, docDict, '', oneWordLabelDefs);
|
| + TKR_addACItemList(qeWords, docDict, '-', oneWordLabelDefs);
|
| +
|
| + TKR_addACItem(qeWords, docDict, 'owner=me', 'Make me the owner');
|
| + TKR_addACItem(qeWords, docDict, 'owner=----', 'Clear the owner field');
|
| + TKR_addACItem(qeWords, docDict, 'cc=me', 'CC me on this issue');
|
| + TKR_addACItem(qeWords, docDict, 'cc=-me', 'Remove me from CC list');
|
| + TKR_addACItemList(qeWords, docDict, 'owner=', indMemberDefs);
|
| + TKR_addACItemList(qeWords, docDict, 'cc=', memberDefs);
|
| + TKR_addACItemList(qeWords, docDict, 'cc=-', memberDefs);
|
| + TKR_addACItemList(qeWords, docDict, 'status=', openDefs);
|
| + TKR_addACItemList(qeWords, docDict, 'status=', closedDefs);
|
| + TKR_addACItem(qeWords, docDict, 'summary=""', 'Set the summary field');
|
| +
|
| + TKR_quickEditStore = new _AC_SimpleStore(qeWords);
|
| + TKR_quickEditStore.docstrings = docDict;
|
| +
|
| + // When we insert an autocomplete value, replace an entire command part.
|
| + // Add just a space after it (not a comma) if it is a complete part,
|
| + // or leave the caret immediately after the completion if we are just helping
|
| + // the user with the command operator.
|
| + TKR_quickEditStore.substitute =
|
| + function(inputValue, caret, completable, completion) {
|
| + var nextTerm = caret;
|
| + while (inputValue.charAt(nextTerm) != ' ' &&
|
| + nextTerm < inputValue.length) {
|
| + nextTerm++;
|
| + }
|
| + while (inputValue.charAt(nextTerm) == ' ' &&
|
| + nextTerm < inputValue.length) {
|
| + nextTerm++;
|
| + }
|
| + return inputValue.substring(0, caret - completable.length) +
|
| + completion.value + ' ' + inputValue.substring(nextTerm);
|
| + };
|
| +}
|
| +
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with all the project
|
| + * custom permissions.
|
| + * @param {Array} customPermissions An array of custom permission names.
|
| + */
|
| +function TKR_setUpCustomPermissionsStore(customPermissions) {
|
| + var permWords = ['View', 'EditIssue', 'AddIssueComment', 'DeleteIssue'];
|
| + var docdict = {
|
| + 'View': '', 'EditIssue': '', 'AddIssueComment': '', 'DeleteIssue': ''};
|
| + for (var i = 0; i < customPermissions.length; i++) {
|
| + permWords.push(customPermissions[i]);
|
| + docdict[customPermissions[i]] = '';
|
| + }
|
| +
|
| + TKR_customPermissionsStore = new _AC_SimpleStore(permWords);
|
| + TKR_customPermissionsStore.docstrings = docdict;
|
| +
|
| + TKR_customPermissionsStore.commaCompletes = false;
|
| +
|
| + TKR_customPermissionsStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with all the well-known project
|
| + * member user names and real names. The store has some
|
| + * monorail-specific methods.
|
| + * TODO(jrobbins): would it be easier to define my own class to use
|
| + * instead of _AC_Simple_Store?
|
| + * @param {Array} memberDefs An array of definitions of the project
|
| + * members. Each definition has a name and docstring.
|
| + */
|
| +function TKR_setUpMemberStore(memberDefs, indMemerDefs) {
|
| + var memberWords = [];
|
| + var indMemberWords = [];
|
| + var docdict = {};
|
| + for (var i = 0; i < memberDefs.length; i++) {
|
| + var member = memberDefs[i];
|
| + memberWords.push(member.name);
|
| + docdict[member.name] = member.doc;
|
| + if(!member.is_group) {
|
| + indMemberWords.push(member.name);
|
| + }
|
| + }
|
| +
|
| + TKR_memberListStore = new _AC_SimpleStore(memberWords);
|
| + TKR_memberListStore.docstrings = docdict;
|
| +
|
| + TKR_memberListStore.completions = function(prefix, tofilter) {
|
| + var fullList = TKR_fullComplete(prefix, memberDefs);
|
| + if (fullList) return fullList;
|
| + return _AC_SimpleStore.prototype.completions.call(this, prefix, tofilter);
|
| + }
|
| +
|
| + TKR_memberListStore.completable = function(inputValue, cursor) {
|
| + if (inputValue == '') return '*member';
|
| + return _AC_SimpleStore.prototype.completable.call(this, inputValue, cursor);
|
| + }
|
| +
|
| + TKR_memberListStore.substitute = TKR_acSubstituteWithComma;
|
| +
|
| + TKR_ownerStore = new _AC_SimpleStore(indMemberWords);
|
| + TKR_ownerStore.docstrings = docdict;
|
| +
|
| + TKR_ownerStore.commaCompletes = false;
|
| +
|
| + TKR_ownerStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| +
|
| + TKR_ownerStore.completions = function(prefix, tofilter) {
|
| + var fullList = TKR_fullComplete(prefix, indMemerDefs);
|
| + if (fullList) return fullList;
|
| + return _AC_SimpleStore.prototype.completions.call(this, prefix, tofilter);
|
| + };
|
| +
|
| + TKR_ownerStore.completable = function(inputValue, cursor) {
|
| + if (!ac_everTyped) return '*owner';
|
| + return inputValue;
|
| + };
|
| +
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Constuct one new autocomplete store for each user-valued custom
|
| + * field that has a needs_perm validation requirement, and thus a
|
| + * list of allowed user indexes.
|
| + * TODO(jrobbins): would it be easier to define my own class to use
|
| + * instead of _AC_Simple_Store?
|
| + * @param {Array} fieldDefs An array of field definitions, only some
|
| + * of which have a 'user_indexes' entry.
|
| + * @param {Array} memberDefs An array of definitions of the project
|
| + * members. Each definition has a name and docstring.
|
| + */
|
| +function TKR_setUpUserAutocompleteStores(fieldDefs, memberDefs) {
|
| + for (var i = 0; i < fieldDefs.length; i++) {
|
| + var fieldDef = fieldDefs[i];
|
| + if (fieldDef['user_indexes']) {
|
| + var userIndexes = fieldDef['user_indexes'];
|
| + var qualifiedMembers = [];
|
| + for (var j = 0; j < userIndexes.length; j++) {
|
| + var mem = memberDefs[userIndexes[j]];
|
| + if (mem) qualifiedMembers.push(mem);
|
| + }
|
| + var us = makeOneUserAutocompleteStore(fieldDef, qualifiedMembers);
|
| + TKR_userAutocompleteStores['custom_' + fieldDef['field_id']] = us;
|
| + }
|
| + }
|
| +}
|
| +
|
| +function makeOneUserAutocompleteStore(fieldDef, memberDefs) {
|
| + var memberWords = [];
|
| + var docdict = {};
|
| + for (var i = 0; i < memberDefs.length; i++) {
|
| + var member = memberDefs[i];
|
| + memberWords.push(member.name);
|
| + docdict[member.name] = member.doc;
|
| + }
|
| +
|
| + var userStore = new _AC_SimpleStore(memberWords);
|
| + userStore.docstrings = docdict;
|
| + userStore.commaCompletes = false;
|
| +
|
| + userStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| +
|
| + userStore.completions = function(prefix, tofilter) {
|
| + var fullList = TKR_fullComplete(prefix, memberDefs);
|
| + if (fullList) return fullList;
|
| + return _AC_SimpleStore.prototype.completions.call(this, prefix, tofilter);
|
| + };
|
| +
|
| + userStore.completable = function(inputValue, cursor) {
|
| + if (!ac_everTyped) return '*custom';
|
| + return inputValue;
|
| + };
|
| +
|
| + return userStore;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with all the components.
|
| + * The store has some monorail-specific methods.
|
| + * @param {Array} componentDefs An array of definitions of components.
|
| + */
|
| +function TKR_setUpComponentStore(componentDefs) {
|
| + var componentWords = [];
|
| + var docdict = {};
|
| + for (var i = 0; i < componentDefs.length; i++) {
|
| + var component = componentDefs[i];
|
| + componentWords.push(component.name);
|
| + docdict[component.name] = component.doc;
|
| + }
|
| +
|
| + TKR_componentListStore = new _AC_SimpleStore(componentWords);
|
| + TKR_componentListStore.docstrings = docdict;
|
| +
|
| + TKR_componentListStore.completions = function(prefix, tofilter) {
|
| + var fullList = TKR_fullComplete(prefix, componentDefs);
|
| + if (fullList) return fullList;
|
| + return _AC_SimpleStore.prototype.completions.call(this, prefix, tofilter);
|
| + }
|
| +
|
| + TKR_componentListStore.substitute = TKR_acSubstituteWithComma;
|
| +
|
| + TKR_componentListStore.completable = function(inputValue, cursor) {
|
| + if (inputValue == '') return '*component';
|
| + return _AC_SimpleStore.prototype.completable.call(this, inputValue, cursor);
|
| + }
|
| +
|
| +}
|
| +
|
| +
|
| +/**
|
| + * An array of definitions of all well-known issue labels. Each
|
| + * definition has the name of the label, and a docstring that
|
| + * describes its meaning.
|
| + */
|
| +var TKR_labelWords = [];
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with all the well-known issue
|
| + * labels for the current project. The store has some DIT-specific methods.
|
| + * TODO(jrobbins): would it be easier to define my own class to use
|
| + * instead of _AC_Simple_Store?
|
| + * @param {Array} labelDefs An array of definitions of the project
|
| + * members. Each definition has a name and docstring.
|
| + */
|
| +function TKR_setUpLabelStore(labelDefs) {
|
| + TKR_labelWords = [];
|
| + var docdict = {};
|
| + for (var i = 0; i < labelDefs.length; i++) {
|
| + var label = labelDefs[i];
|
| + TKR_labelWords.push(label.name);
|
| + docdict[label.name] = label.doc;
|
| + }
|
| +
|
| + TKR_labelStore = new _AC_SimpleStore(TKR_labelWords);
|
| + TKR_labelStore.docstrings = docdict;
|
| +
|
| + TKR_labelStore.commaCompletes = false;
|
| + TKR_labelStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| +
|
| + /* Given what the user typed, return the part of it that should be used
|
| + * to determine the auto-complete options offered to the user. */
|
| + TKR_labelStore.completable = function(inputValue, cursor) {
|
| + if (cursor == 0) {
|
| + return '*label'; // Show every well-known label that is not redundant.
|
| + }
|
| + var start = 0;
|
| + for (var i = cursor; --i >= 0;) {
|
| + var c = inputValue.charAt(i)
|
| + if (c == ' ' || c == ',') {
|
| + start = i + 1;
|
| + break;
|
| + }
|
| + }
|
| + var questionPos = inputValue.indexOf('?');
|
| + if (questionPos >= 0) {
|
| + // Ignore any "?" character and anything after it.
|
| + inputValue = inputValue.substring(start, questionPos);
|
| + }
|
| + var result = inputValue.substring(start, cursor);
|
| + if (inputValue.lastIndexOf('-') > 0 && !ac_everTyped) {
|
| + // Act like a menu: offer all alternative values for the same prefix.
|
| + result = inputValue.substring(
|
| + start, Math.min(cursor, inputValue.lastIndexOf('-')));
|
| + }
|
| + if (inputValue.startsWith('Restrict-') && !ac_everTyped) {
|
| + // If user is in the middle of 2nd part, use that to narrow the choices.
|
| + result = inputValue;
|
| + // If they completed 2nd part, give all choices matching 2-part prefix.
|
| + if (inputValue.lastIndexOf('-') > 8) {
|
| + result = inputValue.substring(
|
| + start, Math.min(cursor, inputValue.lastIndexOf('-') + 1));
|
| + }
|
| + }
|
| +
|
| + return result;
|
| + };
|
| +
|
| + /* Start with all labels or only those that match what the user typed so far,
|
| + * then filter out any that would lead to conflicts or redundancy. */
|
| + TKR_labelStore.completions = function(prefix, tofilter) {
|
| + var comps = TKR_fullComplete(prefix, labelDefs);
|
| + if (comps == null) {
|
| + comps = _AC_SimpleStore.prototype.completions.call(
|
| + this, prefix, tofilter);
|
| + }
|
| +
|
| + var filtered_comps = [];
|
| + for (var i = 0; i < comps.length; i++) {
|
| + var prefix_parts = comps[i].value.split('-');
|
| + var label_prefix = prefix_parts[0].toLowerCase();
|
| + if (comps[i].value.startsWith('Restrict-')) {
|
| + if (!prefix.toLowerCase().startsWith('r')) {
|
| + // Consider restriction labels iff user has started typing.
|
| + continue;
|
| + }
|
| + if (prefix_parts.length > 1) {
|
| + label_prefix += '-' + prefix_parts[1].toLowerCase();
|
| + }
|
| + }
|
| + if (FindInArray(TKR_exclPrefixes, label_prefix) == -1 ||
|
| + TKR_usedPrefixes[label_prefix] == undefined ||
|
| + TKR_usedPrefixes[label_prefix].length == 0 ||
|
| + (TKR_usedPrefixes[label_prefix].length == 1 &&
|
| + TKR_usedPrefixes[label_prefix][0] == ac_focusedInput)) {
|
| + var uniq = true;
|
| + for (var p in TKR_usedPrefixes) {
|
| + var textFields = TKR_usedPrefixes[p];
|
| + for (var j = 0; j < textFields.length; j++) {
|
| + var tf = textFields[j];
|
| + if (tf.value.toLowerCase() == comps[i].value.toLowerCase() &&
|
| + tf != ac_focusedInput) {
|
| + uniq = false;
|
| + }
|
| + }
|
| + }
|
| + if (uniq) {
|
| + filtered_comps.push(comps[i]);
|
| + }
|
| + }
|
| + }
|
| +
|
| + return filtered_comps;
|
| + };
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with the given strings as choices.
|
| + * @param {Array} choices An array of autocomplete choices.
|
| + */
|
| +function TKR_setUpAutoCompleteStore(choices) {
|
| + TKR_autoCompleteStore = new _AC_SimpleStore(choices);
|
| + var choicesDefs = []
|
| + for (var i = 0; i < choices.length; ++i) {
|
| + choicesDefs.push({'name': choices[i], 'doc': ''});
|
| + }
|
| +
|
| + /**
|
| + * Override the default completions() function to return a list of
|
| + * available choices. It proactively shows all choices when the user has
|
| + * not yet typed anything. It stops offering choices if the text field
|
| + * has a pretty long string in it already. It does not offer choices that
|
| + * have already been chosen.
|
| + */
|
| + TKR_autoCompleteStore.completions = function(prefix, tofilter) {
|
| + if (prefix.length > 18) {
|
| + return [];
|
| + }
|
| + var comps = TKR_fullComplete(prefix, choicesDefs);
|
| + if (comps == null) {
|
| + comps = _AC_SimpleStore.prototype.completions.call(
|
| + this, prefix, tofilter);
|
| + }
|
| +
|
| + var usedComps = {}
|
| + var textFields = document.getElementsByTagName('input');
|
| + for (var i = 0; i < textFields.length; ++i) {
|
| + if (textFields[i].classList.contains('autocomplete')) {
|
| + usedComps[textFields[i].value] = true;
|
| + }
|
| + }
|
| + var unusedComps = []
|
| + for (i = 0; i < comps.length; ++i) {
|
| + if (!usedComps[comps[i].value]) {
|
| + unusedComps.push(comps[i]);
|
| + }
|
| + }
|
| +
|
| + return unusedComps;
|
| + }
|
| +
|
| + /**
|
| + * Override the default completable() function with one that gives a
|
| + * special value when the user has not yet typed anything. This
|
| + * causes TKR_fullComplete() to show all choices. Also, always consider
|
| + * the whole textfield value as an input to completion matching. Otherwise,
|
| + * it would only consider the part after the last comma (which makes sense
|
| + * for gmail To: and Cc: address fields).
|
| + */
|
| + TKR_autoCompleteStore.completable = function(inputValue, cursor) {
|
| + if (inputValue == '') {
|
| + return '*ac';
|
| + }
|
| + return inputValue;
|
| + }
|
| +
|
| + /**
|
| + * Override the default substitute() function to completely replace the
|
| + * contents of the text field when the user selects a completion. Otherwise,
|
| + * it would append, much like the Gmail To: and Cc: fields append autocomplete
|
| + * selections.
|
| + */
|
| + TKR_autoCompleteStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| +
|
| + /**
|
| + * We consider the whole textfield to be one value, not a comma separated
|
| + * list. So, typing a ',' should not trigger an autocomplete selection.
|
| + */
|
| + TKR_autoCompleteStore.commaCompletes = false;
|
| +}
|
| +
|
| +
|
| +/**
|
| + * XMLHTTP object used to fetch autocomplete options from the server.
|
| + */
|
| +var TKR_optionsXmlHttp = undefined;
|
| +
|
| +/**
|
| + * URL used to fetch autocomplete options from the server, WITHOUT the
|
| + * project's cache content timestamp.
|
| + */
|
| +var TKR_optionsURL = undefined;
|
| +
|
| +/**
|
| + * Contact the server to fetch the set of autocomplete options for the
|
| + * projects the user is contributor/member/owner of.
|
| + * If multiValue is set to true then the projectStore is configured to
|
| + * have support for multi-values (useful for example for saved queries where
|
| + * a query can apply to multiple projects).
|
| + */
|
| +function TKR_fetchUserProjects(multiValue) {
|
| + // Set a request token to prevent XSRF leaking of user project lists.
|
| + if (CS_env.token) {
|
| + var postURL = '/hosting/projects.do';
|
| + var xh = XH_XmlHttpCreate()
|
| + var data = 'token=' + CS_env.token;
|
| + var callback = multiValue ? TKR_fetchMultiValProjectsCallback
|
| + : TKR_fetchProjectsCallback;
|
| + XH_XmlHttpPOST(xh, postURL, data, callback);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Sets up the projectStore based on the json data received.
|
| + * The projectStore is setup with support for multiple values.
|
| + * @param {event} event with xhr Response with JSON data of projects.
|
| + */
|
| +function TKR_fetchMultiValProjectsCallback(event) {
|
| + var projects = TKR_getMemberProjects(event)
|
| + if (projects) {
|
| + TKR_setUpProjectStore(projects, true);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Sets up the projectStore based on the json data received.
|
| + * @param {event} event with xhr Response with JSON data of projects.
|
| + */
|
| +function TKR_fetchProjectsCallback(event) {
|
| + var projects = TKR_getMemberProjects(event)
|
| + if (projects) {
|
| + TKR_setUpProjectStore(projects, false);
|
| + }
|
| +}
|
| +
|
| +function TKR_getMemberProjects(event) {
|
| + var xhr = event.target;
|
| + if (xhr) {
|
| + if (xhr.readyState != 4 || xhr.status != 200)
|
| + return;
|
| +
|
| + var projects = [];
|
| + var json = CS_parseJSON(xhr);
|
| + for (var category in json) {
|
| + switch (category) {
|
| + case 'contributorto':
|
| + case 'memberof':
|
| + case 'ownerof':
|
| + for (var i = 0; i < json[category].length; i++) {
|
| + projects.push(json[category][i]);
|
| + }
|
| + break;
|
| + case 'error':
|
| + return;
|
| + default:
|
| + break;
|
| + }
|
| + }
|
| + projects.sort();
|
| + return projects;
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Constuct a new autocomplete store with all the projects that the
|
| + * current user has visibility into. The store has some monorail-specific
|
| + * methods.
|
| + * @param {Array} projects An array of project names.
|
| + * @param {Boolean} multiValue Determines whether the store should support
|
| + * multiple values.
|
| + */
|
| +function TKR_setUpProjectStore(projects, multiValue) {
|
| + TKR_projectStore = new _AC_SimpleStore(projects);
|
| + TKR_projectStore.commaCompletes = !multiValue;
|
| +
|
| + var projectsDefs = []
|
| + var docdict = {}
|
| + for (var i = 0; i < projects.length; ++i) {
|
| + projectsDefs.push({'name': projects[i], 'doc': ''});
|
| + docdict[projects[i]] = '';
|
| + }
|
| +
|
| + TKR_projectStore.docstrings = docdict;
|
| + if (multiValue) {
|
| + TKR_projectStore.substitute = TKR_acSubstituteWithComma;
|
| + } else {
|
| + TKR_projectStore.substitute =
|
| + function(inputValue, cursor, completable, completion) {
|
| + return completion.value;
|
| + };
|
| + }
|
| +
|
| + TKR_projectStore.completions = function(prefix, tofilter) {
|
| + var fullList = TKR_fullComplete(prefix, projectsDefs);
|
| + if (fullList) return fullList;
|
| + return _AC_SimpleStore.prototype.completions.call(this, prefix, tofilter);
|
| + };
|
| +
|
| + TKR_projectStore.completable = function(inputValue, cursor) {
|
| + if (inputValue == '') return '*project';
|
| + if (multiValue)
|
| + return _AC_SimpleStore.prototype.completable.call(
|
| + this, inputValue, cursor);
|
| + else
|
| + return inputValue;
|
| + };
|
| +}
|
| +
|
| +
|
| +/**
|
| + * Contact the server to fetch the set of autocomplete options for the
|
| + * current project. This is done with XMLHTTPRequest because the list
|
| + * could be long, and most of the time, the user will only view an
|
| + * issue not edit it.
|
| + * @param {string} projectName The name of the current project.
|
| + * @param {string} feedName The name of the feed to fetch.
|
| + * @param {string} token The user's url-command-attack-prevention token.
|
| + * @param {number} cct The project's cached-content-timestamp.
|
| + * @param {Object} opt_args Key=value pairs.
|
| + */
|
| +function TKR_fetchOptions(projectName, feedName, token, cct, opt_args) {
|
| + TKR_autoCompleteFeedName = feedName;
|
| + TKR_optionsXmlHttp = XH_XmlHttpCreate();
|
| + var projectPart = projectName ? '/p/' + projectName : '/hosting';
|
| + TKR_optionsURL = (
|
| + projectPart + '/feeds/' + feedName + '?' +
|
| + 'token=' + token);
|
| + for (var arg in opt_args) {
|
| + TKR_optionsURL += '&' + arg + '=' + encodeURIComponent(opt_args[arg]);
|
| + }
|
| +
|
| + XH_XmlHttpGET(
|
| + TKR_optionsXmlHttp, TKR_optionsURL + '&cct=' + cct,
|
| + TKR_issueOptionsFeedCallback);
|
| +}
|
| +
|
| +
|
| +/**
|
| + * The communication with the server has made some progress. If it is
|
| + * done, then process the response.
|
| + */
|
| +function TKR_issueOptionsFeedCallback() {
|
| + if (TKR_optionsXmlHttp.readyState == 4) {
|
| + if (TKR_optionsXmlHttp.status == 200) {
|
| + TKR_gotIssueOptionsFeed(TKR_optionsXmlHttp);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * The server has sent the list of all options. Parse them and then set up each
|
| + * of the label stores.
|
| + * @param {Object} xhr The JSON response object the server. The response JSON
|
| + * consists of one large dictionary with four items: open statuses,
|
| + * closed statuses, issue labels, and project members.
|
| + */
|
| +function TKR_gotIssueOptionsFeed(xhr) {
|
| + var json_data = null;
|
| + try {
|
| + json_data = CS_parseJSON(xhr);
|
| + }
|
| + catch (e) {
|
| + return null;
|
| + }
|
| + indMemerDefs = []
|
| + for (var i = 0; i < json_data.members.length; i++) {
|
| + var member = json_data.members[i];
|
| + if(!member.is_group) {
|
| + indMemerDefs.push(member);
|
| + }
|
| + }
|
| + TKR_setUpStatusStore(json_data.open, json_data.closed);
|
| + TKR_setUpSearchStore(
|
| + json_data.labels, json_data.members, json_data.open, json_data.closed,
|
| + json_data.components, json_data.fields, indMemerDefs);
|
| + TKR_setUpQuickEditStore(
|
| + json_data.labels, json_data.members, json_data.open, json_data.closed,
|
| + indMemerDefs);
|
| + TKR_setUpLabelStore(json_data.labels);
|
| + TKR_setUpComponentStore(json_data.components);
|
| + TKR_setUpMemberStore(json_data.members, indMemerDefs);
|
| + TKR_setUpUserAutocompleteStores(json_data.fields, json_data.members);
|
| + TKR_setUpCustomPermissionsStore(json_data.custom_permissions);
|
| + TKR_exclPrefixes = json_data.excl_prefixes;
|
| + TKR_prepLabelAC(TKR_labelFieldIDPrefix);
|
| + TKR_prepOwnerField(json_data.members);
|
| + TKR_restrict_to_known = json_data.strict;
|
| +}
|
|
|