fit-ui 2.8.3 → 2.9.1

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 +983 -112
  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 +247 -103
  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: 3 } // 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: 1 } // 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>
@@ -22815,6 +23220,7 @@ Fit.Controls.Input = function(ctlId)
22815
23220
  /// <member name="Icon" type="string" default="undefined"> Optional URL to icon/image </member>
22816
23221
  /// <member name="Url" type="string" default="undefined"> Optional URL to associate with tag </member>
22817
23222
  /// <member name="Data" type="string" default="undefined"> Optional data to associate with tag </member>
23223
+ /// <member name="Context" type="string" default="undefined"> Optional context information to associate with tag </member>
22818
23224
  /// </container>
22819
23225
  /// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponseEventHandlerArgs">
22820
23226
  /// <description> Response handler event arguments </description>
@@ -22836,6 +23242,7 @@ Fit.Controls.Input = function(ctlId)
22836
23242
  /// <member name="Type" type="string"> Tag type (marker) </member>
22837
23243
  /// <member name="Url" type="string" default="undefined"> Optional tag URL </member>
22838
23244
  /// <member name="Data" type="string" default="undefined"> Optional tag data </member>
23245
+ /// <member name="Context" type="string" default="undefined"> Optional tag context </member>
22839
23246
  /// </container>
22840
23247
  /// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsTagCreatorCallbackArgs">
22841
23248
  /// <description> TagCreator event arguments </description>
@@ -22872,8 +23279,8 @@ Fit.Controls.Input = function(ctlId)
22872
23279
  /// btoa(JSON.stringify({ creationDate: new Date(), active: true }))
22873
23280
  ///
22874
23281
  /// The data eventuelly results in a tag being added to the editor with the following format:
22875
- /// <a data-tag-type="@" data-tag-id="unique id 1" data-tag-data="..." href="show/1">Tag name 1</a>
22876
- /// The data-tag-data attribute is only declared if the corresponding Data property is defined in data.
23282
+ /// <a data-tag-type="@" data-tag-id="unique id 1" data-tag-data="..." data-tag-context="..." href="show/1">Tag name 1</a>
23283
+ /// The data-tag-data and data-tag-context attributes are only declared if the corresponding Data and Context properties are defined in data.
22877
23284
  /// </member>
22878
23285
  /// <member name="JsonpCallback" type="string" default="undefined"> Name of URL parameter receiving name of JSONP callback function (only for JSONP services) </member>
22879
23286
  /// <member name="JsonpTimeout" type="integer" default="undefined"> Number of milliseconds to allow JSONP request to wait for a response before aborting (only for JSONP services) </member>
@@ -22916,6 +23323,21 @@ Fit.Controls.Input = function(ctlId)
22916
23323
  /// <member name="PreventResizeBeyondMaximumHeight" type="boolean" default="undefined"> Prevent user from resizing editor beyond maximum height (see MaximumHeight property - defaults to False) </member>
22917
23324
  /// </container>
22918
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="Resizable" type="boolean" default="undefined"> Flag indicating whether dialog is resizable </member>
23333
+ /// <member name="Width" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Dialog width </member>
23334
+ /// <member name="MinimumWidth" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum width of dialog </member>
23335
+ /// <member name="MaximumWidth" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum Width of dialog </member>
23336
+ /// <member name="Height" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Dialog height </member>
23337
+ /// <member name="MinimumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum height of dialog </member>
23338
+ /// <member name="MaximumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum height of dialog </member>
23339
+ /// </container>
23340
+
22919
23341
  /// <container name="Fit.Controls.InputTypeDefs.DesignModeConfig">
22920
23342
  /// <description> Configuration for DesignMode </description>
22921
23343
  /// <member name="Plugins" type="Fit.Controls.InputTypeDefs.DesignModeConfigPlugins" default="undefined"> Plugins configuration </member>
@@ -22923,6 +23345,7 @@ Fit.Controls.Input = function(ctlId)
22923
23345
  /// <member name="InfoPanel" type="Fit.Controls.InputTypeDefs.DesignModeConfigInfoPanel" default="undefined"> Information panel configuration </member>
22924
23346
  /// <member name="Tags" type="Fit.Controls.InputTypeDefs.DesignModeConfigTags" default="undefined"> Tags configuration </member>
22925
23347
  /// <member name="AutoGrow" type="Fit.Controls.InputTypeDefs.DesignModeAutoGrow" default="undefined"> Auto grow configuration </member>
23348
+ /// <member name="Detachable" type="Fit.Controls.InputTypeDefs.DesignModeDetachable" default="undefined"> Detachable configuration </member>
22926
23349
  /// </container>
22927
23350
 
22928
23351
  /// <function container="Fit.Controls.Input" name="DesignMode" access="public" returns="boolean">
@@ -22956,6 +23379,7 @@ Fit.Controls.Input = function(ctlId)
22956
23379
  Fit.Validation.ExpectStringValue(((editorConfig || {}).Toolbar || {}).Position, true);
22957
23380
  Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Sticky, true);
22958
23381
  Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).HideInitially, true);
23382
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Detach, true);
22959
23383
  Fit.Validation.ExpectObject((editorConfig || {}).InfoPanel, true);
