fit-ui 2.8.4 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/Controls/Input/maximize-highres.png +0 -0
  2. package/dist/Controls/Input/maximize.png +0 -0
  3. package/dist/Documentation.html +3 -3
  4. package/dist/Fit.UI.css +71 -6
  5. package/dist/Fit.UI.js +973 -106
  6. package/dist/Fit.UI.min.css +2 -2
  7. package/dist/Fit.UI.min.js +1 -1
  8. package/dist/Resources/CKEditor/plugins/custombuttons/plugin.js +34 -0
  9. package/dist/Resources/CKEditor-with-maximize-button/CHANGES.md +2148 -0
  10. package/dist/Resources/CKEditor-with-maximize-button/LICENSE.md +1436 -0
  11. package/dist/Resources/CKEditor-with-maximize-button/README-FitUiChanges.txt +8 -0
  12. package/dist/Resources/CKEditor-with-maximize-button/README.md +39 -0
  13. package/dist/Resources/CKEditor-with-maximize-button/adapters/jquery.js +10 -0
  14. package/dist/Resources/CKEditor-with-maximize-button/build-config.js +79 -0
  15. package/dist/Resources/CKEditor-with-maximize-button/ckeditor.js +987 -0
  16. package/dist/Resources/CKEditor-with-maximize-button/config.js +34 -0
  17. package/dist/Resources/CKEditor-with-maximize-button/contents.css +208 -0
  18. package/dist/Resources/CKEditor-with-maximize-button/lang/da.js +5 -0
  19. package/dist/Resources/CKEditor-with-maximize-button/lang/de.js +5 -0
  20. package/dist/Resources/CKEditor-with-maximize-button/lang/en.js +5 -0
  21. package/dist/Resources/CKEditor-with-maximize-button/plugins/autocomplete/skins/default.css +38 -0
  22. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/LICENSE.md +1244 -0
  23. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/README.md +21 -0
  24. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/dialogs/base64image.js +766 -0
  25. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/dialogs/base64image.original.js +503 -0
  26. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/icons/base64image.png +0 -0
  27. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/icons/hidpi/base64image.png +0 -0
  28. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/lang/da.js +12 -0
  29. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/lang/de.js +12 -0
  30. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/lang/en.js +12 -0
  31. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64image/plugin.js +58 -0
  32. package/dist/Resources/CKEditor-with-maximize-button/plugins/base64imagepaste/plugin.js +91 -0
  33. package/dist/Resources/CKEditor-with-maximize-button/plugins/clipboard/dialogs/paste.js +11 -0
  34. package/dist/Resources/CKEditor-with-maximize-button/plugins/custombuttons/icons/custombuttons.png +0 -0
  35. package/dist/Resources/CKEditor-with-maximize-button/plugins/custombuttons/plugin.js +99 -0
  36. package/dist/Resources/CKEditor-with-maximize-button/plugins/dialog/dialogDefinition.js +4 -0
  37. package/dist/Resources/CKEditor-with-maximize-button/plugins/dialog/styles/dialog.css +18 -0
  38. package/dist/Resources/CKEditor-with-maximize-button/plugins/dragresize/LICENSE +19 -0
  39. package/dist/Resources/CKEditor-with-maximize-button/plugins/dragresize/_source.css +85 -0
  40. package/dist/Resources/CKEditor-with-maximize-button/plugins/dragresize/package.json +19 -0
  41. package/dist/Resources/CKEditor-with-maximize-button/plugins/dragresize/plugin.js +395 -0
  42. package/dist/Resources/CKEditor-with-maximize-button/plugins/dragresize/readme.md +35 -0
  43. package/dist/Resources/CKEditor-with-maximize-button/plugins/emoji/assets/iconsall.png +0 -0
  44. package/dist/Resources/CKEditor-with-maximize-button/plugins/emoji/assets/iconsall.svg +58 -0
  45. package/dist/Resources/CKEditor-with-maximize-button/plugins/emoji/emoji.json +1 -0
  46. package/dist/Resources/CKEditor-with-maximize-button/plugins/emoji/skins/default.css +237 -0
  47. package/dist/Resources/CKEditor-with-maximize-button/plugins/icons.png +0 -0
  48. package/dist/Resources/CKEditor-with-maximize-button/plugins/icons_hidpi.png +0 -0
  49. package/dist/Resources/CKEditor-with-maximize-button/plugins/link/dialogs/anchor.js +8 -0
  50. package/dist/Resources/CKEditor-with-maximize-button/plugins/link/dialogs/link.js +30 -0
  51. package/dist/Resources/CKEditor-with-maximize-button/plugins/link/images/anchor.png +0 -0
  52. package/dist/Resources/CKEditor-with-maximize-button/plugins/link/images/hidpi/anchor.png +0 -0
  53. package/dist/Resources/CKEditor-with-maximize-button/plugins/pastefromword/filter/default.js +42 -0
  54. package/dist/Resources/CKEditor-with-maximize-button/plugins/pastetools/filter/common.js +24 -0
  55. package/dist/Resources/CKEditor-with-maximize-button/plugins/pastetools/filter/image.js +12 -0
  56. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/dialog.css +1 -0
  57. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/dialog_ie.css +1 -0
  58. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/dialog_ie7.css +1 -0
  59. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/dialog_ie8.css +1 -0
  60. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/dialog_iequirks.css +1 -0
  61. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/dialog_opera.css +1 -0
  62. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/editor.css +1 -0
  63. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/editor_gecko.css +1 -0
  64. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/editor_ie.css +1 -0
  65. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/editor_ie7.css +1 -0
  66. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/editor_ie8.css +1 -0
  67. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/editor_iequirks.css +1 -0
  68. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/icons.png +0 -0
  69. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/icons_hidpi.png +0 -0
  70. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/arrow.png +0 -0
  71. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/close.png +0 -0
  72. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/hidpi/close.png +0 -0
  73. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/hidpi/lock-open.png +0 -0
  74. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/hidpi/lock.png +0 -0
  75. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/hidpi/refresh.png +0 -0
  76. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/lock-open.png +0 -0
  77. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/lock.png +0 -0
  78. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/images/refresh.png +0 -0
  79. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/readme.md +35 -0
  80. package/dist/Resources/CKEditor-with-maximize-button/skins/bootstrapck/skin.js +10 -0
  81. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/dialog.css +5 -0
  82. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/dialog_ie.css +5 -0
  83. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/dialog_ie8.css +5 -0
  84. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/dialog_iequirks.css +5 -0
  85. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/editor.css +5 -0
  86. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/editor_gecko.css +5 -0
  87. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/editor_ie.css +5 -0
  88. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/editor_ie8.css +5 -0
  89. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/editor_iequirks.css +5 -0
  90. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/icons.png +0 -0
  91. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/icons_hidpi.png +0 -0
  92. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/arrow.png +0 -0
  93. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/close.png +0 -0
  94. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/hidpi/close.png +0 -0
  95. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/hidpi/lock-open.png +0 -0
  96. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/hidpi/lock.png +0 -0
  97. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/hidpi/refresh.png +0 -0
  98. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/lock-open.png +0 -0
  99. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/lock.png +0 -0
  100. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/refresh.png +0 -0
  101. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/images/spinner.gif +0 -0
  102. package/dist/Resources/CKEditor-with-maximize-button/skins/moono-lisa/readme.md +46 -0
  103. package/dist/Resources/CKEditor-with-maximize-button/styles.js +137 -0
  104. package/dist/Resources/CKEditor-with-maximize-button/vendor/promise.js +13 -0
  105. package/package.json +1 -1
  106. package/types/index.d.ts +231 -102
  107. package/dist/Resources/ckeditor_4.17.2_6f06412961d8.zip +0 -0
package/dist/Fit.UI.js CHANGED
@@ -682,7 +682,7 @@ Fit._internal =
682
682
  {
683
683
  Core:
684
684
  {
685
- VersionInfo: { Major: 2, Minor: 8, Patch: 4 } // Do NOT modify format - version numbers are programmatically changed when releasing new versions - MUST be on a separate line!
685
+ VersionInfo: { Major: 2, Minor: 9, Patch: 0 } // Do NOT modify format - version numbers are programmatically changed when releasing new versions - MUST be on a separate line!
686
686
  }
687
687
  };
688
688
 
@@ -2909,7 +2909,7 @@ Fit.Browser.GetScreenDimensions = function(onlyAvailable)
2909
2909
  }
2910
2910
 
2911
2911
  /// <function container="Fit.Browser" name="IsMobile" access="public" static="true" returns="boolean">
2912
- /// <description> Returns value indicating whether devices currently being used is a mobile device or not </description>
2912
+ /// <description> Returns value indicating whether device is a mobile device or not </description>
2913
2913
  /// <param name="includeTablets" type="boolean" default="true"> Value indicating whether tablets are considered mobile devices or not </param>
2914
2914
  /// </function>
2915
2915
  Fit.Browser.IsMobile = function(includeTablets)
@@ -2927,6 +2927,14 @@ Fit.Browser.IsMobile = function(includeTablets)
2927
2927
  return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(nav) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(nav.substr(0,4)));
2928
2928
  }
2929
2929
 
2930
+ /// <function container="Fit.Browser" name="IsTouchEnabled" access="public" static="true" returns="boolean">
2931
+ /// <description> Returns value indicating whether device is touch enabled or not </description>
2932
+ /// </function>
2933
+ Fit.Browser.IsTouchEnabled = function()
2934
+ {
2935
+ return ("ontouchstart" in window);
2936
+ }
2937
+
2930
2938
 
2931
2939
  /// <function container="Fit.Browser" name="Log" access="public" static="true">
2932
2940
  /// <description> Log message or object </description>
@@ -2968,6 +2976,7 @@ Fit.Browser.LogDeprecated = function(msg)
2968
2976
  /// <member name="IsMobile" type="boolean"> Boolean indicating whether this is a mobile device (tablet or phone) </member>
2969
2977
  /// <member name="IsPhone" type="boolean"> Boolean indicating whether this is a phone </member>
2970
2978
  /// <member name="IsTablet" type="boolean"> Boolean indicating whether this is a tablet device </member>
2979
+ /// <member name="IsTouchEnabled" type="boolean"> Boolean indicating whether this is a touch enabled device </member>
2971
2980
  /// </container>
2972
2981
 
2973
2982
  /// <container name="Fit.BrowserTypeDefs.BrowserInfo" extends="Fit.BrowserTypeDefs.BrowserDetails">
@@ -3014,6 +3023,7 @@ Fit.Browser.GetInfo = function(returnAppInfo)
3014
3023
  Fit._internal.Browser.Info.IsMobile = Fit.Browser.IsMobile();
3015
3024
  Fit._internal.Browser.Info.IsPhone = Fit.Browser.IsMobile(false); // Phone only
3016
3025
  Fit._internal.Browser.Info.IsTablet = (Fit.Browser.IsMobile() === true && Fit.Browser.IsMobile(false) === false); // Tablet only
3026
+ Fit._internal.Browser.Info.IsTouchEnabled = Fit.Browser.IsTouchEnabled();
3017
3027
  }
3018
3028
 
3019
3029
  return Fit.Core.Clone(Fit._internal.Browser.Info); // Clone to ensure values are not shared and potentially changed
@@ -5187,6 +5197,28 @@ Fit.String.DecodeHtml = function(str)
5187
5197
  return str.replace(/&quot;/g, "\"").replace(/&#39;/g, "'").replace(/&#039;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
5188
5198
  }
5189
5199
 
