1/** GLOBAL POST NAMES */ 2 3const POST_NAME_PAGE_TAG = "ddm-page-tag"; 4const POST_NAME_PAGE_REFERRER = "ddm-page-referrer"; 5const POST_NAME_TRANSLATION_KEY = "ddm-translation-key"; 6const POST_NAME_TRANSLATION_TEXT = "ddm-translation-text"; 7const POST_NAME_TRANSLATION_KEY_ARRAY = "ddm-translation-key-array"; 8 9const POST_NAME_MEDIA_ID = "ddm-media-id"; 10const POST_NAME_MEDIA_FILE_PATH = "ddm-media-file-path"; 11const POST_NAME_MEDIA_FILE_NAME = "ddm-media-file-name"; 12const POST_NAME_MEDIA_TYPE_NAME = "ddm-media-type-name"; 13const POST_NAME_MEDIA_UPLOAD_FILE = "ddm-media-upload-file"; 14const POST_NAME_MEDIA_UPLOAD_REPLACE_FILE = "ddm-media-upload-replace-file"; 15 16const POST_NAME_PAGE_MEDIA_KEY = "ddm-media-key"; 17const POST_NAME_PAGE_MEDIA_ALT_TEXT = "ddm-media-alt-text"; 18 19const POST_NAME_PARAMETER_KEY = "ddm-parameter-key"; 20const POST_NAME_PARAMETER_VALUE = "ddm-parameter-value"; 21const POST_NAME_PARAMETER_IS_VARIABLE = "ddm-parameter-is-variable"; 22 23const POST_NAME_DUI_PARENT_ID = "ddm-dui-parent-id"; 24const POST_NAME_DUI_SORT_ORDER_CHANGE_DIRECTION = "ddm
24-dui-sort-order-change-direction"; 25const POST_NAME_DUI_PARENT_DEFINITION_KEY = "ddm-dui-parent-definition-key"; 26const POST_NAME_DUI_COLLECTION_DEFINITION_KEY = "ddm-dui-collection-definition-key"; 27const POST_NAME_DUI_OPTIONS = "ddm-dui-options"; 28const POST_NAME_DUI_FILTER_CATEGORY_KEY = "ddm-dui-filter-category-key"; 29const POST_NAME_DUI_FILTER_OPTION_ID = "ddm-dui-filter-option-id"; 30 31/** GLOBAL COOKIE NAMES */ 32 33const COOKIE_NAME_LANGUAGE = "ddm-language"; 34 35/** MEDIA TYPES */ 36 37const MEDIA_TYPE_IMAGE = "image"; 38const MEDIA_TYPE_VIDEO = "video"; 39const MEDIA_TYPE_AUDIO = "audio"; 40const MEDIA_TYPE_DOCUMENT = "document"; 41 42/** EVENTS */ 43 44const EVENT_LISTENER_DYNAMIC_UI_PARENT_ADDED = "dynamic-ui-parent-added"; 45const EVENT_LISTENER_DYNAMIC_UI_PARENT_DELETED = "dynamic-ui-parent-deleted"; 46const EVENT_LISTENER_MEDIA_EDITOR_CONTENT_CHANGED = "media-editor-content-changed"; 47 48/** FILE UPLOAD **/ 49 50const UPLOAD_RESULT_CODE_OK = 0x3a6746; 51const UPLOAD_RESULT_CODE_ERROR = 0x88cae1; 52const UPLOAD_RESULT_CODE_FILE_EXISTS = 0xd45a0b; 53 54const classNameTextFieldEditMode = "ddm-admin-textinput-active"; 55var mediumEditor = null; 56 57$(document).ready(function(){ 58 InitDdmFramework(); 59}); 60 61/** 62 * Initializes DDM Framework 63 * Checks cookie permission and takes measures if needed 64 * Adds padding-top if ddm-page-start-after-header class name was found 65 * 66 */ 67function InitDdmFramework(){ 68 69 CheckCookiePermission(); 70 71 var clientPage = $('.ddm-page-start-after-header'); 72 var header = $('.ddm-specific-header'); 73 if(clientPage.length > 0 && header.length > 0){ 74 setTimeout(function(){ // fix for getBoundingClientRect().top returning zero sometimes 75 clientPage.css("padding-top", header[0].getBoundingClientRect().top + header.height()); 76 }, 1); 77 } 78} 79 80/** 81 * Stores text in database using ajax call 82 * @param {string} textKey database translation key 83 * @param {string} text desired text 84 * @param {function} onTextSaved callback when the saving process is done 85 */ 86function SaveTextToDatabase(textKey, text, onTextSaved) { 87 var postData = { }; 88 postData[POST_NAME_PAGE_TAG] = $('#pageTag').val(); 89 postData[POST_NAME_TRANSLATION_KEY] = textKey; 90 postData[POST_NAME_TRANSLATION_TEXT] = text; 91 92 jQuery.ajax({ 93 url: "/ddm-framework/util/ajax/update-page-text.php", 94 type: 'POST', 95 data: postData, 96 responseType: 'json', 97 success:function(response){ 98 if(response.result == true) 99 onTextSaved(); 100 else AjaxErrorAlert("something went wrong, could not store text in database: " + response.message); 101 }, 102 error:function (request, status, error){ 103 AjaxErrorAlert("something went wrong, could not store text in database: " + request.responseText); 104 } 105 }); 106} 107 108function UpdateParameterInDatabase(parameterKey, parameterValue, isVariable, onSuccess, onError){ 109 var postData = { }; 110 postData[POST_NAME_PARAMETER_KEY] = parameterKey; 111 postData[POST_NAME_PARAMETER_VALUE] = parameterValue; 112 postData[POST_NAME_PARAMETER_IS_VARIABLE] = isVariable; 113 114 jQuery.ajax({ 115 url: "/ddm-framework/util/ajax/update-parameter.php", 116 type: 'POST', 117 data: postData, 118 responseType: 'json', 119 success:function(response){ 120 if(response.result == true){ 121 if(typeof onSuccess === "function") 122 onSuccess(); 123 } 124 else console.error(response.message); 125 }, 126 error:function (request, status, error){ 127 if(typeof onError === "function") 128 onError(request.responseText); 129 else console.error(request.responseText); 130 } 131 }); 132} 133 134/** 135 * Loads multiple texts from database using ajax. Returns json including the texts 136 * @param {string} textKeys separated by comma 137 * @param {function} onComplete callback 138 */ 139function LoadMultipleTextsFromDatabase(textKeys, onComplete){ 140 141 var postData = {}; 142 postData[POST_NAME_PAGE_TAG] = $('#pageTag').val(); 143 postData[POST_NAME_TRANSLATION_KEY_ARRAY] = textKeys; 144 145 jQuery.ajax({ 146 url: "/ddm-framework/util/ajax/load-page-text-multiple.php", 147 type: 'POST', 148 data: postData, 149 responseType: 'json', 150 success:function(response){ 151 if(response.result == true) 152 onComplete(response.data); 153 else{ 154 AjaxErrorAlert("something went wrong, could load texts from database: " + response.message); 155 } 156 }, 157 error:function (request, status, error){ 158 AjaxErrorAlert("something went wrong, could load texts from database: " + request.responseText); 159 } 160 }); 161} 162 163/** 164 * Checks whether the passed element or one the parent elements has set "position=fixed" 165 * @param {element} element 166 * @return {boolean} isFixed 167 */ 168function IsElementOrParentFixed(element) { 169 var $element = $(element); 170 var $checkElements = $element.add($element.parents()); 171 var isFixed = false;
172 $checkElements.each(function(){ 173 if ($(this).css("position") === "fixed") { 174 isFixed = true; 175 return false; 176 } 177 }); 178 return isFixed; 179} 180 181/** 182 * @deprecated since version 1.0.9 - please use /modules/content-editor/url-editor instead 183 * Shows an overlay letting the user change the text of the url and the url itself 184 * @param {element} element The label having an <A> element as parent (is a url) 185 */ 186function ShowUrlEditor(element, hideTextInput){ 187 var editor = $('#ddm-url-editor'); 188 editor.show(); 189 190 var inputText = $('#ddm-url-edit-input-text'); 191 var inputUrl = $('#ddm-url-edit-input-url'); 192 var inputTarget = $('#ddm-url-edit-input-target'); 193 var errorLabel = $('#ddm-url-editor-error-label'); 194 errorLabel.hide(); 195 editor.find('#ddm-url-editor-text-edit').toggle(!hideTextInput); // hide text input if requested 196 197 var href = element.parent().attr('href'); 198 var urlTarget = element.parent().attr('target'); 199 inputUrl.val(href); 200 inputText.val(element.html()); 201 inputTarget.prop('checked', urlTarget == '_blank'); 202 203 $('#ddm-url-edit-button-cancel').unbind('click').bind('click', function(){ 204 editor.hide(); 205 }); 206 207 $('#ddm-url-edit-button-save').unbind('click').bind('click', function(){ 208 var text = inputText.val(); 209 210 if(!IsTextValid(text)){ 211 ShowTextInvalidAlert(); 212 return; 213 } 214 215 var key = element.attr('data-key'); 216 var urlKey = key + "-url"; // url database key is same as text id but with "-url" suffix - see DDM-Toolbox.DDMUrl (function) 217 var urlTarget = key + "-target"; // target database key is same as text id but with "-target" suffix - see DDM-Toolbox.DDMUrl (function) 218 var target = inputTarget.prop('checked') ? '_blank' : '_self'; 219 220 var valuesToBeSaved = []; 221 valuesToBeSaved.push({key: key, value: text}); 222 valuesToBeSaved.push({key: urlKey, value: inputUrl.val()}); 223 valuesToBeSaved.push({key: urlTarget, value: target}); 224 225 valuesToBeSaved.forEach(function(item, index){ 226 SaveTextToDatabase(item.key, item.value, function(){ 227 if(index == valuesToBeSaved.length - 1){ 228 element.html(text); 229 element.parent().attr("href", inputUrl.val()); 230 element.parent().attr("target", target); 231 editor.hide(); 232 } 233 }); 234 }); 235 }); 236} 237 238/** 239 * @deprecated since version 1.0.9 - please use /modules/content-editor/url-editor instead 240 * Shows available url editor actions next to the url text field 241 * @param {element} textField textField that is referenced 242 */ 243function ShowUrlEditorActions(textField){ 244 $("#textEditorActions").hide(); 245 var urlEditorActionContainer = $("#urlEditorActions"); 246 SetEditorActionContainerPosition(textField, urlEditorActionContainer); 247 var followUrlButton = $("#urlEditorActionFollowUrl"); 248 var editUrlButton = $("#urlEditorActionEdit"); 249 var cancelButton = $("#urlEditorActionCancel"); 250 251 followUrlButton.unbind('click').bind('click', function(){ 252 urlEditorActionContainer.hide(); 253 var href = textField.parent().attr('href'); 254 window.open(href, "_self"); 255 }); 256 257 editUrlButton.unbind('click').bind('click', function(){ 258 urlEditorActionContainer.hide(); 259 ShowUrlEditor(textField); 260 }); 261 262 cancelButton.unbind('click').bind('click', function(){ 263 urlEditorActionContainer.hide(); 264 }); 265 266 urlEditorActionContainer.show(); 267} 268 269/** 270 * @deprecated since version 1.0.9 271 * Shows an overlay letting the user replace an image by uploading a new one 272 * @param {boolean} visibility true or false => visible or hidden 273 * @param {string} imageKey id/key of the image to be changed 274 * @param {int} mediaID id of the media entry in database table "media" 275 */ 276function SetImageEditorVisibility(visibility, imageKey, mediaID){ 277 var editor = $('#ddm-image-editor'); 278 if(visibility) 279 editor.show(); 280 else editor.hide(); 281 $('#uploadForm').removeClass('dark'); 282 $('#uploadForm').css('backgroundImage', 'none'); 283 $('#ddm-image-upload-preview').hide(); 284 $('.ddm-image-upload-alt-text-input').hide(); 285 $('#ddm-image-upload-alt-text-input').val(''); 286 $('#uploadMessageBox').empty(); 287 $('#imageKey').val(imageKey); 288 $('#ddm-upload-form-button-set-image').prop('disabled', true); 289 if(mediaID != undefined && mediaID != '' && mediaID > 0){ 290 var image = $('#' + imageKey); 291 var imageSrc = image.attr('src'); 292 var imageAltText = image.attr('alt'); 293 window.mediaFilePath = imageSrc; 294 window.mediaID = mediaID; 295 if(imageSrc != undefined && imageSrc != ''){ 296 $('#uploadForm').css('backgroundImage', 'url("' + imageSrc + '")'); 297 $('#uploadForm').addClass('dark'); 298 $('#ddm-upload-form-button-set-image').prop('disabled', false); 299 $('#ddm-image-upload-alt-text-input').val(imageAltText); 300 $('.ddm-image-upload-alt-text-input').show(); 301 } 302 } 303} 304 305/** 306 * @deprecated since version 1.0.9 307 * Upload 308 * please use /modules/content-editor/media-editor instead 309 */ 310 311var textKeyUploading = "ddm-page-text-image-upload-uploading"; 312var textKeyChooseFile = "ddm-page-text-image-upload-choose-another-file"; 313var textKeyError = "ddm-page-text-image-upload-error";
314var textKeySuccess = "ddm-page-text-image-upload-success"; 315var textKeys = textKeyUploading + "," + textKeyChooseFile + "," + textKeyError + "," + textKeySuccess; 316 317window.onload = function() { 318 319 var adminLoggedIn = $("body").hasClass("ddm-admin-mode"); 320 if(!adminLoggedIn) return; 321 LoadMultipleTextsFromDatabase(textKeys, function(texts){ 322 InitUploadDialog(texts); 323 }); 324 325}; 326 327/** 328 * @deprecated since version 1.0.9 329 * Inits updload dialog using the passed texts from database 330 * @param {Array} texts 331 */ 332function InitUploadDialog(texts){ 333 var textUploading = texts[textKeyUploading] != null ? texts[textKeyUploading] : "Uploading..."; 334 var textChooseFile = texts[textKeyChooseFile] != null ? texts[textKeyChooseFile] : "Choose another file"; 335 var textError = texts[textKeyError] != null ? texts[textKeyError] : "Upload failed."; 336 var textSuccess = texts[textKeySuccess] != null ? texts[textKeySuccess] : "successfully uploaded."; 337 338 var buttonSetImage = $('#ddm-upload-form-button-set-image'); 339 buttonSetImage.prop('disabled', true); 340 341 buttonSetImage.unbind("click").bind("click", function(){ 342 343 var imageKey = $('#imageKey').val(); 344 345 var postData = {}; 346 postData[POST_NAME_MEDIA_ID] = window.mediaID; 347 postData[POST_NAME_PAGE_MEDIA_KEY] = imageKey; 348 postData[POST_NAME_PAGE_MEDIA_ALT_TEXT] = $('#ddm-image-upload-alt-text-input').val(); 349 350 jQuery.ajax({ 351 url: "/ddm-framework/util/ajax/update-page-media.php", 352 type: 'POST', 353 responseType: 'json', 354 data: postData, 355 success:function(data){ 356 var img = $('img[data-key="' + imageKey + '"'); 357 img.attr("src", data.mediaFilePath); 358 img.attr("alt", $('#ddm-image-upload-alt-text-input').val()); 359 SetImageEditorVisibility(false); 360 }, 361 error:function (){ 362 363 } 364 }); 365 }); 366 367 var buttonUpload = document.getElementById('uploadBtn'), 368 progressBar = document.getElementById('progressBar'), 369 progressOuter = document.getElementById('progressOuter'), 370 uploadMessageBox = document.getElementById('uploadMessageBox'); 371 372 $('ddm-image-upload-preview').hide(); 373 var imageUploader = NewUploader( 374 buttonUpload, 375 'imageUploadDropZone', 376 'image/*', 377 function(){ /* OnStartXHR */ 378 progressOuter.style.display = 'block'; // make progress bar visible 379 }, 380 function() { /* OnSubmit */ 381 var data = {}; 382 data[POST_NAME_MEDIA_TYPE_NAME] = "image"; 383 imageUploader.setData(data); 384 uploadMessageBox.innerHTML = ''; // empty the message box 385 buttonUpload.innerHTML = textUploading; 386 }, 387 function( filename, response ) { /* onComplete */ 388 buttonUpload.innerHTML = textChooseFile; 389 progressOuter.style.display = 'none'; // hide progress bar when upload is completed 390 if ( !response ) { 391 uploadMessageBox.innerHTML = textError; 392 return; 393 } 394 if ( response.result === true ) { 395 buttonSetImage.prop('disabled', false); 396 $('#uploadForm').css('backgroundImage', 'url("' + EscapeTags( response.mediaFilePath ) + '")'); 397 $('#uploadForm').css('backgroundSize', 'cover'); 398 $('.ddm-image-upload-alt-text-input').show(); 399 $('#ddm-image-upload-alt-text-input').focus(); 400 $('#uploadForm').addClass('dark'); 401 window.mediaFilePath = response.mediaFilePath; 402 window.mediaID = response.mediaID; 403 } else { 404 buttonSetImage.prop('disabled', true); 405 $('#ddm-image-upload-preview').hide(); 406 if ( response.msg ) { 407 uploadMessageBox.innerHTML = "Error: " + EscapeTags( response.message ); 408 } else { 409 uploadMessageBox.innerHTML = textError; 410 } 411 $('#uploadForm').css('backgroundImage', 'url(/ddm-framework/img/upload_image_error.png)'); 412 $('#uploadForm').css('backgroundSize', '70%'); 413 $('#uploadForm').addClass('dark'); 414 $('.ddm-image-upload-alt-text-input').hide(); 415 $('#ddm-image-upload-alt-text-input').val(''); 416 } 417 }, 418 function() { /* onError */ 419 progressOuter.style.display = 'none'; 420 uploadMessageBox.innerHTML = 'Unable to upload file'; 421 $('#uploadForm').css('backgroundImage', 'url(/ddm-framework/img/upload_image_error.png)'); 422 $('#uploadForm').css('backgroundSize', '70%'); 423 $('#uploadForm').addClass('dark'); 424 $('.ddm-image-upload-alt-text-input').hide(); 425 $('#ddm-image-upload-alt-text-input').val(''); 426 } 427 ); 428 imageUploader.setProgressBar( progressBar ); 429} 430 431 432/** 433 * Initializes a new ajax uploader 434 * @param {Element} uploadButton Button that should trigger the upload system 435 * @param {Element} dropzone Element that is used for drag-and-drop events (e.g. image is being dragged from desktop to this element and upload will start) 436 * @param {String} fileType Type of the file to be uploaded - in MIME type format (i.e. image/*) 437 * @param {Function} onStartXHR Called when upload starts 438 * @param {Function} onSubmit Called when upload was submitted 439 * @param {Function} onComplete Called when upload is completed successfully 440 * @param {Function} onError Called when an error occurred while uploading 441 */ 442function NewUploader(uploadButton, dropzone, fileType, onStartXHR, onSubmit, onComplete, onError){ 443 var imageUploader = new ss.SimpleUpload({ 444 button: uploadButton, 445 dropzone: dropzone, 446 accept: fileType, 447 url: '/ddm-framework/modules/upload/upload.php', 448 progressUrl: '/ddm-framework/lib/upload/uploadProgress.php', 449 name: 'uploadfile', 450 multipart: true, 451 responseType: 'json', 452 debug: false, 453 startXHR: function(){ 454 onStartXHR(); 455 }, 456 onSubmit: function(filename, extension, uploadBtn, size){ 457 onSubmit(filename, extension, uploadBtn, size); 458 }, 459 onComplete: function(filename, response, uploadBtn, size) { 460 onComplete(filename, response, uploadBtn, size); 461 }, 462 onError: function(filename, type, status, statusText, response, uploadBtn, size){ 463 onError(filename, type, status, statusText, response, uploadBtn, size); 464 }, 465 onExtError: function( filename, extension ) { 466 onError("Error: Extension '." + extension + "' not allowed"); 467 }, 468 onSizeError: function( filename, fileSize, uploadBtn ) { 469 onError("Error: Filesize exceeded"); 470 }, 471 }); 472 return imageUploader; 473} 474 475/* Cookies */ 476 477/** 478 * Checks if the cookie 'cookiePermission' is set to "1" -> calls "DeleteAllCookies" if true 479 */ 480function CheckCookiePermission(){ 481 if(GetCookieValue('cookie-consent-accepted') != 'true'){ 482 DeleteAllCookies(); 483 } 484} 485 486/** 487 * Returns value of cookie with desired key "cookieName" 488 * @param {String} cookieName 489 */ 490function GetCookieValue(cookieName) { 491 var cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)'); 492 return cookieValue ? cookieValue.pop() : ''; 493} 494 495/** 496 * Deletes all cookies except ddm-language 497 */ 498function DeleteAllCookies() { 499 var cookies = document.cookie.split(";"); 500 501 for (var i = 0; i < cookies.length; i++) { 502 var cookie = cookies[i]; 503 var eqPos = cookie.indexOf("="); 504 var name = eqPos >
504 -1 ? cookie.substr(0, eqPos) : cookie; 505 document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"; 506 } 507} 508 509/** 510 * Sets a value ("cookieValue") to a cookie using key "cookieName" and expires "expireDays" 511 * @param {String} cookieName key of the cookie 512 * @param {String} cookieValue value of the cookie 513 * @param {Number} expireDays Amount of days the cookie shall be valid 514 */ 515function SetCookie(cookieName, cookieValue, expireDays) { 516 var date = new Date(); 517 date.setTime(date.getTime() + (expireDays*24*60*60*1000)); 518 var expires = "expires="+ date.toUTCString(); 519 document.cookie = cookieName + "=" + cookieValue + ";" + expires + ";path=/"; 520} 521 522/* Util */ 523 524/** 525 * Returns base url of website. E.g. https://ddm.studio/ 526 * Does not end with a "/" 527 */ 528function GetBaseUrl(){ 529 var currentUrl = window.location; 530 return currentUrl.protocol + "//" + currentUrl.host; 531} 532 533/** 534 * Shows an error modal using sweetAlert, shows the desired text 535 * @param {String} errorText 536 */ 537function AjaxErrorAlert(errorText){ 538 if(typeof Swal == "function") 539 Swal.fire({ 540 icon: 'error', 541 html: errorText 542 }); 543 else alert(errorText); 544} 545 546/** 547 * Sets the language cookie if the user accepted cookies before 548 * @param {String} name languageName 549 */ 550function ChangeLanguage(name){ 551 SetCookie(COOKIE_NAME_LANGUAGE, name, 31); 552 location.reload(); 553} 554 555function EscapeTags( str ) { 556 return String( str ) 557 .replace( /&/g, '&' ) 558 .replace( /"/g, '"' ) 559 .replace( /'/g, ''' ) 560 .replace( /</g, '<' ) 561 .replace( />/g, '>' ); 562} 563 564/** 565 * Removes all html tags from text while keeping the line breaks 566 * @param {string} html 567 */ 568function UntagHtml(html) 569{ 570 html = html.replace(/<br>/g,"$br$"); 571 html = html.replace(/(?:\r\n|\r|\n)/g, '$n$'); 572 var tmp = document.createElement("DIV"); 573 tmp.innerHTML = html; 574 html = tmp.textContent||tmp.innerText; 575 html = html.replace(/\$br\$/g,"<br>"); 576 html = html.replace(/\$n\$/g,"<br>"); 577 return html; 578} 579/* 580---------------------------------------------------------------------------------------------------------- 581---------------------------------------------------------------------------------------------------------- 582-- -- 583-- DYNAMIC UI -- 584-- -- 585---------------------------------------------------------------------------------------------------------- 586---------------------------------------------------------------------------------------------------------- 587*/ 588 589$(document).ready(function(){ 590 $(document).on('click', '.ddm-dui-add-parent', function(event){ 591 event.preventDefault(); 592 event.stopPropagation(); 593 var contextMenu = $(this).parent().find('.ddm-context-menu'); 594 if(contextMenu.children('ul').children().length == 1) 595 $(contextMenu).find('li').click(); 596 else contextMenu.toggleClass("active"); 597 }); 598 599 /* Load more functionality */ 600 $(document).on('click', '[data-dui-load-more="true"]', function(event){ 601 LoadMore(GetCollectionDefinitionKey(this)); 602 }); 603}); 604 605/** 606 * Uses the passed element to find out the collection definition key of the parent collection div 607 * @param {Element} element Any child element of dui collection 608 * @returns string collectionDefinitionKey 609 */ 610function GetCollectionDefinitionKey(element){ 611 return $(element).parents('.ddm-dui-collection-container').attr('data-dui-collection-definition-key'); 612} 613 614/** 615 * Adds a new DUI parent in the database and appends a new one to the user interface 616 * @param {String} collectionDefinitionKey 617 * @param {String} parentDefinitionKey 618 * @param {bool} openDuiParentEditor 619 */ 620function AddDuiParent(collectionDefinitionKey, parentDefinitionKey, openDuiParentEditor){ 621 let pageTag = $('#pageTag').val(); 622 623 if(openDuiParentEditor){ 624 location.href = GetBaseUrl() 625 + "/ddm-framework/modules/dynamic-ui/form/dynamic-ui-parent-editor-form.php?" 626 + POST_NAME_DUI_COLLECTION_DEFINITION_KEY + "=" + collectionDefinitionKey + "&" 627 + POST_NAME_DUI_PARENT_DEFINITION_KEY + "=" + parentDefinitionKey + "&" 628 + POST_NAME_PAGE_TAG + "=" + pageTag + "&" 629 + POST_NAME_PAGE_REFERRER + "=" + location.href; 630 return; 631 } 632 633 var postData = { }; 634 postData[POST_NAME_DUI_COLLECTION_DEFINITION_KEY] = collectionDefinitionKey; 635 postData[POST_NAME_DUI_PARENT_DEFINITION_KEY] = parentDefinitionKey; 636 postData[POST_NAME_PAGE_TAG] = pageTag; 637 postData[POST_NAME_DUI_OPTIONS] = JSON.stringify(GetDuiOptions(collectionDefinitionKey)); 638 639 var parentAppendingElement = $('div[data-dui-collection-definition-key="' + collectionDefinitionKey + '"]').children('*[data-dui-parent-addition-elememt="true"]'); 640 var url = "/ddm-framework/modules/dynamic-ui/ajax/create-parent.php"; 641 $.ajax({ 642 type: "POST", 643 url: url, 644 data: postData, 645 dataType: 'json', 646 success: function(response, text) 647 { 648 if(response.result){ 649 let descending = GetDuiOptions(collectionDefinitionKey).descending; 650 if(descending) 651 $(response.data).insertAfter(parentAppendingElement).hide().show('normal'); 652 else $(response.data).insertBefore(parentAppendingElement).hide().show('normal'); 653 654 let $parent = $(response.data); 655 let parentID = $parent.attr("data-parent-id"); 656 $(document).trigger(EVENT_LISTENER_DYNAMIC_UI_PARENT_ADDED, { collectionDefinitionKey: collectionDefinitionKey, parentID: parentID }); 657 } 658 else AjaxErrorAlert("Error while adding element: " + response.message);
659 parentAppendingElement.find('.ddm-context-menu').removeClass("active"); 660 }, 661 error: function(request, status, error){ 662 AjaxErrorAlert("Error while adding element: " + request.responseText); 663 } 664 }); 665} 666 667/** 668 * Deletes a DUI parent from database and removes it from the user interface 669 * @param {Event} event 670 * @param {Number} parentID 671 */ 672function DeleteDuiParentFromCollection(event, parentID){ 673 $(event.target).parents(".ddm-context-menu").remove(); // remove context menu from DOM 674 675 DeleteDuiParent(parentID, 676 function(data){ 677 // success 678 let parent = $('#dui_parent_' + parentID); 679 let collectionDefinitionKey = parent.closest(".ddm-dui-collection-container").attr("data-dui-collection-definition-key"); 680 if(data.result == true) 681 parent.hide('normal', function(){ 682 parent.remove(); 683 $(document).trigger(EVENT_LISTENER_DYNAMIC_UI_PARENT_DELETED, { collectionDefinitionKey: collectionDefinitionKey, parentID: parentID }); 684 }); 685 else AjaxErrorAlert("Error while deleting element: " + data.message); 686 }, 687 function(errorText){ 688 // error 689 AjaxErrorAlert("Unknown eror while deleting parent element: " + errorText); 690 } 691 ); 692} 693 694/** 695 * Deletes a DUI parent from database and calls 'successUrl' on success 696 * @param {Event} event 697 * @param {String} successUrl URL to be opened after deleting the parent. If null: function tries to remove the parent from DOM 698 */ 699function DeleteDuiParentWithUserConfirmation(parentID, successUrl){ 700 Swal.fire({ 701 icon : "question", 702 title : "Delete?", 703 text : "All linked components (pages, texts) are also deleted during the deletion process. Are you sure you want to continue deleting?", 704 showCloseButton: true, 705 showCancelButton: true, 706 showConfirmButton: true 707 }).then(function(result){ 708 if (!result.value) return; 709 710 DeleteDuiParent(parentID, 711 function(data){ 712 // success 713 if(data.result == true){ 714 if(successUrl) 715 window.location.href = successUrl; 716 else{ 717 let parent = $('#dui_parent_' + parentID); 718 let collectionDefinitionKey = parent.closest(".ddm-dui-collection-container").attr("data-dui-collection-definition-key"); 719 parent.hide('normal', function(){ 720 parent.remove(); 721 $(document).trigger(EVENT_LISTENER_DYNAMIC_UI_PARENT_DELETED, { collectionDefinitionKey: collectionDefinitionKey, parentID: parentID }); 722 }); 723 } 724 } 725 else AjaxErrorAlert("Error while deleting element: " + data.message); 726 }, 727 function(errorText){ 728 // error 729 AjaxErrorAlert("Unknown eror while deleting parent element: " + errorText); 730 } 731 ); 732 }); 733} 734 735/** 736 * Deletes a DUI parent from database 737 * @param {Number} parentID 738 * @param {Function} onSuccess called when parent has been deleted successfully 739 * @param {Function} onError called when an error occurred while deleting the parent 740 */ 741function DeleteDuiParent(parentID, onSuccess, onError){ 742 var url = "/ddm-framework/modules/dynamic-ui/ajax/delete-parent.php"; 743 744 var postData = {}; 745 postData[POST_NAME_DUI_PARENT_ID] = parentID; 746 747 $.ajax({ 748 type: "POST", 749 url: url, 750 data: postData, 751 dataType: 'json', 752 success: function(data) 753 { 754 if(typeof onSuccess === 'function') 755 onSuccess(data); 756 }, 757 error: function(request, status, error){ 758 if(typeof onError === 'function') 759 onError(request.responseText); 760 AjaxErrorAlert("Unknown eror while deleting parent element: " + request.responseText); 761 } 762 }); 763} 764 765/** 766 * Changes the sort order of a parent using ajax and swaps 2 elements in the user interface 767 * @param {Number} parentID 768 * @param {String} changeDirection "forward" or "backward" 769 */ 770function ChangeDuiSortOrder(event, parentID, changeDirection){ 771 var url = "/ddm-framework/modules/dynamic-ui/ajax/change-parent-sort-order.php"; 772 773 var postData = {}; 774 postData[POST_NAME_DUI_PARENT_ID] = parentID; 775 776 var parent = $('#dui_parent_' + parentID); 777 778 let descending = GetDuiOptions(GetCollectionDefinitionKey(parent)).descending; 779 let databaseChangeDirection = changeDirection; 780 781 if(changeDirection == 'forward' && descending) // change direction if the sort order is descending 782 databaseChangeDirection = 'backward'; 783 784 if(changeDirection == 'backward' && descending) // change direction if the sort order is descending 785 databaseChangeDirection = 'forward'; 786 787 postData[POST_NAME_DUI_SORT_ORDER_CHANGE_DIRECTION] = databaseChangeDirection; 788 789 $(event.target).parents(".ddm-context-menu").remove(); // remove context menu from DOM 790 791 $.ajax({ 792 type: "POST", 793 url: url, 794 data: postData, 795 dataType: 'json', 796 success: function(data) 797 { 798 if(data.result != true){ 799 AjaxErrorAlert("Eror while changing sort order of parent element: " + data.message); 800 return; 801 } 802 803 // swap them 804 if(changeDirection == 'forward'){ 805 var swapParent = parent.prev(); 806 swapParent.swap({ 807 target: parent.attr('id'), 808 opacity: "1", 809 speed: 300, 810 callback: function() { 811 swapParent.removeAttr('style'); 812 parent.removeAttr('style'); 813 swapParent.before(parent); 814 } 815 }); 816 } 817 else if(changeDirection == 'backward'){ 818 var swapParent = parent.next(); 819 if(swapParent.attr('data-dui-parent-addition-elememt') != 'true'){ // admin parent item used to add items, don't change its position 820 parent.swap({ 821 target: swapParent.attr('id'), 822 opacity: "1", 823 speed: 300, 824 callback: function() { 825 swapParent.removeAttr('style'); 826 parent.removeAttr('style'); 827 parent.before(swapParent); 828 } 829 }); 830 } 831 } 832 }, 833 error: function(request, status, error){ 834 AjaxErrorAlert("Unknown eror while changing sort order of parent element: " + request.responseText); 835 } 836 }); 837} 838 839/** 840 * Opens the dynamic ui parent editor 841 * @param {Number} parentID 842 */ 843function OpenDuiParentEditor(parentID){ 844 var parentElement = $('#dui_parent_' + parentID); 845 var form = parentElement.children('form'); 846 form.find('[name="' + POST_NAME_PAGE_REFERRER + '"]').val(location.href); 847 form.attr("action", "/ddm-framework/modules/dynamic-ui/form/dynamic-ui-parent-editor-form.php"); 848 form.submit(); 849} 850 851/** 852 * Returns options of collection with passed collectionDefinitionKey 853 * Javascript equivalent object of /ddm-framework/modules/dynamic-ui/dui-options.php 854 * @param {String} collectionDefinitionKey 855 */ 856function GetDuiOptions(collectionDefinitionKey){ 857 let optionsRaw = $('div[data-dui-collection-definition-key="' + collectionDefinitionKey + '"]' + ' > input[name="' + POST_NAME_DUI_OPTIONS + '"]').val(); 858 let options = {}; 859 if(optionsRaw) 860 options = JSON.parse(optionsRaw); 861 return options; 862} 863 864/** 865 * Updates the options input field with the passed options (converts them to json before) 866 * @param {String} collectionDefinitionKey 867 * @param {Object} options 868 */ 869function SetDuiOptions(collectionDefinitionKey, options){ 870 let optionsJson = JSON.stringify(options); 871 $('div[data-dui-collection-definition-key="' + collectionDefinitionKey + '"]' + ' input[name="' + POST_NAME_DUI_OPTIONS + '"]').val(optionsJson); 872} 873 874/** 875 * Reloads collection using the passed collectionDefinitionKey and options 876 * @param {string} collectionDefinitionKey 877 * @param {Object} options Javascript equivalent object of /ddm-framework/modules/dynamic-ui/dui-options.php 878 */ 879function ReloadDuiCollection(collectionDefinitionKey, options, onSuccess){ 880 if(!options) options = {}; 881 882 var postData = {}; 883 postData[POST_NAME_PAGE_TAG] = $('#pageTag').val(); 884 postData[POST_NAME_DUI_COLLECTION_DEFINITION_KEY] = collectionDefinitionKey; 885 postData[POST_NAME_DUI_OPTIONS] = JSON.stringify(options); 886 $.ajax({ 887 url: "/ddm-framework/modules/dynamic-ui/ajax/load-collection.php", 888 data: postData, 889 type: "POST", 890 dataType: 'json', 891 success: function(response, text) 892 { 893 if(response.result){ 894 $('[data-dui-collection-definition-key="' + collectionDefinitionKey + '"]').replaceWith(response.data); 895 let parentCount = $(response.data).children('.ddm
895-dui-parent').not('[data-dui-parent-addition-elememt="true"]').length; 896 if(typeof onSuccess === 'function') 897 onSuccess(parentCount); 898 } 899 else AjaxErrorAlert("Error while filtering: " + response.message); 900 }, 901 error: function(request, status, error){ 902 AjaxErrorAlert("Error while filtering: " + request.responseText); 903 } 904 }); 905} 906 907/** 908 * Loads parents from database using the passed collectionDefinitionKey 909 * Appends the newly loaded parents to the collection 910 * @param {string} collectionDefinitionKey 911 */ 912function LoadMore(collectionDefinitionKey){ 913 let options = GetDuiOptions(collectionDefinitionKey); 914 var loadMoreWrapper = $('[data-dui-collection-definition-key="' + collectionDefinitionKey + '"] .ddm-dui-load-more'); 915 loadMoreWrapper.find('.ddm-button').hide(); 916 var progressCircle = loadMoreWrapper.find('progress'); 917 progressCircle.show(); 918 919 options.startIndex += options.maxElementCount; 920 921 var postData = {}; 922 postData[POST_NAME_PAGE_TAG] = $('#pageTag').val(); 923 postData[POST_NAME_DUI_COLLECTION_DEFINITION_KEY] = collectionDefinitionKey; 924 postData[POST_NAME_DUI_OPTIONS] = JSON.stringify(options); 925 $.ajax({ 926 url: "/ddm-framework/modules/dynamic-ui/ajax/load-parents.php", 927 data: postData, 928 type: "POST", 929 dataType: 'json', 930 success: function(response, text) 931 { 932 progressCircle.hide(); 933 if(response.result){ 934 $(response.data).insertBefore(loadMoreWrapper).hide().show('normal'); 935 loadMoreWrapper.remove(); 936 SetDuiOptions(collectionDefinitionKey, options); 937 } 938 else AjaxErrorAlert("Error while filtering: " + response.message); 939 }, 940 error: function(request, status, error){ 941 progressCircle.hide(); 942 AjaxErrorAlert("Error while filtering: " + request.responseText); 943 } 944 }); 945} 946/** Cookie Consent **/ 947 948const cookieTypes = [ 949 "essential", 950 "functional", 951 "statistics", 952 "marketing" 953]; 954 955$(function(){ 956 $(".cookie-consent-popover").each(function(index, element){ 957 let popover = $(element); 958 let popoverOptions = { 959 content: function () { 960 return popover.siblings('.cookie-consent-popover-content').html(); 961 }, 962 html: true, 963 sanitize: false 964 }; 965 966 popover.popover(popoverOptions); 967 }); 968 969 // close popovers on outside click 970 $("html").on("mousedown", function (e) { 971 var l = $(e.target); 972 if (l.parents(".popover").length == 0 && l.parents(".medium-editor-toolbar").length == 0) { 973 $(".popover").each(function () { 974 $(this).popover("hide"); 975 }); 976 } 977 }); 978 979 $(document).on('click', '#cookie-consent-button-accept-all', function(event){ 980 event.preventDefault(); 981 $('.cookie-consent-option input[type="checkbox"]').each(function(index, item){ 982 let $this = $(item); 983 let cookieType = $this.attr("data-cookie-type"); 984 $this.prop("checked", true); 985 SetCookie("cookie-permission-" + cookieType, true, 365); 986 987 SetCookieTrigger(cookieType); 988 }); 989 SetCookie("cookie-consent-accepted", true, 365); 990 setTimeout(HideCookieConsent, 400); 991 }); 992 993 $(document).on('click', '#cookie-consent-button-accept-selected', function(event){ 994 event.preventDefault(); 995 cookieTypes.forEach(function(cookieType, index){ 996 let cookieAccepted = $('#checkbox-cookie-type-' + cookieType).prop("checked"); 997 SetCookie("cookie-permission-" + cookieType, cookieAccepted, 365); 998 if(cookieAccepted) 999 SetCookieTrigger(cookieType); 1000 }); 1001 SetCookie("cookie-consent-accepted", true, 365); 1002 HideCookieConsent(); 1003 }); 1004 1005 if(GetCookieValue("cookie-consent-accepted") !== "true"){ 1006 ShowCookieConsent(); 1007 } 1008}); 1009 1010function ShowCookieConsent(){ 1011 $('.cookie-consent-overlay, .cookie-consent').addClass('active');
1012 cookieTypes.forEach(function(cookieType, index){ 1013 $('#checkbox-cookie-type-' + cookieType).prop("checked", GetCookieValue("cookie-permission-" + cookieType) == "true"); 1014 }); 1015 $('#checkbox-cookie-type-essential').prop("checked", true); 1016} 1017 1018function HideCookieConsent(){ 1019 $('.cookie-consent-overlay, .cookie-consent').removeClass('active'); 1020} 1021 1022function SetCookieTrigger(cookieType){ 1023 switch(cookieType){ 1024 case "statistics": 1025 window.dataLayer.push({ 1026 event: 'ddmStatisticalCookieTrigger' 1027 }); 1028 break; 1029 case "marketing": 1030 window.dataLayer.push({ 1031 event: 'ddmMarketingCookieTrigger' 1032 }); 1033 break; 1034 default: 1035 // nothing to do here 1036 break; 1037 } 1038} 1039/** Text Editor **/ 1040 1041$(function(){ 1042 $(document.body).on('paste','.ddm-admin-textinput', function(event){ 1043 // event.preventDefault(); 1044 // let originalEvent = event.originalEvent; 1045 // let pastedText = ''; 1046 // if (typeof originalEvent.clipboardData === 'undefined') 1047 // pastedText = window.clipboardData.getData('Text'); 1048 // else 1049 // pastedText = originalEvent.clipboardData.getData('text/plain'); 1050 1051 // var untaggedText = UntagHtml(pastedText); 1052 // PasteHtmlAtCaret(untaggedText); 1053 SetEditorActionContainerPosition($(this), $("#textEditorActions")); 1054 }); 1055}); 1056 1057 1058/** 1059 * 1060 * @param {function} event callback 1061 * @param {string} key elementId of label 1062 * Creates an editable textField of a passed label (key), adds text editor action buttons besides the label 1063 */ 1064function MakeTextEditable(event, element){ 1065 event.stopPropagation(); 1066 event.preventDefault(); 1067 1068 $("#urlEditorActions").hide(); 1069 1070 var textField = $(element); 1071 var key = textField.attr('data-key'); 1072 1073 if(textField.attr('contenteditable') === 'true') 1074 return; // do not start editing again if it is already active 1075 1076 if(textField.parents(".ddm-editable-url-box").length > 0){ // textField is child of EditableUrlBox 1077 if(event.ctrlKey) return; // do not start editing text field if it is a child of editableUrlBox and user holds ctrlKey down to open the url 1078 } 1079 1080 var onOriginalValueRestored = function(){ 1081 var isParentAnchor = textField.parent().prop("tagName") == "A"; 1082 var isEditableUrl = isParentAnchor 1083 & textField.parent().attr("data-editable") == "true" 1084 & !textField.parent().hasClass("ddm-editable-url-box"); 1085 var extendedTextFormatting = $('#extended-text-formatting').val() == 1; 1086 1087 if(isEditableUrl){ 1088 ShowUrlEditorActions(textField); 1089 return; 1090 } 1091 1092 var originalValue = textField.val(); //store the value of the element 1093 1094 textField.attr("contenteditable", "true"); 1095 textField.addClass(classNameTextFieldEditMode); 1096 textField.focus(); 1097 textField.val(''); //clear the value of the element 1098 textField.val(originalValue); 1099 1100 let mediumEditorButtons = [ 1101 'bold', 1102 'italic', 1103 'underline', 1104 'strikethrough', 1105 'html' 1106 ]; 1107 if(!isParentAnchor) 1108 mediumEditorButtons.push('anchor'); 1109 1110 if(extendedTextFormatting){ 1111 mediumEditorButtons.push('orderedlist'); 1112 mediumEditorButtons.push('unorderedlist'); 1113 } 1114 1115 let allowHtmlPasing = (textField.attr("data-html-paste") == 'true'); 1116 1117 mediumEditor = new MediumEditor(element, { 1118 autoLink: false, 1119 buttonLabels: 'fontawesome', 1120 spellcheck: false, 1121 imageDragging: false, 1122 placeholder: false, 1123 paste: { 1124 forcePlainText: !allowHtmlPasing, 1125 cleanPastedHTML: !allowHtmlPasing, 1126 // cleanTags: ['meta', 'p'], 1127 // unwrapTags: ['p'], 1128 doPaste: function (pastedHTML, pastedPlain, editable) { 1129 if(!allowHtmlPasing){ 1130 var untaggedText = UntagHtml(pastedPlain); 1131 PasteHtmlAtCaret(untaggedText); 1132 } 1133 SetEditorActionContainerPosition(textField, $("#textEditorActions")); 1134 } 1135 }, 1136 toolbar: { 1137 /* These are the default options for the toolbar, 1138 if nothing is passed this is what is used */ 1139 allowMultiParagraphSelection: true, 1140 buttons: mediumEditorButtons, 1141 diffLeft: 0, 1142 diffTop: -10, 1143 firstButtonClass: 'medium-editor-button-first', 1144 lastButtonClass: 'medium-editor-button-last', 1145 relativeContainer: null, 1146 standardizeSelectionStart: false, 1147 static: false, 1148 /* options which only apply when static is true */ 1149 align: 'center',
1150 sticky: false, 1151 updateOnEmptySelection: true 1152 }, 1153 anchor: { 1154 targetCheckbox: true, 1155 targetCheckboxText: 'Open in new tab' 1156 } 1157 }); 1158 1159 var textEditorActionContainer = $("#textEditorActions"); 1160 SetEditorActionContainerPosition(textField, textEditorActionContainer); 1161 1162 var eventType = /Trident/.test( navigator.userAgent ) ? 'keyup' : 'input'; 1163 1164 textField.unbind(eventType).bind(eventType, function() { 1165 // change position of text editor action container when the textField size changes 1166 SetEditorActionContainerPosition(textField, textEditorActionContainer); 1167 }); 1168 1169 window.addEventListener("resize", function(){ 1170 if(textField.hasClass(classNameTextFieldEditMode)) 1171 SetEditorActionContainerPosition(textField, textEditorActionContainer); 1172 }); 1173 1174 var saveButton = $("#textEditorActionSave"); 1175 var cancelButton = $("#textEditorActionCancel"); 1176 1177 saveButton.unbind('click').bind('click', function(){ 1178 var text = textField.html(); 1179 if(!IsTextValid(text)){ 1180 ShowTextInvalidAlert(function(){ 1181 textField.focus(); 1182 }); 1183 return; 1184 } 1185 1186 let breakPlaceholder = '%%BREAK_PLACEHOLDER%%'; 1187 text = text.replace(/<p[^>]*><br><\/p>/g, breakPlaceholder); 1188 text = text.replace(/<br><\/p>/g, breakPlaceholder); 1189 text = text.replace(/<br>/g, breakPlaceholder); 1190 text = text.replace(/<\/p>/g, breakPlaceholder); 1191 text = text.replace(/<p[^>]*>/g, ''); 1192 text = text.replace(/<div[^>]*>/g, '').replace(/<\/div>/g, ''); 1193 text = text.replace(/%%BREAK_PLACEHOLDER%%/g, '<br>'); 1194 1195 // check if text ends with "<br>" 1196 // replace break tag if text ends with a single break tag and not with multiple (like '<br><br>') 1197 const BreakTag = "<br>"; 1198 const BreakTagLength = BreakTag.length; 1199 if(text.substring(text.length - BreakTagLength, text.length) === BreakTag){ 1200 if(text.substring(text.length - 2 * BreakTagLength, text.length - BreakTagLength) !== BreakTag) 1201 text = text.substring(0, text.length - BreakTagLength); 1202 } 1203 1204 let isParameter = textField.attr("data-is-parameter") === "true"; 1205 let isVariable = textField.attr("data-parameter-is-variable") === "true"; 1206 1207 if(isParameter){ 1208 UpdateParameterInDatabase(key, text, isVariable, function(){ 1209 textEditorActionContainer.hide(); 1210 $("label[data-key='" + key + "']").each(function(){ 1211 $(this).attr("contenteditable", "false").html(text); 1212 }); 1213 }); 1214 } else { 1215 SaveTextToDatabase(key, text, function(){ 1216 textEditorActionContainer.hide(); 1217 $("label[data-key='" + key + "']").each(function(){ 1218 $(this).attr("contenteditable", "false").html(text); 1219 }); 1220 }); 1221 } 1222 1223 ResetTextFieldToDefault(textField); 1224 }); 1225 1226 cancelButton.unbind('click').bind('click', function(){ 1227 RestoreOriginalValue(textField); 1228 textEditorActionContainer.hide(); 1229 }); 1230 1231 textEditorActionContainer.show(); 1232 } 1233 1234 // reset other active text editor fields to default 1235 RestoreOriginalValue($('label[contenteditable="true"]:first'), onOriginalValueRestored); 1236} 1237 1238function RestoreOriginalValue(textField, onFinishedCallback){ 1239 if(textField == null || textField.length == 0){ 1240 if(onFinishedCallback != undefined) 1241 onFinishedCallback(); 1242 return; 1243 } 1244 if(mediumEditor !== null) mediumEditor.destroy(); 1245 var key = textField.attr('data-key'); 1246 1247 let isParameter = textField.attr("data-is-parameter") === "true"; 1248 1249 if(isParameter){ 1250 LoadParameterFromDatabase(key, function(parameterValue){ 1251 textField.html(parameterValue); 1252 ResetTextFieldToDefault(textField); 1253 }); 1254 }else{ 1255 LoadTextFromDatabase(key, function(text){ 1256 textField.html(text); 1257 ResetTextFieldToDefault(textField); 1258 }); 1259 } 1260 1261} 1262 1263function LoadTextFromDatabase(translationKey, callback){ 1264 var postData = { }; 1265 postData[POST_NAME_PAGE_TAG] = $('#pageTag').val(); 1266 postData[POST_NAME_TRANSLATION_KEY] = translationKey; 1267 jQuery.ajax({ 1268 url: "/ddm-framework/util/ajax/load-page-text-single.php", 1269 type: 'POST', 1270 data: postData, 1271 responseType: 'json', 1272 success:function(response){ 1273 if(response.result == true){ 1274 if(typeof callback === "function") 1275 callback(response.data); 1276 }else AjaxErrorAlert("something went wrong, could load text from database: " + response.message); 1277 }, 1278 error:function (request, status, error){ 1279 AjaxErrorAlert("something went wrong, could load text from database: " + request.responseText); 1280 } 1281 }); 1282} 1283 1284function LoadParameterFromDatabase(parameterKey, callback){ 1285 var postData = { }; 1286 postData[POST_NAME_PAGE_TAG] = $('#pageTag').val(); 1287 postData[POST_NAME_PARAMETER_KEY] = parameterKey; 1288 jQuery.ajax({ 1289 url: "/ddm-framework/util/ajax/load-parameter-value.php", 1290 type: 'POST', 1291 data: postData, 1292 responseType: 'json', 1293 success:function(response){ 1294 if(response.result == true){ 1295 if(typeof callback === "function") 1296 callback(response.data); 1297 }else AjaxErrorAlert("something went wrong, could load text from database: " + response.message); 1298 }, 1299 error:function (request, status, error){ 1300 AjaxErrorAlert("something went wrong, could load text from database: " + request.responseText); 1301 } 1302 }); 1303} 1304 1305/** 1306 * Sets contenteditable to false and removes the active editor styles 1307 * Label is in default mode afterwards 1308 * @param {element} textField label with contenteditable tag 1309 */ 1310function ResetTextFieldToDefault(textField){ 1311 textField.attr("contenteditable", "false"); 1312 textField.removeClass("ddm-admin-textinput-active"); 1313 textField.unbind("input"); 1314} 1315 1316/**
1317 * Returns false when text contains nothing other than spaces or is empty 1318 * @param {string} text 1319 */ 1320function IsTextValid(text){ 1321 return text.length >= 1 && text.replace(/ /g, "").trim() != ''; 1322} 1323 1324/** 1325 * shows an alert telling the user that the entered text is too short 1326 * @param {function} onConfirm callback when user clicks on the confirmation button 1327 */ 1328function ShowTextInvalidAlert(onConfirm){ 1329 var textKeyTitle = "ddm-page-text-text-editor-text-too-short-title"; 1330 var textKeyMessage = "ddm-page-text-text-editor-text-too-short-message"; 1331 var textKeyButtonText = "ddm-page-text-ok"; 1332 var textKeys = textKeyTitle + "," + textKeyMessage + "," + textKeyButtonText; 1333 LoadMultipleTextsFromDatabase(textKeys, function(texts){ 1334 var title = texts[textKeyTitle] != null ? texts[textKeyTitle] : "Oops!"; 1335 var message = texts[textKeyMessage] != null ? texts[textKeyMessage] : "Text must contain at least one character."; 1336 var buttonText = texts[textKeyButtonText] != null ? texts[textKeyButtonText] : "OK"; 1337 Swal.fire({ 1338 title: title, 1339 text: message, 1340 icon: 'error', 1341 confirmButtonText: buttonText 1342 }).then( function(result) { 1343 if (result.value) { 1344 if (onConfirm != null && onConfirm instanceof Function) { 1345 onConfirm(); 1346 } 1347 } 1348 }); 1349 }); 1350} 1351 1352/** 1353 * Sets position of text editor action container based on the textField position 1354 * @param {element} textField label with contenteditable tag 1355 */ 1356function SetEditorActionContainerPosition(textField, editorActionContainer){ 1357 var fixed = IsElementOrParentFixed(textField); 1358 1359 editorActionContainer.attr("style", ''); // clear inline styles 1360 editorActionContainer.css("position", fixed ? "fixed" : "absolute"); 1361 if(fixed) 1362 editorActionContainer.css("z-index", "710"); 1363 1364 var leftPos = textField.offset().left + textField.width() + 10; 1365 var viewPortWidth = $(window).width(); 1366 if(leftPos + editorActionContainer.width() > viewPortWidth) 1367 leftPos = viewPortWidth - editorActionContainer.width() - 5; 1368 editorActionContainer.css("left", leftPos + 'px'); 1369 if(fixed) 1370 editorActionContainer.css("top", textField.offset().top - $(document).scrollTop() + 'px'); 1371 else editorActionContainer.css("top", textField.offset().top + 'px'); 1372} 1373 1374/** 1375 * Pastes text into active textField 1376 * @param {text} html 1377 */ 1378function PasteHtmlAtCaret(html) { 1379 var sel, range; 1380 if (window.getSelection) { 1381 // IE9 and non-IE 1382 sel = window.getSelection(); 1383 if (sel.getRangeAt && sel.rangeCount) { 1384 range = sel.getRangeAt(0); 1385 range.deleteContents(); 1386 1387 // Range.createContextualFragment() would be useful here but is 1388 // only relatively recently standardized and is not supported in 1389 // some browsers (IE9, for one) 1390 html = html.replace(/<p[^>]*>/g, '').replace(/<\/p>/g, '<br><br>'); 1391 html = html.replace(/<div[^>]*>/g, '').replace(/<\/p>/g, '<br><br>'); 1392 var el = document.createElement("div"); 1393 el.innerHTML = html; 1394 var frag = document.createDocumentFragment(), node, lastNode; 1395 while ( (node = el.firstChild) ) { 1396 lastNode = frag.appendChild(node); 1397 } 1398 range.insertNode(frag); 1399 1400 // Preserve the selection 1401 if (lastNode) { 1402 range = range.cloneRange(); 1403 range.setStartAfter(lastNode); 1404 range.collapse(true); 1405 sel.removeAllRanges(); 1406 sel.addRange(range); 1407 } 1408 } 1409 } else if (document.selection && document.selection.type != "Control") { 1410 // IE < 9 1411 document.selection.createRange().pasteHTML(html); 1412 } 1413} 1414 1415 const validImageUploadFileExtensions = ['gif','png','jpg','jpeg','svg']; 1416 const validVideoUploadFileExtensions = ['mp4','webm']; 1417 const validAudioUploadFileExtensions = ['wav','mpeg','ogg','mp3']; 1418 const validDocumentUploadFileExtensions = ['pdf']; 1419 1420 const acceptImageUploadFileExtensions = '.gif,.png,.jpg,.jpeg,.svg'; 1421 const acceptVideoUploadFileExtensions = '.mp4,.webm'; 1422 const acceptAudioUploadFileExtensions = '.wav,.mpeg,.ogg,.mp3'; 1423 const acceptDocumentUploadFileExtensions = '.pdf'; 1424 1425 const maxUploadFileSizeImage = 10000; 1426 const maxUploadFileSizeVideo = 100000; 1427 const maxUploadFileSizeAudio = 120000; 1428 const maxUploadFileSizeDocument = 30000; 1429 const textMediaEditorUploading = "Wird hochgeladen..."; 1430 const textMediaEditorChooseFile = "Datei auswählen"; 1431 const textMediaEditorError = "Fehlgeschlagen. Upload war nicht erfolgreich."; 1432 const textMediaEditorSuccess = "Erfolgreich hochgeladen."; 1433/** Media Editor **/ 1434 1435function MediaEditor(mediaKey){ 1436 $('*[data-media-reference="' + mediaKey + '"]').click(); 1437} 1438 1439$(function(){ 1440 InitMediaEditor(); 1441}); 1442 1443var fileBuffer = null; 1444var replaceFileIfExists = false;
1445 1446 1447function InitMediaEditor(){ 1448 1449 /* Variables */ 1450 var mediaUploader = null; // initialized later 1451 var mediaUploadForm = $('#media-upload-form'); 1452 var videoPreviewWrapper = $('.media-editor-video-preview-wrapper'); 1453 var audioPreviewWrapper = $('.media-editor-audio-preview-wrapper'); 1454 var documentPreviewWrapper = $('.media-editor-document-preview-wrapper'); 1455 var altTextContainer = $('.ddm-image-upload-alt-text-input'); 1456 var altTextInput = $('#ddm-media-editor-alt-text'); 1457 var uploadMessageBoxSuccess = $('#upload-message-box-success'); 1458 var uploadMessageBoxError = $('#upload-message-box-error'); 1459 var buttonMediaEditorSet = $('#ddm-media-upload-form-button-set'); 1460 var buttonMediaEditorReplaceFile = $('#ddm-media-upload-form-button-replace-file'); 1461 var buttonMediaEditorCancel = $('#ddm-media-upload-form-button-cancel'); 1462 var storageMediaKey = $('#ddm-media-editor-media-key'); 1463 var storageMediaID = $('#ddm-media-editor-media-id'); 1464 var storageMediaFilePath = $('#ddm-media-editor-media-file-path'); 1465 var storageMediaMimeType = $('#ddm-media-editor-media-mime-type'); 1466 1467 function SetMediaEditorVisibility(visible){ 1468 var editor = $('#ddm-media-editor'); 1469 if(visible) 1470 editor.show(); 1471 else editor.hide(); 1472 } 1473 1474 $("body").on("click", "*[data-editor='true']", function(event){ 1475 event.preventDefault(); 1476 event.stopPropagation(); 1477 1478 var editorButton = $(this); 1479 var mediaKey = editorButton.attr("data-media-reference"); 1480 var mediaType = editorButton.attr("data-media-type"); 1481 var mediaSrc = editorButton.attr("data-media-src"); 1482 var mediaAlt = editorButton.attr("data-media-alt"); 1483 var mediaID = editorButton.attr("data-media-id"); 1484 1485 InitMediaEditor(mediaType); 1486 BufferMediaInformation(mediaKey, mediaID, mediaSrc); 1487 var mediaElement = $('*[data-key="' + mediaKey + '"]'); 1488 1489 if(mediaType === MEDIA_TYPE_IMAGE) 1490 SetupEditorWithImage(mediaSrc, mediaAlt); 1491 if(mediaType === MEDIA_TYPE_VIDEO) 1492 SetupEditorWithVideo(mediaSrc, mediaElement.attr("type")); 1493 if(mediaType === MEDIA_TYPE_AUDIO) 1494 SetupEditorWithAudio(mediaSrc, mediaElement.attr("type")); 1495 if(mediaType === MEDIA_TYPE_DOCUMENT) 1496 SetupEditorWithDocument(mediaSrc, mediaElement.attr("type")); 1497 1498 SetMediaEditorVisibility(true); 1499 }); 1500 1501 function InitMediaEditor(mediaType){ 1502 mediaUploadForm.removeClass('dark'); 1503 mediaUploadForm.css('backgroundImage', 'none'); 1504 altTextContainer.hide(); 1505 altTextInput.val(''); 1506 videoPreviewWrapper.hide(); 1507 audioPreviewWrapper.hide(); 1508 documentPreviewWrapper.hide(); 1509 uploadMessageBoxSuccess.hide(); 1510 uploadMessageBoxError.empty(); 1511 buttonMediaEditorSet.show(); 1512 buttonMediaEditorSet.prop('disabled', true); 1513 buttonMediaEditorReplaceFile.hide(); 1514 replaceFileIfExists = false; 1515 1516 // init uploader 1517 if(mediaUploader != null) 1518 mediaUploader.destroy(); 1519 InitUploader(mediaType); 1520 } 1521 1522 function SetupEditorWithImage(src, alt){ 1523 if(src != undefined && src != ''){ 1524 mediaUploadForm.css('backgroundImage', 'url("' + src + '?' + new Date().getTime() + '")'); 1525 mediaUploadForm.addClass('dark'); 1526 buttonMediaEditorSet.prop('disabled', false); 1527 altTextInput.val(alt); 1528 altTextContainer.show(); 1529 } 1530 } 1531 1532 function SetupEditorWithVideo(src, fileType){ 1533 if(src != undefined && src != ''){ 1534 var videoPreview = $('#media-editor-video-preview'); 1535 videoPreview.attr("src", src + '?' + new Date().getTime()); 1536 videoPreview.attr("type", fileType); 1537 var video = videoPreview.parent()[0]; 1538 video.load(); 1539 video.play(); 1540 mediaUploadForm.addClass('dark'); 1541 buttonMediaEditorSet.prop('disabled', false); 1542 videoPreviewWrapper.show(); 1543 } 1544 } 1545 1546 function SetupEditorWithAudio(src, fileType){ 1547 if(src != undefined && src != ''){ 1548 var audioPreview = $('#media-editor-audio-preview'); 1549 audioPreview.attr("src", src + '?' + new Date().getTime()); 1550 audioPreview.attr("type", fileType); 1551 var audio = audioPreview.parent()[0]; 1552 audio.load(); 1553 mediaUploadForm.addClass('dark'); 1554 buttonMediaEditorSet.prop('disabled', false); 1555 audioPreviewWrapper.show(); 1556 } 1557 } 1558 1559 function SetupEditorWithDocument(src, fileType){ 1560 if(src != undefined && src != ''){ 1561 var docPreview = $('#media-editor-document-preview'); 1562 mediaUploadForm.addClass('dark'); 1563 buttonMediaEditorSet.prop('disabled', false); 1564 documentPreviewWrapper.show(); 1565 } 1566 } 1567 1568 function BufferMediaInformation(mediaKey, mediaID, src){ 1569 storageMediaKey.val(mediaKey); 1570 storageMediaID.val(mediaID); 1571 storageMediaFilePath.val(src); 1572 } 1573 1574 function InitUploader(mediaType){ 1575 // following texts are generated in media-editor.php (textMediaEditorUploading, textMediaEditorChooseFile, textMediaEditorError, textMediaEditorSuccess) 1576 var textUploading = textMediaEditorUploading; 1577 var textChooseFile = textMediaEditorChooseFile; 1578 var textError = textMediaEditorError; 1579 var textSuccess = textMediaEditorSuccess; 1580 1581 var allowedExtensions = null; 1582 var acceptedExtensions = null; 1583 var maxFileSize = null; 1584 1585 switch(mediaType){ 1586 case MEDIA_TYPE_IMAGE: 1587 allowedExtensions = validImageUploadFileExtensions; 1588 acceptedExtensions = acceptImageUploadFileExtensions; 1589 maxFileSize = maxUploadFileSizeImage; 1590 break; 1591 case MEDIA_TYPE_VIDEO: 1592 allowedExtensions = validVideoUploadFileExtensions; 1593 acceptedExtensions = acceptVideoUploadFileExtensions; 1594 maxFileSize = maxUploadFileSizeVideo; 1595 break; 1596 case MEDIA_TYPE_AUDIO: 1597 allowedExtensions = validAudioUploadFileExtensions; 1598 acceptedExtensions = acceptAudioUploadFileExtensions; 1599 maxFileSize = maxUploadFileSizeAudio; 1600 break; 1601 case MEDIA_TYPE_DOCUMENT: 1602 allowedExtensions = validDocumentUploadFileExtensions; 1603 acceptedExtensions = acceptDocumentUploadFileExtensions; 1604 maxFileSize = maxUploadFileSizeDocument; 1605 break; 1606 } 1607 1608 var buttonUpload = document.getElementById('ddm-media-editor-button-upload'), 1609 progressBar = document.getElementById('ddm-media-editor-progress-bar'), 1610 progressOuter = document.getElementById('ddm-media-editor-progress-wrapper'); 1611 1612 1613 mediaUploader = new ss.SimpleUpload({ 1614 button: buttonUpload, 1615 dropzone: 'media-upload-dropzone', 1616 allowedExtensions: allowedExtensions, 1617 accept: acceptedExtensions, 1618 url: '/ddm-framework/modules/upload/upload.php', 1619 progressUrl: '/ddm-framework/lib/upload/uploadProgress.php', 1620 name: POST_NAME_MEDIA_UPLOAD_FILE, 1621 multipart: true, 1622 responseType: 'json', 1623 debug: false, 1624 autoSubmit: false, 1625 maxSize: maxFileSize, 1626 startXHR: function(){ 1627 progressOuter.style.display = 'flex'; // make progress bar visible 1628 }, 1629 onChange: function( filename, extension, uploadBtn, fileSize, file ){ 1630 CheckFileExistence(filename, mediaType, function(result, message){ 1631 if(result === true) 1632 mediaUploader.submit(); 1633 else OnUploadFileExists(message); 1634 }); 1635 }, 1636 onSubmit: function(filename, extension, uploadBtn, size){ 1637 this.setProgressBar(progressBar); 1638 var data = {}; 1639 data[POST_NAME_MEDIA_TYPE_NAME] = mediaType; 1640 data[POST_NAME_MEDIA_UPLOAD_REPLACE_FILE] = true; 1641 mediaUploader.setData(data); 1642 uploadMessageBoxSuccess.hide(); 1643 uploadMessageBoxError.empty(); 1644 buttonUpload.innerHTML = textUploading; 1645 }, 1646 onComplete: function(filename, response, uploadBtn, size) { 1647 replaceFileIfExists = false;
1648 progressOuter.style.display = 'none'; // hide progress bar when upload is completed 1649 if (!response) { 1650 uploadMessageBoxError.html(textError); 1651 return; 1652 } 1653 1654 let resultCode = response.resultCode; 1655 1656 switch(resultCode){ 1657 case UPLOAD_RESULT_CODE_OK: 1658 uploadMessageBoxSuccess.show(); 1659 buttonUpload.innerHTML = textChooseFile; 1660 OnUploadSuccess(response.mediaID, response.mediaFilePath, response.fileType); 1661 break; 1662 case UPLOAD_RESULT_CODE_ERROR: 1663 buttonUpload.innerHTML = textChooseFile; 1664 let errorMessage = response.message; 1665 if (errorMessage){ 1666 errorMessage = "Error: " + EscapeTags(errorMessage); 1667 } else { 1668 errorMessage = textError; 1669 } 1670 OnUploadError(errorMessage); 1671 break; 1672 case UPLOAD_RESULT_CODE_FILE_EXISTS: 1673 OnUploadFileExists(response.message); 1674 break; 1675 } 1676 }, 1677 onError: function(filename, type, status, statusText, response, uploadBtn, size){ 1678 progressOuter.style.display = 'none'; 1679 OnUploadError(textError); 1680 }, 1681 onExtError: function( filename, extension ) { 1682 console.log("exterror"); 1683 OnUploadError("Error: Filetype '." + extension + "' not allowed. Allowed file types: " + acceptedExtensions); 1684 }, 1685 onSizeError: function( filename, fileSize, uploadBtn ) { 1686 console.log("onSizeError"); 1687 OnUploadError("Error: File size exceeded. (" + (fileSize/1000).toFixed(1) + "MB instead of max. " + (maxFileSize/1000).toFixed(1) + "MB)"); 1688 }, 1689 }); 1690 } 1691 1692 function OnUploadSuccess(mediaID, mediaFilePath, fileType){ 1693 buttonMediaEditorSet.show(); 1694 buttonMediaEditorSet.prop('disabled', false); 1695 buttonMediaEditorReplaceFile.hide(); 1696 mediaUploadForm.css('backgroundImage', 'none'); 1697 var mediaType = GetEditingMediaType(); 1698 switch(mediaType){ 1699 case MEDIA_TYPE_IMAGE: 1700 // show alt text input if it's about an image 1701 altTextContainer.show(); 1702 altTextInput.focus();
1703 SetMediaUploadFormBackground(mediaFilePath); 1704 break; 1705 case MEDIA_TYPE_VIDEO: 1706 SetupEditorWithVideo(mediaFilePath, fileType); 1707 break; 1708 case MEDIA_TYPE_AUDIO: 1709 SetupEditorWithAudio(mediaFilePath, fileType); 1710 break; 1711 case MEDIA_TYPE_DOCUMENT: 1712 SetupEditorWithDocument(mediaFilePath, fileType); 1713 break; 1714 } 1715 storageMediaFilePath.val(mediaFilePath); 1716 storageMediaID.val(mediaID); 1717 storageMediaMimeType.val(fileType); 1718 } 1719 1720 function OnUploadError(errorMessage){ 1721 buttonMediaEditorSet.show(); 1722 buttonMediaEditorSet.prop('disabled', true); 1723 buttonMediaEditorReplaceFile.hide(); 1724 uploadMessageBoxError.html(errorMessage); 1725 altTextContainer.hide(); 1726 videoPreviewWrapper.hide(); 1727 audioPreviewWrapper.hide(); 1728 SetMediaUploadFormBackground('/ddm-framework/img/upload_image_error.png') 1729 } 1730 1731 function OnUploadFileExists(message){ 1732 buttonMediaEditorSet.hide(); 1733 buttonMediaEditorReplaceFile.show(); 1734 altTextContainer.hide(); 1735 uploadMessageBoxError.html(message);
1736 SetMediaUploadFormBackground(null); 1737 } 1738 1739 function GetEditingMediaType(){ 1740 var editingElement = $('*[data-media-reference="' + storageMediaKey.val() + '"'); 1741 return editingElement.attr("data-media-type"); 1742 } 1743 1744 function SetMediaUploadFormBackground(mediaFilePath){ 1745 if(mediaFilePath) 1746 mediaUploadForm.css('backgroundImage', 'url("' + EscapeTags(mediaFilePath) + '?' + new Date().getTime() + '")'); 1747 else mediaUploadForm.css('backgroundImage', null); 1748 mediaUploadForm.css('backgroundSize', 'cover'); 1749 mediaUploadForm.addClass('dark'); 1750 } 1751 1752 /** Button Click Listeners */ 1753 1754 buttonMediaEditorCancel.unbind("click").bind("click", function(event){ 1755 event.preventDefault(); 1756 SetMediaEditorVisibility(false); 1757 }); 1758 1759 buttonMediaEditorReplaceFile.unbind("click").bind("click", function(){ 1760 mediaUploader.submit(); 1761 }); 1762 1763 buttonMediaEditorSet.unbind("click").bind("click", function(event){ 1764 event.preventDefault(); 1765 1766 var mediaKey = storageMediaKey.val(); 1767 var mediaID = storageMediaID.val(); 1768 var altText = altTextInput.val(); 1769 1770 var postData = {}; 1771 postData[POST_NAME_MEDIA_ID] = mediaID; 1772 postData[POST_NAME_PAGE_MEDIA_KEY] = mediaKey; 1773 postData[POST_NAME_PAGE_MEDIA_ALT_TEXT] = altText; 1774 1775 mediaUploadForm.find("video")[0].pause(); 1776 mediaUploadForm.find("audio")[0].pause(); 1777 1778 jQuery.ajax({ 1779 url: "/ddm-framework/util/ajax/update-page-media.php", 1780 type: 'POST', 1781 responseType: 'json', 1782 data: postData, 1783 success:function(data){ 1784 let mediaElement = $('*[data-key="' + mediaKey + '"'); 1785 let editorButton = $('*[data-media-reference="' + mediaKey + '"'); 1786 let mediaFilePath = data.mediaFilePath; 1787 let mediaType = editorButton.attr("data-media-type"); 1788 1789 mediaElement.attr("src", mediaFilePath + '?' + new Date().getTime()); 1790 editorButton.attr("data-media-src", mediaFilePath); 1791 editorButton.attr("data-media-id", mediaID); 1792 switch(mediaType){ 1793 case "image": 1794 // image -> set alternate text after changing source 1795 mediaElement.attr("alt", altText); 1796 editorButton.attr("data-media-alt", altText); 1797 break; 1798 case "video": 1799 case "audio": 1800 // video/audio -> reload video or audio after changing source 1801 mediaElement.parent()[0].load(); 1802 mediaElement.attr("type", storageMediaMimeType.val()); 1803 break; 1804 case "document": 1805 let fileName = mediaFilePath.split('\\').pop().split('/').pop(); 1806 mediaElement.attr("href", mediaFilePath + '?' + new Date().getTime()); 1807 mediaElement.attr("download", fileName); 1808 break; 1809 } 1810 var changedData = {}; 1811 changedData[POST_NAME_PAGE_MEDIA_KEY] = mediaKey; 1812 changedData[POST_NAME_MEDIA_ID] = mediaID; 1813 changedData[POST_NAME_MEDIA_FILE_PATH] = mediaFilePath; 1814 changedData[POST_NAME_PAGE_MEDIA_ALT_TEXT] = altText; 1815 $(document).trigger(EVENT_LISTENER_MEDIA_EDITOR_CONTENT_CHANGED, changedData); 1816 SetMediaEditorVisibility(false); 1817 }, 1818 error:function (xhr, status, error){ 1819 Swal.fire({ 1820 type: "error", 1821 html: xhr.responseText 1822 }); 1823 } 1824 }); 1825 }); 1826 1827 function CheckFileExistence(fileName, mediaType, callback){ 1828 var formData = new FormData(); 1829 formData.append(POST_NAME_MEDIA_FILE_NAME, fileName); 1830 formData.append(POST_NAME_MEDIA_TYPE_NAME, mediaType); 1831 formData.append(POST_NAME_MEDIA_UPLOAD_REPLACE_FILE, replaceFileIfExists); 1832 1833 $.ajax({ 1834 url: '/ddm-framework/modules/upload/file-validation.php', 1835 data: formData, 1836 type: 'POST', 1837 dataType: 'json', 1838 contentType: false, 1839 processData: false, 1840 success: function(response){ 1841 if(typeof callback === "function") 1842 callback(response.resultCode === UPLOAD_RESULT_CODE_OK, response.message); 1843 }, 1844 error: function(xhr, status, error){ 1845 console.error("Error while checking file existence... " + error); 1846 if(typeof callback === "function") 1847 callback(false, error); 1848 } 1849 }); 1850 } 1851} 1852/** Url Editor **/ 1853 1854const EditableUrlBoxHeightThreshold = 100; // height (in px) - if height of url box is smaller than this value, the "center-vertical" class will be added to the contextMenuOpener 1855 1856$(function(){ 1857 var urlBoxes = $('.ddm-editable-url-box'); 1858 urlBoxes.each(function(index, urlBox){ 1859 let $urlBox = $(urlBox); 1860 let height = $urlBox.height();
1861 let contextMenuOpener = $urlBox.children('.ddm-context-menu-opener'); 1862 if(height < 100 && contextMenuOpener.length > 0){ 1863 contextMenuOpener.addClass("center-vertical"); 1864 } 1865 }); 1866 1867 urlBoxes.on("mouseover", function(event){ 1868 urlBoxes.find(".ddm-admin-textinput").css('cursor', event.ctrlKey ? 'pointer' : ''); 1869 }); 1870 1871 $(document).on('keydown keyup', function(event) { 1872 urlBoxes.find(".ddm-admin-textinput").css('cursor', event.ctrlKey ? 'pointer' : ''); 1873 }); 1874 1875 urlBoxes.find(".ddm-admin-textinput").click(function(event){ 1876 if(event.ctrlKey){ 1877 event.preventDefault(); 1878 OpenUrl(event, $(this).parents(".ddm-editable-url-box").attr("data-key")); 1879 } 1880 }); 1881}); 1882 1883/** 1884 * Shows an overlay letting the user change the text of the url and the url itself 1885 * @param {Event} event 1886 * @param {String} key 1887 */ 1888function EditUrl(event, key){ 1889 event.preventDefault(); 1890 1891 CloseContextMenu(); // close the context menu 1892 1893 let urlEditor = $('#ddm-url-editor'); 1894 urlEditor.show(); 1895 1896 let urlElement = $('a[data-key=' + key + ']'); 1897 1898 var inputUrl = $('#ddm-url-edit-input-url'); 1899 var inputTarget = $('#ddm-url-edit-input-target'); 1900 var errorLabel = $('#ddm-url-editor-error-label'); 1901 var inputText = urlEditor.find('#ddm-url-editor-text-edit'); 1902 inputText.hide(); // hide text input 1903 errorLabel.hide(); 1904 1905 var href = urlElement.attr('href'); 1906 var urlTarget = urlElement.attr('target'); 1907 inputUrl.val(href); 1908 inputTarget.prop('checked', urlTarget == '_blank'); 1909 1910 $('#ddm-url-edit-button-cancel').unbind('click').bind('click', function(){ 1911 urlEditor.hide(); 1912 }); 1913 1914 $('#ddm-url-edit-button-save').unbind('click').bind('click', function(){ 1915 inputText.hide(); // hide text input 1916 var urlKey = key + "-url"; // url database key is same as text id but with "-url" suffix - see DDM-Toolbox.DDMUrl (function) 1917 var urlTarget = key + "-target"; // target database key is same as text id but with "-target" suffix - see DDM-Toolbox.DDMUrl (function) 1918 var target = inputTarget.prop('checked') ? '_blank' : '_self'; 1919 1920 var valuesToBeSaved = []; 1921 valuesToBeSaved.push({key: urlKey, value: inputUrl.val()}); 1922 valuesToBeSaved.push({key: urlTarget, value: target}); 1923 1924 valuesToBeSaved.forEach(function(item, index){ 1925 SaveTextToDatabase(item.key, item.value, function(){ 1926 if(index == valuesToBeSaved.length - 1){ 1927 urlElement.attr("href", inputUrl.val()); 1928 urlElement.attr("target", target); 1929 urlEditor.hide(); 1930 } 1931 }); 1932 }); 1933 }); 1934} 1935 1936/** 1937 * Opens URL based on tag information (URL, target) 1938 * @param {Object} event event 1939 * @param {String} key database storage key of the editable url 1940 */ 1941function OpenUrl(event, key){ 1942 event.preventDefault(); 1943 let urlElement = $('a[data-key=' + key + ']')[0]; 1944 window.open(urlElement.href, urlElement.target, '' ); 1945} 1946/** Checkbox Editor **/ 1947 1948$(document).on('change', '.ddm-checkbox[data-editable="true"]', function(){ 1949 let ddmCheckbox = $(this); 1950 let parameterKey = ddmCheckbox.attr('data-parameter-key'); 1951 let checkbox = ddmCheckbox.children('input[type="checkbox"]'); 1952 let checkboxValue = 0; 1953 if(checkbox.length > 0) 1954 checkboxValue = checkbox.prop('checked') ? 1 : 0; 1955 UpdateParameterInDatabase(parameterKey, checkboxValue, false, function(){ 1956 ddmCheckbox.attr("data-checked", checkbox.prop('checked')); 1957 }); 1958}); 1959/** Hideable **/ 1960 1961$(function(){ 1962 $('.ddm-hideable > .ddm-hideable-controls .ddm-admin-checkbox input[type="checkbox"]').on("change", function(){ 1963 1964 let checked = this.checked; 1965 let hideableContainer = $(this).parents(".ddm-hideable"); 1966 let hideableContent = hideableContainer.children(".ddm-hideable-content"); 1967 1968 if(checked) 1969 hideableContent.slideUp("slow"); 1970 else hideableContent.slideDown("slow"); 1971 }); 1972}); 1973$(function(){ 1974 const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); 1975 const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0
1975); 1976 1977 $(document).on('click', ".ddm-context-menu-opener", function(e){ 1978 e.preventDefault(); 1979 e.stopPropagation(); 1980 let $this = $(this); 1981 let fixed = IsElementOrParentFixed(this); 1982 let contextMenu = $this.parent().children('.ddm-context-menu'); 1983 let clone = contextMenu.clone(true); 1984 let positionX = $this.offset().left - contextMenu.width(); 1985 let positionY = $this.offset().top; 1986 1987 let screenPadding = 10; 1988 let documentWidth = fixed ? vw : $(document).width(); 1989 let documentHeight = fixed ? vh : $(document).height(); 1990 1991 if(fixed) 1992 positionY = positionY - $(document).scrollTop(); 1993 1994 // prevent going off screen left 1995 if(positionX <= screenPadding) 1996 positionX = screenPadding; 1997 1998 // prevent going off screen right 1999 if(positionX + contextMenu.width + screenPadding > documentWidth) 2000 positionX = documentWidth - screenPadding - contextMenu.width; 2001 2002 // prevent going off screen top 2003 if(positionY < screenPadding) 2004 positionY = screenPadding; 2005 2006 // prevent going off screen bottom 2007 if(positionY + contextMenu.height() + screenPadding > documentHeight) 2008 positionY = documentHeight - contextMenu.height() - screenPadding; 2009 2010 2011 clone.css("position", fixed ? "fixed" : "absolute"); 2012 clone.css("left", positionX + "px"); 2013 clone.css("right", "auto"); 2014 clone.css("top", positionY + "px"); 2015 if(fixed) 2016 clone.css("z-index", 700); 2017 $('body').append(clone); 2018 2019 setTimeout(function(){ 2020 clone.toggleClass("active"); 2021 }, 20); 2022 }); 2023 2024 $('.ddm-context-menu').unbind('click').bind('click', function(e){ 2025 e.stopPropagation(); 2026 }); 2027 2028 $('html').unbind('click').bind('click', function(event) { 2029 if($(event.target).hasClass('ddm-admin-action-button')) return; // do not close context menu if user clicked on admin action button, such as text editor buttons (save, cancel) 2030 CloseContextMenu(); 2031 }); 2032}); 2033 2034function CloseContextMenu(){ 2035 $('.ddm-context-menu').removeClass("active"); 2036 $('body > .ddm-context-menu').remove(); 2037}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.