22960
23384
  Fit.Validation.ExpectString(((editorConfig || {}).InfoPanel || {}).Text, true);
22961
23385
  Fit.Validation.ExpectString(((editorConfig || {}).InfoPanel || {}).Alignment, true);
@@ -22969,6 +23393,30 @@ Fit.Controls.Input = function(ctlId)
22969
23393
  Fit.Validation.ExpectNumber((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Value, true);
22970
23394
  Fit.Validation.ExpectStringValue((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Unit, true);
22971
23395
  Fit.Validation.ExpectBoolean(((editorConfig || {}).AutoGrow || {}).PreventResizeBeyondMaximumHeight, true);
23396
+ Fit.Validation.ExpectObject((editorConfig || {}).Detachable, true);
23397
+ Fit.Validation.ExpectString(((editorConfig || {}).Detachable || {}).Title, true);
23398
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Maximizable, true);
23399
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Maximized, true);
23400
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Draggable, true);
23401
+ Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Resizable, true);
23402
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).Width, true);
23403
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).Width || {}).Value, true);
23404
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).Width || {}).Unit, true);
23405
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MinimumWidth, true);
23406
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MinimumWidth || {}).Value, true);
23407
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MinimumWidth || {}).Unit, true);
23408
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MaximumWidth, true);
23409
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MaximumWidth || {}).Value, true);
23410
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MaximumWidth || {}).Unit, true);
23411
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).Height, true);
23412
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).Height || {}).Value, true);
23413
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).Height || {}).Unit, true);
23414
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MinimumHeight, true);
23415
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MinimumHeight || {}).Value, true);
23416
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MinimumHeight || {}).Unit, true);
23417
+ Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MaximumHeight, true);
23418
+ Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MaximumHeight || {}).Value, true);
23419
+ Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MaximumHeight || {}).Unit, true);
22972
23420
 
22973
23421
  if (editorConfig && editorConfig.Tags)
22974
23422
  {
@@ -23021,6 +23469,26 @@ Fit.Controls.Input = function(ctlId)
23021
23469
  designEditorConfig = Fit.Core.Clone(editorConfig); // Clone to prevent external code from making changes later
23022
23470
  }
23023
23471
 
23472
+ // Notice: Identical logic found in Value(..)!
23473
+ if (designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeExternalBlobUrlsOnDispose === true)
23474
+ {
23475
+ // Keep track of image blobs added via Value(..) so we can dispose of them automatically.
23476
+ // When RevokeExternalBlobUrlsOnDispose is True it basically means that the Input control
23477
+ // is allowed (and expected) to take control over memory management for these blobs
23478
+ // based on the rule set in RevokeBlobUrlsOnDispose.
23479
+ // This code is also found in Value(..) since images might be added after editor has been created.
23480
+
23481
+ var blobUrls = Fit.String.ParseImageBlobUrls(me.Value());
23482
+
23483
+ Fit.Array.ForEach(blobUrls, function(blobUrl)
23484
+ {
23485
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
23486
+ {
23487
+ Fit.Array.Add(imageBlobUrls, blobUrl);
23488
+ }
23489
+ });
23490
+ }
23491
+
23024
23492
  if (me.MultiLine() === false)
23025
23493
  {
23026
23494
  me.MultiLine(true);
@@ -23361,6 +23829,11 @@ Fit.Controls.Input = function(ctlId)
23361
23829
 
23362
23830
  var focused = me.Focused();
23363
23831
 
23832
+ if (focused === true) // Make sure focus is preserved when editor is destroyed
23833
+ {
23834
+ me.GetDomElement().focus();
23835
+ }
23836
+
23364
23837
  if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
23365
23838
  {
23366
23839
  if (Fit._internal.Controls.Input.ActiveDialogForEditor !== null)
@@ -23411,31 +23884,25 @@ Fit.Controls.Input = function(ctlId)
23411
23884
  me._internal.Data("toolbar-sticky", null);
23412
23885
 
23413
23886
  revertToSingleLineIfNecessary();
23414
-
23415
- // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons
23416
- Fit.Dom.Attribute(me.GetDomElement(), "tabindex", null);
23417
-
23418
23887
  if (focused === true)
23419
23888
  {
23420
- if (Fit.Browser.GetBrowser() === "MSIE" && Fit.Browser.GetVersion() < 9 && me.MultiLine() === false)
23421
- {
23422
- // On IE8 input.focus() does not work if input field is switched to a single line control
23423
- // above (MultiLine(false)). Wrapping the code in setTimeout(..) solves the problem.
23889
+ // On IE8 input.focus() does not work if input field is switched to a traditional input field,
23890
+ // or if input field is hidden/invisible. It's just not reliable and not worth it. Remove focus
23891
+ // from the control in IE8 when DesignMode is disabled and preserve focus in every other browser.
23424
23892
 
23425
- setTimeout(function()
23426
- {
23427
- if (me === null)
23428
- return; // Control was disposed
23429
-
23430
- input.focus();
23431
- }, 0);
23893
+ if (isIe8 === false)
23894
+ {
23895
+ input.focus();
23432
23896
  }
23433
23897
  else
23434
23898
  {
23435
- input.focus();
23899
+ me.GetDomElement().blur(); // Control container was given focus further up - this will fire OnBlur as expected
23436
23900
  }
23437
23901
  }
23438
23902
 
23903
+ // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons
23904
+ Fit.Dom.Attribute(me.GetDomElement(), "tabindex", null);
23905
+
23439
23906
  repaint();
23440
23907
  }
23441
23908
  }
@@ -23469,6 +23936,17 @@ Fit.Controls.Input = function(ctlId)
23469
23936
  return debounceOnChangeTimeout;
23470
23937
  }