5200
+ /// <function container="Fit.String" name="ParseImageBlobUrls" access="public" static="true" returns="string[]">
5201
+ /// <description> Parse and return image blob URLs from image tags </description>
5202
+ /// <param name="str" type="string"> String to parse </param>
5203
+ /// </function>
5204
+ Fit.String.ParseImageBlobUrls = function(str)
5205
+ {
5206
+ var imageBlobUrls = [];
5207
+ var blobImages = str.match(/<img .*?src=(["'])blob:.+?\1.*?>/gi) || [];
5208
+
5209
+ Fit.Array.ForEach(blobImages, function(img)
5210
+ {
5211
+ var blobUrl = img.match(/src=(["'])(blob:.*?)\1/i)[2]; // 0 = Full match, 1 = double quote or ping character, 2 = blob URL
5212
+
5213
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
5214
+ {
5215
+ Fit.Array.Add(imageBlobUrls, blobUrl);
5216
+ }
5217
+ });
5218
+
5219
+ return imageBlobUrls;
5220
+ }
5221
+
5190
5222
  /// <function container="Fit.String" name="Hash" access="public" static="true" returns="integer">
5191
5223
  /// <description> Get Java compatible Hash Code from string </description>
5192
5224
  /// <param name="str" type="string"> String to get hash code from </param>
@@ -6741,15 +6773,17 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6741
6773
 
6742
6774
  // Private properties
6743
6775
 
6776
+ var me = this;
6744
6777
  var elm = domElm;
6745
6778
  var posState = null; // { position: "", left: "", top: "" };
6746
6779
  var trgElm = (domTriggerElm ? domTriggerElm : null);
6747
- var me = this;
6780
+ var bringToFrontOnActivation = false;
6748
6781
 
6749
6782
  var onDragStart = null;
6750
6783
  var onDragging = null;
6751
6784
  var onDragStop = null;
6752
6785
 
6786
+ var activationEventId = -1;
6753
6787
  var mouseDownEventId = -1;
6754
6788
 
6755
6789
  // Construct
@@ -6761,15 +6795,33 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6761
6795
  Fit.Dom.AddClass(elm, "FitDragDropDraggable");
6762
6796
  Fit.Dom.AddClass((trgElm !== null ? trgElm : elm), "FitDragDropDraggableHandle");
6763
6797
 
6764
- // Mouse down
6798
+ // Bring to front on activation
6765
6799
 
6766
- mouseDownEventId = Fit.Events.AddHandler(((trgElm !== null) ? trgElm : elm), "mousedown", function(e)
6800
+ activationEventId = Fit.Events.AddHandler(elm, (Fit.Browser.IsTouchEnabled() === true ? "touchstart" : "mousedown"), function(e)
6767
6801
  {
6768
- var ev = e || window.event;
6802
+ if (bringToFrontOnActivation === true)
6803
+ {
6804
+ me.BringToFront();
6805
+ }
6806
+ });
6769
6807
 
6808
+ // Mouse down
6809
+
6810
+ mouseDownEventId = Fit.Events.AddHandler(((trgElm !== null) ? trgElm : elm), (Fit.Browser.IsTouchEnabled() === true ? "touchstart" : "mousedown"), function(e)
6811
+ {
6770
6812
  if (Fit.DragDrop.Draggable._internal.active !== null)
6771
6813
  return; // Skip - current element is a draggable parent to which event propagated
6772
6814
 
6815
+ var ev = Fit.Events.GetEvent(e);
6816
+
6817
+ if (onDragStart)
6818
+ {
6819
+ if (onDragStart(elm) === false)
6820
+ {
6821
+ return;
6822
+ }
6823
+ }
6824
+
6773
6825
  Fit.Dom.AddClass(elm, "FitDragDropDragging");
6774
6826
 
6775
6827
  // Initial positioning (used by Reset())
@@ -6792,12 +6844,9 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6792
6844
  }
6793
6845
 
6794
6846
  // Mouse position in viewport
6795
- var mouseXviewport = (ev.clientX || e.pageX);
6796
- var mouseYviewport = (ev.clientY || e.pageY);
6797
-
6798
- // Make sure element being dragged is on top of every other draggable element
6799
- Fit.DragDrop.Draggable._internal.zIndex++;
6800
- elm.style.zIndex = Fit.DragDrop.Draggable._internal.zIndex;
6847
+ var pp = Fit.Events.GetPointerState().Coordinates.ViewPort;
6848
+ var mouseXviewport = pp.X;
6849
+ var mouseYviewport = pp.Y;
6801
6850
 
6802
6851
  // Create state information object for draggable currently being dragged
6803
6852
  var state =
@@ -6806,8 +6855,8 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6806
6855
  Positioning: "relative",
6807
6856
  Mouse: {Viewport: {X: -1, Y: -1}, Document: {X: -1, Y: -1}},
6808
6857
  Position: {Viewport: {X: -1, Y: -1}, Document: {X: -1, Y: -1}, Offset: {X: -1, Y: -1}},
6809
- Events: { OnDragStart: onDragStart, OnDragging: onDragging, OnDragStop: onDragStop },
6810
- OnSelectStart : document.onselectstart
6858
+ Events: { OnDragStart: onDragStart, OnDragging: onDragging, OnDragStop: onDragStop }/*,
6859
+ OnSelectStart : document.onselectstart*/
6811
6860
  };
6812
6861
 
6813
6862
  var positioning = Fit.Dom.GetComputedStyle(elm, "position");
@@ -6816,8 +6865,9 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6816
6865
  state.Positioning = positioning;
6817
6866
  }
6818
6867
 
6819
- // Disable text selection for legacy browsers
6820
- document.onselectstart = function() { return false; }
6868
+ // Disable text selection for legacy browsers.
6869
+ // DISABLED: Now handled using Fit.Events.PreventDefault(ev).
6870
+ //document.onselectstart = function() { return false; }
6821
6871
 
6822
6872
  // Find mouse position in viewport
6823
6873
  state.Mouse.Viewport.X = mouseXviewport;
@@ -6867,12 +6917,10 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6867
6917
  Fit.Array.Add(Fit.DragDrop.DropZone._internal.dropzones, draggableDropZone);
6868
6918
  }
6869
6919
 
6870
- if (state.Events.OnDragStart)
6871
- state.Events.OnDragStart(elm);
6872
-
6873
- /*if (ev.preventDefault)
6874
- ev.preventDefault();
6875
- ev.cancelBubble = true;*/
6920
+ if (ev.type === "touchstart")
6921
+ {
6922
+ Fit.Events.PreventDefault(ev); // Stop page scrolling on iOS (required in both ontouchstart and ontouchmove handlers)
6923
+ }
6876
6924
  });
6877
6925
 
6878
6926
  // Mouse Up
@@ -6881,13 +6929,11 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6881
6929
  {
6882
6930
  Fit.DragDrop.Draggable._internal.mouseUpRegistered = true;
6883
6931
 
6884
- Fit.Events.AddHandler(document, "mouseup", function(e)
6932
+ Fit.Events.AddHandler(document, (Fit.Browser.IsTouchEnabled() === true ? "touchend" : "mouseup"), function(e)
6885
6933
  {
6886
6934
  if (Fit.DragDrop.Draggable._internal.active === null)
6887
6935
  return;
6888
6936
 
6889
- var ev = e || window.event;
6890
-
6891
6937
  var draggableState = Fit.DragDrop.Draggable._internal.active;
6892
6938
  var draggable = Fit.DragDrop.Draggable._internal.active.Draggable;
6893
6939
 
@@ -6920,7 +6966,7 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6920
6966
  draggableState.Events.OnDragStop(draggable);
6921
6967
 
6922
6968
  // Restore OnSelectStart event
6923
- document.onselectstart = draggableState.OnSelectStart;
6969
+ //document.onselectstart = draggableState.OnSelectStart;
6924
6970
  });
6925
6971
  }
6926
6972
 
@@ -6930,12 +6976,12 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6930
6976
  {
6931
6977
  Fit.DragDrop.Draggable._internal.mouseMoveRegistered = true;
6932
6978
 
6933
- Fit.Events.AddHandler(document, "mousemove", function(e)
6979
+ Fit.Events.AddHandler(document, (Fit.Browser.IsTouchEnabled() === true ? "touchmove" : "mousemove"), { passive: false /* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener */ }, function(e)
6934
6980
  {
6935
6981
  if (Fit.DragDrop.Draggable._internal.active === null)
6936
6982
  return;
6937
6983
 
6938
- var ev = e || window.event;
6984
+ var ev = Fit.Events.GetEvent(e);
6939
6985
 
6940
6986
  // Handle draggable
6941
6987
 
@@ -6944,8 +6990,16 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
6944
6990
  var elm = draggable.GetElement();
6945
6991
 
6946
6992
  // Mouse position in viewport
6947
- var mouseXviewport = (ev.clientX || e.pageX);
6948
- var mouseYviewport = (ev.clientY || e.pageY);
6993
+ var pp = Fit.Events.GetPointerState().Coordinates.ViewPort;
6994
+ var mouseXviewport = pp.X;
6995
+ var mouseYviewport = pp.Y;
6996
+
6997
+ // Prevent user from moving object out of viewport
6998
+ var vpDim = Fit.Browser.GetViewPortDimensions();
6999
+ mouseXviewport = mouseXviewport >= 0 ? mouseXviewport : 0;
7000
+ mouseXviewport = mouseXviewport <= vpDim.Width ? mouseXviewport : vpDim.Width;
7001
+ mouseYviewport = mouseYviewport >= 0 ? mouseYviewport : 0;
7002
+ mouseYviewport = mouseYviewport <= vpDim.Height ? mouseYviewport : vpDim.Height;
6949
7003
 
6950
7004
  // Positioning (fixed, absolute, or relative)
6951
7005
  var positioning = Fit.DragDrop.Draggable._internal.active.Positioning;
@@ -7045,12 +7099,55 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
7045
7099
  if (dropzoneActive.OnEnter)
7046
7100
  dropzoneActive.OnEnter(dropzoneActive.DropZone);
7047
7101
  }
7102
+
7103
+ // Stop page scrolling on iOS (required in both ontouchstart and ontouchmove handlers).
7104
+ // Ontouchmove must be registered with { passive: false } for this to work in browsers
7105
+ // which defaults to passive:true. This also prevents text selection on older browsers
7106
+ // not supporting user-select:none.
7107
+
7108
+ Fit.Events.PreventDefault(ev);
7048
7109
  });
7049
7110
  }
7050
7111
  }
7051
7112
 
7052
7113
  // Public
7053
7114
 
7115
+ /// <function container="Fit.DragDrop.Draggable" name="BringToFrontOnActivation" access="public" returns="boolean">
7116
+ /// <description> Get/set flag indicating whether to bring draggable to front when activated </description>
7117
+ /// <param name="val" type="boolean" default="undefined"> If defined, a value of True enables functionality, False disables it (default) </param>
7118
+ /// </function>
7119
+ this.BringToFrontOnActivation = function(val)
7120
+ {
7121
+ Fit.Validation.ExpectBoolean(val, true);
7122
+
7123
+ if (Fit.Validation.IsSet(val) === true)
7124
+ {
7125
+ bringToFrontOnActivation = val;
7126
+
7127
+ // NOTICE: z-index is not reset when BringToFrontOnActivation is disabled.
7128
+ // It may have been enabled at some point, but we keep the z-index to make
7129
+ // sure the element remains below other elements that have BringToFrontOnActivation
7130
+ // enabled. Resetting the z-index style attribute will cause the z-index value of
7131
+ // 99999 from CSS to be applied when dragging the element, causing it to temporarily
7132
+ // be positioned above elements with BringToFrontOnActivation enabled while dragging it.
7133
+ // That becomes bad UX when the user stops dragging it and releases it, at which point
7134
+ // the temporary z-index value of 99999 from CSS is no longer in effect and z-index:1200
7135
+ // takes effect again, which causes the element to be hidden behind elements with
7136
+ // BringToFrontOnActivation enabled.
7137
+ }
7138
+
7139
+ return bringToFrontOnActivation;
7140
+ }
7141
+
7142
+ /// <function container="Fit.DragDrop.Draggable" name="BringToFront" access="public">
7143
+ /// <description> Bring draggable to front </description>
7144
+ /// </function>
7145
+ this.BringToFront = function()
7146
+ {
7147
+ Fit.DragDrop.Draggable._internal.zIndex++;
7148
+ elm.style.zIndex = Fit.DragDrop.Draggable._internal.zIndex;
7149
+ }
7150
+
7054
7151
  /// <function container="Fit.DragDrop.Draggable" name="Reset" access="public">
7055
7152
  /// <description> Reset draggable to initial position </description>
7056
7153
  /// </function>
@@ -7062,6 +7159,7 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
7062
7159
  elm.style.position = posState.position;
7063
7160
  elm.style.left = posState.left;
7064
7161
  elm.style.top = posState.top;
7162
+ elm.style.zIndex = "";
7065
7163
 
7066
7164
  posState = null;
7067
7165
  }
@@ -7091,9 +7189,15 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
7091
7189
  Fit.Dom.RemoveClass(elm, "FitDragDropDraggable");
7092
7190
  Fit.Dom.RemoveClass((trgElm !== null ? trgElm : elm), "FitDragDropDraggableHandle");
7093
7191
 
7192
+ Fit.Events.RemoveHandler(elm, activationEventId);
7094
7193
  Fit.Events.RemoveHandler(((trgElm !== null) ? trgElm : elm), mouseDownEventId);
7095
7194
 
7096
- me = elm = posState = trgElm = onDragStart = onDragging = onDragStop = mouseDownEventId = null;
7195
+ if (Fit.DragDrop.Draggable._internal.active !== null && Fit.DragDrop.Draggable._internal.active.Draggable === me)
7196
+ {
7197
+ Fit.DragDrop.Draggable._internal.active = null;
7198
+ }
7199
+
7200
+ me = elm = posState = trgElm = bringToFrontOnActivation = onDragStart = onDragging = onDragStop = activationEventId = mouseDownEventId = null;
7097
7201
  }
7098
7202
 
7099
7203
  // Event handling
@@ -14608,16 +14712,21 @@ Fit.Controls.Dialog = function(controlId)
14608
14712
  var dialog = me.GetDomElement();
14609
14713
  var focusTrapStart = null;
14610
14714
  var focusTrapEnd = null;
14611
- var title = null;
14715
+ var titleContainer = null;
14716
+ var titleText = null;
14612
14717
  var titleButtons = null;
14613
14718
  var cmdMaximize = null;
14614
14719
  var cmdDismiss = null;
14615
14720
  var content = null;
14616
14721
  var iframe = null;
14722
+ var resizer = null;
14617
14723
  var buttons = null;
14618
14724
  var modal = false;
14619
14725
  var layer = null;
14620
14726
 
14727
+ var draggable = null;
14728
+ var suppressPositioning = false;
14729
+
14621
14730
  var width = null;
14622
14731
  var minWidth = null;
14623
14732
  var maxWidth = null;
@@ -14642,6 +14751,7 @@ Fit.Controls.Dialog = function(controlId)
14642
14751
  Fit.Dom.AddClass(dialog, "FitUiControlDialog");
14643
14752
  Fit.Dom.Data(dialog, "framed", "false");
14644
14753
  Fit.Dom.Data(dialog, "maximized", "false");
14754
+ Fit.Dom.Data(dialog, "resizable", "false");
14645
14755
 
14646
14756
  focusTrapStart = document.createElement("div");
14647
14757
  focusTrapStart.tabIndex = 0;
@@ -14716,44 +14826,43 @@ Fit.Controls.Dialog = function(controlId)
14716
14826
 
14717
14827
  if (val !== undefined) // Allow null to remove title
14718
14828
  {
14719
- if (val === null && title !== null)
14829
+ if (val === null && titleContainer !== null) // Remove title
14720
14830
  {
14721
- if (titleButtons !== null)
14831
+ if (titleButtons !== null || draggable !== null) // Clear title but keep title bar used for buttons and drag support
14722
14832
  {
14723
- Fit.Dom.Text(title, "");
14724
- Fit.Dom.Add(title, titleButtons);
14833
+ Fit.Dom.Text(titleText, "");
14725
14834
  }
14726
- else
14835
+ else // Remove title bar
14727
14836
  {
14728
- Fit.Dom.Remove(title);
14729
- title = null;
14837
+ Fit.Dom.Remove(titleContainer);
14838
+ titleContainer = null;
14839
+ titleText = null;
14730
14840
 
14731
14841
  setContentHeight();
14732
14842
  updatePosition();
14733
14843
  }
14734
14844
  }
14735
- else
14845
+ else // Set title
14736
14846
  {
14737
- if (title === null)
14847
+ if (titleContainer === null)
14738
14848
  {
14739
- title = document.createElement("div");
14740
- Fit.Dom.AddClass(title, "FitUiControlDialogTitle");
14741
- Fit.Dom.InsertAfter(focusTrapStart, title);
14742
- }
14849
+ titleContainer = document.createElement("div");
14850
+ Fit.Dom.AddClass(titleContainer, "FitUiControlDialogTitle");
14851
+ Fit.Dom.InsertAfter(focusTrapStart, titleContainer);
14743
14852
 
14744
- Fit.Dom.Text(title, val);
14745
-
14746
- if (titleButtons !== null)
14747
- {
14748
- Fit.Dom.Add(title, titleButtons);
14853
+ titleText = document.createElement("div");
14854
+ Fit.Dom.AddClass(titleText, "FitUiControlDialogTitleText");
14855
+ Fit.Dom.Add(titleContainer, titleText);
14749
14856
  }
14750
14857
 
14858
+ Fit.Dom.Text(titleText, val);
14859
+
14751
14860
  setContentHeight();
14752
14861
  updatePosition();
14753
14862
  }
14754
14863
  }
14755
14864
 
14756
- return (title !== null ? Fit.Dom.Text(title) : null);
14865
+ return (titleText !== null ? Fit.Dom.Text(titleText) : null);
14757
14866
  }
