{"version":3,"file":"viewform.js","names":["offlineIdUrlParam","window","location","href","match","OFFLINEID","matrixQuestionId","matrixLineId","indexParam","isMatrix","isSurvey","formWidth","globalInit","selectedAction","matrixUndo","changedDrawings","matrixAttachments","matrixActions","savedFormData","uploadFileTrackers","matrixConfig","dataBound","resetMatrixHeader","resizable","selectable","change","handleMatrixSelect","matrixConnectedFormsConfig","filterable","gridFilterOptions","filterMenuInit","leanformsNext","updateSearchFilterMenu","sortable","datePickerMap","datepicker","datetimepicker","timepicker","typeIdMap","closeDropdownMenu","e","$this","$parent","$","find","attr","removeClass","remove","document","off","toggleDropdownMenu","this","parent","hasClass","loadPartIndex","documentElement","createElement","addClass","insertAfter","on","setTimeout","scrollToElement","element","animationDuration","destinationOffset","offset","top","headerHeight","outerHeight","scrollContainer","newScrollTop","animate","scrollTop","list","children","each","$part","partTitle","text","partId","el","li","append","openInfo","q","title","length","data","toFront","infoContent","html","css","kendoWindow","width","getPopupWidth","height","getPopupHeight","resources","viewForm","extraInfo","visible","modal","close","deactivate","destroy","center","open","delForm","formControls","showOldConfirmDialog","confirmDeleteTitle","confirmDeleteMessage","dialogResult","serializedFormData","val","form","querySelector","setAttribute","baseUrl","FORMGUID","submit","toggleFavorite","stopImmediatePropagation","action","ajax","url","BASEID","type","error","onAjaxError","success","toggleClass","label","currentText","newText","resetDatasetLinkbuttons","$body","keyCode","target","nodeName","preventDefault","duplicateMatrixLine","focus","isIosPWA","createUploads","FORMID","initActionsGrid","id","core","supportsOffline","hide","indexedDatabase","formTypeUsers","where","userId","USERID","formTypeBaseId","first","formTypeOffline","toggleOfflineLabel","navigator","onLine","after","datasetOfflineWarning","$title","stepChanged","showRoute","win","routeTitle","iframe","content","reload","addEventListener","printHelper","before","needsHelperButtons","test","userAgent","toggleCheckbox","a","b","prop","copyCheckbox","src","trg","beforesave","update","split","k","item","is","removeAttr","handleCopyCheckbox","copyFields","sourceId","targetList","isBeforeSave","sourceElement","sourceValue","targetIds","i","targetId","targetElement","loadData","isFieldCopy","name","value","handleCopy","body","input","prevAll","add","vanillaInput","get","cursorPosition","selectionStart","beforeCursor","slice","afterCursor","newCursorPosition","setSelectionRange","attachmentDrawing","attachmentContainer","htmlEls","attachmentDrawingContainer","canvasElement","sizeHelper","undoButton","closeButton","confirmButton","colorButtons","defaultColorButton","eq","metaViewport","sidebarToggle","canvas","fabric","Canvas","renderOnAddRemove","selection","isDrawingMode","onUploadError","setColor","colorButton","color","freeDrawingBrush","resizeCanvas","scale","getWidth","zoom","getZoom","setDimensions","setViewportTransform","upperCanvasEl","closeDrawing","toggleZoomPrevention","removeEventListener","passive","async","closeAttachmentDrawing","getObjects","dataUri","toDataURL","format","imageBlob","dataUriToBlob","fetch","then","res","blob","file","File","offlineId","db","attachments","catch","err","handlePotentialQuotaError","handleError","wasSaved","previousGuid","filename","questionId","removeData","attachmentFormData","FormData","method","uploadUrl","uploadXHR","XMLHttpRequest","onUploadLoadFactory","container","onUploadLoad","status","uploaded","JSON","parse","response","newGuid","guid","link","displayName","newUrl","encodeURIComponent","send","disableZoomPrevention","state","preventScale","hasPreventScale","includes","replace","toggle","lastItemIndex","renderAll","openAttachmentDrawing","clear","imageElement","naturalWidth","naturalHeight","fabricImage","Image","setWidth","setHeight","removeAttachment","previewContainer","isCopy","isNewAttachment","isSavedMatrix","fileName","URL","revokeObjectURL","delete","showConfirmDialog","confirmRemoveAttachment","removeCopiedAttachments","uploadInputs","forEach","isDisabled","kendoUpload","saveUrl","autoUpload","localization","select","attachmentsButtonText","dropFilesHere","attachmentsDropText","files","push","uid","showFileList","enabled","onUploadSuccess","getDisplayName","currentName","existingNames","map","attachment","checkName","indexOf","extensionIndex","lastIndexOf","extension","newName","operation","createPreviewElement","rawFile","sender","kendoFile","isImage","getOfflineUrl","previewEl","offline","options","step","offlineFormSaved","isDatasetValue","datasetField","buttons","drawOnAttachmentText","attachmentPreview","preview","img","endsWith","iconSuffix","getFileIconSuffix","icon","filenameText","download","button","currentStep","tracker","index","jElement","copyForm","copyPopupTitle","FORMTYPEID","loadCopyData","dataToCopy","showMatrixAttachmentNotification","loadXml","notify","copySuccessMessage","matrixAttachmentCopyWarning","updateLock","release","noop","releaseLock","checkRequired","isValid","elements","querySelectorAll","isVisible","attachmentsDivId","getAttribute","innerHTML","validation","hideError","showError","validationMessages","required","hasAttribute","resetButton","parentNode","resetButtonIsVisible","resetButtonIsEnabled","trim","closest","matrixIsVisible","matrixIsEnabled","checkDatasetUsername","callback","usernames","contentType","stringify","invalidUsername","isValidUsername","showUsernameDeclineDialog","xhr","readyState","username","usernameDeclinedWindow","datasetUsernameTexts","draggable","htmlEncode","buttonLabel","delayedResetMatrixHeader","thead","hideColumn","selected","rowNumber","isTotal","RegExp","wrapper","toggleMatrix","tableId","showText","hideText","$link","isAllVisible","table","matrixGrid","$headerCell","showColumn","initMatrixPopup","openMatrixPopup","matrixFormTypeId","mode","dataInput","canEdit","params","matrix","lines","matrixDiv","join","popup","setOptions","getDisplayValue","questionEl","getElementById","isRadioButton","radioLabelId","textContent","tagName","toLowerCase","selectedOption","listId","checkboxLabelId","getMatrixLineData","line","lineFields","formData","serializeArray","questionIdRegex","filter","question","questionData","field","createElementNS","getMatrixLineAttachments","needsDrawingUpdate","substring","drawingData","datasetValue","json","setMatrixXMLToInput","jqXML","removeMatrixLine","dataInputId","parseInt","lineLocalAttachments","lineId","lineActions","getMatrixLineActions","confirmRemoveMatrixAction","oid","dataSource","refresh","customRemoveMatrixLine","hasOwnProperty","redrawMatrix","closeMatrixUndo","matrixLineDeletedText","matrixLineUndoButtonText","notification","duration","restoreMatrixLine","localAttachments","hideNotification","prependTo","newLine","clone","getNextLineId","empty","header","fieldId","renewMatrixDialog","saveMatrixLine","keepOpen","toggleSaveButtons","isDataCorrector","validateForm","scrollOffset","customValidation","toggleLoadingOverlay","lineData","parentQuestionId","handleMatrixSave","application","lockScreen","kendo","ui","progress","idElement","parseXML","prepend","nextId","addMatrixLineXml","oldLine","replaceWith","addMatrixActions","actionGrid","clonedAction","extend","questionBase","questionNumber","getMatrixLineNumber","getMatrixAttachments","isOffline","fileData","attachmentData","toUpload","drawings","linesData","lineAttachments","toUploadData","isNew","metadata","isFromMatrix","localGuid","oldGuid","isImageDrawingUpdate","getUsedLists","listIds","selectedList","sendForm","copyReadOnlyFields","usedLists","listsParam","actionsGrid","actionsParam","_data","fullData","concat","saveFormOffline","serverData","fullMatrixAttachmentData","matrixAttachmentData","matrixDrawings","Object","keys","config","hasMissingFiles","hasChangedDrawings","isMissingAny","uploadMissingAttachments","completeAttachments","completeMatrixAttachments","saveFormOnNetwork","resetFormSaveUI","hasBeenSavedOnline","handleFormSaveError","arguments","setProgress","uploadRequests","completedUploads","missingGuids","request","createUploadRequest","drawing","matrixData","totalUploads","upload","abort","console","isDrawing","matrixAttachment","matrixAttachmentDataItem","undefined","Math","round","bind","attachmentsParam","matrixAttachmentsParam","fullStringData","param","dataType","savedId","showExportButtons","updatedMatrices","updatedMatrixId","drawingId","hoverCursor","forEachObject","fabricObject","set","saveSuccessMessage","codeDisplayEl","code","confirmUrl","restartId","setIframeUrl","attachmentPromises","previewElements","offlineAttachments","dbPromise","changedDrawingElements","hasOfflineId","put","flatMatrixAttachments","reduce","accu","updatePromise","Promise","all","attachmentIds","filesAmount","drawingsIndex","fileIds","drawingIds","matrixIds","formTitle","offlineData","formTypeId","baseId","createdAt","Date","now","transaction","forms","returnValue","syncOfflineData","savedOfflineMessage","inner","quotaErrorMessage","saveForm","forward","datasetUsernameValid","exportForm","downloadFile","serialize","defaultFormExport","percent","style","setProperty","previewAttachment","serviceWorker","message","resetRowNumber","renderNumber","loadActions","schema","model","fields","closedByName","description","initiatorId","initiatorName","planDate","remarks","selectedType","statusHtml","informGroupAllowUser","informGroup","transport","read","DataSource","grid","setDataSource","refreshActions","kendoGrid","columns","actionGridTexts","encoded","number","template","dataItem","dating","formatDate","formats","dateDisplayFormat","initiator","user","closedDate","closedBy","hidden","scrollable","firstCells","cell","kendoTooltip","position","refreshActionGrid","saveAction","usersInGroup","isClosed","selectedUser","parseDateString","stateClass","closedById","USERNAME","stateText","ok","statenotsaved","showAction","groupId","toggleActionButtons","closeActionDialog","editAction","deleteAction","badge","parentQuestionNumber","lineNumber","badgeTitle","matrixLineNumber","toggleOffline","installOffline","tryPersistIndexedDB","removeOffline","cacheUrl","markNetworkError","formTypes","lastChange","lists","init","toolbar","matrices","typeSavedOfflineMessage","updateOfflineMenu","noNetworkMessage","typeRemovedOfflineMessage","storage","persist","handleOnChange","vanillaElement","checkMessage","vanillaElementId","substr","fillDependantList","calc","checkRights","changeDependentDate","dispatchEvent","Event","validateRangeState","dateHandleOnChange","dialogCounter","closeMessageDialog","messages","copyToQuestionBase","isFirstMessage","rawMessage","safeMessageHtml","sanitizeHtml","xmlDecode","xmlEncode","next","obj","initialized","copyToQuestion","msg","validateValueCompare","leftValue","operator","rightValue","msgWindow","actions","msgid","msgno","safeMessage","messageContainer","contents","messageTitle","setDateValue","_date","amount","amountfield","unit","fld","fieldAmount","selectedIndex","isNaN","addMinutes","addHours","addDays","addMonths","addYears","dateWidgetType","displayDate","getDate","getMonth","getFullYear","displayTime","pad","getHours","getMinutes","dateType","num","size","s","triggerEl","dateTriggers","isAmountField","dateFields","$sourceEl","targetField","$targetField","sourceBaseId","sourceDate","sourceType","datePickerDate","isDateStr","getDateFromString","vCacheEls","vCache","cached","Array","from","getFormulaValue","str","customDefaultValue","hasCustomDefaultValue","defaultValue","alternateDefaultValue","isArray","selectedRadio","radio","checked","checkedCheckboxes","checkbox","parseFloat","total","toString","optionAlt","numericVal","ex","formulaCache","triggerId","triggeredFormulas","formulaTriggers","callCalculate","formulaId","calculateFormula","f","result","eval","formula","handleFormulaError","decimals","currentVal","toFixed","initFormulaCache","initCalc","erroredFormulas","exception","formulaErrorMessage","rightActions","deny","allow","forcedDeny","validateRights","isCheckbox","triggeredRights","rights","checkedRights","right","matched","values","newAction","uniqueTargetId","targetType","toDelete","getValue","senderData","activeRights","affectedId","rightType","rightAction","affectedType","isPart","isQuestion","isPartDisabled","$trg","matchedForcedDeny","forcedDenyRights","some","applyEvenOddParts","toggleQuestions","not","$q","questionTypes","radioButton","dropdown","toggleQuestion","dateTime","date","toggleDateInput","role","enable","matrixContainer","toggleAttachment","toggleCanvas","$canvas","o","isOdd","initRights","rightsInitOrder","rightsTrigger","lookupDataset","loadDatasetRow","getDataset","formId","isNewForm","dialog","keyField","found","keyValue","trigger","isDataset","datasetData","keyFieldId","formGuid","closeDatasetDialog","canvasMaxWidth","canvases","onResize","currentMaxWidth","parentWidth","wrapperEl","offsetWidth","canvasWidth","getHeight","setBackground","rect","Rect","x","y","fill","stroke","strokeWidth","clearCanvas","createCanvas","readOnly","delayedCreateCanvas","canvasEl","$canvasEl","canDraw","hasDrawing","Boolean","canvasConfig","drawJSONOnCanvas","onCanvasMouseDown","onCanvasPathCreate","loadFromJSON","canvasLoaded","parseDrawing","jsonObject","unbind","lowerCanvasEl","allowDuplicateLine","lineCount","headerCells","hasCode","ri","duplicateButton","duplicateLabel","row","getMatrixCell","outerHTML","hasTotal","totalRow","totalLabel","Number","th","$th","thid","vals","filePath","newTot","calculateTotal","mailA","appendChild","rel","noDrawing","currentTotal","formatIsXml","shouldRunEvents","missingDropdownOption","att","timeSpent","performance","matrixLineXml","loadMatrixLocalAttachments","xmlData","datasetGuidInput","show","formDataUrl","formDataResult","formDataResultText","formDataXml","DOMParser","parseFromString","$formDataXml","startsWith","idx","questionInput","questionValue","questionAttachmentsFromDataset","md","previousSibling","explanation","copiedAttachmentsOfflineExplanation","getElementsByName","localAttachment","isUnsavedMatrix","getCopiedFileObject","questionType","selectRadioId","$dropdown","optionExists","originalOption","removeDatasetAttachments","currentValue","previewCreatorFactory","parsedDate","datePickerType","parentElement","classList","contains","dataset","replicatedValue","fileQuestionId","routeStep","optionNotFoundMessage","log","handleAfterLoad","setFormData","fromDataset","newArray","offlineIds","previewOptions","loadOfflineAttachments","ids","anyOf","attachmentId","createObjectURL","setMatrixToolbar","formContainer","appendTo","tryLoadMatrixData","loadOfflineData","actionParam","loadFormData","eventBroadcaster","broadcast","broadcasts","FORM_SAVED_DATA_LOADED","openForm","guidInput","openConnectedFromMatrix","event","isDirty","saveContinueDialog","dirtyTitle","dirtyText","dirtyFalseLabel","dirtyTrueLabel","dirtyTrueSaveLabel","dialogContent","removeQueryParameter","setQueryParameter","history","replaceState","path","downloadFileBlob","capture","getFormData","uploads","innerText"],"sources":["../../../scripts/leanforms/views/viewform.js"],"sourcesContent":["/*\r\n * Init\r\n */\r\nvar offlineIdUrlParam = window.location.href.match(/&offline-id=(\\d+)/);\r\nvar OFFLINEID = offlineIdUrlParam ? +offlineIdUrlParam[1] : -1;\r\n\r\n// Set by loadData, also used by attachment drawing.\r\nvar matrixQuestionId;\r\nvar matrixLineId;\r\n\r\nvar indexParam = window.location.href.match(/&index=(-?\\d+)/);\r\nvar isMatrix = !!indexParam;\r\nvar isSurvey = false;\r\n\r\nvar formWidth;\r\nvar globalInit = true;\r\nvar selectedAction = null;\r\nvar matrixUndo = null;\r\nvar changedDrawings = {};\r\nvar matrixAttachments = {};\r\nvar matrixActions = [];\r\n\r\nlet savedFormData;\r\n\r\n// Object to track files for each upload control.\r\nconst uploadFileTrackers = {};\r\n\r\nvar matrixConfig = {\r\n dataBound: resetMatrixHeader,\r\n resizable: true,\r\n selectable: true,\r\n change: handleMatrixSelect\r\n};\r\n\r\nvar matrixConnectedFormsConfig = {\r\n resizable: true,\r\n filterable: gridFilterOptions,\r\n filterMenuInit: leanformsNext.updateSearchFilterMenu,\r\n sortable: true,\r\n selectable: false \r\n};\r\n\r\nconst datePickerMap = {\r\n \"datepicker\": \"kendoDatePicker\",\r\n \"datetimepicker\": \"kendoDateTimePicker\",\r\n \"timepicker\": \"kendoTimePicker\"\r\n};\r\n\r\nconst typeIdMap = {\r\n \"0\": \"date\",\r\n \"1\": \"time\",\r\n \"2\": \"both\"\r\n};\r\n\r\nresetDatasetLinkbuttons();\r\n\r\n$(function () {\r\n formWidth = $(document).width();\r\n\r\n var $body = $(\"body\");\r\n\r\n $body.on(\"keydown\", function (e) {\r\n if (e.keyCode == 13 && (e.target.nodeName !== \"A\" && e.target.nodeName !== \"TEXTAREA\" && $(e.target).attr(\"data-role\") !== \"upload\")) {\r\n e.preventDefault();\r\n }\r\n });\r\n\r\n $(\".matrix-div\").on(\"click\", \".matrix-duplicate-button\", duplicateMatrixLine);\r\n document.documentElement.focus();\r\n\r\n if (leanformsNext.isIosPWA) {\r\n $body.addClass(\"is-ios-pwa\");\r\n }\r\n\r\n setTimeout(function () {\r\n createUploads();\r\n }, 0);\r\n\r\n if (FORMID !== \"-1\") {\r\n setTimeout(function () {\r\n initActionsGrid({ id: FORMID });\r\n }, 0);\r\n }\r\n\r\n if (!core.supportsOffline() || $body.hasClass(\"frame-master\")) {\r\n $(\"#btnToggleOffline\").parent().hide();\r\n }\r\n else {\r\n if (!isMatrix) {\r\n indexedDatabase.formTypeUsers\r\n .where({ userId: USERID, formTypeBaseId: +BASEID })\r\n .first(function (formTypeOffline) {\r\n if (formTypeOffline) {\r\n toggleOfflineLabel();\r\n }\r\n });\r\n }\r\n\r\n if (!window.navigator.onLine) {\r\n $(\"button.dataset\").parent().after(\"
\" + resources.datasetOfflineWarning + \"
\");\r\n }\r\n }\r\n});\r\n\r\n/**\r\n * Submenu\r\n */\r\nfunction closeDropdownMenu(e, $this, $parent) {\r\n $parent = $parent || $(\".btn-group.open\");\r\n $this = $this || $parent.find(\"button.dropdown-toggle\");\r\n\r\n $this.attr(\"aria-expanded\", \"true\")\r\n $parent.removeClass(\"open\");\r\n\r\n $(\".dropdown-backdrop\").remove();\r\n $(document).off(\"click\", closeDropdownMenu);\r\n}\r\n\r\nfunction toggleDropdownMenu() {\r\n var $this = $(this);\r\n var $parent = $this.parent();\r\n var isOpen = $parent.hasClass(\"open\");\r\n\r\n if (!isOpen) {\r\n if ($parent.hasClass(\"part-index-menu\")) {\r\n loadPartIndex();\r\n }\r\n\r\n if (\"ontouchstart\" in document.documentElement) {\r\n // If mobile we use a backdrop because click events don't delegate on iOS.\r\n $(document.createElement(\"div\"))\r\n .addClass(\"dropdown-backdrop\")\r\n .insertAfter($this)\r\n .on(\"click\", closeDropdownMenu);\r\n }\r\n\r\n closeDropdownMenu();\r\n\r\n $this.attr(\"aria-expanded\", \"true\")\r\n $parent.addClass(\"open\");\r\n\r\n setTimeout(function () {\r\n $(document).on(\"click\", closeDropdownMenu);\r\n }, 0);\r\n }\r\n else {\r\n closeDropdownMenu(null, $this, $parent);\r\n }\r\n}\r\n\r\n$(\".dropdown-toggle\").on(\"click\", toggleDropdownMenu);\r\n$(\".dropdown-menu a\").on(\"click\", function (e) {\r\n e.preventDefault();\r\n});\r\n\r\n/*\r\n * Part index menu\r\n */\r\nfunction scrollToElement(element, animationDuration) {\r\n var destinationOffset = element.offset().top;\r\n var headerHeight = $(\".js-fixed-header\").outerHeight();\r\n var scrollContainer = $(\"html, body\");\r\n var newScrollTop = destinationOffset - headerHeight;\r\n\r\n if (animationDuration > 0) {\r\n scrollContainer.animate({\r\n scrollTop: newScrollTop\r\n }, animationDuration);\r\n }\r\n else {\r\n scrollContainer.scrollTop(newScrollTop - 10);\r\n }\r\n}\r\n\r\n$(\".part-index-menu\").on(\"click\", \"a\", function (e) {\r\n e.preventDefault();\r\n\r\n var partId = $(this).attr(\"href\");\r\n var $part = $(partId);\r\n\r\n var $title = $part.find(\".formpart-title\");\r\n $title.removeClass(\"scroll-reached\");\r\n\r\n scrollToElement($part, 300);\r\n\r\n setTimeout(function () {\r\n $title.addClass(\"scroll-reached\");\r\n }, 200);\r\n});\r\n\r\nfunction loadPartIndex() {\r\n var list = $(\".part-index-list\");\r\n list.children(\":not(.dropdown-divider-header)\").remove();\r\n\r\n $(\".form .formpart\").each(function () {\r\n var $part = $(this);\r\n\r\n if ($part.hasClass(\"hidden\") || $part.hasClass(\"js-index-hidden\")) {\r\n return;\r\n }\r\n\r\n var partTitle = $part.find(\"h2\").text();\r\n var partId = $part.attr(\"id\");\r\n\r\n var el = $(\"\");\r\n el.attr(\"href\", \"#\" + partId);\r\n el.text(partTitle);\r\n\r\n var li = $(\"\");\r\n li.append(el);\r\n\r\n list.append(li);\r\n })\r\n}\r\n\r\n/*\r\n * Misc UI actions\r\n */\r\nfunction openInfo(q, title) {\r\n if ($(\"#dialog_\" + q).length > 0) {\r\n $(\"#dialog_\" + q).data(\"kendoWindow\").toFront();\r\n return false;\r\n }\r\n\r\n // Sanitized server side.\r\n var infoContent = $(\"#info_\" + q).html();\r\n $(\"#windowcontainer\").append(\"\").addClass(\"print-textarea form-control visible-print-block\").text(text);\r\n $(this).before(printHelper);\r\n });\r\n});\r\n\r\n/*\r\n * Numeric input touchscreen helper buttons.\r\n */\r\nvar needsHelperButtons = /Android|iPhone|iPod/i.test(window.navigator.userAgent);\r\n$(document.body).toggleClass(\"show-helper-buttons\", needsHelperButtons);\r\n$(\".numeric-helper-button\").on(\"click\", function () {\r\n var input = $(this).prevAll(\"input\");\r\n var add = $(this).text();\r\n var val = input.val();\r\n var vanillaInput = input.get(0)\r\n var cursorPosition = vanillaInput.selectionStart;\r\n var beforeCursor = val.slice(0, cursorPosition);\r\n var afterCursor = val.slice(cursorPosition);\r\n var newCursorPosition = cursorPosition + 1;\r\n\r\n input.val(beforeCursor + add + afterCursor).focus();\r\n vanillaInput.setSelectionRange(newCursorPosition, newCursorPosition);\r\n});\r\n\r\n/*\r\n * Checkbox un-check helper.\r\n */\r\nfunction toggleCheckbox(a, b) {\r\n $(\"#\" + b).prop(\"checked\", !$(a).prop(\"checked\"));\r\n}\r\n\r\n/*\r\n * Copy from other question.\r\n */\r\nfunction copyCheckbox(src, trg, beforesave) {\r\n var target = trg.split(\";\")\r\n var update;\r\n\r\n for (var k = 0; k < target.length; k++) {\r\n update = true;\r\n\r\n if (beforesave) {\r\n update = false;\r\n\r\n $(\"input[name^=\\\"\" + src + \"#\\\"]\").each(function () {\r\n var item = $(this).attr(\"name\").split(\"#\")[1];\r\n\r\n if ($(\"input[name^=\\\"\" + target[k] + \"#\" + item + \"\\\"]\").is(\":disabled\")) {\r\n $(\"input[name^=\\\"\" + target[k] + \"#\" + item + \"\\\"]\").removeAttr(\"disabled\");\r\n }\r\n\r\n update = true;\r\n });\r\n\r\n if ($(\"#id_\" + target[k]).is(\":disabled\")) {\r\n $(\"#id_\" + target[k]).removeAttr(\"disabled\");\r\n\r\n update = true;\r\n }\r\n }\r\n\r\n if (update) {\r\n $(\"input[name^=\\\"\" + src + \"#\\\"]\").each(function () {\r\n var item = $(this).attr(\"name\").split(\"#\")[1];\r\n $(\"input[name^=\\\"\" + target[k] + \"#\" + item + \"\\\"]\").prop(\"checked\", $(this).is(\":checked\"));\r\n });\r\n\r\n $(\"#id_\" + target[k]).prop(\"checked\", $(this).is(\":checked\"));\r\n }\r\n }\r\n}\r\n\r\nfunction handleCopyCheckbox(src, trg) {\r\n $(\"input[name^=\\\"\" + src + \"#\\\"]\").each(function () {\r\n $(this).change(function () {\r\n copyCheckbox(src, trg, false);\r\n })\r\n });\r\n\r\n $(\"#id_\" + src).change(function () {\r\n copyCheckbox(src, trg, false);\r\n })\r\n}\r\n\r\nfunction copyFields(sourceId, targetList, isBeforeSave) {\r\n var sourceElement = $(\"#id_\" + sourceId);\r\n var sourceValue = sourceElement.val();\r\n var targetIds = targetList.split(\";\");\r\n\r\n for (var i = 0; i < targetIds.length; i++) {\r\n var targetId = targetIds[i];\r\n var update = true;\r\n var targetElement = $(\"#id_\" + targetId);\r\n\r\n if (isBeforeSave) {\r\n if (targetElement.is(\":disabled\")) {\r\n targetElement.removeAttr(\"disabled\");\r\n }\r\n else {\r\n update = false;\r\n }\r\n }\r\n\r\n if (update) {\r\n loadData({\r\n isFieldCopy: true,\r\n data: [{\r\n name: targetId,\r\n value: sourceValue,\r\n sourceElement: sourceElement\r\n }]\r\n });\r\n }\r\n }\r\n}\r\n\r\nfunction handleCopy(sourceId, targetList) {\r\n $(\"#id_\" + sourceId).change(function () {\r\n copyFields(sourceId, targetList, false);\r\n });\r\n}\r\n\r\n/*\r\n * Drawing on attachment \r\n */\r\nvar attachmentDrawing = (function () {\r\n var htmlEls = $(document.documentElement).add(window.top.document.documentElement);\r\n var attachmentDrawingContainer = $(\".attachment-draw-container\");\r\n var canvasElement = $(\".attachment-draw-canvas\");\r\n var sizeHelper = $(\".attachment-draw-size-helper\");\r\n var undoButton = $(\".attachment-draw-undo\");\r\n var closeButton = $(\".attachment-draw-close\");\r\n var confirmButton = $(\".attachment-draw-confirm\");\r\n var colorButtons = $(\".attachment-draw-color\");\r\n var defaultColorButton = colorButtons.eq(0);\r\n var metaViewport = window.top.document.querySelector(\"meta[name=viewport]\");\r\n var sidebarToggle = window.top.$(\".resize-sidebar\");\r\n var attachmentContainer;\r\n\r\n var canvas = new fabric.Canvas(canvasElement.get(0), {\r\n renderOnAddRemove: false,\r\n selection: false,\r\n isDrawingMode: true\r\n });\r\n\r\n if (leanformsNext.isIosPWA) {\r\n // In iOS PWA, vertical movement would not follow the pointer correctly.\r\n // This is the only reliable fix I could find.\r\n canvas.upperCanvasEl.addEventListener(\"touchmove\", function (e) {\r\n e.preventDefault();\r\n });\r\n }\r\n\r\n canvas.freeDrawingBrush.width = 3;\r\n\r\n function openAttachmentDrawing() {\r\n canvas.clear();\r\n window.addEventListener(\"resize\", resizeCanvas, { passive: true });\r\n\r\n // Need to do this first to get correct dimensions.\r\n attachmentDrawingContainer.css(\"visibility\", \"hidden\");\r\n attachmentDrawingContainer.removeClass(\"hidden\");\r\n\r\n htmlEls.addClass(\"is-drawing-active\");\r\n toggleZoomPrevention(true);\r\n\r\n // Reset zoom so image size applies correctly.\r\n canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);\r\n\r\n attachmentContainer = $(this).parent().parent();\r\n\r\n var imageElement = attachmentContainer.find(\".attachment-item\").get(0);\r\n var width = imageElement.naturalWidth;\r\n var height = imageElement.naturalHeight;\r\n var fabricImage = new fabric.Image(imageElement, {\r\n width: width,\r\n height: height\r\n });\r\n\r\n sizeHelper.attr(\"src\", imageElement.src);\r\n\r\n canvas.setWidth(width);\r\n canvas.setHeight(height);\r\n\r\n canvas.add(fabricImage);\r\n canvas.renderAll();\r\n\r\n // We need some arbitrary timeout apparently.\r\n setTimeout(() => {\r\n resizeCanvas();\r\n\r\n attachmentDrawingContainer.css(\"visibility\", \"visible\");\r\n }, 100);\r\n\r\n setColor(defaultColorButton);\r\n }\r\n\r\n async function dataUriToBlob(dataUri) {\r\n return await fetch(dataUri).then(res => res.blob());\r\n }\r\n\r\n function closeDrawing() {\r\n attachmentDrawingContainer.addClass(\"hidden\");\r\n\r\n htmlEls.removeClass(\"is-drawing-active\");\r\n toggleZoomPrevention(false);\r\n\r\n window.removeEventListener(\"resize\", resizeCanvas, { passive: true });\r\n }\r\n\r\n async function closeAttachmentDrawing() {\r\n attachmentDrawingContainer.addClass(\"hidden\");\r\n\r\n htmlEls.removeClass(\"is-drawing-active\");\r\n toggleZoomPrevention(false);\r\n\r\n window.removeEventListener(\"resize\", resizeCanvas, { passive: true });\r\n\r\n // No need to upload when no (new) drawing is present.\r\n var hasDrawnPaths = canvas.getObjects().length > 1;\r\n if (!hasDrawnPaths) {\r\n return;\r\n }\r\n\r\n var dataUri = canvas.toDataURL({ format: \"jpeg\" });\r\n var imageBlob = await dataUriToBlob(dataUri);\r\n var file = new File([imageBlob], \"image-with-drawing.jpg\");\r\n\r\n attachmentContainer\r\n .find(\".attachment-item\")\r\n .attr(\"src\", dataUri);\r\n\r\n var offlineId = attachmentContainer.data(\"offline-id\");\r\n var wasSavedOffline = offlineId > 0;\r\n\r\n if (wasSavedOffline) {\r\n db.attachments.update(offlineId, {\r\n data: file,\r\n }).catch(function (err) {\r\n if (!handlePotentialQuotaError(err)) {\r\n leanformsNext.handleError(err);\r\n }\r\n });\r\n // TODO update link?\r\n\r\n return;\r\n }\r\n\r\n var previousState = attachmentContainer.data(\"state\");\r\n var wasSaved = previousState === \"saved\";\r\n var previousGuid = attachmentContainer.data(\"guid\");\r\n var filename = attachmentContainer.data(\"filename\");\r\n var questionId = attachmentContainer.data(\"question\");\r\n\r\n if (wasSaved) {\r\n // If immediate online save fails,\r\n // This is used by hasMissingAttachments() to retry on form save.\r\n attachmentContainer.data(\"needsDrawingUpdate\", true);\r\n }\r\n else {\r\n // Will make sure that save if retried on form save if it fails now.\r\n // This has a small chance of leaving orphaned attachments in the /temp/ folder.\r\n attachmentContainer.removeData(\"guid\");\r\n }\r\n\r\n attachmentContainer.data(\"file\", file);\r\n\r\n var attachmentFormData = new FormData();\r\n attachmentFormData.append(\"data\", file);\r\n\r\n var uploadUrl = baseUrl + \"/api/attachments/\" + questionId;\r\n var method;\r\n\r\n if (wasSaved) {\r\n if (isMatrix) {\r\n attachmentFormData.append(\"isFromMatrix\", true);\r\n attachmentFormData.append(\"matrixQuestionId\", matrixQuestionId);\r\n attachmentFormData.append(\"matrixLineId\", matrixLineId);\r\n }\r\n\r\n attachmentFormData.append(\"filename\", filename);\r\n attachmentFormData.append(\"formId\", FORMID);\r\n\r\n method = \"PUT\";\r\n }\r\n else {\r\n attachmentFormData.append(\"previousGuid\", previousGuid);\r\n\r\n method = \"POST\";\r\n }\r\n\r\n var uploadXHR = new XMLHttpRequest();\r\n\r\n uploadXHR.addEventListener(\"load\", onUploadLoadFactory(attachmentContainer, wasSaved), false);\r\n uploadXHR.addEventListener(\"error\", onUploadError, false);\r\n\r\n uploadXHR.open(method, uploadUrl, true);\r\n uploadXHR.send(attachmentFormData);\r\n }\r\n\r\n function onUploadLoadFactory(container, wasSaved) {\r\n return function onUploadLoad() {\r\n if (this.status !== 200) {\r\n leanformsNext.handleError(this);\r\n\r\n return;\r\n }\r\n\r\n var uploaded = JSON.parse(this.response);\r\n var newGuid = uploaded.guid;\r\n\r\n container.data(\"guid\", newGuid);\r\n\r\n var link = container.find(\"a\");\r\n var displayName = container.find(\".attachment-text\").text();\r\n var newUrl = baseUrl + uploaded.url + \"&displayName=\" + encodeURIComponent(displayName);\r\n\r\n link.attr(\"href\", newUrl);\r\n\r\n if (wasSaved) {\r\n container.data(\"filename\", uploaded.filename);\r\n container.removeData(\"needsDrawingUpdate\");\r\n\r\n if (isMatrix) {\r\n // TODO: Update matrix xml to make sure it keeps working even if they don't save the matrix line.\r\n }\r\n }\r\n }\r\n }\r\n\r\n function onUploadError() {\r\n // Error is likely due to being offline and can be ignored, save will be retried by hasMissingAttachments().\r\n // Download link may be wrong until that happens.\r\n }\r\n\r\n closeButton.on(\"click\", closeDrawing);\r\n confirmButton.on(\"click\", closeAttachmentDrawing);\r\n colorButtons.on(\"click\", function () {\r\n setColor($(this));\r\n });\r\n\r\n function setColor(colorButton) {\r\n colorButtons.removeClass(\"is-active\");\r\n colorButton.addClass(\"is-active\");\r\n\r\n var color = colorButton.css(\"backgroundColor\");\r\n\r\n canvas.freeDrawingBrush.color = color;\r\n }\r\n\r\n function resizeCanvas() {\r\n var width = sizeHelper.width();\r\n var height = sizeHelper.height();\r\n var canvasWidth = canvas.getWidth();\r\n\r\n var scale = width / canvasWidth;\r\n var zoom = canvas.getZoom();\r\n\r\n zoom *= scale;\r\n\r\n canvas.freeDrawingBrush.width = 3 / zoom;\r\n canvas.setDimensions({ width: width, height: height });\r\n canvas.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);\r\n }\r\n\r\n var disableZoomPrevention = false;\r\n\r\n function toggleZoomPrevention(state) {\r\n if (disableZoomPrevention) {\r\n return;\r\n }\r\n\r\n var preventScale = \", maximum-scale=1\";\r\n var hasPreventScale = metaViewport.content.includes(preventScale);\r\n\r\n if (state && hasPreventScale) {\r\n // Is iPhone, where zoom should always be prevented due to automatic zooming.\r\n disableZoomPrevention = true;\r\n\r\n return;\r\n }\r\n\r\n if (state) {\r\n metaViewport.content += preventScale;\r\n } else if (!state) {\r\n metaViewport.content = metaViewport.content.replace(preventScale, \"\");\r\n }\r\n\r\n // Toggle also functions as forced repaint, required for meta viewport update to work.\r\n sidebarToggle.toggle(!state);\r\n }\r\n\r\n undoButton.on(\"click\", function () {\r\n var lastItemIndex = (canvas.getObjects().length - 1);\r\n var item = canvas.item(lastItemIndex);\r\n\r\n if (item.get(\"type\") === \"path\") {\r\n canvas.remove(item);\r\n canvas.renderAll();\r\n }\r\n });\r\n\r\n return {\r\n open: openAttachmentDrawing\r\n };\r\n})();\r\n\r\n/*\r\n * Attachments\r\n */\r\n$(\".form\").on(\"click\", \"button.preview-attachment\", leanformsNext.previewAttachment);\r\n$(\".form\").on(\"click\", \"button.delete-attachment\", removeAttachment);\r\n$(\".form\").on(\"click\", \"button.draw-attachment\", attachmentDrawing.open);\r\n\r\nfunction removeAttachment() {\r\n var button = $(this);\r\n var previewContainer = button.parent().parent();\r\n\r\n var questionId = previewContainer.data(\"question\");\r\n var guid = previewContainer.data(\"guid\");\r\n var isCopy = previewContainer.data(\"isCopy\");\r\n var isNewAttachment = previewContainer.data(\"state\") === \"new\";\r\n var isSavedMatrix = !isNewAttachment && FORMGUID === \"00000000-0000-0000-0000-000000000000\" && parent.FORMGUID;\r\n\r\n if (isNewAttachment) {\r\n var offlineId = previewContainer.data(\"offline-id\");\r\n var url = previewContainer.find(\"a\").attr(\"href\");\r\n\r\n previewContainer.remove();\r\n\r\n if (guid) {\r\n $.ajax({\r\n url: baseUrl + \"/api/attachments/temp\",\r\n type: \"DELETE\",\r\n data: { fileName: guid },\r\n error: leanformsNext.onAjaxError,\r\n success: function () { }\r\n });\r\n }\r\n else if (typeof offlineId === \"number\" && offlineId > -1) {\r\n if (url) {\r\n URL.revokeObjectURL(url);\r\n }\r\n\r\n indexedDatabase.attachments.delete(offlineId).catch(leanformsNext.handleError);\r\n }\r\n }\r\n else if (isSavedMatrix) {\r\n previewContainer.remove();\r\n }\r\n else {\r\n var fileName = previewContainer.data(\"filename\");\r\n\r\n formControls.showConfirmDialog(resources.confirmRemoveAttachment, function () {\r\n if (isCopy && FORMGUID === \"00000000-0000-0000-0000-000000000000\") {\r\n removeCopiedAttachments(questionId, fileName);\r\n previewContainer.remove();\r\n }\r\n else {\r\n $.ajax({\r\n url: baseUrl + \"/api/attachments\",\r\n type: \"DELETE\",\r\n data: {\r\n fileName: fileName,\r\n guid: FORMGUID,\r\n questionId: questionId\r\n },\r\n error: leanformsNext.onAjaxError,\r\n success: function () {\r\n previewContainer.remove();\r\n }\r\n });\r\n }\r\n });\r\n }\r\n}\r\n\r\nfunction createUploads() {\r\n uploadInputs.forEach(function (questionId) { \r\n var input = $(\"#id_\" + questionId);\r\n var isDisabled = input.prop(\"disabled\");\r\n\r\n // In initialize file upload tracker array.\r\n uploadFileTrackers[questionId] = [];\r\n\r\n input.kendoUpload({\r\n async: {\r\n saveUrl: baseUrl + \"/api/attachments/\" + questionId,\r\n autoUpload: true\r\n },\r\n localization: {\r\n select: resources.attachmentsButtonText,\r\n dropFilesHere: resources.attachmentsDropText\r\n },\r\n select: function (e) {\r\n // Store selected files in the order the upload control returns them (so we can maintain order when rendering previews).\r\n e.files.forEach(file => uploadFileTrackers[questionId].push(file.uid));\r\n },\r\n showFileList: false,\r\n enabled: !isDisabled,\r\n success: onUploadSuccess,\r\n error: onUploadError\r\n });\r\n });\r\n}\r\n\r\nfunction getDisplayName(currentName, questionId) {\r\n var existingNames = $.map(\r\n $(\"#attachments_\" + questionId).find(\".attachment-container\"), function (attachment) {\r\n return $(attachment).find(\".attachment-text\").text();\r\n }\r\n );\r\n\r\n function checkName(name, i) {\r\n var isDuplicateName = existingNames.indexOf(name) > -1;\r\n if (!isDuplicateName) {\r\n return name;\r\n }\r\n\r\n var extensionIndex = name.lastIndexOf(\".\");\r\n var fileName = extensionIndex > -1 ? name.slice(0, extensionIndex) : name;\r\n var extension = extensionIndex > -1 ? name.slice(extensionIndex) : \"\";\r\n\r\n var newName = \"\";\r\n\r\n if (!i) {\r\n i = 1;\r\n newName = fileName + \" (1)\" + extension;\r\n }\r\n else {\r\n newName = fileName.replace(/^(.*)( \\(\\d+\\))$/, \"$1 (\" + i + \")\") + extension;\r\n }\r\n\r\n return checkName(newName, i + 1);\r\n }\r\n\r\n return checkName(currentName);\r\n}\r\n\r\nfunction onUploadSuccess(e) {\r\n if (e.operation !== \"upload\") {\r\n return;\r\n }\r\n\r\n const questionId = this.name;\r\n\r\n e.files.forEach(function (file) {\r\n var displayName = getDisplayName(e.response.displayName, questionId);\r\n\r\n createPreviewElement({\r\n url: baseUrl + e.response.url + \"&displayName=\" + encodeURIComponent(displayName),\r\n displayName: displayName,\r\n questionId: questionId,\r\n guid: e.response.guid,\r\n uid: file.uid,\r\n file: file.rawFile,\r\n state: \"new\"\r\n });\r\n });\r\n}\r\n\r\nfunction onUploadError(e) {\r\n var response = e.XMLHttpRequest;\r\n\r\n if (FORMID !== \"-1\" || e.operation !== \"upload\" || response.status !== 0 || !core.supportsOffline()) {\r\n // TODO: show error.\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n\r\n var questionId = e.sender.name;\r\n var kendoFile = e.files[0];\r\n var fileName = kendoFile.name;\r\n var file = kendoFile.rawFile;\r\n var displayName = getDisplayName(fileName, questionId);\r\n var url = \"\";\r\n\r\n if (formControls.isImage(fileName)) {\r\n url = getOfflineUrl(file);\r\n }\r\n\r\n var previewEl = createPreviewElement({\r\n url: url,\r\n displayName: displayName,\r\n questionId: questionId,\r\n file: file,\r\n uid: kendoFile.uid,\r\n offline: true,\r\n state: \"new\"\r\n });\r\n\r\n if (isMatrix) {\r\n return;\r\n }\r\n\r\n indexedDatabase.attachments.add({\r\n type: \"file\",\r\n data: file,\r\n displayName: displayName,\r\n questionId: questionId\r\n }).then(function (id) {\r\n previewEl.data(\"offline-id\", id);\r\n });\r\n}\r\n\r\nfunction createPreviewElement(options) {\r\n var src = options.url;\r\n var offlineId = options.offlineId;\r\n var fileName = options.fileName;\r\n var displayName = options.displayName;\r\n var guid = options.guid;\r\n var file = options.file;\r\n var questionId = options.questionId;\r\n var state = options.state;\r\n var step = options.step;\r\n var offlineFormSaved = options.offlineFormSaved;\r\n var isDatasetValue = options.isDatasetValue;\r\n var isCopy = options.isCopy;\r\n var datasetField = options.datasetField;\r\n\r\n var previewContainer = $(\"#attachments_\" + questionId);\r\n\r\n if (!previewContainer.length) {\r\n var canvas = $(\"#C_\" + questionId);\r\n\r\n canvas\r\n .data(\"state\", state)\r\n .data(\"filename\", fileName);\r\n\r\n return;\r\n }\r\n\r\n var container = $(\"