23471
23938
 
23939
+ // ============================================
23940
+ // Protected
23941
+ // ============================================
23942
+
23943
+ this._internal = (this._internal ? this._internal : {});
23944
+
23945
+ this._internal.DesignModeEnabledAndReady = function()
23946
+ {
23947
+ return designModeEnabledAndReady();
23948
+ }
23949
+
23472
23950
  // ============================================
23473
23951
  // Private
23474
23952
  // ============================================
@@ -23531,8 +24009,8 @@ Fit.Controls.Input = function(ctlId)
23531
24009
  }
23532
24010
 
23533
24011
  var langSupport = ["da", "de", "en"];
23534
- var locale = Fit.Internationalization.Locale().length === 2 ? Fit.Internationalization.Locale() : Fit.Internationalization.Locale().substring(0, 2);
23535
- var lang = Fit.Array.Contains(langSupport, locale) === true ? locale : "en";
24012
+ var localeCode = Fit.Internationalization.Locale().length === 2 ? Fit.Internationalization.Locale() : Fit.Internationalization.Locale().substring(0, 2);
24013
+ var lang = Fit.Array.Contains(langSupport, localeCode) === true ? localeCode : "en";
23536
24014
  var plugins = [];
23537
24015
  var toolbar = [];
23538
24016
  var mentions = [];
@@ -23546,7 +24024,7 @@ Fit.Controls.Input = function(ctlId)
23546
24024
  Fit.Array.Add(plugins, "emoji");
23547
24025
  }
23548
24026
 
23549
- if ((config.Plugins && config.Plugins.Images && config.Plugins.Images.Enabled === true) || (config.Toolbar && config.Toolbar.Images === true))
24027
+ if (designModeEnableImagePlugin() === true)
23550
24028
  {
23551
24029
  if (config.Toolbar && config.Toolbar.Images === true)
23552
24030
  {
@@ -23556,6 +24034,8 @@ Fit.Controls.Input = function(ctlId)
23556
24034
  plugins = Fit.Array.Merge(plugins, ["base64imagepaste", "dragresize"]);
23557
24035
  }
23558
24036
 
24037
+ Fit.Array.Add(plugins, "custombuttons");
24038
+
23559
24039
  // Add toolbar buttons
23560
24040
 
23561
24041
  if (!config.Toolbar || config.Toolbar.Formatting !== false)
@@ -23616,6 +24096,43 @@ Fit.Controls.Input = function(ctlId)
23616
24096
  items: insert
23617
24097
  });
23618
24098
  }
24099
+
24100
+ var customButtons = [];
24101
+ var customToolbarGroups = [];
24102
+
24103
+ if (config.Toolbar.Detach === true)
24104
+ {
24105
+ Fit.Array.Add(customButtons,
24106
+ {
24107
+ Label: locale.Detach,
24108
+ Command: "Detach",
24109
+ Icon: Fit.GetUrl() + "/Controls/Input/" + (window.devicePixelRatio === 2 ? "maximize-highres.png" : "maximize.png"),
24110
+ Callback: function(args)
24111
+ {
24112
+ //console.log("Command " + args.Command.name + " executed", args);
24113
+ openDetachedDesignEditor();
24114
+ }
24115
+ });
24116
+
24117
+ /*Fit.Array.Add(customButtons,
24118
+ {
24119
+ Label: "Testing 1-2-3",
24120
+ Command: "TestButton",
24121
+ Icon: "/files/images/Bird.png",
24122
+ Callback: function(args)
24123
+ {
24124
+ alert("Hello world");
24125
+ }
24126
+ });*/
24127
+
24128
+ Fit.Array.Add(customToolbarGroups,
24129
+ {
24130
+ name: "DetachableEditor",
24131
+ items: ["Detach"/*, "TestButton"*/]
24132
+ });
24133
+ }
24134
+
24135
+ toolbar = Fit.Array.Merge(toolbar, customToolbarGroups);
23619
24136
  }
23620
24137
 
23621
24138
  // Configure tags/mentions plugin
@@ -23804,11 +24321,11 @@ Fit.Controls.Input = function(ctlId)
23804
24321
 
23805
24322
  if (alternativeItem !== null)