14758
14867
 
14759
14868
  /// <function container="Fit.Controls.Dialog" name="Width" access="public" returns="Fit.TypeDefs.CssValue">
@@ -14775,12 +14884,17 @@ Fit.Controls.Dialog = function(controlId)
14775
14884
  width = { Value: val, Unit: ((Fit.Validation.IsSet(unit) === true) ? unit : "px") };
14776
14885
  dialog.style.width = width.Value + width.Unit;
14777
14886
 
14778
- if (minWidth === null)
14887
+ // Disable default min-width and default max-width from CSS when width alone is set, unless Resizable is
14888
+ // enabled, in which case we must prevent dialog from "collapsing" completely due to resizing, or become
14889
+ // larger than the viewport. When resizing is enabled, appropriate values for min-width and max-width are
14890
+ // set from CSS, but can be overridden using MinimumWidth(..) and MaximumWidth(..)
14891
+
14892
+ if (minWidth === null && me.Resizable() === false)
14779
14893
  {
14780
14894
  dialog.style.minWidth = "0";
14781
14895
  }
14782
14896
 
14783
- if (maxWidth === null)
14897
+ if (maxWidth === null && me.Resizable() === false)
14784
14898
  {
14785
14899
  dialog.style.maxWidth = "none";
14786
14900
  }
@@ -14819,6 +14933,7 @@ Fit.Controls.Dialog = function(controlId)
14819
14933
 
14820
14934
  // defaultValue must match min-width in Dialog.css
14821
14935
  var defaultValue = { Value: 280, Unit: "px" };
14936
+ var defaultValueResizable = { Value: 15, Unit: "em" };
14822
14937
 
14823
14938
  if (Fit.Validation.IsSet(val) === true)
14824
14939
  {
@@ -14830,12 +14945,17 @@ Fit.Controls.Dialog = function(controlId)
14830
14945
  else
14831
14946
  {
14832
14947
  minWidth = null;
14833
- dialog.style.minWidth = (width !== null ? "0" : ""); // Apply "0" (no min-width) if width is set
14948
+ dialog.style.minWidth = (width !== null && me.Resizable() === false ? "0" : ""); // Apply "0" (no min-width) if width is set, to override min-width from CSS - similar logic found in Width(..)
14834
14949
  }
14835
14950
 
14836
14951
  updatePosition();
14837
14952
  }
14838
14953
 
14954
+ if (minWidth === null && me.Resizable() === true)
14955
+ {
14956
+ return defaultValueResizable;
14957
+ }
14958
+
14839
14959
  return (minWidth !== null ? minWidth : (width !== null ? width : defaultValue));
14840
14960
  }
14841
14961
 
@@ -14851,6 +14971,7 @@ Fit.Controls.Dialog = function(controlId)
14851
14971
 
14852
14972
  // defaultValue must match max-width in Dialog.css
14853
14973
  var defaultValue = { Value: 800, Unit: "px" };
14974
+ var defaultValueResizable = { Value: 100, Unit: "%" };
14854
14975
 
14855
14976
  if (Fit.Validation.IsSet(val) === true)
14856
14977
  {
@@ -14862,12 +14983,17 @@ Fit.Controls.Dialog = function(controlId)
14862
14983
  else
14863
14984
  {
14864
14985
  maxWidth = null;
14865
- dialog.style.maxWidth = (width !== null ? "none" : ""); // Apply "none" (no max-width) if width is set
14986
+ dialog.style.maxWidth = (width !== null && me.Resizable() === false ? "none" : ""); // Apply "none" (no max-width) if width is set, to override max-width from CSS - similar logic found in Width(..)
14866
14987
  }
14867
14988
 
14868
14989
  updatePosition();
14869
14990
  }
14870
14991
 
14992
+ if (maxWidth === null && me.Resizable() === true)
14993
+ {
14994
+ return defaultValueResizable;
14995
+ }
14996
+
14871
14997
  return (maxWidth !== null ? maxWidth : (width !== null ? width : defaultValue));
14872
14998
  }
14873
14999
 
@@ -14916,6 +15042,7 @@ Fit.Controls.Dialog = function(controlId)
14916
15042
 
14917
15043
  // defaultValue must match min-height in Dialog.css (which is not defined)
14918
15044
  var defaultValue = { Value: -1, Unit: "px" };
15045
+ var defaultValueResizable = { Value: 10, Unit: "em" };
14919
15046
 
14920
15047
  if (Fit.Validation.IsSet(val) === true)
14921
15048
  {
@@ -14934,6 +15061,11 @@ Fit.Controls.Dialog = function(controlId)
14934
15061
  updatePosition();
14935
15062
  }
14936
15063
 
15064
+ if (minHeight === null && me.Resizable() === true)
15065
+ {
15066
+ return defaultValueResizable;
15067
+ }
15068
+
14937
15069
  return (minHeight !== null ? minHeight : defaultValue);
14938
15070
  }
14939
15071
 
@@ -14949,6 +15081,7 @@ Fit.Controls.Dialog = function(controlId)
14949
15081
 
14950
15082
  // defaultValue must match max-height in Dialog.css (which is not defined)
14951
15083
  var defaultValue = { Value: -1, Unit: "px" };
15084
+ var defaultValueResizable = { Value: 100, Unit: "%" };
14952
15085
 
14953
15086
  if (Fit.Validation.IsSet(val) === true)
14954
15087
  {
@@ -14967,6 +15100,11 @@ Fit.Controls.Dialog = function(controlId)
14967
15100
  updatePosition();
14968
15101
  }
14969
15102
 
15103
+ if (maxHeight === null && me.Resizable() === true)
15104
+ {
15105
+ return defaultValueResizable;
15106
+ }
15107
+
14970
15108
  return (maxHeight !== null ? maxHeight : defaultValue);
14971
15109
  }
14972
15110
 
@@ -14983,9 +15121,19 @@ Fit.Controls.Dialog = function(controlId)
14983
15121
  if (val !== modal && me.IsOpen() === true)
14984
15122
  {
14985
15123
  if (val === true)
15124
+ {
15125
+ layer.style.zIndex = dialog.style.zIndex; // Update in case dialog was/is draggable and it was brought to front (z-index set by Fit.DragDrop.Draggable when Draggable is enabled)
14986
15126
  Fit.Dom.InsertBefore(dialog, layer);
15127
+
15128
+ if (me.IsOpen() === true)
15129
+ {
15130
+ me.BringToFront();
15131
+ }
15132
+ }
14987
15133
  else
15134
+ {
14988
15135
  Fit.Dom.Remove(layer);
15136
+ }
14989
15137
  }
14990
15138
 
14991
15139
  modal = val;
@@ -15199,6 +15347,84 @@ Fit.Controls.Dialog = function(controlId)
15199
15347
  return (cmdDismiss !== null);
15200
15348
  }
15201
15349
 
