Skip to content

Commit

Permalink
confirmActionFunc is not supporting a compromised async function fix #…
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewtelnov authored Aug 18, 2023
1 parent 221ef84 commit 67adc7c
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 31 deletions.
1 change: 1 addition & 0 deletions src/entries/chunks/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export {
export { IsMobile, IsTouch, _setIsTouch } from "../../utils/devices";
export {
confirmAction,
confirmActionAsync,
detectIEOrEdge,
doKey2ClickUp,
doKey2ClickDown,
Expand Down
10 changes: 1 addition & 9 deletions src/knockout/koquestion_file.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import * as ko from "knockout";
import { Serializer,
Question,
QuestionFactory,
QuestionFileModel,
getOriginalEvent,
confirmAction,
detectIEOrEdge,
loadFileFromBase64
} from "survey-core";
import { Serializer, QuestionFactory, QuestionFileModel, getOriginalEvent } from "survey-core";
import { QuestionImplementor } from "./koquestion";

class QuestionFileImplementor extends QuestionImplementor {
Expand Down
20 changes: 11 additions & 9 deletions src/question_file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { property, propertyArray, Serializer } from "./jsonobject";
import { QuestionFactory } from "./questionfactory";
import { EventBase } from "./base";
import { UploadingFileError, ExceedSizeError } from "./error";
import { surveyLocalization } from "./surveyStrings";
import { SurveyError } from "./survey-error";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { confirmAction, detectIEOrEdge, loadFileFromBase64 } from "./utils/utils";
import { confirmActionAsync, detectIEOrEdge, loadFileFromBase64 } from "./utils/utils";
import { ActionContainer } from "./actions/container";
import { Action } from "./actions/action";
import { Helpers } from "./helpers";
Expand Down Expand Up @@ -582,23 +581,26 @@ export class QuestionFileModel extends Question {
this.onChange(src);
}
doClean = (event: any) => {
var src = event.currentTarget || event.srcElement;
if (this.needConfirmRemoveFile) {
var isConfirmed = confirmAction(this.confirmRemoveAllMessage);
if (!isConfirmed) return;
confirmActionAsync(this.confirmRemoveAllMessage, () => { this.clearFilesCore(); });
return;
}
this.clearFilesCore();
}
private clearFilesCore(): void {
if(this.rootElement) {
this.rootElement.querySelectorAll("input")[0].value = "";
}
this.clear();
}
doRemoveFile(data: any) {
if (this.needConfirmRemoveFile) {
var isConfirmed = confirmAction(
this.getConfirmRemoveMessage(data.name)
);
if (!isConfirmed) return;
confirmActionAsync(this.getConfirmRemoveMessage(data.name), () => { this.removeFileCore(data); });
return;
}
this.removeFileCore(data);
}
private removeFileCore(data: any): void {
const previewIndex = this.previewValue.indexOf(data);
this.removeFileByContent(previewIndex === -1 ? data : this.value[previewIndex]);
}
Expand Down
10 changes: 8 additions & 2 deletions src/question_matrixdynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { SurveyError } from "./survey-error";
import { MinRowCountError } from "./error";
import { IAction } from "./actions/action";
import { settings } from "./settings";
import { confirmAction } from "./utils/utils";
import { confirmActionAsync } from "./utils/utils";
import { DragDropMatrixRows } from "./dragdrop/matrix-rows";
import { IShortcutText, ISurveyImpl, IProgressInfo } from "./base-interfaces";
import { CssClassBuilder } from "./utils/cssClassBuilder";
Expand Down Expand Up @@ -566,7 +566,13 @@ export class QuestionMatrixDynamicModel extends QuestionMatrixDropdownModelBase
if(confirmDelete === undefined) {
confirmDelete = this.isRequireConfirmOnRowDelete(index);
}
if (confirmDelete && !confirmAction(this.confirmDeleteText)) return;
if (confirmDelete) {
confirmActionAsync(this.confirmDeleteText, () => { this.removeRowAsync(index, row); });
return;
}
this.removeRowAsync(index, row);
}
private removeRowAsync(index: number, row: MatrixDropdownRowModelBase): void {
if (!!row && !!this.survey && !this.survey.matrixRowRemoving(this, index, row)) return;
this.onStartRowAddingRemoving();
this.removeRowCore(index);
Expand Down
12 changes: 6 additions & 6 deletions src/question_paneldynamic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { HashTable, Helpers } from "./helpers";

import {
IElement,
IQuestion,
Expand All @@ -11,8 +10,7 @@ import {
IProgressInfo,
} from "./base-interfaces";
import { SurveyElement } from "./survey-element";
import { surveyLocalization } from "./surveyStrings";
import { ILocalizableOwner, LocalizableString } from "./localizablestring";
import { LocalizableString } from "./localizablestring";
import {
TextPreProcessorValue,
QuestionTextProcessor,
Expand All @@ -23,7 +21,7 @@ import { JsonObject, property, Serializer } from "./jsonobject";
import { QuestionFactory } from "./questionfactory";
import { KeyDuplicationError } from "./error";
import { settings } from "./settings";
import { confirmAction, mergeValues } from "./utils/utils";
import { confirmActionAsync } from "./utils/utils";
import { SurveyError } from "./survey-error";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { ActionContainer } from "./actions/container";
Expand Down Expand Up @@ -1236,9 +1234,11 @@ export class QuestionPanelDynamicModel extends Question
* @see canRemovePanel
*
*/
public removePanelUI(value: any) {
public removePanelUI(value: any): void {
if (!this.canRemovePanel) return;
if (!this.confirmDelete || confirmAction(this.confirmDeleteText)) {
if(this.confirmDelete) {
confirmActionAsync(this.confirmDeleteText, () => { this.removePanel(value); });
} else {
this.removePanel(value);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ export var settings = {
confirmActionFunc: function (message: string): boolean {
return confirm(message);
},
/**
* A property that allows you to display a custom confirm dialog in async mode instead of the standard browser dialog. Set this property to a function that renders your custom dialog window in async mode.
* @param message A message to be displayed in the confirm dialog window.
* @param callback A callback function that should be called with res paramter equals to true if action is confirmed and equals to false otherwise.
*/
confirmActionAsyncFunc: function (message: string, resFunc: (res: boolean) => void): boolean {
//when you finish with displaying your dialog, call the resFunc as resFunc(true) or resFunc(false).
//You should return true to tell that you use this function
return false;
},
/**
* A minimum width value for all survey elements.
*
Expand Down
19 changes: 15 additions & 4 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@ function confirmAction(message: string): boolean {
return settings.confirmActionFunc(message);
return confirm(message);
}
function detectIEBrowser() {
function confirmActionAsync(message: string, funcOnYes: () => void, funcOnNo?: () => void): void {
const callbackFunc = (res: boolean): void => {
if(res) funcOnYes();
else if(!!funcOnNo) funcOnNo();
};
if(!!settings && !!settings.confirmActionAsyncFunc) {
if(settings.confirmActionAsyncFunc(message, callbackFunc)) return;
}
callbackFunc(confirmAction(message));
}
function detectIEBrowser(): boolean {
if (typeof window === "undefined") return false;
const ua: string = window.navigator.userAgent;
const oldIe: number = ua.indexOf("MSIE ");
const elevenIe: number = ua.indexOf("Trident/");
return oldIe > -1 || elevenIe > -1;
}
function detectIEOrEdge() {
function detectIEOrEdge(): boolean {
if (typeof window === "undefined") return false;
if (typeof (<any>detectIEOrEdge).isIEOrEdge === "undefined") {
const ua: string = window.navigator.userAgent;
Expand All @@ -37,7 +47,7 @@ function detectIEOrEdge() {
}
return (<any>detectIEOrEdge).isIEOrEdge;
}
function loadFileFromBase64(b64Data: string, fileName: string) {
function loadFileFromBase64(b64Data: string, fileName: string): void {
try {
const byteString: string = atob(b64Data.split(",")[1]);

Expand All @@ -64,7 +74,7 @@ function loadFileFromBase64(b64Data: string, fileName: string) {
}
} catch (err) { }
}
function isMobile() {
function isMobile(): boolean {
return (
typeof window !== "undefined" && typeof window.orientation !== "undefined"
);
Expand Down Expand Up @@ -392,6 +402,7 @@ export {
classesToSelector,
compareVersions,
confirmAction,
confirmActionAsync,
detectIEOrEdge,
detectIEBrowser,
loadFileFromBase64,
Expand Down
31 changes: 30 additions & 1 deletion tests/question_matrixdynamictests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8709,4 +8709,33 @@ QUnit.test("Errors: matrixdropdown + mobile mode", function (assert) {
assert.equal(table.rows[0].cells[2].hasQuestion, true);
assert.equal(table.rows[0].cells[3].isErrorsCell, true);
assert.equal(table.rows[0].cells[4].hasQuestion, true);
});
});
QUnit.test("matrixdynamic.removeRow & confirmActionAsyncFunc, #6736", function (assert) {
const prevAsync = settings.confirmActionAsyncFunc;

const survey = new SurveyModel({
elements: [
{ type: "matrixdynamic", name: "matrix",
columns: [{ name: "col1" }]
}
]
});
const q = <QuestionMatrixDynamicModel>survey.getQuestionByName("matrix");
q.value = [{ col1: 1 }, { col1: 2 }, { col1: 3 }];
let f_resFunc = (res: boolean): void => {};
settings.confirmActionAsyncFunc = (message: string, resFunc: (res: boolean) => void): boolean => {
f_resFunc = resFunc;
return true;
};
q.removeRow(1, true);
assert.equal(q.visibleRows.length, 3, "We are waiting for async function");
f_resFunc(false);
assert.equal(q.visibleRows.length, 3, "confirm action return false");
q.removeRow(1, true);
assert.equal(q.visibleRows.length, 3, "We are waiting for async function, #2");
f_resFunc(true);
assert.equal(q.visibleRows.length, 2, "confirm action return true");
assert.deepEqual(q.value, [{ col1: 1 }, { col1: 3 }], "Row is deleted correctly");

settings.confirmActionAsyncFunc = prevAsync;
});
37 changes: 37 additions & 0 deletions tests/surveypaneldynamictests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5979,3 +5979,40 @@ QUnit.test("Make sure that panel is not collapsed on focusing the question", fun
q2.focus();
assert.equal(survey.currentPageNo, 1);
});
QUnit.test("paneldynamic.removePanelUI & confirmActionAsyncFunc, #6736", function(assert) {
const prevAsync = settings.confirmActionAsyncFunc;
const survey = new SurveyModel({
questions: [
{
type: "paneldynamic",
name: "panel1",
templateElements: [
{
type: "text",
name: "q1",
},
]
},
],
});
const panel = <QuestionPanelDynamicModel>survey.getQuestionByName("panel1");
panel.confirmDelete = true;
panel.value = [{ q1: 1 }, { q1: 2 }, { q1: 3 }];
assert.equal(panel.panelCount, 3, "There are 3 panels by default");
let f_resFunc = (res: boolean): void => {};
settings.confirmActionAsyncFunc = (message: string, resFunc: (res: boolean) => void): boolean => {
f_resFunc = resFunc;
return true;
};
panel.removePanelUI(1);
assert.equal(panel.panelCount, 3, "We are waiting for async function");
f_resFunc(false);
assert.equal(panel.panelCount, 3, "confirm action return false");
panel.removePanelUI(1);
assert.equal(panel.panelCount, 3, "We are waiting for async function, #2");
f_resFunc(true);
assert.equal(panel.panelCount, 2, "confirm action return true");
assert.deepEqual(panel.value, [{ q1: 1 }, { q1: 3 }], "Row is deleted correctly");

settings.confirmActionAsyncFunc = prevAsync;
});

0 comments on commit 67adc7c

Please sign in to comment.