23806
24323
  {
23807
- return '<a data-tag-type="' + (alternativeItem.Type || trigger.Marker) + '" data-tag-id="' + (alternativeItem.Value || item.Value) + '"' + (alternativeItem.Data || item.Data ? ' data-tag-data="' + (alternativeItem.Data || item.Data) + '"' : '') + (alternativeItem.Url || item.Url ? ' href="' + (alternativeItem.Url || item.Url) + '"' : 'href=""') + '>' + (alternativeItem.Title || (trigger.Marker + item.Title)) + '</a>';
24324
+ return '<a data-tag-type="' + (alternativeItem.Type || trigger.Marker) + '" data-tag-id="' + (alternativeItem.Value || item.Value) + '"' + (alternativeItem.Data || item.Data ? ' data-tag-data="' + (alternativeItem.Data || item.Data) + '"' : '') + (alternativeItem.Context || item.Context ? ' data-tag-context="' + (alternativeItem.Context || item.Context) + '"' : '') + (alternativeItem.Url || item.Url ? ' href="' + (alternativeItem.Url || item.Url) + '"' : 'href=""') + '>' + (alternativeItem.Title || (trigger.Marker + item.Title)) + '</a>';
23808
24325
  }
23809
24326
  else
23810
24327
  {
23811
- return '<a data-tag-type="' + trigger.Marker + '" data-tag-id="' + item.Value + '"' + (item.Data ? ' data-tag-data="' + item.Data + '"' : '') + (item.Url ? ' href="' + item.Url + '"' : 'href=""') + '>' + trigger.Marker + item.Title + '</a>';
24328
+ return '<a data-tag-type="' + trigger.Marker + '" data-tag-id="' + item.Value + '"' + (item.Data ? ' data-tag-data="' + item.Data + '"' : '') + (item.Context ? ' data-tag-context="' + item.Context + '"' : '') + (item.Url ? ' href="' + item.Url + '"' : 'href=""') + '>' + trigger.Marker + item.Title + '</a>';
23812
24329
  }
23813
24330
  }
23814
24331
  };
@@ -23819,7 +24336,7 @@ Fit.Controls.Input = function(ctlId)
23819
24336
  mention.feed = Fit.Core.CreateDebouncer(mention.feed, trigger.DebounceQuery || 300).Invoke;
23820
24337
  }
23821
24338
 
23822
- Fit.Array.Add(mentions, mention)
24339
+ Fit.Array.Add(mentions, mention);
23823
24340
  });
23824
24341
  }
23825
24342
 
@@ -23864,7 +24381,7 @@ Fit.Controls.Input = function(ctlId)
23864
24381
  toolbarLocation: designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.Position === "Bottom" ? "bottom" : "top",
23865
24382
  uiColor: Fit._internal.Controls.Input.Editor.Skin === "moono-lisa" || Fit._internal.Controls.Input.Editor.Skin === null ? "#FFFFFF" : undefined,
23866
24383
  //allowedContent: true, // http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules and http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent
23867
- extraAllowedContent: "a[data-tag-type,data-tag-id,data-tag-data]", // https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-extraAllowedContent
24384
+ extraAllowedContent: "a[data-tag-type,data-tag-id,data-tag-data,data-tag-context]", // https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-extraAllowedContent
23868
24385
  language: lang,
23869
24386
  disableNativeSpellChecker: me.CheckSpelling() === false,
23870
24387
  readOnly: me.Enabled() === false,
@@ -23889,6 +24406,7 @@ Fit.Controls.Input = function(ctlId)
23889
24406
  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)
23890
24407
  toolbar: toolbar,
23891
24408
  removeButtons: "", // Set to empty string to prevent CKEditor from removing buttons such as Underline
24409
+ customButtons: customButtons,
23892
24410
  mentions: mentions,
23893
24411
  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
23894
24412
  on:
@@ -23987,17 +24505,21 @@ Fit.Controls.Input = function(ctlId)
23987
24505
  if (Fit.Dom.GetComputedStyle(toolbarContainer, "position") === "sticky") // False on non-supported browsers as position:sticky is applied via the @supports CSS rule
23988
24506
  {
23989
24507
  var scrollParent = Fit.Dom.GetScrollParent(me.GetDomElement());
23990
- var toolbarPosition = me._internal.Data("toolbar-position"); // top | bottom
23991
24508
 
23992
- if (toolbarPosition === "top")
24509
+ 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
23993
24510
  {
23994
- var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-top"); // E.g. "28px"
23995
- toolbarContainer.style.top = paddingOffset !== "0px" ? "-" + paddingOffset : "";
23996
- }
23997
- else
23998
- {
23999
- var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-bottom"); // E.g. "28px"
24000
- toolbarContainer.style.bottom = paddingOffset !== "0px" ? "-" + paddingOffset : "";
24511
+ var toolbarPosition = me._internal.Data("toolbar-position"); // top | bottom
24512
+
24513
+ if (toolbarPosition === "top")
24514
+ {
24515
+ var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-top"); // E.g. "28px"
24516
+ toolbarContainer.style.top = paddingOffset !== "0px" ? "-" + paddingOffset : "";
24517
+ }
24518
+ else
24519
+ {
24520
+ var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-bottom"); // E.g. "28px"
24521
+ toolbarContainer.style.bottom = paddingOffset !== "0px" ? "-" + paddingOffset : "";
24522
+ }
24001
24523
  }
24002
24524
  }
24003
24525
  }
@@ -24161,7 +24683,7 @@ Fit.Controls.Input = function(ctlId)
24161
24683
  // place focus at the end of the editor as expected.
24162
24684
  if (me.Focused() === true)
24163
24685
  {
24164
- designEditor.focus();
24686
+ 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
24165
24687
  }