15350
+ /// <function container="Fit.Controls.Dialog" name="Draggable" access="public" returns="boolean">
15351
+ /// <description> Get/set flag indicating whether dialog can be moved around on screen </description>
15352
+ /// <param name="val" type="boolean" default="undefined"> If defined, a value of True enables dragging, False disables it (default) </param>
15353
+ /// </function>
15354
+ this.Draggable = function(val)
15355
+ {
15356
+ Fit.Validation.ExpectBoolean(val, true);
15357
+
15358
+ if (Fit.Validation.IsSet(val) === true)
15359
+ {
15360
+ if (val === true && draggable === null)
15361
+ {
15362
+ if (me.Title() === null) // Ensure title element
15363
+ {
15364
+ me.Title("");
15365
+ }
15366
+
15367
+ draggable = new Fit.DragDrop.Draggable(me.GetDomElement(), titleText);
15368
+ draggable.BringToFrontOnActivation(true);
15369
+ draggable.OnDragStart(function()
15370
+ {
15371
+ if (me.Maximized() === true)
15372
+ {
15373
+ return false; // Prevent dragging while maximized
15374
+ }
15375
+
15376
+ suppressPositioning = true;
15377
+ });
15378
+ }
15379
+ else if (val === false && draggable !== null)
15380
+ {
15381
+ draggable.Dispose();
15382
+ draggable = null;
15383
+ suppressPositioning = false;
15384
+
15385
+ if (me.Title() === "")
15386
+ {
15387
+ me.Title(null); // Remove title element - will remain enabled if buttons (dismiss/maximize) are present though
15388
+ }
15389
+
15390
+ updatePosition();
15391
+ }
15392
+ }
15393
+
15394
+ return (draggable !== null);
15395
+ }
15396
+
15397
+ /// <function container="Fit.Controls.Dialog" name="Resizable" access="public" returns="boolean">
15398
+ /// <description> Get/set flag indicating whether dialog can be resized by the user </description>
15399
+ /// <param name="val" type="boolean" default="undefined"> If defined, a value of True enables resizing, False disables it (default) </param>
15400
+ /// </function>
15401
+ this.Resizable = function(val)
15402
+ {
15403
+ Fit.Validation.ExpectBoolean(val, true);
15404
+
15405
+ if (Fit.Validation.IsSet(val) === true)
15406
+ {
15407
+ if (val === true && resizer === null)
15408
+ {
15409
+ resizer = createResizerElement();
15410
+ Fit.Dom.InsertAfter(content, resizer);
15411
+ Fit.Dom.Data(dialog, "resizable", "true");
15412
+ }
15413
+ else if (val === false && resizer !== null)
15414
+ {
15415
+ Fit.Dom.Remove(resizer);
15416
+ resizer = null;
15417
+ Fit.Dom.Data(dialog, "resizable", "false");
15418
+
15419
+ // Undo width/height set by user
15420
+ me.Width(me.Width().Value, me.Width().Unit);
15421
+ me.Height(me.Height().Value, me.Height().Unit);
15422
+ }
15423
+ }
15424
+
15425
+ return (resizer !== null);
15426
+ }
15427
+
15202
15428
  /// <function container="Fit.Controls.Dialog" name="AddButton" access="public">
15203
15429
  /// <description> Add button to dialog </description>
15204
15430
  /// <param name="btn" type="Fit.Controls.Button"> Instance of Fit.Controls.Button </param>
@@ -15298,6 +15524,18 @@ Fit.Controls.Dialog = function(controlId)
15298
15524
  return Fit.Dom.IsRooted(dialog);
15299
15525
  }
15300
15526
 
15527
+ /// <function container="Fit.Controls.Dialog" name="BringToFront" access="public">
15528
+ /// <description> Bring draggable dialog to front </description>
15529
+ /// </function>
15530
+ this.BringToFront = function()
15531
+ {
15532
+ if (draggable !== null)
15533
+ {
15534
+ draggable.BringToFront();
15535
+ layer.style.zIndex = dialog.style.zIndex; // Ensure modal layer always remains exactly behind dialog with the same z-index value
15536
+ }
15537
+ }
15538
+
15301
15539
  /// <function container="Fit.Controls.Dialog" name="Open" access="public">
15302
15540
  /// <description> Open dialog </description>
15303
15541
  /// <param name="renderTarget" type="DOMElement" default="undefined">
@@ -15313,8 +15551,13 @@ Fit.Controls.Dialog = function(controlId)
15313
15551
  if (me.IsOpen() === true)
15314
15552
  return;
15315
15553
 
15554
+ me.BringToFront();
15555
+
15316
15556
  if (modal === true)
15557
+ {
15558
+ layer.style.zIndex = dialog.style.zIndex; // Ensure modal layer always remains exactly behind dialog with the same z-index value
15317
15559
  Fit.Dom.Add(renderTarget || document.body, layer);
15560
+ }
15318
15561
 
15319
15562
  Fit.Dom.Add(renderTarget || document.body, dialog);
15320
15563
 
@@ -15422,6 +15665,11 @@ Fit.Controls.Dialog = function(controlId)
15422
15665
  cmdDismiss.Dispose();
15423
15666
  }
15424
15667
 
15668
+ if (draggable !== null)
15669
+ {
15670
+ draggable.Dispose();
15671
+ }
15672
+
15425
15673
  if (buttons !== null)
15426
15674
  {
15427
15675
  Fit.Array.ForEach(Fit.Array.Copy(buttons.children), function(buttonElm) // Using Copy(..) since Dispose() modifies children collection
@@ -15440,7 +15688,7 @@ Fit.Controls.Dialog = function(controlId)
15440
15688
  Fit.Events.RemoveHandler(window, resizeHandlerId);
15441
15689
  }
15442
15690
 
15443
- me = dialog = title = titleButtons = cmdMaximize = cmdDismiss = content = buttons = modal = layer = width = minWidth = maxWidth = height = minHeight = maxHeight = mutationObserverId = resizeHandlerId = onDismissHandlers = onCloseHandlers = isClosing = null;
15691
+ me = dialog = focusTrapStart = focusTrapEnd = titleContainer = titleText = titleButtons = cmdMaximize = cmdDismiss = content = iframe = resizer = buttons = modal = layer = draggable = suppressPositioning = width = minWidth = maxWidth = height = minHeight = maxHeight = mutationObserverId = resizeHandlerId = onDismissHandlers = onCloseHandlers = isClosing = null;
15444
15692
 
15445
15693
  base();
15446
15694
  });
@@ -15488,6 +15736,17 @@ Fit.Controls.Dialog = function(controlId)
15488
15736
  Fit.Array.Add(onCloseHandlers, cb);
15489
15737
  }
15490
15738
 
15739
+ // ============================================
15740
+ // Protected
15741
+ // ============================================
15742
+
15743
+ this._internal = (this._internal ? this._internal : {});
15744
+
15745
+ this._internal.GetLayerElement = function()
15746
+ {
15747
+ return layer;
15748
+ }
15749
+
15491
15750
  // ============================================
15492
15751
  // Private
15493
15752
  // ============================================
@@ -15499,6 +15758,130 @@ Fit.Controls.Dialog = function(controlId)
15499
15758
  return div;
15500
15759
  }
15501
15760
 
15761
+ function createResizerElement()
15762
+ {
15763
+ // NOTICE: Resizing a dialog with an iframe:
15764
+ // Resizing might not work properly with iframes with content from a foreign domain,
15765
+ // since browsers won't dispatch mouse/touch events over foreign domains.
15766
+ // Some older browsers won't even dispatch the events over iframes with content
15767
+ // originating from the same domain as the one hosting the main page.
15768
+ // This becomes a problem if the user is able to quickly move the mouse over the
15769
+ // iframe while resizing, and the computer/browser is not fast enough to "catch up" with
15770
+ // the movement of the resize handle. Slowly resizing the dialog in this case works.
15771
+ // Possible solution:
15772
+ // If we need better support for resizing dialogs with iframes, consider using a timer
15773
+ // which on a regular basis (2-10 times per second) updates the dialog size based on the
15774
+ // pointer position. That way the missing invocation of OnTouchMove or OnMouseMove is mitigated.
15775
+ // It should also dispatch the logic found in OnTouchEnd, OnTouchCancel and OnMouseUp if the
15776
+ // timer detects that the pointer (mouse or finger) has been released. This can be determined
15777
+ // using: Fit.Events.GetPointerState().Buttons.Touch || Fit.Events.GetPointerState().Buttons.Primary
15778
+ // However, make sure this issue is resolved first: https://github.com/Jemt/Fit.UI/issues/153
15779
+ // The issue relates to the reliablity of Fit.Events.GetPointerState().Buttons.Primary since it
15780
+ // is unset OnMouseOut, which will be triggered if mouse/finger leaves resize handle.
15781
+
15782
+ var resizer = document.createElement("div");
15783
+ Fit.Dom.AddClass(resizer, "FitUiControlDialogResizer");
15784
+
15785
+ Fit.Events.AddHandler(resizer, (Fit.Browser.IsTouchEnabled() === true ? "touchstart" : "mousedown"), function(e)
15786
+ {
15787
+ if (me.Maximized() === true)
15788
+ {
15789
+ return; // Prevent resizing while maximized
15790
+ }
15791
+
15792
+ var ev = Fit.Events.GetEvent(e);
15793
+
15794
+ var initPos = Fit.Events.GetPointerState().Coordinates.ViewPort;
15795
+ var initDim = { Width: me.GetDomElement().offsetWidth, Height: me.GetDomElement().offsetHeight };
15796
+
15797
+ var moveHandler = null; // OnMouseMove or OnTouchMove event ID
15798
+ var releaseHandler = null; // OnMouseUp or OnTouchEnd event ID
15799
+ var cancelHandler = null; // OnTouchCancel event ID (only set on touch devices)
15800
+
15801
+ var cleanup = function()
15802
+ {
15803
+ Fit.Events.RemoveHandler(document, moveHandler);
15804
+ Fit.Events.RemoveHandler(document, releaseHandler);
15805
+
15806
+ if (cancelHandler !== null)
15807
+ {
15808
+ Fit.Events.RemoveHandler(document, cancelHandler);
15809
+ }
15810
+ };
15811
+
15812
+ moveHandler = Fit.Events.AddHandler(document, (Fit.Browser.IsTouchEnabled() === true ? "touchmove" : "mousemove"), { passive: false /* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener */ }, function(e)
15813
+ {
15814
+ if (me === null) // Unlikely, but theoretically possible that dialog gets disposed while being resized
15815
+ {
15816
+ cleanup();
15817
+ return;
15818
+ }
15819
+
15820
+ var ev = Fit.Events.GetEvent(e);
15821
+
15822
+ // Determine mouse/finger travel
15823
+
15824
+ var pp = Fit.Events.GetPointerState().Coordinates.ViewPort;
15825
+ var left = pp.X - initPos.X; // Positive = moved right, negative = moved left
15826
+ var top = pp.Y - initPos.Y; // Positive = moved down, negative = moved up
15827
+
15828
+ if (suppressPositioning === false) // Dialog remains centered - adjust amount of resizing needed ("double up")
15829
+ {
15830
+ left = left * 2; // Double up when dialog is centered since both left and right side is resized
15831
+ top = top * 1.3; // Approximate value to adjust resizing vertically - not 100% accurate - resize handle will not follow pointer precisely, but close enough - reason: dialog is not centered 50/50 vertically but 25/75, so a static value such as 1.3 is not accurate - the actual adjustment needed for accurate positioning is variable
15832
+ }
15833
+
15834
+ // Calculate and apply new width and height
15835
+
15836
+ var newWidth = initDim.Width + left;
15837
+ var newHeight = initDim.Height + top;
15838
+
15839
+ if (newWidth > 0)
15840
+ {
15841
+ dialog.style.width = newWidth + "px";
15842
+ }
15843
+
15844
+ if (newHeight > 0)
15845
+ {
15846
+ dialog.style.height = newHeight + "px";
15847
+ setContentHeight();
15848
+ }
15849
+
15850
+ if (newWidth > 0 || newHeight > 0)
15851
+ {
15852
+ updatePosition();
15853
+ }
15854
+
15855
+ // Stop page scrolling on iOS (required in both ontouchstart and ontouchmove handlers).
15856
+ // Ontouchmove must be registered with { passive: false } for this to work in browsers
15857
+ // which defaults to passive:true. This also prevents text selection on older browsers
15858
+ // not supporting user-select:none.
15859
+
15860
+ Fit.Events.PreventDefault(ev);
15861
+ });
15862
+
15863
+ releaseHandler = Fit.Events.AddHandler(document, (Fit.Browser.IsTouchEnabled() === true ? "touchend" : "mouseup"), function(e)
15864
+ {
15865
+ cleanup();
15866
+ });
15867
+
15868
+ if (Fit.Browser.IsTouchEnabled() === true)
15869
+ {
15870
+ cancelHandler = Fit.Events.AddHandler(document, "touchcancel", function(e) // Can be triggered on iOS by swiping down notification panel or control center while resizing
15871
+ {
15872
+ cleanup();
15873
+ });
15874
+ }
15875
+
15876
+ if (ev.type === "touchstart")
15877
+ {
15878
+ Fit.Events.PreventDefault(ev); // Stop page scrolling on iOS (required in both ontouchstart and ontouchmove handlers)
15879
+ }
15880
+ });
15881
+
15882
+ return resizer;
15883
+ }
15884
+
15502
15885
  function setContentHeight()
