spitfirepm 1.20.91 → 1.20.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/globals.d.ts CHANGED
@@ -76,6 +76,7 @@ declare global {
76
76
  WindowHasFocus: boolean;
77
77
  clearHomeTabCount: any;
78
78
  DocumentChangedByAnotherUser: DocumentChangedByAnotherUser;
79
+ PostbackRefresh: RefreshPageParts;
79
80
  refreshPartbyName: RefreshPartbyName;
80
81
  refreshPageParts: RefreshPageParts;
81
82
  }
@@ -255,12 +255,19 @@ export declare class sfRestClient {
255
255
  *
256
256
  */
257
257
  PopQAInfo(forElement: JQuery<HTMLElement>, queryOptions?: QAInfoOptions, resolveKey?: (forElement: JQuery<HTMLElement>) => string, resolveValue?: (forElement: JQuery<HTMLElement>, queryOptions: QAInfoOptions) => string, onPostback?: (eventTarget: string, eventArgs: string) => void): JQuery<HTMLElement>;
258
+ private AdjustFluidDialog;
259
+ UpdateFluidDialogs(): void;
260
+ AutoSizeDialog($D: JQuery<HTMLElement>): JQuery<HTMLElement>;
258
261
  /** checks with server up to 5 times a second. promise resolves when task has ended or callback returns true;
259
262
  * @param taskKey guid key for task
260
263
  * @param sessionClient optional existing SessionClient
261
264
  * @param progressCallback optional callback for when task status is not 200; if this callback returns true, promise is resolved
262
265
  */
263
266
  WaitForTask(taskKey: GUID, sessionClient?: SessionClient, progressCallback?: (state: _SwaggerClientExports.HttpResponseJsonContent) => boolean): Promise<_SwaggerClientExports.HttpResponseJsonContent>;
267
+ ExportCompetitiveBidData(): void;
268
+ static $CurrentPleaseWaitDialog: JQuery<HTMLDivElement> | null;
269
+ HeyPleaseWait(): JQuery<HTMLDivElement>;
270
+ ClearPleaseWaitDialog(): void;
264
271
  /**
265
272
  * Maps .NET placeholders (dn) to webix placeholders (dx)
266
273
  * Important: order matters (eg: dd must be remapped before d, or the d map would be used)
@@ -442,16 +449,24 @@ export declare class sfRestClient {
442
449
  * - Nav To (dcmodules and admin tools)
443
450
  */
444
451
  InvokeAction(actionString: string | _SwaggerClientExports.MenuAction, rowData?: DataModelRow): void;
452
+ /** Creates an exchange token and calls OpenWindowsLinkHelper() */
445
453
  FollowLinkViaSFLink(targetURL: string, afterOpenArg?: boolean | string | [string, string] | Function, autoCloseDoc?: boolean): void;
446
454
  /**
447
455
  *
448
456
  * @param et token passed to sfLink
449
- * @param afterOpenArg boolean/true: closes document page; false/0: posts back default refresh; ['e','a']: posts back e with a;
457
+ * @param afterOpenArg boolean/true: closes document page; false/0: posts back default refresh; ['e','a']: posts back e with a; callback function is passed et
450
458
  * @param autoCloseDoc
451
459
  */
452
460
  OpenWindowsLinkHelper(et: string, afterOpenArg?: boolean | string | [string, string] | Function, autoCloseDoc?: boolean): void;
453
- /** Finds $$ and other replacable values */
461
+ /** Finds $$ markers and other replacable values.
462
+ * Markers are case sensative
463
+ */
454
464
  protected ExpandActionMarkers(rawAction: string, rowData?: DataModelRow): string;
465
+ /** given a mark name, returns the replacement value
466
+ * @see ExpandActionMarkers()
467
+ * @argument markerName such as $$Project or $$PDSKEY
468
+ * @argument rowData object containing row data - first source for value to replace the marker
469
+ */
455
470
  protected GetActionMarkerReplacement(markerName: string, rowData?: DataModelRow): string;
456
471
  protected getCookie(cname: string): string;
457
472
  /**
@@ -485,13 +500,15 @@ export declare class sfRestClient {
485
500
  * @param defaultResponse
486
501
  */
487
502
  VModalPage(vpg: string, opts: string, w: number, h: number, defaultResponse: string | undefined): void;
488
- static GAPageHitSent: boolean;
503
+ private static GALastPageHitSent;
489
504
  static GAMonitorSendFailed: boolean;
490
505
  private GAMonitorPageHit;
491
506
  private GAMonitorSend;
492
507
  GAMonitorEvent(propertyID: string, clientID: string, category: string, action: string, label: string, value: number): JQuery.Promise<any, any, any>;
493
- sfGAEvent(category: string, action: string, label: string, value: number): void;
494
- sfGADialogEvent(action: string, dialogName: string): void;
508
+ /** Google Analytics Event */
509
+ GAEvent(category: string, action: string, label: string, value: number): void;
510
+ /** Shortcut that calls GAEvent("dialog",action, dialogName,1) */
511
+ GADialogEvent(action: string, dialogName: string): void;
495
512
  private ValueHasWildcard;
496
513
  private sfClearACHeighLimit;
497
514
  /**
@@ -515,7 +532,7 @@ export declare class sfRestClient {
515
532
  * @argument title optional title
516
533
  * @argument uiAlertIcon if specified, and not false, ui-icon class name (see https://api.jqueryui.com/theming/icons/)
517
534
  */
518
- jqAlert(msg: string, title?: string, uiAlertIcon?: string): JQuery<HTMLElement>;
535
+ jqAlert(msg: string, title?: string, uiAlertIcon?: string): JQuery<HTMLDivElement>;
519
536
  /** Opens a jquery ui dialog with an iframe to display the requested url
520
537
  * @param url a same-site url
521
538
  * @param eventId optional - used when dialog is closed
@@ -7,15 +7,7 @@ const _SwaggerClientExports = require("./SwaggerClients");
7
7
  const $ = require("jquery");
8
8
  const BrowserExtensionChecker_1 = require("./BrowserExtensionChecker");
9
9
  //import {dialog} from "jquery-ui";
10
- const ClientPackageVersion = "1.20.91";
11
- //export type GUID = string //& { isGuid: true };
12
- /* eslint-disable prefer-template */
13
- /* eslint-disable no-extend-native */
14
- /* eslint-disable prefer-spread */
15
- /* eslint-disable no-undef */
16
- /* eslint-disable prefer-arrow-callback */
17
- /* eslint-disable vars-on-top */
18
- /* eslint-disable no-var */
10
+ const ClientPackageVersion = "1.20.92";
19
11
  // original script created by Stan York and modified for typescript and linter requirements by Uladzislau Kumakou
20
12
  var LoggingLevels;
21
13
  (function (LoggingLevels) {
@@ -55,7 +47,7 @@ class PartStorageData {
55
47
  PartStorageData._LoadedParts.set(this._ReferenceKey, this);
56
48
  this._InitializationResultPromise = null;
57
49
  if (!PartStorageData._SiteURL) {
58
- var ApplicationPath = window.location.pathname.substr(1, window.location.pathname.substr(1).indexOf("/"));
50
+ var ApplicationPath = window.location.pathname.substring(1, window.location.pathname.substring(1).indexOf("/") + 1);
59
51
  PartStorageData._SiteURL = `${window.location.origin}/${ApplicationPath || 'sfPMS'}`;
60
52
  }
61
53
  }
@@ -343,7 +335,7 @@ class sfRestClient {
343
335
  this.EmptyKey = "00000000-0000-0000-0000-000000000000";
344
336
  this._CachedDVRequests = new Map();
345
337
  this.ThisInstanceID = sfRestClient.InstanceSerialNumberSource++;
346
- var ApplicationPath = window.location.pathname.substr(1, window.location.pathname.substr(1).indexOf("/"));
338
+ var ApplicationPath = window.location.pathname.substring(1, window.location.pathname.substring(1).indexOf("/") + 1);
347
339
  this._SiteURL = `${window.location.origin}/${ApplicationPath || 'sfPMS'}`;
348
340
  this._SiteRootURL = `/${ApplicationPath || 'sfPMS'}`;
349
341
  this.exports = _SwaggerClientExports;
@@ -372,6 +364,10 @@ class sfRestClient {
372
364
  console.log(`sfClient#${this.ThisInstanceID} ${this.ClientVersion}; Window[${window.name}]; ${ThisIsGlobal ? "Global" : ""}`);
373
365
  if (ThisIsGlobal) {
374
366
  var RESTClient = this;
367
+ if (!$.hasData)
368
+ $.fn.extend({ hasData: function (name) {
369
+ return this.data(name) !== undefined;
370
+ } });
375
371
  sfRestClient.ExternalToolsLoadedPromise = RESTClient.AssureJQUITools($("div").first());
376
372
  if ($("title").text().length === 0)
377
373
  $("title").text("Spitfire PM");
@@ -1175,6 +1171,53 @@ class sfRestClient {
1175
1171
  // });
1176
1172
  return forElement;
1177
1173
  }
1174
+ AdjustFluidDialog(dialog, $this) {
1175
+ if (!$this)
1176
+ $this = dialog.closest("ui-dialog");
1177
+ var $DialogContent = $this.find(".ui-dialog-content");
1178
+ // if fluid option == true
1179
+ if (dialog.options.fluid) {
1180
+ dialog.option("width", "auto"); // temp...to calc required size
1181
+ var wWidth = $(window).width();
1182
+ var dWidth = $DialogContent.width();
1183
+ // check window width against dialog width
1184
+ if (wWidth < (dWidth + 50)) {
1185
+ // keep dialog from filling entire screen
1186
+ $this.css("max-width", "90%");
1187
+ }
1188
+ else {
1189
+ // fix maxWidth bug
1190
+ dialog.option("width", dWidth);
1191
+ }
1192
+ //reposition dialog
1193
+ dialog.option("position", dialog.options.position);
1194
+ }
1195
+ }
1196
+ UpdateFluidDialogs() {
1197
+ var RESTClient = this;
1198
+ var $visible = $(".ui-dialog:visible");
1199
+ if ($visible.length === 0)
1200
+ $visible = self.top.$(".ui-dialog:visible");
1201
+ // each open dialog
1202
+ $visible.each(function () {
1203
+ var $this = $(this);
1204
+ var dialog = $this.find(".ui-dialog-content").data("ui-dialog");
1205
+ RESTClient.AdjustFluidDialog(dialog, $this);
1206
+ });
1207
+ }
1208
+ AutoSizeDialog($D) {
1209
+ var RESTClient = this;
1210
+ var dialogOptions = {
1211
+ width: 400,
1212
+ minWidth: 250,
1213
+ height: "auto",
1214
+ fluid: true
1215
+ };
1216
+ $D.dialog(dialogOptions);
1217
+ //$D.dialog("options","width", $D.width());
1218
+ RESTClient.UpdateFluidDialogs();
1219
+ return $D;
1220
+ }
1178
1221
  /** checks with server up to 5 times a second. promise resolves when task has ended or callback returns true;
1179
1222
  * @param taskKey guid key for task
1180
1223
  * @param sessionClient optional existing SessionClient
@@ -1209,6 +1252,97 @@ class sfRestClient {
1209
1252
  result(taskResult);
1210
1253
  });
1211
1254
  }
1255
+ ExportCompetitiveBidData() {
1256
+ var PostBackArgs = "";
1257
+ var TemplateTypeCode = "BA";
1258
+ var RESTClient = this;
1259
+ console.log(`sfExportCobra(${TemplateTypeCode}) ${PostBackArgs}`);
1260
+ $("DIV#divDialogExportGrid").remove();
1261
+ var $Box = $("<div id='divDialogExportGrid' />");
1262
+ var $Dialog;
1263
+ $Dialog = $Box.load(`${RESTClient._SiteURL}/ajhx/ExportGridDialog.html`, function PopFromParentDialogLoaded() {
1264
+ // after loaded
1265
+ var $TemplateDocument = $Box.find("#txtTemplate");
1266
+ var $ButtonPane = $Box.parent().find(".ui-dialog-buttonpane");
1267
+ var $CancelBTN = $ButtonPane.find("#btnDismiss");
1268
+ RESTClient.sfAC($TemplateDocument, "TemplateList", TemplateTypeCode);
1269
+ $TemplateDocument.autocomplete("option", "minLength", 0).autocomplete("option", "delay", 200);
1270
+ $TemplateDocument.data("acPostbackKey", false); // prevent Auto complete from stuffing the key into our input field
1271
+ $TemplateDocument.on("sfAutoCompletedKV sfLookup.Stored", function (e, kv) {
1272
+ console.log(`ExportGrid Template-on:${e.type} = ${kv}`);
1273
+ $TemplateDocument.data("TemplateKey", kv);
1274
+ PostBackArgs = kv;
1275
+ }).on("sfAC.response", function (e, choices) {
1276
+ if (choices.content.length === 1) {
1277
+ var newChoice = choices.content[0];
1278
+ $TemplateDocument.val(newChoice.value).data("acKey", newChoice.key).data("acChange", true);
1279
+ }
1280
+ }).on("focus", () => {
1281
+ $TemplateDocument.autocomplete("search", $TemplateDocument.val());
1282
+ $TemplateDocument.trigger("select");
1283
+ });
1284
+ $TemplateDocument.trigger("focus");
1285
+ RESTClient.AutoSizeDialog($Dialog);
1286
+ return false;
1287
+ }).dialog({
1288
+ title: "Export Competitive Bid Data...",
1289
+ close: function () { $Dialog.dialog('destroy'); }
1290
+ });
1291
+ var DialogButtons = [{
1292
+ text: "Export",
1293
+ "id": "btnExport",
1294
+ click: function () {
1295
+ RESTClient.GADialogEvent("Completed", "ExportCobra");
1296
+ $Dialog.dialog("close");
1297
+ RESTClient.HeyPleaseWait();
1298
+ var api = new _SwaggerClientExports.ExcelToolsClient();
1299
+ api.getCobraExport(PostBackArgs, RESTClient.GetPageContextValue("dsCacheKey")).then(crt => {
1300
+ RESTClient.WaitForTask(crt).then((crtResult) => {
1301
+ RESTClient.ClearPleaseWaitDialog();
1302
+ if (crtResult.ThisReason.indexOf("Failed!") >= 0) {
1303
+ RESTClient.DisplayUserNotification(crtResult.ThisReason);
1304
+ }
1305
+ else {
1306
+ console.log(crtResult.ThisReason);
1307
+ var fn = "CobraExport.xlsx";
1308
+ if (crtResult.ThisReason?.startsWith("{")) {
1309
+ var result = JSON.parse(crtResult.ThisReason);
1310
+ if (result.fn)
1311
+ fn = result.fn;
1312
+ }
1313
+ RESTClient.DisplayUserNotification();
1314
+ window.open(`${top.sfClient._SiteURL}/sfImg.ashx/CRT/${crt}/${fn}`);
1315
+ }
1316
+ });
1317
+ });
1318
+ //setTimeout("top.sfClient.ClearPleaseWaitDialog(); if (top.sfClient.ClearInClientSidePostbackFlag) top.sfClient.ClearInClientSidePostbackFlag(); //ExportCobra", 2345);
1319
+ }
1320
+ }, {
1321
+ "id": "btnDismiss",
1322
+ text: "Dismiss",
1323
+ click: function () {
1324
+ $Dialog.dialog("close");
1325
+ }
1326
+ }];
1327
+ $Dialog.dialog('option', 'buttons', DialogButtons);
1328
+ //return false; // do not return false, IE anchor href issue
1329
+ }
1330
+ HeyPleaseWait() {
1331
+ // this variant applies inside a frame
1332
+ if (sfRestClient.$CurrentPleaseWaitDialog && sfRestClient.$CurrentPleaseWaitDialog.is(":visible"))
1333
+ return sfRestClient.$CurrentPleaseWaitDialog;
1334
+ sfRestClient.$CurrentPleaseWaitDialog = this.jqAlert("Please wait...", "Working", "ui-icon-locked").addClass("sfPleaseWait");
1335
+ var DialogButtons = {};
1336
+ if (DialogButtons)
1337
+ sfRestClient.$CurrentPleaseWaitDialog.dialog('option', 'buttons', DialogButtons);
1338
+ return sfRestClient.$CurrentPleaseWaitDialog;
1339
+ }
1340
+ ClearPleaseWaitDialog() {
1341
+ if (sfRestClient.$CurrentPleaseWaitDialog) {
1342
+ sfRestClient.$CurrentPleaseWaitDialog.dialog("close");
1343
+ sfRestClient.$CurrentPleaseWaitDialog = null;
1344
+ }
1345
+ }
1212
1346
  /**
1213
1347
  * Converts traditional .NET date formats to Webix formats
1214
1348
  * @param dotNetFormat something like d or m/d/yyyy
@@ -1550,7 +1684,7 @@ class sfRestClient {
1550
1684
  var UseID;
1551
1685
  var url = sfRestClient._Options.PopNewDocLegacyURL;
1552
1686
  if (options?.indexOf("&UseID")) {
1553
- UseID = options.substr(options?.indexOf("&UseID") + 7, 36);
1687
+ UseID = options.substring(options?.indexOf("&UseID") + 7, 36);
1554
1688
  }
1555
1689
  else
1556
1690
  UseID = await this.NewGuid(); // todo: fix this!!!
@@ -1559,7 +1693,7 @@ class sfRestClient {
1559
1693
  url = url.sfFormat(thisRestClient._SiteURL, dtk, project, options);
1560
1694
  if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
1561
1695
  console.log("PopNewDoc opening {0} DTK {1} using {2}".sfFormat(UseID, dtk, url));
1562
- var TargetTab = UseID.substr(UseID.lastIndexOf("-") + 1).toLowerCase();
1696
+ var TargetTab = UseID.substring(UseID.lastIndexOf("-") + 1).toLowerCase();
1563
1697
  //todo: determine if we need the "how many tabs" logic and dialog
1564
1698
  if (!window) {
1565
1699
  console.error("PopNewDoc() Must be called from a browser window");
@@ -1603,7 +1737,7 @@ class sfRestClient {
1603
1737
  url = url.sfFormat(thisRestClient._SiteURL, id);
1604
1738
  if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
1605
1739
  console.log("PopDoc opening DMK {0} DTK {1} using {2}".sfFormat(id, thisDocType, url));
1606
- var TargetTab = url.substr(url.lastIndexOf("-") + 1).toLowerCase();
1740
+ var TargetTab = url.substring(url.lastIndexOf("-") + 1).toLowerCase();
1607
1741
  //todo: determine if we need the "how many tabs" logic and dialog
1608
1742
  if (!window) {
1609
1743
  console.error("PopDoc() Must be called from a browser window");
@@ -1964,11 +2098,11 @@ class sfRestClient {
1964
2098
  if (pgHash.length > 0)
1965
2099
  pgname = pgHash; // for xb style
1966
2100
  if (pgname.indexOf("/") >= 0)
1967
- pgname = pgname.substr(pgname.lastIndexOf("/") + 1);
2101
+ pgname = pgname.substring(pgname.lastIndexOf("/") + 1);
1968
2102
  if (pgname.indexOf("?") >= 0)
1969
- pgname = pgname.substr(0, pgname.indexOf("?"));
2103
+ pgname = pgname.substring(0, pgname.indexOf("?"));
1970
2104
  if (pgname.indexOf(".") >= 0)
1971
- pgname = pgname.substr(0, pgname.indexOf("."));
2105
+ pgname = pgname.substring(0, pgname.indexOf("."));
1972
2106
  return pgname;
1973
2107
  }
1974
2108
  XBVariantOfPageName(classicPageName) {
@@ -2084,7 +2218,7 @@ class sfRestClient {
2084
2218
  rItem = `Unexpected ${typeof rItemRaw}`;
2085
2219
  sortPad = ((isGuid || (index.endsWith("ID")) || isJS) ? "" : " ");
2086
2220
  if (isJS) {
2087
- rItem = `<i class="fas fa-boxes sfShowPointer" data-js="${rItem.substr(11)}"></i>`;
2221
+ rItem = `<i class="fas fa-boxes sfShowPointer" data-js="${rItem.substring(11)}"></i>`;
2088
2222
  }
2089
2223
  else if (isGuid && rItem !== RESTClient.EmptyKey) {
2090
2224
  rItem = `${rItem} &nbsp;<i class="far fa-clipboard clsEnabledImgBtn" title="Copy" data-text="${rItem}"></i>`;
@@ -2284,7 +2418,7 @@ class sfRestClient {
2284
2418
  var rx = /javascript:(PopMSWindowTool|PopXLTool|PopAuditTool)\(['"`](?<URL>.*)[`'"]\)/gm;
2285
2419
  var match = rx.exec(ActionString);
2286
2420
  if (match && match.groups) {
2287
- targetURL = match.groups.URL;
2421
+ targetURL = this.ExpandActionMarkers(match.groups.URL, rowData);
2288
2422
  }
2289
2423
  else
2290
2424
  console.warn("InvokeAction() could not parse", actionString);
@@ -2295,9 +2429,12 @@ class sfRestClient {
2295
2429
  console.warn("InvokeAction::tools not really done", ActionString);
2296
2430
  top.location.href = ActionString;
2297
2431
  }
2432
+ else if (ActionString.startsWith("javascript:sfExportCompetitiveBidData")) {
2433
+ this.ExportCompetitiveBidData();
2434
+ }
2298
2435
  else if (ActionString.startsWith("javascript:")) {
2299
2436
  try {
2300
- eval(ActionString.substr(11));
2437
+ eval(ActionString.substring(11));
2301
2438
  }
2302
2439
  catch (ex) {
2303
2440
  console.warn("InvokeAction::failed javascript ", ActionString, ex.message);
@@ -2312,10 +2449,14 @@ class sfRestClient {
2312
2449
  console.warn("InvokeAction() could not handle ", actionString);
2313
2450
  }
2314
2451
  }
2452
+ /** Creates an exchange token and calls OpenWindowsLinkHelper() */
2315
2453
  FollowLinkViaSFLink(targetURL, afterOpenArg, autoCloseDoc) {
2316
2454
  var RESTClient = this;
2317
2455
  if (targetURL.endsWith("&Project="))
2318
2456
  targetURL += RESTClient.GetPageProjectKey();
2457
+ console.log(`sfLink(${targetURL})`);
2458
+ if (targetURL.indexOf("$$") > 0)
2459
+ targetURL = this.ExpandActionMarkers(targetURL);
2319
2460
  if (!top?.ClickOnceExtension.HasDotNetApplicationExtension()) {
2320
2461
  var RetryLater = `FollowLinkViaSFLink('${targetURL}'`;
2321
2462
  if (typeof afterOpenArg !== "undefined") {
@@ -2380,7 +2521,7 @@ class sfRestClient {
2380
2521
  /**
2381
2522
  *
2382
2523
  * @param et token passed to sfLink
2383
- * @param afterOpenArg boolean/true: closes document page; false/0: posts back default refresh; ['e','a']: posts back e with a;
2524
+ * @param afterOpenArg boolean/true: closes document page; false/0: posts back default refresh; ['e','a']: posts back e with a; callback function is passed et
2384
2525
  * @param autoCloseDoc
2385
2526
  */
2386
2527
  OpenWindowsLinkHelper(et, afterOpenArg, autoCloseDoc) {
@@ -2401,7 +2542,7 @@ class sfRestClient {
2401
2542
  }
2402
2543
  xscript = `setTimeout(\'${innerScript};\', 242);` + xscript;
2403
2544
  }
2404
- else if (!afterOpenArg || typeof afterOpenArg === "string" || (Array.isArray(afterOpenArg) && afterOpenArg.length === 2)) {
2545
+ else if (typeof self.PostbackRefresh === "function" && !afterOpenArg || typeof afterOpenArg === "string" || (Array.isArray(afterOpenArg) && afterOpenArg.length === 2)) {
2405
2546
  if (!afterOpenArg)
2406
2547
  afterOpenArg = "ibtnRefreshAttachList";
2407
2548
  var pbArg = 'AfterPopFVC';
@@ -2419,18 +2560,28 @@ class sfRestClient {
2419
2560
  xscript = `setTimeout(\'${innerScript};\', ${innerDelay});` + xscript;
2420
2561
  setTimeout(xscript, 211);
2421
2562
  }
2422
- /** Finds $$ and other replacable values */
2563
+ /** Finds $$ markers and other replacable values.
2564
+ * Markers are case sensative
2565
+ */
2423
2566
  ExpandActionMarkers(rawAction, rowData) {
2424
2567
  if (rawAction.indexOf("$$PROJECT") >= 0) {
2425
2568
  rawAction = rawAction.replaceAll("$$PROJECT", this.GetActionMarkerReplacement("$$PROJECT", rowData));
2426
2569
  }
2570
+ if (rawAction.indexOf("$$PDSKEY") >= 0) {
2571
+ rawAction = rawAction.replaceAll("$$PDSKEY", this.GetActionMarkerReplacement("$$PDSKEY", rowData));
2572
+ }
2427
2573
  // general case: regex??
2428
2574
  return rawAction;
2429
2575
  }
2576
+ /** given a mark name, returns the replacement value
2577
+ * @see ExpandActionMarkers()
2578
+ * @argument markerName such as $$Project or $$PDSKEY
2579
+ * @argument rowData object containing row data - first source for value to replace the marker
2580
+ */
2430
2581
  GetActionMarkerReplacement(markerName, rowData) {
2431
2582
  var result = "";
2432
2583
  if (markerName.startsWith("$$")) {
2433
- markerName = markerName.substr(2, 1) + markerName.substr(3).toLowerCase();
2584
+ markerName = markerName.substring(2, 3) + markerName.substring(3).toLowerCase();
2434
2585
  }
2435
2586
  if (rowData)
2436
2587
  result = this.FieldValueFromRow(rowData, markerName);
@@ -2438,6 +2589,9 @@ class sfRestClient {
2438
2589
  if (markerName === "Project") {
2439
2590
  result = this.GetPageProjectKey();
2440
2591
  }
2592
+ if (markerName === "Pdskey") {
2593
+ result = this.GetPageContextValue("dsCacheKey");
2594
+ }
2441
2595
  }
2442
2596
  return result;
2443
2597
  }
@@ -2590,14 +2744,13 @@ class sfRestClient {
2590
2744
  });
2591
2745
  }
2592
2746
  GAMonitorPageHit(propertyID, clientID, url, title) {
2593
- if (sfRestClient.GAPageHitSent)
2594
- return;
2747
+ var RESTClient = this;
2595
2748
  if (!url) {
2596
- url = `${top?.location?.origin}${top?.location?.pathname}`;
2597
- var s = top?.location.search;
2598
- if (s && (!s.startsWith("?id=")) && (!s.startsWith("?add=")))
2599
- url = url + s;
2749
+ url = `${RESTClient._SiteURL}/${RESTClient.ResolvePageName()}`;
2600
2750
  }
2751
+ var pageHash = url.sfHashCode();
2752
+ if (sfRestClient.GALastPageHitSent === pageHash)
2753
+ return;
2601
2754
  if (!title)
2602
2755
  title = $('title').text();
2603
2756
  var payload = {
@@ -2608,14 +2761,14 @@ class sfRestClient {
2608
2761
  dl: url,
2609
2762
  dt: title,
2610
2763
  };
2611
- sfRestClient.GAPageHitSent = true;
2764
+ sfRestClient.GALastPageHitSent = pageHash;
2612
2765
  return this.GAMonitorSend(payload)
2613
2766
  .done(function (data, textStatus, jqXHR) {
2614
- console.log(`GAMonitor(pageview:${top?.location.pathname}) ok`);
2767
+ console.log(`GAMonitor(pageview:${url}) ok`);
2615
2768
  })
2616
2769
  .fail(function (jqXHR, textStatus) {
2617
2770
  sfRestClient.GAMonitorSendFailed = true;
2618
- console.warn(`GAMonitor(pgvw:${top?.location.pathname}) failed: ${jqXHR.responseText} `);
2771
+ console.warn(`GAMonitor(pgvw:${url}) failed: ${jqXHR.responseText} `);
2619
2772
  });
2620
2773
  }
2621
2774
  GAMonitorSend(payload) {
@@ -2634,8 +2787,7 @@ class sfRestClient {
2634
2787
  });
2635
2788
  }
2636
2789
  GAMonitorEvent(propertyID, clientID, category, action, label, value) {
2637
- if (!sfRestClient.GAPageHitSent)
2638
- this.GAMonitorPageHit(propertyID, clientID);
2790
+ this.GAMonitorPageHit(propertyID, clientID);
2639
2791
  var payload = {
2640
2792
  v: 1,
2641
2793
  t: "event",
@@ -2648,13 +2800,14 @@ class sfRestClient {
2648
2800
  };
2649
2801
  return this.GAMonitorSend(payload)
2650
2802
  .done(function (data, textStatus, jqXHR) {
2651
- console.log(`GAMonitor(${category}:${action}) ok`);
2803
+ console.log(`GAMonitor(${category}:${action}) ${label} ok`);
2652
2804
  })
2653
2805
  .fail(function (jqXHR, textStatus) {
2654
2806
  console.warn(`GAMonitor(${category}:${action}) failed: ${jqXHR.responseText}`);
2655
2807
  });
2656
2808
  }
2657
- sfGAEvent(category, action, label, value) {
2809
+ /** Google Analytics Event */
2810
+ GAEvent(category, action, label, value) {
2658
2811
  if (!value)
2659
2812
  value = 0;
2660
2813
  if (sfRestClient._WCC.GAFMOptOut) {
@@ -2663,8 +2816,9 @@ class sfRestClient {
2663
2816
  }
2664
2817
  this.GAMonitorEvent('UA-6465434-4', sfRestClient._WCC.SiteID, category, action, label, value);
2665
2818
  }
2666
- sfGADialogEvent(action, dialogName) {
2667
- this.sfGAEvent("Dialog", action, dialogName, 1);
2819
+ /** Shortcut that calls GAEvent("dialog",action, dialogName,1) */
2820
+ GADialogEvent(action, dialogName) {
2821
+ this.GAEvent("Dialog", action, dialogName, 1);
2668
2822
  }
2669
2823
  ValueHasWildcard(theVal) {
2670
2824
  if (theVal.indexOf("%") >= 0)
@@ -3448,10 +3602,10 @@ class sfRestClient {
3448
3602
  var ThisSuffix = "_dv";
3449
3603
  if (item.DataField?.startsWith("cmp_")) {
3450
3604
  // cmp_realfieldname_suffix
3451
- var RealFieldName = item.DataField.substr(4);
3605
+ var RealFieldName = item.DataField.substring(4);
3452
3606
  if (!(RealFieldName in rawRow)) {
3453
3607
  if (RealFieldName.indexOf("_") > 0)
3454
- RealFieldName = RealFieldName.substr(0, RealFieldName.indexOf("_"));
3608
+ RealFieldName = RealFieldName.substring(0, RealFieldName.indexOf("_"));
3455
3609
  if ((RealFieldName in rawRow && !(item.DataField in rawRow))) {
3456
3610
  var FieldValue = thisPart.RestClient.FieldValueFromRow(rawRow, RealFieldName);
3457
3611
  var DependsOn;
@@ -4037,7 +4191,7 @@ sfRestClient._Options = {
4037
4191
  ProjectXBURL: '{0}/spax.html#!/main/projectDashboard?project={1}',
4038
4192
  TaskStatePollInterval: 357
4039
4193
  };
4040
- sfRestClient.GAPageHitSent = false; // !!! this needs work
4194
+ sfRestClient.GALastPageHitSent = 0; // !!! this needs work
4041
4195
  sfRestClient.GAMonitorSendFailed = false;
4042
4196
  sfRestClient.$LookupDialogStack = [];
4043
4197
  sfRestClient._NextPingTimerID = undefined;