24166
24688
  }, 0);
24167
24689
  },
@@ -24227,11 +24749,18 @@ Fit.Controls.Input = function(ctlId)
24227
24749
 
24228
24750
  if (elm.tagName === "A" && Fit.Dom.Data(elm, "tag-id") !== null)
24229
24751
  {
24230
- designEditorSuppressPaste = true;
24231
- setTimeout(function() // Postpone - otherwise we won't be able to temporarily disable some of the buttons (https://jsfiddle.net/ymv56znq/14/)
24752
+ // Notice that selectionChange handler is invoked while editor is loading if control was given initial focus.
24753
+ // But at this point the toolbar buttons are not yet available to be disabled, so disableDesignEditorButtons()
24754
+ // won't work. However, as soon as the editor is done loading, focus is re-assigned to the editable area
24755
+ // which will trigger selectionChange handler once again, at which point designModeEnabledAndReady() returns true.
24756
+ if (designModeEnabledAndReady() === true)
24232
24757
  {
24233
- disableDesignEditorButtons();
24234
- }, 0);
24758
+ designEditorSuppressPaste = true;
24759
+ setTimeout(function() // Postpone - otherwise we won't be able to temporarily disable some of the buttons (https://jsfiddle.net/ymv56znq/14/)
24760
+ {
24761
+ disableDesignEditorButtons();
24762
+ }, 0);
24763
+ }
24235
24764
  }
24236
24765
  else
24237
24766
  {
@@ -24560,6 +25089,258 @@ Fit.Controls.Input = function(ctlId)
24560
25089
  }
24561
25090
  }
24562
25091
 