15503
15886
  {
15504
15887
  if (me.IsOpen() === false)
@@ -15512,10 +15895,10 @@ Fit.Controls.Dialog = function(controlId)
15512
15895
 
15513
15896
  content.style.height = "";
15514
15897
 
15515
- if ((buttons !== null || title !== null) && (me.Maximized() === true || me.Height().Value !== -1 || me.MinimumHeight().Value !== -1 || me.MaximumHeight().Value !== -1))
15898
+ if ((buttons !== null || titleContainer !== null) && (me.Maximized() === true || me.Height().Value !== -1 || me.MinimumHeight().Value !== -1 || me.MaximumHeight().Value !== -1))
15516
15899
  {
15517
15900
  var dh = dialog.offsetHeight;
15518
- var th = (title !== null ? title.offsetHeight : 0);
15901
+ var th = (titleContainer !== null ? titleContainer.offsetHeight : 0);
15519
15902
  var bh = (buttons !== null ? buttons.offsetHeight : 0);
15520
15903
 
15521
15904
  content.style.height = (dh - th - bh) + "px";
@@ -15524,6 +15907,9 @@ Fit.Controls.Dialog = function(controlId)
15524
15907
 
15525
15908
  function updatePosition()
15526
15909
  {
15910
+ if (suppressPositioning === true)
15911
+ return;
15912
+
15527
15913
  if (me.IsOpen() === false)
15528
15914
  return;
15529
15915
 
@@ -15547,13 +15933,18 @@ Fit.Controls.Dialog = function(controlId)
15547
15933
  elm.style.top = "0px";
15548
15934
 
15549
15935
  var dim = Fit.Browser.GetViewPortDimensions();
15550
- var offsetLeft = Math.floor((dim.Width / 2) - (elm.offsetWidth / 2)); // Center horizontally - place center of dialog 1/2 (50%) from the left
15551
- var offsetTop = Math.floor((dim.Height / 3) - (elm.offsetHeight / 2)); // Place center of dialog 1/3 (33%) from the top
15936
+ var offsetTop = (dim.Height - elm.offsetHeight) * 0.25; // Place dialog vertically with 25% of available space above dialog and remaining 75% space below dialog
15937
+ var offsetLeft = (dim.Width - elm.offsetWidth) * 0.5; // Place dialog exactly in the middle horizontally
15552
15938
 
15553
- if (offsetTop < 0)
15939
+ if (offsetTop < 0) // Value becomes negative if dialog is higher than viewport
15940
+ {
15554
15941
  offsetTop = 0;
15555
- if (offsetLeft < 0)
15942
+ }
15943
+
15944
+ if (offsetLeft < 0) // Value becomes negative if dialog is wider than viewport
15945
+ {
15556
15946
  offsetLeft = 0;
15947
+ }
15557
15948
 
15558
15949
  elm.style.left = offsetLeft + "px";
15559
15950
  elm.style.top = offsetTop + "px";
@@ -15567,6 +15958,12 @@ Fit.Controls.Dialog = function(controlId)
15567
15958
  {
15568
15959
  Fit.Dom.Remove(titleButtons);
15569
15960
  titleButtons = null;
15961
+
15962
+ if (me.Title() === "")
15963
+ {
15964
+ me.Title(null); // Remove title element - will remain enabled if Draggable is enabled
15965
+ }
15966
+
15570
15967
  return;
15571
15968
  }
15572
15969
 
@@ -15579,7 +15976,7 @@ Fit.Controls.Dialog = function(controlId)
15579
15976
 
15580
15977
  titleButtons = document.createElement("div");
15581
15978
  Fit.Dom.AddClass(titleButtons, "FitUiControlDialogTitleButtons");
15582
- Fit.Dom.Add(title, titleButtons);
15979
+ Fit.Dom.Add(titleContainer, titleButtons);
15583
15980
  }
15584
15981
 
15585
15982
  // Add/re-add to ensure proper order
@@ -15624,7 +16021,9 @@ Fit.Controls.Dialog._internal.BaseDialog = function(content, showCancel, cb)
15624
16021
  var d = new Fit.Controls.Dialog();
15625
16022
  d.Content(content.replace(/\n/g, "<br>"));
15626
16023
  d.Modal(true);
16024
+
15627
16025
  Fit.Dom.AddClass(d.GetDomElement(), "FitUiControlDialogBase");
16026
+ Fit.Dom.AddClass(d._internal.GetLayerElement(), "FitUiControlDialogBaseModalLayer");
15628
16027
 
15629
16028
  // Declare buttons
15630
16029
 
@@ -21693,6 +22092,7 @@ Fit.Controls.Input = function(ctlId)
21693
22092
  var designEditorMustDisposeWhenReady = false;
21694
22093
  var designEditorUpdateSizeDebouncer = -1;
21695
22094
  var designEditorActiveToolbarPanel = null; // { DomElement: HTMLElement, UnlockFocusStateIfEmojiPanelIsClosed: function, CloseEmojiPanel: function }
22095
+ var designEditorDetached = null; // { GetValue: function, Reload: function, Dispose: function }
21696
22096
  //var htmlWrappedInParagraph = false;
21697
22097
  var wasAutoChangedToMultiLineMode = false; // Used to revert to single line if multi line was automatically enabled along with DesignMode(true), Maximizable(true), or Resizable(true)
21698
22098
  var minimizeHeight = -1;
@@ -21708,6 +22108,7 @@ Fit.Controls.Input = function(ctlId)
21708
22108
  var debounceOnChangeTimeout = -1;
21709
22109
  var debouncedOnChange = null;
21710
22110
  var imageBlobUrls = []; // Specific to DesignMode
22111
+ var locale = null;
21711
22112
 
21712
22113
  // ============================================
21713
22114
  // Init
@@ -21773,6 +22174,7 @@ Fit.Controls.Input = function(ctlId)
21773
22174
  me._internal.Data("designmode", "false");
21774
22175
 
21775
22176
  Fit.Internationalization.OnLocaleChanged(localize);
22177
+ localize();
21776
22178
 
21777
22179
  me.OnBlur(function(sender)
21778
22180
  {
@@ -22015,20 +22417,22 @@ Fit.Controls.Input = function(ctlId)
22015
22417
  input.value = val;
22016
22418
  }
22017
22419
 
22420
+ // Notice: Identical logic found in DesignMode(true, config)!
22018
22421
  if (designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeExternalBlobUrlsOnDispose === true)
22019
22422
  {
22020
- // Keep track of image blobs added via Value(..) so we can dispose them automatically.
22423
+ // Keep track of image blobs added via Value(..) so we can dispose of them automatically.
22021
22424
  // When RevokeExternalBlobUrlsOnDispose is True it basically means that the Input control
22022
22425
  // is allowed (and expected) to take control over memory management for these blobs
22023
22426
  // based on the rule set in RevokeBlobUrlsOnDispose.
22427
+ // This code is also found in DesignMode(true, config) since images might be added before
22428
+ // editor is created, in which case we do not yet have the editor configuration used to determine
22429
+ // the desired behaviour.
22024
22430
 
22025
- var blobImages = val.match(/<img .*?src=(["'])blob:.+?\1.*?>/gi) || [];
22431
+ var blobUrls = Fit.String.ParseImageBlobUrls(val);
22026
22432
 
22027
- Fit.Array.ForEach(blobImages, function(img)
22433
+ Fit.Array.ForEach(blobUrls, function(blobUrl)
22028
22434
  {
22029
- var blobUrl = img.match(/src=(["'])(blob:.*?)\1/i)[2];
22030
-
22031
- if (Fit.Array.Contains(blobImages, blobUrl) === false)
22435
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
22032
22436
  {
22033
22437
  Fit.Array.Add(imageBlobUrls, blobUrl);
22034
22438
  }
@@ -22240,7 +22644,7 @@ Fit.Controls.Input = function(ctlId)
22240
22644
  });
22241
22645
  }
22242
22646
 
22243
- me = orgVal = preVal = input = cmdResize = designEditor = designEditorDom = designEditorDirty = designEditorDirtyPending = designEditorConfig = designEditorReloadConfig = designEditorRestoreButtonState = designEditorSuppressPaste = designEditorSuppressOnResize = designEditorMustReloadWhenReady = designEditorMustDisposeWhenReady = designEditorUpdateSizeDebouncer = designEditorActiveToolbarPanel /*= htmlWrappedInParagraph*/ = wasAutoChangedToMultiLineMode = minimizeHeight = maximizeHeight = minMaxUnit = maximizeHeightConfigured = resizable = nativeResizableAvailable = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = null;
22647
+ me = orgVal = preVal = input = cmdResize = designEditor = designEditorDom = designEditorDirty = designEditorDirtyPending = designEditorConfig = designEditorReloadConfig = designEditorRestoreButtonState = designEditorSuppressPaste = designEditorSuppressOnResize = designEditorMustReloadWhenReady = designEditorMustDisposeWhenReady = designEditorUpdateSizeDebouncer = designEditorActiveToolbarPanel = designEditorDetached /*= htmlWrappedInParagraph*/ = wasAutoChangedToMultiLineMode = minimizeHeight = maximizeHeight = minMaxUnit = maximizeHeightConfigured = resizable = nativeResizableAvailable = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = locale = null;
22244
22648
 
22245
22649
  base();
22246
22650
  });
@@ -22751,7 +23155,7 @@ Fit.Controls.Input = function(ctlId)
22751
23155
  /// <description> Configuration for image plugins </description>
22752
23156
  /// <member name="Enabled" type="boolean"> Flag indicating whether to enable image plugins or not (defaults to False) </member>
22753
23157
  /// <member name="EmbedType" type="'base64' | 'blob'" default="undefined">
22754
- /// How to store and embed images. Base64 is persistent while blob (default) is temporary
23158
+ /// How to store and embed images. Base64 (default) is persistent while blob is temporary
22755
23159
  /// and must be extracted from memory and uploaded/stored to be permanantly persisted.
22756
23160
  /// References to blobs can be parsed from the HTML value produced by the editor.
22757
23161
  /// </member>
@@ -22785,6 +23189,7 @@ Fit.Controls.Input = function(ctlId)
22785
23189
  /// <member name="Links" type="boolean" default="undefined"> Enable links (defaults to True) </member>
22786
23190
  /// <member name="Emojis" type="boolean" default="undefined"> Enable emoji button (defaults to False) </member>
22787
23191
  /// <member name="Images" type="boolean" default="undefined"> Enable image button (defaults to false) </member>
23192
+ /// <member name="Detach" type="boolean" default="undefined"> Enable detach button (defaults to false) </member>
22788
23193
  /// <member name="Position" type="'Top' | 'Bottom'" default="undefined"> Toolbar position (defaults to Top) </member>
22789
23194
  /// <member name="Sticky" type="boolean" default="undefined"> Make toolbar stick to edge of scroll container on supported browsers when scrolling (defaults to False) </member>
22790
23195
  /// <member name="HideInitially" type="boolean" default="undefined"> Hide toolbar until control gains focus (defaults to False) </member>
@@ -22918,6 +23323,20 @@ Fit.Controls.Input = function(ctlId)
22918
23323
  /// <member name="PreventResizeBeyondMaximumHeight" type="boolean" default="undefined"> Prevent user from resizing editor beyond maximum height (see MaximumHeight property - defaults to False) </member>
22919
23324
  /// </container>
22920
23325
 
23326
+ /// <container name="Fit.Controls.InputTypeDefs.DesignModeDetachable">
23327
+ /// <description> Detachable configuration </description>
23328
+ /// <member name="Title" type="string" default="undefined"> Dialog title </member>
23329
+ /// <member name="Maximizable" type="boolean" default="undefined"> Flag indicating whether dialog is maximizable </member>
23330
+ /// <member name="Maximized" type="boolean" default="undefined"> Flag indicating whether dialog is initially maximized </member>
23331
+ /// <member name="Draggable" type="boolean" default="undefined"> Flag indicating whether dialog is draggable </member>
23332
+ /// <member name="Width" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Dialog width </member>
23333
+ /// <member name="MinimumWidth" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum width of dialog </member>
23334
+ /// <member name="MaximumWidth" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum Width of dialog </member>
23335
+ /// <member name="Height" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Dialog height </member>
23336
+ /// <member name="MinimumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum height of dialog </member>
23337
+ /// <member name="MaximumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum height of dialog </member>
23338
+ /// </container>
23339
+
22921
23340
  /// <container name="Fit.Controls.InputTypeDefs.DesignModeConfig">
22922
23341
  /// <description> Configuration for DesignMode </description>
22923
23342
  /// <member name="Plugins" type="Fit.Controls.InputTypeDefs.DesignModeConfigPlugins" default="undefined"> Plugins configuration </member>
@@ -22925,6 +23344,7 @@ Fit.Controls.Input = function(ctlId)
22925
23344
  /// <member name="InfoPanel" type="Fit.Controls.InputTypeDefs.DesignModeConfigInfoPanel" default="undefined"> Information panel configuration </member>
22926
23345
  /// <member name="Tags" type="Fit.Controls.InputTypeDefs.DesignModeConfigTags" default="undefined"> Tags configuration </member>
22927
23346
  /// <member name="AutoGrow" type="Fit.Controls.InputTypeDefs.DesignModeAutoGrow" default="undefined"> Auto grow configuration </member>
23347
+ /// <member name="Detachable" type="Fit.Controls.InputTypeDefs.DesignModeDetachable" default="undefined"> Detachable configuration </member>
22928
23348
  /// </container>
22929
23349
 
22930
23350
  /// <function container="Fit.Controls.Input" name="DesignMode" access="public" returns="boolean">
@@ -22958,6 +23378,7 @@ Fit.Controls.Input = function(ctlId)
22958
23378
  Fit.Validation.ExpectStringValue(((editorConfig || {}).Toolbar || {}).Position, true);
22959
23379
  Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Sticky, true);
22960
23380
  Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).HideInitially, true);
23381
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Detach, true);
22961
23382
  Fit.Validation.ExpectObject((editorConfig || {}).InfoPanel, true);
22962
23383
  Fit.Validation.ExpectString(((editorConfig || {}).InfoPanel || {}).Text, true);
22963
23384
  Fit.Validation.ExpectString(((editorConfig || {}).InfoPanel || {}).Alignment, true);
@@ -22971,6 +23392,29 @@ Fit.Controls.Input = function(ctlId)
22971
23392
  Fit.Validation.ExpectNumber((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Value, true);
22972
23393
  Fit.Validation.ExpectStringValue((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Unit, true);
22973
23394
  Fit.Validation.ExpectBoolean(((editorConfig || {}).AutoGrow || {}).PreventResizeBeyondMaximumHeight, true);
23395
+ Fit.Validation.ExpectObject((editorConfig || {}).Detachable, true);
23396
+ Fit.Validation.ExpectString(((editorConfig || {}).Detachable || {}).Title, true);
23397
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Maximizable, true);
23398
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Maximized, true);
23399
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Draggable, true);
23400
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).Width, true);
23401
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).Width || {}).Value, true);
23402
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).Width || {}).Unit, true);
23403
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MinimumWidth, true);
23404
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MinimumWidth || {}).Value, true);
23405
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MinimumWidth || {}).Unit, true);
23406
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MaximumWidth, true);
23407
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MaximumWidth || {}).Value, true);
23408
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MaximumWidth || {}).Unit, true);
23409
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).Height, true);
23410
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).Height || {}).Value, true);
23411
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).Height || {}).Unit, true);
23412
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MinimumHeight, true);
23413
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MinimumHeight || {}).Value, true);
23414
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MinimumHeight || {}).Unit, true);
23415
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MaximumHeight, true);
23416
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MaximumHeight || {}).Value, true);
23417
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MaximumHeight || {}).Unit, true);
22974
23418
 