25092
+ function openDetachedDesignEditor()
25093
+ {
25094
+ me._internal.FocusStateLocked(true);
25095
+
25096
+ var deConfig = null;
25097
+
25098
+ var updateDetachedConfiguration = function()
25099
+ {
25100
+ deConfig = Fit.Core.Clone(designEditorConfig || {});
25101
+
25102
+ // Configure detached editor like original, but with a few required changes.
25103
+ // We need to make sure image blobs are handled properly, that detached editor
25104
+ // cannot created another detached editor, that the toolbar is initially visible
25105
+ // at the top of the dialog, and that auto grow is disabled.
25106
+
25107
+ if (designModeEnableImagePlugin() === true)
25108
+ {
25109
+ deConfig = Fit.Core.Merge(deConfig, // Override image plugin configuration
25110
+ {
25111
+ Plugins:
25112
+ {
25113
+ Images:
25114
+ {
25115
+ Enabled: true,
25116
+ EmbedType: deConfig.Plugins && deConfig.Plugins.Images && deConfig.Plugins.Images.EmbedType,
25117
+
25118
+ // Image blobs added in detached editor must always be disposed if no longer referenced.
25119
+ // Furthermore we make sure image blobs originating from main editor are never disposed.
25120
+ // When detached editor is closed, images transfered from detached editor to main editor
25121
+ // are added to the main editor's index over image blobs so that the main editor becomes
25122
+ // responsible for the memory management of these.
25123
+ // If detached editor is closed without transfering changes (canceled), all images found
25124
+ // in the detached editor, which are not referenced in the main editor, are disposed.
25125
+ // See OnClick handlers for OK and Cancel buttons.
25126
+
25127
+ RevokeBlobUrlsOnDispose: "UnreferencedOnly", // Make dialog editor preserve newly added (and still referenced) image blobs when disposed
25128
+ RevokeExternalBlobUrlsOnDispose: false // Make dialog editor preserve images blobs initially added from main editor
25129
+ }
25130
+ }
25131
+ });
25132
+ }
25133
+
25134
+ deConfig.Toolbar = deConfig.Toolbar || {};
25135
+ deConfig.Toolbar.Detach = false;
25136
+ deConfig.Toolbar.Position = "Top";
25137
+ deConfig.Toolbar.Sticky = false;
25138
+ deConfig.Toolbar.HideInitially = false;
25139
+
25140
+ delete deConfig.AutoGrow;
25141
+ };
25142
+
25143
+ updateDetachedConfiguration();
25144
+
25145
+ // Create dialog
25146
+
25147
+ var dia = new Fit.Controls.Dialog();
25148
+ Fit.Dom.AddClass(dia.GetDomElement(), "FitUiControlInputDetached");
25149
+
25150
+ // Create editor
25151
+
25152
+ var de = new Fit.Controls.Input();
25153
+
25154
+ var setDetachedEditorSettings = function()
25155
+ {
25156
+ de.Width(100, "%");
25157
+ de.Height(-1);
25158
+ de.CheckSpelling(me.CheckSpelling());
25159
+ de.DesignMode(true, deConfig);
25160
+ };
25161
+
25162
+ setDetachedEditorSettings();
25163
+ de.Value(me.Value());
25164
+
25165
+ // Make editor adjust to the dimensions of the dialog
25166
+
25167
+ // Adjust editor size to fit dialog using a timer.
25168
+ // Alternatively expose an OnResize event on Fit.Controls.Dialog, use it in
25169
+ // combination with window.onresize, and adjust editor when these events are triggered.
25170
+ // However, the process of monitoring the dimensions using a timer is practically free,
25171
+ // so even though the timer interupts browser events such as onmousemove, onscroll, etc.,
25172
+ // it creates no lack at all.
25173
+ var height = -1
25174
+ var dimMonitorId = setInterval(function()
25175
+ {
25176
+ if (height === -1 && de._internal.DesignModeEnabledAndReady() === false)
25177
+ {
25178
+ return;
25179
+ }
25180
+
25181
+ var newHeight = dia.GetDomElement().offsetHeight;
25182
+
25183
+ if (newHeight !== height)
25184
+ {
25185
+ height = newHeight
25186
+ de.Height(Fit.Dom.GetInnerDimensions(dia.GetContentDomElement()).Height);
25187
+ }
25188
+ }, 250);
25189
+
25190
+ // Configure dialog
25191
+
25192
+ var setDialogSettings = function(update)
25193
+ {
25194
+ var detachConfig = deConfig.Detachable || {};
25195
+ detachConfig = Fit.Core.Merge(detachConfig, // Apply default values - existing properties are preserved (Fit.Core.MergeOverwriteBehaviour.Never)
25196
+ {
25197
+ Title: "",
25198
+ Maximizable: true,
25199
+ Maximized: false,
25200
+ Draggable: true,
25201
+ Resizable: true,
25202
+ Width: detachConfig.Width ? detachConfig.Width : { Value: 850, Unit: "px" },
25203
+ MinimumWidth: detachConfig.MinimumWidth ? detachConfig.MinimumWidth : { Value: 20, Unit: "em" },
25204
+ MaximumWidth: detachConfig.MaximumWidth ? detachConfig.MaximumWidth : { Value: 100, Unit: "%" },
25205
+ Height: detachConfig.Height ? detachConfig.Height : { Value: 550, Unit: "px" },
25206
+ MinimumHeight: detachConfig.MinimumHeight ? detachConfig.MinimumHeight : { Value: 12, Unit: "em" },
25207
+ MaximumHeight: detachConfig.MaximumHeight ? detachConfig.MaximumHeight : { Value: 100, Unit: "%" }
25208
+ }, Fit.Core.MergeOverwriteBehaviour.Never);
25209
+
25210
+ var updateDimensions = (!detachConfig.Resizable || update !== true);
25211
+
25212
+ dia.Title(detachConfig.Title);
25213
+ dia.Modal(true);
25214
+ dia.Draggable(detachConfig.Draggable);
25215
+ dia.Resizable(detachConfig.Resizable);
25216
+ dia.Maximizable(detachConfig.Maximizable);
25217
+ dia.Maximized(detachConfig.Maximized);
25218
+ updateDimensions === true && dia.Width(detachConfig.Width.Value, detachConfig.Width.Unit || "px");
25219
+ updateDimensions === true && dia.Height(detachConfig.Height.Value, detachConfig.Height.Unit || "px");
25220
+ dia.MinimumWidth(detachConfig.MinimumWidth.Value, detachConfig.MinimumWidth.Unit || "px");
25221
+ dia.MinimumHeight(detachConfig.MinimumHeight.Value, detachConfig.MinimumHeight.Unit || "px");
25222
+ dia.MaximumWidth(detachConfig.MaximumWidth.Value, detachConfig.MaximumWidth.Unit || "px");
25223
+ dia.MaximumHeight(detachConfig.MaximumHeight.Value, detachConfig.MaximumHeight.Unit || "px");
25224
+ };
25225
+
25226
+ setDialogSettings();
25227
+
25228
+ // Localization support
25229
+
25230
+ var localizeDetachedEditor = function()
25231
+ {
25232
+ // Editor itself is already localized, so we just need to
25233
+ // localize the dialog. The locale variable will already have
25234
+ // been updated by the OnLocaleChanged handler registered in init().
25235
+
25236
+ cmdOk.Title(locale.Ok);
25237
+ cmdCancel.Title(locale.Cancel);
25238
+ };
25239
+ Fit.Internationalization.OnLocaleChanged(localizeDetachedEditor);
25240
+
25241
+ // Expose detached editor API
25242
+
25243
+ designEditorDetached =
25244
+ {
25245
+ GetValue: function()
25246
+ {
25247
+ return de.Value();
25248
+ },
25249
+
25250
+ Reload: function()
25251
+ {
25252
+ // Update configuration
25253
+ updateDetachedConfiguration();
25254
+
25255
+ // Update editor
25256
+ setDetachedEditorSettings();
25257
+
25258
+ // Update dialog
25259
+ setDialogSettings(true);
25260
+
25261
+ // Make dimension monitor ensure proper editor height (dialog height might have been changed)
25262
+ height = -1;
25263
+ },
25264
+
25265
+ Dispose: function()
25266
+ {
25267
+ clearInterval(dimMonitorId);
25268
+ Fit.Internationalization.RemoveOnLocaleChanged(localizeDetachedEditor);
25269
+ de.Dispose();
25270
+ dia.Dispose(); // Will also dispose associated buttons
25271
+ designEditorDetached = null;
25272
+ }
25273
+ };
25274
+
25275
+ var cmdOk = new Fit.Controls.Button();
25276
+ cmdOk.Title(locale.Ok);
25277
+ cmdOk.Icon("check");
25278
+ cmdOk.Type(Fit.Controls.ButtonType.Success);
25279
+ cmdOk.OnClick(function(sender)
25280
+ {
25281
+ var referencedBlobUrls = Fit.String.ParseImageBlobUrls(de.Value());
25282
+ Fit.Array.ForEach(referencedBlobUrls, function(blobUrl)
25283
+ {
25284
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
25285
+ {
25286
+ Fit.Array.Add(imageBlobUrls, blobUrl);
25287
+ }
25288
+ });
25289
+
25290
+ me.Value(de.Value());
25291
+
25292
+ designEditorDetached.Dispose();
25293
+
25294
+ me.Focused(true);
25295
+ me._internal.FocusStateLocked(false);
25296
+ });
25297
+ dia.AddButton(cmdOk);
25298
+
25299
+ var cmdCancel = new Fit.Controls.Button();
25300
+ cmdCancel.Title(locale.Cancel);
25301
+ cmdCancel.Icon("ban");
25302
+ cmdCancel.Type(Fit.Controls.ButtonType.Danger);
25303
+ cmdCancel.OnClick(function(sender)
25304
+ {
25305
+ var closeDialog = function()
25306
+ {
25307
+ var referencedBlobUrls = Fit.String.ParseImageBlobUrls(de.Value());
25308
+ Fit.Array.ForEach(referencedBlobUrls, function(blobUrl)
25309
+ {
25310
+ if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false) // Only remove images added in dialog editor
25311
+ {
25312
+ URL.revokeObjectURL(blobUrl);
25313
+ }
25314
+ });
25315
+
25316
+ designEditorDetached.Dispose();
25317
+
25318
+ me.Focused(true);
25319
+ me._internal.FocusStateLocked(false);
25320
+ };
25321
+
25322
+ if (de.IsDirty() === true)
25323
+ {
25324
+ Fit.Controls.Dialog.Confirm(locale.CancelConfirmTitle + "<br><br>" + locale.CancelConfirmDescription, function(res)
25325
+ {
25326
+ if (res === true)
25327
+ {
25328
+ closeDialog();
25329
+ }
25330
+ });
25331
+ }
25332
+ else
25333
+ {
25334
+ closeDialog();
25335
+ }
25336
+ });
25337
+ dia.AddButton(cmdCancel);
25338
+
25339
+ dia.Open();
25340
+ de.Render(dia.GetContentDomElement());
25341
+ de.Focused(true);
25342
+ }
25343
+
24563
25344
  function revertToSingleLineIfNecessary()
24564
25345
  {
24565
25346
  if (wasAutoChangedToMultiLineMode === true && me.Maximizable() === false && me.Resizable() === Fit.Controls.InputResizing.Disabled && me.DesignMode() === false)
@@ -24623,10 +25404,25 @@ Fit.Controls.Input = function(ctlId)
24623
25404
  var height = me.Height();
24624
25405
  var currentWasAutoChangedToMultiLineMode = wasAutoChangedToMultiLineMode; // DesignMode(false) will result in wasAutoChangedToMultiLineMode being set to false if DesignMode(true) changed the control to MultiLine mode
24625
25406
 
25407
+ // Prevent detached editor from being closed when reloading, e.g. if CheckSpelling is changed.
25408
+ // Editor will also be reloaded if a different editor configuration is passed to DesignMode(true, updatedConfig).
25409
+ var detachedEditor = null;
25410
+ if (designEditorDetached !== null)
25411
+ {
25412
+ detachedEditor = designEditorDetached;
25413
+ designEditorDetached = null; // Prevent me.DesignMode(false), which in turn calls destroyDesignEditorInstance(), from closing detached editor dialog
25414
+ }
25415
+
24626
25416
  me.DesignMode(false);
24627
25417
  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
24628
25418
  designEditorReloadConfig = null;
24629
25419
 
25420
+ if (detachedEditor !== null)
25421
+ {
25422
+ designEditorDetached = detachedEditor;
25423
+ designEditorDetached.Reload(); // Reload detached editor to reflect any changes made to configuration
25424
+ }
25425
+
24630
25426
  me.Height(height.Value, height.Unit);
24631
25427
  wasAutoChangedToMultiLineMode = currentWasAutoChangedToMultiLineMode;
24632
25428
  }
@@ -24645,9 +25441,14 @@ Fit.Controls.Input = function(ctlId)
24645
25441
 
24646
25442
  designEditor.destroy();
24647
25443
 
25444
+ if (designEditorDetached !== null)
25445
+ {
25446
+ designEditorDetached.Dispose();
25447
+ }
25448
+
24648
25449
  designEditor = null;
24649
25450
  designEditorDom = null;
24650
- designEditorDirty = false;
25451
+ //designEditorDirty = false; // Do NOT reset this! We need to preserve dirty state in case DesignMode is reloaded!
24651
25452
  designEditorDirtyPending = false;
24652
25453
  //designEditorConfig = null; // Do NOT nullify this! We need it, in case DesignMode is toggled!
24653
25454
  //designEditorReloadConfig = null; // Do NOT nullify this! We need it, in case DesignMode is reloaded!