22975
23419
  if (editorConfig && editorConfig.Tags)
22976
23420
  {
@@ -23023,6 +23467,26 @@ Fit.Controls.Input = function(ctlId)
23023
23467
  designEditorConfig = Fit.Core.Clone(editorConfig); // Clone to prevent external code from making changes later
23024
23468
  }
23025
23469
 
23470
+ // Notice: Identical logic found in Value(..)!
23471
+ if (designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeExternalBlobUrlsOnDispose === true)
23472
+ {
23473
+ // Keep track of image blobs added via Value(..) so we can dispose of them automatically.
23474
+ // When RevokeExternalBlobUrlsOnDispose is True it basically means that the Input control
23475
+ // is allowed (and expected) to take control over memory management for these blobs
23476
+ // based on the rule set in RevokeBlobUrlsOnDispose.
23477
+ // This code is also found in Value(..) since images might be added after editor has been created.
23478
+
23479
+ var blobUrls = Fit.String.ParseImageBlobUrls(me.Value());
23480
+
23481
+ Fit.Array.ForEach(blobUrls, function(blobUrl)
23482
+ {
23483
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
23484
+ {
23485
+ Fit.Array.Add(imageBlobUrls, blobUrl);
23486
+ }
23487
+ });
23488
+ }
23489
+
23026
23490
  if (me.MultiLine() === false)
23027
23491
  {
23028
23492
  me.MultiLine(true);
@@ -23363,6 +23827,11 @@ Fit.Controls.Input = function(ctlId)
23363
23827
 
23364
23828
  var focused = me.Focused();
23365
23829
 
23830
+ if (focused === true) // Make sure focus is preserved when editor is destroyed
23831
+ {
23832
+ me.GetDomElement().focus();
23833
+ }
23834
+
23366
23835
  if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
23367
23836
  {
23368
23837
  if (Fit._internal.Controls.Input.ActiveDialogForEditor !== null)
@@ -23413,31 +23882,25 @@ Fit.Controls.Input = function(ctlId)
23413
23882
  me._internal.Data("toolbar-sticky", null);
23414
23883
 
23415
23884
  revertToSingleLineIfNecessary();
23416
-
23417
- // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons
23418
- Fit.Dom.Attribute(me.GetDomElement(), "tabindex", null);
23419
-
23420
23885
  if (focused === true)
23421
23886
  {
23422
- if (Fit.Browser.GetBrowser() === "MSIE" && Fit.Browser.GetVersion() < 9 && me.MultiLine() === false)
23423
- {
23424
- // On IE8 input.focus() does not work if input field is switched to a single line control
23425
- // above (MultiLine(false)). Wrapping the code in setTimeout(..) solves the problem.
23887
+ // On IE8 input.focus() does not work if input field is switched to a traditional input field,
23888
+ // or if input field is hidden/invisible. It's just not reliable and not worth it. Remove focus
23889
+ // from the control in IE8 when DesignMode is disabled and preserve focus in every other browser.
23426
23890
 
23427
- setTimeout(function()
23428
- {
23429
- if (me === null)
23430
- return; // Control was disposed
23431
-
23432
- input.focus();
23433
- }, 0);
23891
+ if (isIe8 === false)
23892
+ {
23893
+ input.focus();
23434
23894
  }
23435
23895
  else
23436
23896
  {
23437
- input.focus();
23897
+ me.GetDomElement().blur(); // Control container was given focus further up - this will fire OnBlur as expected
23438
23898
  }
23439
23899
  }
23440
23900
 
23901
+ // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons
23902
+ Fit.Dom.Attribute(me.GetDomElement(), "tabindex", null);
23903
+
23441
23904
  repaint();
23442
23905
  }
23443
23906
  }
@@ -23471,6 +23934,17 @@ Fit.Controls.Input = function(ctlId)
23471
23934
  return debounceOnChangeTimeout;
23472
23935
  }
23473
23936
 
23937
+ // ============================================
23938
+ // Protected
23939
+ // ============================================
23940
+
23941
+ this._internal = (this._internal ? this._internal : {});
23942
+
23943
+ this._internal.DesignModeEnabledAndReady = function()
23944
+ {
23945
+ return designModeEnabledAndReady();
23946
+ }
23947
+
23474
23948
  // ============================================
23475
23949
  // Private
23476
23950
  // ============================================
@@ -23533,8 +24007,8 @@ Fit.Controls.Input = function(ctlId)
23533
24007
  }
23534
24008
 
23535
24009
  var langSupport = ["da", "de", "en"];
23536
- var locale = Fit.Internationalization.Locale().length === 2 ? Fit.Internationalization.Locale() : Fit.Internationalization.Locale().substring(0, 2);
23537
- var lang = Fit.Array.Contains(langSupport, locale) === true ? locale : "en";
24010
+ var localeCode = Fit.Internationalization.Locale().length === 2 ? Fit.Internationalization.Locale() : Fit.Internationalization.Locale().substring(0, 2);
24011
+ var lang = Fit.Array.Contains(langSupport, localeCode) === true ? localeCode : "en";
23538
24012
  var plugins = [];
23539
24013
  var toolbar = [];
23540
24014
  var mentions = [];
@@ -23548,7 +24022,7 @@ Fit.Controls.Input = function(ctlId)
23548
24022
  Fit.Array.Add(plugins, "emoji");
23549
24023
  }
23550
24024
 
23551
- if ((config.Plugins && config.Plugins.Images && config.Plugins.Images.Enabled === true) || (config.Toolbar && config.Toolbar.Images === true))
24025
+ if (designModeEnableImagePlugin() === true)
23552
24026
  {
23553
24027
  if (config.Toolbar && config.Toolbar.Images === true)
23554
24028
  {
@@ -23558,6 +24032,8 @@ Fit.Controls.Input = function(ctlId)
23558
24032
  plugins = Fit.Array.Merge(plugins, ["base64imagepaste", "dragresize"]);
23559
24033
  }
23560
24034
 
24035
+ Fit.Array.Add(plugins, "custombuttons");
24036
+
23561
24037
  // Add toolbar buttons
23562
24038
 
23563
24039
  if (!config.Toolbar || config.Toolbar.Formatting !== false)
@@ -23618,6 +24094,43 @@ Fit.Controls.Input = function(ctlId)
23618
24094
  items: insert
23619
24095
  });
23620
24096
  }
24097
+
24098
+ var customButtons = [];
24099
+ var customToolbarGroups = [];
24100
+
24101
+ if (config.Toolbar.Detach === true)
24102
+ {
24103
+ Fit.Array.Add(customButtons,
24104
+ {
24105
+ Label: locale.Detach,
24106
+ Command: "Detach",
24107
+ Icon: Fit.GetUrl() + "/Controls/Input/" + (window.devicePixelRatio === 2 ? "maximize-highres.png" : "maximize.png"),
24108
+ Callback: function(args)
24109
+ {
24110
+ //console.log("Command " + args.Command.name + " executed", args);
24111
+ openDetachedDesignEditor();
24112
+ }
24113
+ });
24114
+
24115
+ /*Fit.Array.Add(customButtons,
24116
+ {
24117
+ Label: "Testing 1-2-3",
24118
+ Command: "TestButton",
24119
+ Icon: "/files/images/Bird.png",
24120
+ Callback: function(args)
24121
+ {
24122
+ alert("Hello world");
24123
+ }
24124
+ });*/
24125
+
24126
+ Fit.Array.Add(customToolbarGroups,
24127
+ {
24128
+ name: "DetachableEditor",
24129
+ items: ["Detach"/*, "TestButton"*/]
24130
+ });
24131
+ }
24132
+
24133
+ toolbar = Fit.Array.Merge(toolbar, customToolbarGroups);
23621
24134
  }