@@ -24657,6 +25458,7 @@ Fit.Controls.Input = function(ctlId)
24657
25458
  designEditorMustReloadWhenReady = false;
24658
25459
  designEditorMustDisposeWhenReady = false;
24659
25460
  designEditorActiveToolbarPanel = null;
25461
+ designEditorDetached = null;
24660
25462
 
24661
25463
  if (designEditorUpdateSizeDebouncer !== -1)
24662
25464
  {
@@ -24688,12 +25490,47 @@ Fit.Controls.Input = function(ctlId)
24688
25490
  return designEditorDom !== null; // Editor is fully loaded when editor DOM is made available
24689
25491
  }
24690
25492
 
25493
+ function designModeEnableImagePlugin()
25494
+ {
25495
+ var config = designEditorConfig || {};
25496
+ var enableImagePlugin = (config.Plugins && config.Plugins.Images && config.Plugins.Images.Enabled === true) || (config.Toolbar && config.Toolbar.Images === true) || false;
25497
+
25498
+ // Force enable image support if images are contained in value - otherwise editor will remove them
25499
+ if (enableImagePlugin === false && designEditorDetached !== null && designEditorDetached.GetValue().indexOf("<img ") > -1)
25500
+ {
25501
+ enableImagePlugin = true;
25502
+ }
25503
+ if (enableImagePlugin === false && me.Value().indexOf("<img ") > -1)
25504
+ {
25505
+ enableImagePlugin = true;
25506
+ }
25507
+
25508
+ return enableImagePlugin;
25509
+ }
25510
+
24691
25511
  function localize()
24692
25512
  {
25513
+ locale = Fit.Internationalization.GetLocale(me);
25514
+
24693
25515
  if (me.DesignMode() === true)
24694
25516
  {
25517
+ // Prevent reloadEditor() from reloading detached editor.
25518
+ // It will automatically reload when locale is changed.
25519
+ // Without this guard the editor would reload twice.
25520
+ var detachedEditor = null;
25521
+ if (designEditorDetached !== null)
25522
+ {
25523
+ detachedEditor = designEditorDetached;
25524
+ designEditorDetached = null; // Prevent reloadEditor() from reloading detached editor
25525
+ }
25526
+
24695
25527
  // Re-create editor with new language
24696
25528
  reloadEditor();
25529
+
25530
+ if (detachedEditor !== null)
25531
+ {
25532
+ designEditorDetached = detachedEditor;
25533
+ }
24697
25534
  }
24698
25535
  }
24699
25536
 
@@ -24810,6 +25647,40 @@ Fit.Controls.InputResizing = // Enums must exist runtime
24810
25647
  Horizontal: "Horizontal",
24811
25648
  Vertical: "Vertical"
24812
25649
  };
25650
+ Fit.Internationalization.AddLocalization(Fit.Controls.Input,
25651
+ {
25652
+ // Primary locales must be registered before country specific overrides.
25653
+ // Example order: en, en_GB, de, de_AT, etc.
25654
+ // All locales inherit from en. All country specific overrides inherit
25655
+ // from their general locale (e.g. de_AT inherits from de).
25656
+ // English (en) MUST be defined, and be defined first!
25657
+
25658
+ "en":
25659
+ {
25660
+ "Detach": "Open in dialog",
25661
+ "Ok": "OK",
25662
+ "Cancel": "Cancel",
25663
+ "CancelConfirmTitle": "Discard changes?",
25664
+ "CancelConfirmDescription": "Are you sure you want to discard the changes?"
25665
+ },
25666
+ "da":
25667
+ {
25668
+ "Detach": "Åben i dialog",
25669
+ "Ok": "OK",
25670
+ "Cancel": "Annullér",
25671
+ "CancelConfirmTitle": "Annullér ændringer?",
25672
+ "CancelConfirmDescription": "Er du sikker på at du vil annullere ændringerne?"
25673
+ },
25674
+ "de":
25675
+ {
25676
+ "Detach": "Im Dialog öffnen",
25677
+ "Ok": "OK",
25678
+ "Cancel": "Abbrechen",
25679
+ "CancelConfirmTitle": "Änderungen verwerfen?",
25680
+ "CancelConfirmDescription": "Möchten Sie die Änderungen wirklich verwerfen?"
25681
+ }
25682
+ });
25683
+
24813
25684
  /// <container name="Fit.Controls.ListView" extends="Fit.Controls.PickerBase;Fit.Controls.Component">
24814
25685
  /// Picker control which allows for entries
24815
25686
  /// to be selected in the DropDown control.
@@ -26128,7 +26999,7 @@ Fit.Controls.SoftLog = function(controlId)
26128
26999
 
26129
27000
  /// <function container="Fit.Controls.SoftLog" name="MaxEntries" access="public" returns="integer">
26130
27001
  /// <description> Get/set number of log entries preserved </description>
26131
- /// <param name="val" type="boolean" default="undefined"> If defined, changes number of log entries preserved </param>
27002
+ /// <param name="val" type="integer" default="undefined"> If defined, changes number of log entries preserved </param>
26132
27003
  /// </function>
26133
27004
  this.MaxEntries = function(val)
26134
27005
  {