23622
24135
 
23623
24136
  // Configure tags/mentions plugin
@@ -23891,6 +24404,7 @@ Fit.Controls.Input = function(ctlId)
23891
24404
  resize_dir: resizable === Fit.Controls.InputResizing.Enabled ? "both" : resizable === Fit.Controls.InputResizing.Vertical ? "vertical" : resizable === Fit.Controls.InputResizing.Horizontal ? "horizontal" : "none", // Specific to resize plugin (horizontal | vertical | both - https://ckeditor.com/docs/ckeditor4/latest/features/resize.html)
23892
24405
  toolbar: toolbar,
23893
24406
  removeButtons: "", // Set to empty string to prevent CKEditor from removing buttons such as Underline
24407
+ customButtons: customButtons,
23894
24408
  mentions: mentions,
23895
24409
  emoji_minChars: 9999, // Impossible requirement to number of search characters to "disable" emoji auto complete menu - we cannot make it work properly with light dismissable panels/callouts since we have no event available for registering the data-disable-light-dismiss="true" attribute, and it's not very useful in any case
23896
24410
  on:
@@ -23989,17 +24503,21 @@ Fit.Controls.Input = function(ctlId)
23989
24503
  if (Fit.Dom.GetComputedStyle(toolbarContainer, "position") === "sticky") // False on non-supported browsers as position:sticky is applied via the @supports CSS rule
23990
24504
  {
23991
24505
  var scrollParent = Fit.Dom.GetScrollParent(me.GetDomElement());
23992
- var toolbarPosition = me._internal.Data("toolbar-position"); // top | bottom
23993
24506
 
23994
- if (toolbarPosition === "top")
24507
+ if (scrollParent !== null) // In case editor is hosted in a container with position:fixed with overflow:hidden, in which case Fit.Dom.GetScrollParent(..) returns null
23995
24508
  {
23996
- var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-top"); // E.g. "28px"
23997
- toolbarContainer.style.top = paddingOffset !== "0px" ? "-" + paddingOffset : "";
23998
- }
23999
- else
24000
- {
24001
- var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-bottom"); // E.g. "28px"
24002
- toolbarContainer.style.bottom = paddingOffset !== "0px" ? "-" + paddingOffset : "";
24509
+ var toolbarPosition = me._internal.Data("toolbar-position"); // top | bottom
24510
+
24511
+ if (toolbarPosition === "top")
24512
+ {
24513
+ var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-top"); // E.g. "28px"
24514
+ toolbarContainer.style.top = paddingOffset !== "0px" ? "-" + paddingOffset : "";
24515
+ }
24516
+ else
24517
+ {
24518
+ var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-bottom"); // E.g. "28px"
24519
+ toolbarContainer.style.bottom = paddingOffset !== "0px" ? "-" + paddingOffset : "";
24520
+ }
24003
24521
  }
24004
24522
  }
24005
24523
  }
@@ -24163,7 +24681,7 @@ Fit.Controls.Input = function(ctlId)
24163
24681
  // place focus at the end of the editor as expected.
24164
24682
  if (me.Focused() === true)
24165
24683
  {
24166
- designEditor.focus();
24684
+ designEditor.focus(); // Won't work on iOS as assigning focus must be the result of a direct user interaction, which this is not since it is postponed using setTimeout(..) and async. loading of the editor
24167
24685
  }
24168
24686
  }, 0);
24169
24687
  },
@@ -24229,11 +24747,18 @@ Fit.Controls.Input = function(ctlId)
24229
24747
 
24230
24748
  if (elm.tagName === "A" && Fit.Dom.Data(elm, "tag-id") !== null)
24231
24749
  {
24232
- designEditorSuppressPaste = true;
24233
- setTimeout(function() // Postpone - otherwise we won't be able to temporarily disable some of the buttons (https://jsfiddle.net/ymv56znq/14/)
24750
+ // Notice that selectionChange handler is invoked while editor is loading if control was given initial focus.
24751
+ // But at this point the toolbar buttons are not yet available to be disabled, so disableDesignEditorButtons()
24752
+ // won't work. However, as soon as the editor is done loading, focus is re-assigned to the editable area
24753
+ // which will trigger selectionChange handler once again, at which point designModeEnabledAndReady() returns true.
24754
+ if (designModeEnabledAndReady() === true)
24234
24755
  {
24235
- disableDesignEditorButtons();
24236
- }, 0);
24756
+ designEditorSuppressPaste = true;
24757
+ setTimeout(function() // Postpone - otherwise we won't be able to temporarily disable some of the buttons (https://jsfiddle.net/ymv56znq/14/)
24758
+ {
24759
+ disableDesignEditorButtons();
24760
+ }, 0);
24761
+ }
24237
24762
  }
24238
24763
  else
24239
24764
  {
@@ -24562,6 +25087,258 @@ Fit.Controls.Input = function(ctlId)
24562
25087
  }
24563
25088
  }
24564
25089
 
25090
+ function openDetachedDesignEditor()
25091
+ {
25092
+ me._internal.FocusStateLocked(true);
25093
+
25094
+ var deConfig = null;
25095
+
25096
+ var updateDetachedConfiguration = function()
25097
+ {
25098
+ deConfig = Fit.Core.Clone(designEditorConfig || {});
25099
+
25100
+ // Configure detached editor like original, but with a few required changes.
25101
+ // We need to make sure image blobs are handled properly, that detached editor
25102
+ // cannot created another detached editor, that the toolbar is initially visible
25103
+ // at the top of the dialog, and that auto grow is disabled.
25104
+
25105
+ if (designModeEnableImagePlugin() === true)
25106
+ {
25107
+ deConfig = Fit.Core.Merge(deConfig, // Override image plugin configuration
25108
+ {
25109
+ Plugins:
25110
+ {
25111
+ Images:
25112
+ {
25113
+ Enabled: true,
25114
+ EmbedType: deConfig.Plugins && deConfig.Plugins.Images && deConfig.Plugins.Images.EmbedType,
25115
+
25116
+ // Image blobs added in detached editor must always be disposed if no longer referenced.
25117
+ // Furthermore we make sure image blobs originating from main editor are never disposed.
25118
+ // When detached editor is closed, images transfered from detached editor to main editor
25119
+ // are added to the main editor's index over image blobs so that the main editor becomes
25120
+ // responsible for the memory management of these.
25121
+ // If detached editor is closed without transfering changes (canceled), all images found
25122
+ // in the detached editor, which are not referenced in the main editor, are disposed.
25123
+ // See OnClick handlers for OK and Cancel buttons.
25124
+
25125
+ RevokeBlobUrlsOnDispose: "UnreferencedOnly", // Make dialog editor preserve newly added (and still referenced) image blobs when disposed
25126
+ RevokeExternalBlobUrlsOnDispose: false // Make dialog editor preserve images blobs initially added from main editor
25127
+ }
25128
+ }
25129
+ });
25130
+ }
25131
+
25132
+ deConfig.Toolbar = deConfig.Toolbar || {};
25133
+ deConfig.Toolbar.Detach = false;
25134
+ deConfig.Toolbar.Position = "Top";
25135
+ deConfig.Toolbar.Sticky = false;
25136
+ deConfig.Toolbar.HideInitially = false;
25137
+
25138
+ delete deConfig.AutoGrow;
25139
+ };
25140
+
25141
+ updateDetachedConfiguration();
25142
+
25143
+ // Create dialog
25144
+
25145
+ var dia = new Fit.Controls.Dialog();
25146
+ Fit.Dom.AddClass(dia.GetDomElement(), "FitUiControlInputDetached");
25147
+
25148
+ // Create editor
25149
+
25150
+ var de = new Fit.Controls.Input();
25151
+
25152
+ var setDetachedEditorSettings = function()
25153
+ {
25154
+ de.Width(100, "%");
25155
+ de.Height(-1);
25156
+ de.CheckSpelling(me.CheckSpelling());
25157
+ de.DesignMode(true, deConfig);
25158
+ };
25159
+
25160
+ setDetachedEditorSettings();
25161
+ de.Value(me.Value());
25162
+
25163
+ // Make editor adjust to the dimensions of the dialog
25164
+
25165
+ // Adjust editor size to fit dialog using a timer.
25166
+ // Alternatively expose an OnResize event on Fit.Controls.Dialog, use it in
25167
+ // combination with window.onresize, and adjust editor when these events are triggered.
25168
+ // However, the process of monitoring the dimensions using a timer is practically free,
25169
+ // so even though the timer interupts browser events such as onmousemove, onscroll, etc.,
25170
+ // it creates no lack at all.
25171
+ var height = -1
25172
+ var dimMonitorId = setInterval(function()
25173
+ {
25174
+ if (height === -1 && de._internal.DesignModeEnabledAndReady() === false)
25175
+ {
25176
+ return;
25177
+ }
25178
+
25179
+ var newHeight = dia.GetDomElement().offsetHeight;
25180
+
25181
+ if (newHeight !== height)
25182
+ {
25183
+ height = newHeight
25184
+ de.Height(Fit.Dom.GetInnerDimensions(dia.GetContentDomElement()).Height);
25185
+ }
25186
+ }, 250);
25187
+
25188
+ // Configure dialog
25189
+
25190
+ var setDialogSettings = function(update)
25191
+ {
25192
+ var detachConfig = deConfig.Detachable || {};
25193
+ detachConfig = Fit.Core.Merge(detachConfig, // Apply default values - existing properties are preserved (Fit.Core.MergeOverwriteBehaviour.Never)
25194
+ {
25195
+ Title: "",
25196
+ Maximizable: true,
25197
+ Maximized: false,
25198
+ Draggable: true,
25199
+ Resizable: true,
25200
+ Width: detachConfig.Width ? detachConfig.Width : { Value: 850, Unit: "px" },
25201
+ MinimumWidth: detachConfig.MinimumWidth ? detachConfig.MinimumWidth : { Value: 20, Unit: "em" },
25202
+ MaximumWidth: detachConfig.MaximumWidth ? detachConfig.MaximumWidth : { Value: 100, Unit: "%" },
25203
+ Height: detachConfig.Height ? detachConfig.Height : { Value: 550, Unit: "px" },
25204
+ MinimumHeight: detachConfig.MinimumHeight ? detachConfig.MinimumHeight : { Value: 12, Unit: "em" },
25205
+ MaximumHeight: detachConfig.MaximumHeight ? detachConfig.MaximumHeight : { Value: 100, Unit: "%" }
25206
+ }, Fit.Core.MergeOverwriteBehaviour.Never);
25207
+
25208
+ var updateDimensions = (!detachConfig.Resizable || update !== true);
25209
+
25210
+ dia.Title(detachConfig.Title);
25211
+ dia.Modal(true);
25212
+ dia.Draggable(detachConfig.Draggable);
25213
+ dia.Resizable(detachConfig.Resizable);
25214
+ dia.Maximizable(detachConfig.Maximizable);
25215
+ dia.Maximized(detachConfig.Maximized);
25216
+ updateDimensions === true && dia.Width(detachConfig.Width.Value, detachConfig.Width.Unit || "px");
25217
+ updateDimensions === true && dia.Height(detachConfig.Height.Value, detachConfig.Height.Unit || "px");
25218
+ dia.MinimumWidth(detachConfig.MinimumWidth.Value, detachConfig.MinimumWidth.Unit || "px");
25219
+ dia.MinimumHeight(detachConfig.MinimumHeight.Value, detachConfig.MinimumHeight.Unit || "px");
25220
+ dia.MaximumWidth(detachConfig.MaximumWidth.Value, detachConfig.MaximumWidth.Unit || "px");
25221
+ dia.MaximumHeight(detachConfig.MaximumHeight.Value, detachConfig.MaximumHeight.Unit || "px");
25222
+ };
25223
+
25224
+ setDialogSettings();
25225
+
25226
+ // Localization support
25227
+
25228
+ var localizeDetachedEditor = function()
25229
+ {
25230
+ // Editor itself is already localized, so we just need to
25231
+ // localize the dialog. The locale variable will already have
25232
+ // been updated by the OnLocaleChanged handler registered in init().
25233
+
25234
+ cmdOk.Title(locale.Ok);
25235
+ cmdCancel.Title(locale.Cancel);
25236
+ };
25237
+ Fit.Internationalization.OnLocaleChanged(localizeDetachedEditor);
25238
+
25239
+ // Expose detached editor API
25240
+
25241
+ designEditorDetached =
25242
+ {
25243
+ GetValue: function()
25244
+ {
25245
+ return de.Value();
25246
+ },
25247
+
25248
+ Reload: function()
25249
+ {
25250
+ // Update configuration
25251
+ updateDetachedConfiguration();
25252
+
25253
+ // Update editor
25254
+ setDetachedEditorSettings();
25255
+
25256
+ // Update dialog
25257
+ setDialogSettings(true);
25258
+
25259
+ // Make dimension monitor ensure proper editor height (dialog height might have been changed)
25260
+ height = -1;
25261
+ },
25262
+
25263
+ Dispose: function()
25264
+ {
25265
+ clearInterval(dimMonitorId);
25266
+ Fit.Internationalization.RemoveOnLocaleChanged(localizeDetachedEditor);
25267
+ de.Dispose();
25268
+ dia.Dispose(); // Will also dispose associated buttons
25269
+ designEditorDetached = null;
25270
+ }
25271
+ };
25272
+
25273
+ var cmdOk = new Fit.Controls.Button();
25274
+ cmdOk.Title(locale.Ok);
25275
+ cmdOk.Icon("check");
25276
+ cmdOk.Type(Fit.Controls.ButtonType.Success);
25277
+ cmdOk.OnClick(function(sender)
25278
+ {
25279
+ var referencedBlobUrls = Fit.String.ParseImageBlobUrls(de.Value());
25280
+ Fit.Array.ForEach(referencedBlobUrls, function(blobUrl)
25281
+ {
25282
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
25283
+ {
25284
+ Fit.Array.Add(imageBlobUrls, blobUrl);
25285
+ }
25286
+ });
25287
+
25288
+ me.Value(de.Value());
25289
+
25290
+ designEditorDetached.Dispose();
25291
+
25292
+ me.Focused(true);
25293
+ me._internal.FocusStateLocked(false);
25294
+ });
25295
+ dia.AddButton(cmdOk);
25296
+
25297
+ var cmdCancel = new Fit.Controls.Button();
25298
+ cmdCancel.Title(locale.Cancel);
25299
+ cmdCancel.Icon("ban");
25300
+ cmdCancel.Type(Fit.Controls.ButtonType.Danger);
25301
+ cmdCancel.OnClick(function(sender)
25302
+ {
25303
+ var closeDialog = function()
25304
+ {
25305
+ var referencedBlobUrls = Fit.String.ParseImageBlobUrls(de.Value());
25306
+ Fit.Array.ForEach(referencedBlobUrls, function(blobUrl)
25307
+ {
25308
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false) // Only remove images added in dialog editor
25309
+ {
25310
+ URL.revokeObjectURL(blobUrl);
25311
+ }
25312
+ });
25313
+
25314
+ designEditorDetached.Dispose();
25315
+
25316
+ me.Focused(true);
25317
+ me._internal.FocusStateLocked(false);
25318
+ };
25319
+
25320
+ if (de.IsDirty() === true)
25321
+ {
25322
+ Fit.Controls.Dialog.Confirm(locale.CancelConfirmTitle + "<br><br>" + locale.CancelConfirmDescription, function(res)
25323
+ {
25324
+ if (res === true)
25325
+ {
25326
+ closeDialog();
25327
+ }
25328
+ });
25329
+ }
25330
+ else
25331
+ {
25332
+ closeDialog();
25333
+ }
25334
+ });
25335
+ dia.AddButton(cmdCancel);
25336
+
25337
+ dia.Open();
25338
+ de.Render(dia.GetContentDomElement());
25339
+ de.Focused(true);
25340
+ }
25341
+
24565
25342
  function revertToSingleLineIfNecessary()
24566
25343
  {
24567
25344
  if (wasAutoChangedToMultiLineMode === true && me.Maximizable() === false && me.Resizable() === Fit.Controls.InputResizing.Disabled && me.DesignMode() === false)
@@ -24625,10 +25402,25 @@ Fit.Controls.Input = function(ctlId)
24625
25402
  var height = me.Height();
24626
25403
  var currentWasAutoChangedToMultiLineMode = wasAutoChangedToMultiLineMode; // DesignMode(false) will result in wasAutoChangedToMultiLineMode being set to false if DesignMode(true) changed the control to MultiLine mode
24627
25404
 
25405
+ // Prevent detached editor from being closed when reloading, e.g. if CheckSpelling is changed.
25406
+ // Editor will also be reloaded if a different editor configuration is passed to DesignMode(true, updatedConfig).
25407
+ var detachedEditor = null;
25408
+ if (designEditorDetached !== null)
25409
+ {
25410
+ detachedEditor = designEditorDetached;
25411
+ designEditorDetached = null; // Prevent me.DesignMode(false), which in turn calls destroyDesignEditorInstance(), from closing detached editor dialog
25412
+ }
25413
+
24628
25414
  me.DesignMode(false);
24629
25415
  me.DesignMode(true, reloadConfig || designEditorReloadConfig || undefined); // Use reloadConfig if set (and if reload was not postponed) or use designEditorReloadConfig if reload was postponed with updated editor config
24630
25416
  designEditorReloadConfig = null;
24631
25417
 
25418
+ if (detachedEditor !== null)
25419
+ {
25420
+ designEditorDetached = detachedEditor;
25421
+ designEditorDetached.Reload(); // Reload detached editor to reflect any changes made to configuration
25422
+ }
25423
+
24632
25424
  me.Height(height.Value, height.Unit);
24633
25425
  wasAutoChangedToMultiLineMode = currentWasAutoChangedToMultiLineMode;
24634
25426
  }
@@ -24647,9 +25439,14 @@ Fit.Controls.Input = function(ctlId)
24647
25439
 
24648
25440
  designEditor.destroy();
24649
25441
 
25442
+ if (designEditorDetached !== null)
25443
+ {
25444
+ designEditorDetached.Dispose();
25445
+ }
25446
+
24650
25447
  designEditor = null;
24651
25448
  designEditorDom = null;
24652
- designEditorDirty = false;
25449
+ //designEditorDirty = false; // Do NOT reset this! We need to preserve dirty state in case DesignMode is reloaded!
24653
25450
  designEditorDirtyPending = false;
24654
25451
  //designEditorConfig = null; // Do NOT nullify this! We need it, in case DesignMode is toggled!
24655
25452
  //designEditorReloadConfig = null; // Do NOT nullify this! We need it, in case DesignMode is reloaded!
@@ -24659,6 +25456,7 @@ Fit.Controls.Input = function(ctlId)
24659
25456
  designEditorMustReloadWhenReady = false;
24660
25457
  designEditorMustDisposeWhenReady = false;
24661
25458
  designEditorActiveToolbarPanel = null;
25459
+ designEditorDetached = null;
24662
25460
 
24663
25461
  if (designEditorUpdateSizeDebouncer !== -1)
24664
25462
  {
@@ -24690,12 +25488,47 @@ Fit.Controls.Input = function(ctlId)
24690
25488
  return designEditorDom !== null; // Editor is fully loaded when editor DOM is made available
24691
25489
  }
24692
25490
 
25491
+ function designModeEnableImagePlugin()
25492
+ {
25493
+ var config = designEditorConfig || {};
25494
+ var enableImagePlugin = (config.Plugins && config.Plugins.Images && config.Plugins.Images.Enabled === true) || (config.Toolbar && config.Toolbar.Images === true) || false;
25495
+
25496
+ // Force enable image support if images are contained in value - otherwise editor will remove them
25497
+ if (enableImagePlugin === false && designEditorDetached !== null && designEditorDetached.GetValue().indexOf("<img ") > -1)
25498
+ {
25499
+ enableImagePlugin = true;
25500
+ }
25501
+ if (enableImagePlugin === false && me.Value().indexOf("<img ") > -1)
25502
+ {
25503
+ enableImagePlugin = true;
25504
+ }
25505
+
25506
+ return enableImagePlugin;
25507
+ }
25508
+
24693
25509
  function localize()
24694
25510
  {
25511
+ locale = Fit.Internationalization.GetLocale(me);
25512
+
24695
25513
  if (me.DesignMode() === true)
24696
25514
  {
25515
+ // Prevent reloadEditor() from reloading detached editor.
25516
+ // It will automatically reload when locale is changed.
25517
+ // Without this guard the editor would reload twice.
25518
+ var detachedEditor = null;
25519
+ if (designEditorDetached !== null)
25520
+ {
25521
+ detachedEditor = designEditorDetached;
25522
+ designEditorDetached = null; // Prevent reloadEditor() from reloading detached editor
25523
+ }
25524
+
24697
25525
  // Re-create editor with new language
24698
25526
  reloadEditor();
25527
+
25528
+ if (detachedEditor !== null)
25529
+ {
25530
+ designEditorDetached = detachedEditor;
25531
+ }
24699
25532
  }
24700
25533
  }
24701
25534
 
@@ -24812,6 +25645,40 @@ Fit.Controls.InputResizing = // Enums must exist runtime
24812
25645
  Horizontal: "Horizontal",
24813
25646
  Vertical: "Vertical"
24814
25647
  };
25648
+ Fit.Internationalization.AddLocalization(Fit.Controls.Input,
25649
+ {
25650
+ // Primary locales must be registered before country specific overrides.
25651
+ // Example order: en, en_GB, de, de_AT, etc.
25652
+ // All locales inherit from en. All country specific overrides inherit
25653
+ // from their general locale (e.g. de_AT inherits from de).
25654
+ // English (en) MUST be defined, and be defined first!
25655
+
25656
+ "en":
25657
+ {
25658
+ "Detach": "Open in dialog",
25659
+ "Ok": "OK",
25660
+ "Cancel": "Cancel",
25661
+ "CancelConfirmTitle": "Discard changes?",
25662
+ "CancelConfirmDescription": "Are you sure you want to discard the changes?"
25663
+ },
25664
+ "da":
25665
+ {
25666
+ "Detach": "Åben i dialog",
25667
+ "Ok": "OK",
25668
+ "Cancel": "Annullér",
25669
+ "CancelConfirmTitle": "Annullér ændringer?",
25670
+ "CancelConfirmDescription": "Er du sikker på at du vil annullere ændringerne?"
25671
+ },
25672
+ "de":
25673
+ {
25674
+ "Detach": "Im Dialog öffnen",
25675
+ "Ok": "OK",
25676
+ "Cancel": "Abbrechen",
25677
+ "CancelConfirmTitle": "Änderungen verwerfen?",
25678
+ "CancelConfirmDescription": "Möchten Sie die Änderungen wirklich verwerfen?"
25679
+ }
25680
+ });
25681
+
24815
25682
  /// <container name="Fit.Controls.ListView" extends="Fit.Controls.PickerBase;Fit.Controls.Component">
24816
25683
  /// Picker control which allows for entries
24817
25684
  /// to be selected in the DropDown control.
@@ -26130,7 +26997,7 @@ Fit.Controls.SoftLog = function(controlId)
26130
26997
 
26131
26998
  /// <function container="Fit.Controls.SoftLog" name="MaxEntries" access="public" returns="integer">
26132
26999
  /// <description> Get/set number of log entries preserved </description>
26133
- /// <param name="val" type="boolean" default="undefined"> If defined, changes number of log entries preserved </param>
27000
+ /// <param name="val" type="integer" default="undefined"> If defined, changes number of log entries preserved </param>
26134
27001
  /// </function>
26135
27002
  this.MaxEntries = function(val)
26136
27003
  {