fit-ui 2.5.2 → 2.5.6

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 (26) hide show
  1. package/dist/Documentation.html +2 -2
  2. package/dist/Fit.UI.js +403 -49
  3. package/dist/Fit.UI.min.js +1 -1
  4. package/dist/Resources/CKEditor/CHANGES.md +107 -0
  5. package/dist/Resources/CKEditor/LICENSE.md +1 -0
  6. package/dist/Resources/CKEditor/ckeditor.js +545 -533
  7. package/dist/Resources/CKEditor/contents.css +208 -208
  8. package/dist/Resources/CKEditor/index.html +8 -0
  9. package/dist/Resources/CKEditor/lang/da.js +1 -1
  10. package/dist/Resources/CKEditor/lang/de.js +1 -1
  11. package/dist/Resources/CKEditor/lang/en.js +1 -1
  12. package/dist/Resources/CKEditor/plugins/base64image/dialogs/base64image.js +38 -33
  13. package/dist/Resources/CKEditor/plugins/htmlwriter/plugin.js +364 -0
  14. package/dist/Resources/CKEditor/plugins/link/dialogs/anchor.js +4 -4
  15. package/dist/Resources/CKEditor/skins/moono-lisa/dialog.css +5 -5
  16. package/dist/Resources/CKEditor/skins/moono-lisa/dialog_ie.css +5 -5
  17. package/dist/Resources/CKEditor/skins/moono-lisa/dialog_ie8.css +5 -5
  18. package/dist/Resources/CKEditor/skins/moono-lisa/dialog_iequirks.css +5 -5
  19. package/dist/Resources/CKEditor/skins/moono-lisa/editor.css +5 -5
  20. package/dist/Resources/CKEditor/skins/moono-lisa/editor_gecko.css +5 -5
  21. package/dist/Resources/CKEditor/skins/moono-lisa/editor_ie.css +5 -5
  22. package/dist/Resources/CKEditor/skins/moono-lisa/editor_ie8.css +5 -5
  23. package/dist/Resources/CKEditor/skins/moono-lisa/editor_iequirks.css +5 -5
  24. package/dist/Resources/CKEditor/styles.js +137 -137
  25. package/package.json +1 -1
  26. package/types/index.d.ts +145 -100
package/dist/Fit.UI.js CHANGED
@@ -648,7 +648,7 @@ Fit._internal =
648
648
  {
649
649
  Core:
650
650
  {
651
- VersionInfo: { Major: 2, Minor: 5, Patch: 2 } // Do NOT modify format - version numbers are programmatically changed when releasing new versions - MUST be on a separate line!
651
+ VersionInfo: { Major: 2, Minor: 5, Patch: 6 } // Do NOT modify format - version numbers are programmatically changed when releasing new versions - MUST be on a separate line!
652
652
  }
653
653
  };
654
654
 
@@ -4281,6 +4281,7 @@ Fit.Controls.ControlBase = function(controlId)
4281
4281
 
4282
4282
  var onChangeHandler = me._internal.FireOnChange;
4283
4283
  me._internal.FireOnChange = function() {};
4284
+ me._internal.FireOnChangeSuppressed = true; // Allow specialized controls to detect when OnChange event will be suppressed for performance optimizations
4284
4285
 
4285
4286
  var error = null;
4286
4287
 
@@ -4294,10 +4295,12 @@ Fit.Controls.ControlBase = function(controlId)
4294
4295
  }
4295
4296
 
4296
4297
  me._internal.FireOnChange = onChangeHandler;
4298
+ me._internal.FireOnChangeSuppressed = false;
4297
4299
 
4298
4300
  if (error !== null)
4299
4301
  Fit.Validation.ThrowError(error);
4300
4302
  }
4303
+ this._internal.FireOnChangeSuppressed = false;
4301
4304
 
4302
4305
  this._internal.Data = function(key, val)
4303
4306
  {
@@ -15795,6 +15798,7 @@ Fit.Controls.DropDown = function(ctlId)
15795
15798
  var detectBoundariesRelToViewPort = false; // Flag indicating whether drop down menu should be positioned relative to viewport (true) or scroll parent (false)
15796
15799
  var persistView = false; // Flag indicating whether picker controls should remember and restore its scroll position and highlighted item when reopened
15797
15800
  var highlightFirst = false; // Flag indicating whether picker controls should focus its first node automatically when opened
15801
+ var searchModeOnFocus = false; // Flag indicating whether control goes into search mode when it is focused (search mode clears input field and displays "search.." placeholder)
15798
15802
 
15799
15803
  var onInputChangedHandlers = []; // Invoked when input value is changed - takes two arguments (sender (this), text value)
15800
15804
  var onPasteHandlers = []; // Invoked when a value is pasted - takes two arguments (sender (this), text value)
@@ -16040,6 +16044,53 @@ Fit.Controls.DropDown = function(ctlId)
16040
16044
  }
16041
16045
  });
16042
16046
 
16047
+ // Support for SearchModeOnFocus
16048
+
16049
+ me.OnFocus(function(sender)
16050
+ {
16051
+ if (searchModeOnFocus === true)
16052
+ {
16053
+ searchModeOnFocus = false; // Temporarily disable searchModeOnFocus to allow setInputEditing(..) (which is called by ClearInputForSearch(..)) to change editing state for txtPrimary
16054
+ me._internal.ClearInputForSearch(true); // True = keep DropDown open - do not auto close it
16055
+ searchModeOnFocus = true;
16056
+ }
16057
+ });
16058
+
16059
+ me.OnBlur(function()
16060
+ {
16061
+ if (searchModeOnFocus === true)
16062
+ {
16063
+ searchModeOnFocus = false; // Temporarily disable searchModeOnFocus to allow setInputEditing(..) to change editing state for txtPrimary
16064
+ me._internal.UndoClearInputForSearch();
16065
+ searchModeOnFocus = true;
16066
+ }
16067
+ });
16068
+
16069
+ me.OnChange(function()
16070
+ {
16071
+ // Determine whether value was changed by user or programmatically,
16072
+ // so placeholder is not set when value is assigned programmatically.
16073
+ // Control might not be focused on mobile if opened using arrow icon.
16074
+ // In this case we simply use the DropDown's opened state instead.
16075
+ var controlIsActive = me.Focused() === true || me.IsDropDownOpen() === true;
16076
+
16077
+ if (searchModeOnFocus === true && controlIsActive === true)
16078
+ {
16079
+ if (me.GetSelections().length === 0)
16080
+ {
16081
+ // DropDown has a synthetic placeholder which is displayed
16082
+ // when no items are selected. Remove placeholder from input
16083
+ // field so we do not get two placeholders on top of each other.
16084
+ txtPrimary.placeholder = "";
16085
+ }
16086
+ else
16087
+ {
16088
+ // Display placeholder in input field when items are selected
16089
+ txtPrimary.placeholder = me.Placeholder();
16090
+ }
16091
+ }
16092
+ });
16093
+
16043
16094
  // PickerBase - make picker aware of focused state of host control
16044
16095
 
16045
16096
  me.OnFocus(function()
@@ -16270,7 +16321,7 @@ Fit.Controls.DropDown = function(ctlId)
16270
16321
  itemDropZones[key].Dispose();
16271
16322
  });
16272
16323
 
16273
- me = itemContainer = itemCollection = itemDropZones = arrow = txtPrimary = txtActive = txtEnabled = dropDownMenu = picker = orgSelections = invalidMessage = invalidMessageChanged = initialFocus = maxHeight = prevValue = focusAssigned = closeHandlers = dropZone = isMobile = focusInputOnMobile = detectBoundaries = detectBoundariesRelToViewPort = persistView = highlightFirst = onInputChangedHandlers = onPasteHandlers = onOpenHandlers = onCloseHandlers = suppressUpdateItemSelectionState = suppressOnItemSelectionChanged = clearTextSelectionOnInputChange = prevTextSelection = textSelectionCallback = cmdToggleTextMode = null;
16324
+ me = itemContainer = itemCollection = itemDropZones = arrow = txtPrimary = txtActive = txtEnabled = dropDownMenu = picker = orgSelections = invalidMessage = invalidMessageChanged = initialFocus = maxHeight = prevValue = focusAssigned = closeHandlers = dropZone = isMobile = focusInputOnMobile = detectBoundaries = detectBoundariesRelToViewPort = persistView = highlightFirst = searchModeOnFocus = onInputChangedHandlers = onPasteHandlers = onOpenHandlers = onCloseHandlers = suppressUpdateItemSelectionState = suppressOnItemSelectionChanged = clearTextSelectionOnInputChange = prevTextSelection = textSelectionCallback = cmdToggleTextMode = null;
16274
16325
 
16275
16326
  base();
16276
16327
  });
@@ -17261,9 +17312,30 @@ Fit.Controls.DropDown = function(ctlId)
17261
17312
  if (Fit._internal.DropDown.Current !== null && Fit._internal.DropDown.Current !== me)
17262
17313
  Fit._internal.DropDown.Current.CloseDropDown();
17263
17314
 
17264
- if (txtActive === txtPrimary && me.GetInputValue() === "")
17315
+ if (searchModeOnFocus === false && me.TextSelectionMode() === false && txtActive === txtPrimary && me.GetInputValue() === "")
17265
17316
  {
17266
- me._internal.UndoClearInputForSearch();
17317
+ // Visual Selection Mode with SearchModeOnFocus (which keeps txtPrimary locked in place) disabled:
17318
+
17319
+ // When placing the control in search mode temporarily (not "locked" by
17320
+ // SearchModeOnFocus) via me._internal.ClearInputForSearch() or by entering a value
17321
+ // in txtPrimary, the input control "word wraps" to a new line. When it lose focus,
17322
+ // it returns to its normal position if it is empty.
17323
+ // In Visual Selection Mode with SearchModeOnFocus disabled this poses a problem
17324
+ // since interacting with the picker control results in the input control losing
17325
+ // focus.
17326
+ // So, if the user clicks on an item in the picker control while the control
17327
+ // is temporarily in search mode (remember, SearchModeOnFocus is not enabled),
17328
+ // txtPrimary lose focus, the control's height is changed because the input
17329
+ // control returns to its normal position (it no longer "word wraps"), the picker
17330
+ // control moves up, and the mouse button is released on another element than the
17331
+ // one the user initially clicked on.
17332
+ // OnClick does not fire unless the mouse button is clicked and released
17333
+ // on the same element. So in this situation the picker control would not cause
17334
+ // the given item to be selected. The user would have to try again.
17335
+ // To avoid this, we exit search mode to force txtPrimary back into place when
17336
+ // re-opening the DropDown control.
17337
+
17338
+ me._internal.UndoClearInputForSearch(); // Undo/exit search mode to return txtPrimary to its normal position
17267
17339
  }
17268
17340
 
17269
17341
  // Do this before displaying drop down to prevent dropdown with position:absolute
@@ -17446,8 +17518,10 @@ Fit.Controls.DropDown = function(ctlId)
17446
17518
 
17447
17519
  this._internal = (this._internal ? this._internal : {});
17448
17520
 
17449
- this._internal.ClearInputForSearch = function()
17521
+ this._internal.ClearInputForSearch = function(keepDropDownOpen) // Put input in search mode
17450
17522
  {
17523
+ Fit.Validation.ExpectBoolean(keepDropDownOpen, true);
17524
+
17451
17525
  if (me.TextSelectionMode() === true)
17452
17526
  {
17453
17527
  forceFocusInput(txtPrimary);
@@ -17471,10 +17545,13 @@ Fit.Controls.DropDown = function(ctlId)
17471
17545
  // event to register interactions, which doesn't fire unless the mouse button is released
17472
17546
  // on the same object it was pressed down on. We avoid this by closing the control.
17473
17547
  // We close it in both Text Selection Mode and Visual Selection Mode for consistency.
17474
- me.CloseDropDown();
17548
+ if (keepDropDownOpen !== true)
17549
+ {
17550
+ me.CloseDropDown();
17551
+ }
17475
17552
  }
17476
17553
 
17477
- this._internal.UndoClearInputForSearch = function()
17554
+ this._internal.UndoClearInputForSearch = function() // Undo/exit search mode for input field
17478
17555
  {
17479
17556
  if (me.TextSelectionMode() === true)
17480
17557
  {
@@ -17487,6 +17564,13 @@ Fit.Controls.DropDown = function(ctlId)
17487
17564
  }
17488
17565
  }
17489
17566
 
17567
+ // Focus input field on mobile and keep it focused, even if DropDown was opened using arrow icon
17568
+ this._internal.ForceFocusMobile = function()
17569
+ {
17570
+ focusInputOnMobile = true;
17571
+ focusInput(txtPrimary);
17572
+ }
17573
+
17490
17574
  // ============================================
17491
17575
  // Private
17492
17576
  // ============================================
@@ -17940,12 +18024,45 @@ Fit.Controls.DropDown = function(ctlId)
17940
18024
  focusInputOnMobile = orgFocusInputOnMobile;
17941
18025
  }
17942
18026
 
17943
- function setInputEditing(input, val, keepStateOnParent)
18027
+ /// <function container="Fit.Controls.DropDown" name="SearchModeOnFocus" access="public" returns="boolean">
18028
+ /// <description>
18029
+ /// Clear input and display "Search.." placeholder when control receives focus.
18030
+ /// If SearchModeOnFocus is enabled, InputEnabled will also be enabled. Disabling
18031
+ /// SearchModeOnFocus does not disable InputEnabled.
18032
+ /// </description>
18033
+ /// <param name="val" type="boolean" default="undefined"> If defined, True enables search mode on focus, False disables it </param>
18034
+ /// </function>
18035
+ this.SearchModeOnFocus = function(val)
18036
+ {
18037
+ Fit.Validation.ExpectBoolean(val, true);
18038
+
18039
+ if (Fit.Validation.IsSet(val) === true)
18040
+ {
18041
+ searchModeOnFocus = val;
18042
+
18043
+ if (me.InputEnabled() === false && val === true)
18044
+ {
18045
+ me.InputEnabled(true);
18046
+ }
18047
+ }
18048
+
18049
+ return searchModeOnFocus;
18050
+ }
18051
+
18052
+ function setInputEditing(input, val, keepStateOnParent) // Input being edited "word wraps" to separate line
17944
18053
  {
17945
18054
  Fit.Validation.ExpectInstance(input, HTMLInputElement);
17946
18055
  Fit.Validation.ExpectBoolean(val);
17947
18056
  Fit.Validation.ExpectBoolean(keepStateOnParent, true);
17948
18057
 
18058
+ // Do not change editing state for txtPrimary when SearchModeOnFocus is enabled.
18059
+ // In this case txtPrimary remains locked in editing mode so it remains visible,
18060
+ // even when changing focus within the control and when adding or removing items.
18061
+ if (searchModeOnFocus === true && input === txtPrimary)
18062
+ {
18063
+ return;
18064
+ }
18065
+
17949
18066
  if (keepStateOnParent !== true)
17950
18067
  {
17951
18068
  Fit.Dom.Data(input.parentElement, "editing", val === true ? "true" : null);
@@ -19179,6 +19296,9 @@ Fit.Controls.WSDropDown = function(ctlId)
19179
19296
  var useActionMenu = false;
19180
19297
  var useActionMenuForced = false;
19181
19298
  var useActionMenuAfterLoad = true;
19299
+ var treeViewEnabled = true;
19300
+ var orgPlaceholder = this.Placeholder;
19301
+ var customPlaceholderSet = false;
19182
19302
  var translations = null;
19183
19303
 
19184
19304
  var onRequestHandlers = [];
@@ -19203,7 +19323,7 @@ Fit.Controls.WSDropDown = function(ctlId)
19203
19323
 
19204
19324
  // Create ListView
19205
19325
 
19206
- list = new Fit.Controls.WSListView(ctlId + "__WSListView");
19326
+ list = new Fit.Controls.WSListView(me.GetId() + "__WSListView");
19207
19327
  list.OnRequest(function(sender, eventArgs)
19208
19328
  {
19209
19329
  if (fireEventHandlers(onRequestHandlers, list, eventArgs) === false)
@@ -19255,7 +19375,7 @@ Fit.Controls.WSDropDown = function(ctlId)
19255
19375
 
19256
19376
  // Create TreeView
19257
19377
 
19258
- tree = new Fit.Controls.WSTreeView(ctlId + "__WSTreeView");
19378
+ tree = new Fit.Controls.WSTreeView(me.GetId() + "__WSTreeView");
19259
19379
  tree.Selectable(true); // Make nodes selectable by default when added
19260
19380
  tree.Width(100, "%");
19261
19381
  //tree.Lines(true); // DISABLED - lines do not scale with font size
@@ -19413,7 +19533,7 @@ Fit.Controls.WSDropDown = function(ctlId)
19413
19533
 
19414
19534
  var skipUpdateActionMenuOnChange = false;
19415
19535
 
19416
- actionMenu = new Fit.Controls.ListView(ctlId + "__ActionsListView");
19536
+ actionMenu = new Fit.Controls.ListView(me.GetId() + "__ActionsListView");
19417
19537
  actionMenu.OnSelect(function(sender, item) // Using OnSelect instead of OnItemSelectionChanging since DropDown fires OnItemSelectionChanging when selection is changed, which would result in OnItemSelectionChanging being executed multiple times
19418
19538
  {
19419
19539
  if (item.Value === "SearchMore")
@@ -19422,7 +19542,7 @@ Fit.Controls.WSDropDown = function(ctlId)
19422
19542
  }
19423
19543
  else if (item.Value === "ShowAll")
19424
19544
  {
19425
- me._internal.UndoClearInputForSearch(); // In case user first picked SearchMore, changed their mind, and then selected ShowAll
19545
+ me._internal.UndoClearInputForSearch(); // In case user first picked SearchMore, entered a search value, changed their mind, and then selected ShowAll
19426
19546
 
19427
19547
  useActionMenuAfterLoad = false;
19428
19548
 
@@ -19523,21 +19643,29 @@ Fit.Controls.WSDropDown = function(ctlId)
19523
19643
  // Do not show action menu if the only option available is ShowAll.
19524
19644
  // In this case the user will not be able to select SeachMore, and
19525
19645
  // there is no selected items that can be removed from the control.
19526
- var onlyShowAllOptionDisplayedInActionMenu = actionMenu.GetItems().length === 1 && actionMenu.HasItem("ShowAll") === true;
19646
+ // In this case we ignore useActionMenu === true, even when useActionMenuForced is true.
19647
+ var onlyTheShowAllOptionIsDisplayedInActionMenu = actionMenu.GetItems().length === 1 && actionMenu.HasItem("ShowAll") === true;
19527
19648
 
19528
- if (useActionMenu === true && onlyShowAllOptionDisplayedInActionMenu === false)
19649
+ if (useActionMenu === true && onlyTheShowAllOptionIsDisplayedInActionMenu === false)
19529
19650
  {
19530
19651
  me.SetPicker(actionMenu);
19531
19652
  }
19532
19653
  else
19533
19654
  {
19534
- if (onlyShowAllOptionDisplayedInActionMenu === true)
19655
+ if (onlyTheShowAllOptionIsDisplayedInActionMenu === true)
19535
19656
  {
19536
19657
  useActionMenuAfterLoad = false;
19537
19658
  }
19538
19659
 
19539
- me.SetPicker(tree);
19540
- ensureTreeViewData();
19660
+ if (treeViewEnabled === true)
19661
+ {
19662
+ me.SetPicker(tree);
19663
+ ensureTreeViewData();
19664
+ }
19665
+ else
19666
+ {
19667
+ list.RemoveItems(); // Do not show previous search results again
19668
+ }
19541
19669
  }
19542
19670
  });
19543
19671
 
@@ -19771,12 +19899,111 @@ Fit.Controls.WSDropDown = function(ctlId)
19771
19899
  if (Fit.Validation.IsSet(val) === true && base() !== val)
19772
19900
  {
19773
19901
  base(val);
19774
- updateActionMenu(); // Update action menu to have SearchMore action added/removed depending on whether input is allowed or not
19902
+ localize(); // Add/remove placeholder depending on whether input is enabled or not - also calls updateActionMenu() which will make sure SearchMore action is added/removed depending on whether input is enabled or not
19903
+
19904
+ if (val === false && me.GetPicker() === list)
19905
+ {
19906
+ me.SetPicker(null); // Which picker to use is decided in the OnOpen handler
19907
+ }
19908
+ }
19909
+
19910
+ return base();
19911
+ });
19912
+
19913
+ /// <function container="Fit.Controls.WSDropDown" name="TreeViewEnabled" access="public" returns="boolean">
19914
+ /// <description> Get/set value indicating whether TreeView control is enabled or not </description>
19915
+ /// <param name="val" type="boolean" default="undefined"> If defined, True enables TreeView (default), False disables it </param>
19916
+ /// </function>
19917
+ this.TreeViewEnabled = function(val)
19918
+ {
19919
+ Fit.Validation.ExpectBoolean(val, true);
19920
+
19921
+ if (Fit.Validation.IsSet(val) === true && val !== treeViewEnabled)
19922
+ {
19923
+ treeViewEnabled = val;
19924
+
19925
+ if (useActionMenuForced === false)
19926
+ {
19927
+ // Use action menu if there is no data to display
19928
+
19929
+ if (val === true)
19930
+ {
19931
+ useActionMenu = nodesPopulated === false || tree.GetChildren().length === 0;
19932
+ }
19933
+ else
19934
+ {
19935
+ useActionMenu = true; // User must search to retrieve available options
19936
+ }
19937
+ }
19938
+
19939
+ if (val === false && me.GetPicker() === tree)
19940
+ {
19941
+ me.SetPicker(null); // Which picker to use is decided in the OnOpen handler
19942
+ }
19943
+
19944
+ updateActionMenu(); // Update action menu to have ShowAll action added/removed depending on whether TreeView is enabled or not
19945
+ }
19946
+
19947
+ return treeViewEnabled;
19948
+ }
19949
+
19950
+ /// <function container="Fit.Controls.WSDropDown" name="ListViewEnabled" access="public" returns="boolean">
19951
+ /// <description>
19952
+ /// Get/set flag indicating whether searchable ListView is enabled or not.
19953
+ /// The value provided also determines the value for InputEnabled and vice versa.
19954
+ /// </description>
19955
+ /// <param name="val" type="boolean" default="undefined"> If defined, True enables ListView and search capability (default), False disables it </param>
19956
+ /// </function>
19957
+ this.ListViewEnabled = function(val)
19958
+ {
19959
+ Fit.Validation.ExpectBoolean(val, true);
19960
+ return me.InputEnabled(val);
19961
+ }
19962
+
19963
+ this.SearchModeOnFocus = Fit.Core.CreateOverride(this.SearchModeOnFocus, function(val)
19964
+ {
19965
+ Fit.Validation.ExpectBoolean(val, true);
19966
+
19967
+ if (Fit.Validation.IsSet(val) === true && val !== base())
19968
+ {
19969
+ base(val);
19970
+ updateActionMenu(); // Add/remove search option depending on whether SearchModeOnFocus is enabled or not
19775
19971
  }
19776
19972
 
19777
19973
  return base();
19778
19974
  });
19779
19975
 
19976
+ this.Placeholder = function(val)
19977
+ {
19978
+ Fit.Validation.ExpectString(val, true);
19979
+
19980
+ customPlaceholderSet = true;
19981
+ return orgPlaceholder(val);
19982
+ }
19983
+
19984
+ this.OpenDropDown = Fit.Core.CreateOverride(this.OpenDropDown, function()
19985
+ {
19986
+ if (me.InputEnabled() === true && me.GetInputValue() === "" && me.GetSelections().length === 0 && treeViewEnabled === false)
19987
+ {
19988
+ // Do not open DropDown - it will only contain "Search for more options"
19989
+ // when no items are currently selected and TreeView is disabled, and no
19990
+ // search value has been entered yet.
19991
+ // The control will display "Search.." (or a custom placeholder), making it
19992
+ // obvious what the user need to do to get data - no need to display the action menu.
19993
+
19994
+ if (Fit.Browser.GetInfo().IsMobile === true)
19995
+ {
19996
+ // Focus input on mobile, even if DropDown was opened using
19997
+ // the arrow icon - this will bring up the virtual keyboard.
19998
+ this._internal.ForceFocusMobile();
19999
+ }
20000
+
20001
+ return;
20002
+ }
20003
+
20004
+ base();
20005
+ });
20006
+
19780
20007
  /// <function container="Fit.Controls.WSDropDown" name="GetListView" access="public" returns="Fit.Controls.WSListView">
19781
20008
  /// <description> Get WSListView control used to display data in a flat list view </description>
19782
20009
  /// </function>
@@ -19794,7 +20021,21 @@ Fit.Controls.WSDropDown = function(ctlId)
19794
20021
  }
19795
20022
 
19796
20023
  /// <function container="Fit.Controls.WSDropDown" name="UseActionMenu" access="public">
19797
- /// <description> Get/set value indicating whether control uses the built-in action menu to ease addition and removal of items </description>
20024
+ /// <description>
20025
+ /// Get/set value indicating whether control uses the built-in action menu to ease addition and removal of items.
20026
+ /// If this property is not explicitly set, it will automatically be changed by the control depending on data and other settings.
20027
+ /// The action menu will be enabled if TreeViewEnabled is set to False, as it would otherwise not show anything unless the user
20028
+ /// enters a search value. If TreeViewEnabled is True but no data is provided to the TreeView control upon request, the action menu
20029
+ /// is also enabled.
20030
+ /// If the control does not have any selections, InputEnabled (or its alias ListViewEnabled) is True, and TreeViewEnabled is False,
20031
+ /// no picker will be displayed since the action menu would only display the &quot;Search for options&quot; item - but it should already
20032
+ /// be obvious to the user that searching is required due to the placeholder displaying &quot;Search..&quot; by default.
20033
+ /// Likewise, if TreeViewEnabled is True and InputEnabled (or its alias ListViewEnabled) is False, and no selections are made,
20034
+ /// the action menu would only display &quot;Show available options&quot;. In this case the TreeView will be displayed instead,
20035
+ /// even if UseActionMenu has explicitely been set to True.
20036
+ /// The behaviour described is in place to make sure the action menu is only displayed when it makes sense, since it introduces
20037
+ /// and extra step (click) required by the user to access data.
20038
+ /// </description>
19798
20039
  /// <param name="val" type="boolean" default="undefined"> If defined, True enables the action menu, False disables it </param>
19799
20040
  /// </function>
19800
20041
  this.UseActionMenu = function(val)
@@ -19844,7 +20085,7 @@ Fit.Controls.WSDropDown = function(ctlId)
19844
20085
 
19845
20086
  Fit.Internationalization.RemoveOnLocaleChanged(localize);
19846
20087
 
19847
- me = list = tree = actionMenu = search = forceNewSearch = hideLinesForFlatData = dataRequested = dataLoading = nodesPopulated = requestCount = onDataLoadedCallback = suppressTreeOnOpen = timeOut = currentRequest = classes = autoUpdatedSelections = useActionMenu = useActionMenuForced = useActionMenuAfterLoad = translations = onRequestHandlers = onResponseHandlers = null;
20088
+ me = list = tree = actionMenu = search = forceNewSearch = hideLinesForFlatData = dataRequested = dataLoading = nodesPopulated = requestCount = onDataLoadedCallback = suppressTreeOnOpen = timeOut = currentRequest = classes = autoUpdatedSelections = useActionMenu = useActionMenuForced = useActionMenuAfterLoad = treeViewEnabled = orgPlaceholder = customPlaceholderSet = translations = onRequestHandlers = onResponseHandlers = null;
19848
20089
 
19849
20090
  base();
19850
20091
  });
@@ -20052,18 +20293,21 @@ Fit.Controls.WSDropDown = function(ctlId)
20052
20293
 
20053
20294
  actionMenu.RemoveItems();
20054
20295
 
20055
- if (me.InputEnabled() === true)
20296
+ if (me.InputEnabled() === true && me.SearchModeOnFocus() === false)
20056
20297
  {
20057
20298
  actionMenu.AddItem(searchIcon + translations.SearchMore, "SearchMore");
20058
20299
  }
20059
20300
 
20060
- if (nodesPopulated === false || tree.GetChildren().length > 0)
20301
+ if (treeViewEnabled === true)
20061
20302
  {
20062
- actionMenu.AddItem(showAllIcon + translations.ShowAllOptions, "ShowAll");
20063
- }
20064
- else //if (nodesPopulated === true && tree.GetChildren().length === 0)
20065
- {
20066
- actionMenu.AddItem(showAllIcon + "<i>" + translations.NoneAvailable + ": " + translations.ShowAllOptions + "</i>", "ShowAllNoneFound");
20303
+ if (nodesPopulated === false || tree.GetChildren().length > 0)
20304
+ {
20305
+ actionMenu.AddItem(showAllIcon + translations.ShowAllOptions, "ShowAll");
20306
+ }
20307
+ else //if (nodesPopulated === true && tree.GetChildren().length === 0)
20308
+ {
20309
+ actionMenu.AddItem(showAllIcon + "<i>" + translations.NoneAvailable + "</i>", "ShowAllNoneFound");
20310
+ }
20067
20311
  }
20068
20312
 
20069
20313
  if (addRemoveAll === true)
@@ -20083,6 +20327,11 @@ Fit.Controls.WSDropDown = function(ctlId)
20083
20327
  translations = locale.Translations;
20084
20328
 
20085
20329
  updateActionMenu();
20330
+
20331
+ if (customPlaceholderSet === false)
20332
+ {
20333
+ orgPlaceholder(me.InputEnabled() === true ? translations.Search : "");
20334
+ }
20086
20335
  }
20087
20336
 
20088
20337
  function onDataLoaded(cb)
@@ -20169,11 +20418,13 @@ Fit.Controls.WSDropDown = function(ctlId)
20169
20418
  {
20170
20419
  InvalidSelection : "Invalid selection",
20171
20420
 
20172
- SearchMore : "Search for more options",
20173
- ShowAllOptions : "Show all available options",
20421
+ // WSDropDown
20422
+ Search : "Search..",
20423
+ SearchMore : "Search for options",
20424
+ ShowAllOptions : "Show available options",
20174
20425
  RemoveAll : "Remove all selected",
20175
20426
  Remove : "Remove",
20176
- NoneAvailable : "None available"
20427
+ NoneAvailable : "List with options is empty"
20177
20428
  }
20178
20429
  },
20179
20430
  "da":
@@ -20182,11 +20433,13 @@ Fit.Controls.WSDropDown = function(ctlId)
20182
20433
  {
20183
20434
  InvalidSelection : "Ugyldigt valg",
20184
20435
 
20185
- SearchMore : "Søg efter flere valgmuligheder",
20186
- ShowAllOptions : "Vis alle tilgængelige valgmuligheder",
20436
+ // WSDropDown
20437
+ Search : "Søg..",
20438
+ SearchMore : "Søg efter valgmuligheder",
20439
+ ShowAllOptions : "Vis tilgængelige valgmuligheder",
20187
20440
  RemoveAll : "Fjern alle valgte",
20188
20441
  Remove : "Fjern",
20189
- NoneAvailable : "Ingen tilgængelige"
20442
+ NoneAvailable : "Liste med valgmuligheder er tom"
20190
20443
  }
20191
20444
  },
20192
20445
  "de":
@@ -20195,11 +20448,13 @@ Fit.Controls.WSDropDown = function(ctlId)
20195
20448
  {
20196
20449
  InvalidSelection : "Ungültige Auswahl",
20197
20450
 
20198
- SearchMore : "Nach weiteren Optionen suchen",
20199
- ShowAllOptions : "Alle verfügbaren Optionen anzeigen",
20451
+ // WSDropDown
20452
+ Search : "Suchen..",
20453
+ SearchMore : "Nach Optionen suchen",
20454
+ ShowAllOptions : "Verfügbare Optionen anzeigen",
20200
20455
  RemoveAll : "Alle ausgewählten entfernen",
20201
20456
  Remove : "Entfernen",
20202
- NoneAvailable : "Keine verfügbar"
20457
+ NoneAvailable : "Liste der Optionen ist leer"
20203
20458
  }
20204
20459
  }
20205
20460
  }
@@ -21323,12 +21578,14 @@ Fit.Controls.Input = function(ctlId)
21323
21578
  Fit.Core.Extend(this, Fit.Controls.ControlBase).Apply(ctlId);
21324
21579
 
21325
21580
  var me = this;
21326
- var orgVal = "";
21327
- var preVal = "";
21581
+ var orgVal = ""; // Holds initial value used to determine IsDirty state
21582
+ var preVal = ""; // Holds latest change made by user - used to determine whether OnChange needs to be fired
21328
21583
  var input = null;
21329
21584
  var cmdResize = null;
21330
21585
  var designEditor = null;
21331
- var htmlWrappedInParagraph = false;
21586
+ var designEditorDirty = false;
21587
+ var designEditorDirtyPending = false;
21588
+ //var htmlWrappedInParagraph = false;
21332
21589
  var wasAutoChangedToMultiLineMode = false; // Used to revert to single line if multi line was automatically enabled along with DesignMode(true), Maximizable(true), or Resizable(true)
21333
21590
  var minimizeHeight = -1;
21334
21591
  var maximizeHeight = -1;
@@ -21566,18 +21823,32 @@ Fit.Controls.Input = function(ctlId)
21566
21823
 
21567
21824
  if (Fit.Validation.IsSet(val) === true)
21568
21825
  {
21569
- var fireOnChange = (designEditor === null && me.Value() !== val); // DesignEditor invokes input.onchange() if value is changed
21826
+ var fireOnChange = (me.Value() !== val);
21570
21827
 
21571
21828
  orgVal = (preserveDirtyState !== true ? val : orgVal);
21572
21829
  preVal = val;
21830
+ designEditorDirty = designEditorDirtyPending === true ? true : false;
21831
+ designEditorDirtyPending = false;
21573
21832
 
21574
- if (val.indexOf("<p>") === 0)
21575
- htmlWrappedInParagraph = true; // Indicates that val is comparable with value from CKEditor which wraps content in paragraphs
21833
+ /*if (val.indexOf("<p>") === 0)
21834
+ htmlWrappedInParagraph = true; // Indicates that val is comparable with value from CKEditor which wraps content in paragraphs*/
21576
21835
 
21577
21836
  if (designEditor !== null)
21578
- CKEDITOR.instances[me.GetId() + "_DesignMode"].setData(val);
21837
+ {
21838
+ // NOTICE: Invalid HTML is removed, so an all invalid HTML string will be discarded
21839
+ // by the editor, resulting in the editor's getData() function returning an empty string.
21840
+
21841
+ // Calling setData(..) fires CKEditor's onchange event which in turn fires
21842
+ // Input's OnChange event. Suppress OnChange which is fired further down.
21843
+ me._internal.ExecuteWithNoOnChange(function()
21844
+ {
21845
+ CKEDITOR.instances[me.GetId() + "_DesignMode"].setData(val);
21846
+ });
21847
+ }
21579
21848
  else
21849
+ {
21580
21850
  input.value = val;
21851
+ }
21581
21852
 
21582
21853
  if (Fit._internal.Controls.Input.BlobManager.RevokeExternalBlobUrlsOnDispose === true)
21583
21854
  {
@@ -21604,14 +21875,69 @@ Fit.Controls.Input = function(ctlId)
21604
21875
  }
21605
21876
 
21606
21877
  if (designEditor !== null)
21607
- return CKEDITOR.instances[me.GetId() + "_DesignMode"].getData();
21878
+ {
21879
+ // If user has not changed value, then return the value initially set.
21880
+ // CKEditor may change (optimize) HTML when applied, but we always want
21881
+ // the value initially set when no changes have been made by the user.
21882
+ // See additional comments regarding this in the IsDirty() implementation.
21883
+ if (designEditorDirty === false)
21884
+ {
21885
+ return orgVal;
21886
+ }
21887
+
21888
+ var curVal = CKEDITOR.instances[me.GetId() + "_DesignMode"].getData();
21889
+
21890
+ // Remove extra line break added by htmlwriter plugin at the end: <p>Hello world</p>\n
21891
+ curVal = curVal.replace(/<\/p>\n$/, "</p>");
21892
+
21893
+ // Remove empty class attribute on <img> tags which may be temporarily set when selecting
21894
+ // an image using the dragresize plugin. This plugin adds a CSS class (ckimgrsz) to the image
21895
+ // tag while being selected, although the class name is removed when calling getData() above.
21896
+ // However, the empty class attribute is useless, so we remove it. It also results in IsDirty()
21897
+ // returning True while the image is selected if we keep it. Actually the class attribute should
21898
+ // never have been returned since the allowedContent option does not allow it - might be a minor bug.
21899
+ curVal = curVal.replace(/(<img.*?) class=""(.*?>)/, "$1$2"); // Not using /g switch as only one image can be selected
21900
+
21901
+ return curVal;
21902
+ }
21608
21903
 
21609
21904
  return input.value;
21610
21905
  }
21611
21906
 
21907
+ // See documentation on ControlBase
21908
+ this.UserValue = Fit.Core.CreateOverride(this.UserValue, function(val)
21909
+ {
21910
+ if (Fit.Validation.IsSet(val) === true && designEditor !== null)
21911
+ {
21912
+ designEditorDirtyPending = true;
21913
+ }
21914
+
21915
+ return base(val);
21916
+ });
21917
+
21612
21918
  // See documentation on ControlBase
21613
21919
  this.IsDirty = function()
21614
21920
  {
21921
+ if (designEditor !== null)
21922
+ {
21923
+ // Never do value comparison in DesignMode.
21924
+ // A value such as "Hello world" could have been provided,
21925
+ // which by CKEditor would be returned as "<p>Hello world</p>".
21926
+ // A value such as '<p style="text-align: center;">Hello</p>' could
21927
+ // also have been set, which by CKEditor would be optimized to
21928
+ // '<p style="text-align:center">Hello</p>' via ACF (Advanced Content Filter):
21929
+ // https://ckeditor.com/docs/ckeditor4/latest/guide/dev_advanced_content_filter.html
21930
+ // Furthermore invalid HTML is removed while valid HTML is kept.
21931
+ // All this makes it very difficult to reliably determine dirty state
21932
+ // by comparing values. Therefore, if the user changed anything by interacting
21933
+ // with the editor, or UserValue(..) was called, always consider the value dirty.
21934
+
21935
+ // Another positive of avoiding value comparison to determine dirty state
21936
+ // is that retrieving the value from CKEditor is fairly expensive.
21937
+
21938
+ return designEditorDirty;
21939
+ }
21940
+
21615
21941
  return (orgVal !== me.Value());
21616
21942
  }
21617
21943
 
@@ -21712,7 +22038,7 @@ Fit.Controls.Input = function(ctlId)
21712
22038
  });
21713
22039
  }
21714
22040
 
21715
- me = orgVal = preVal = input = cmdResize = designEditor = htmlWrappedInParagraph = wasAutoChangedToMultiLineMode = minimizeHeight = maximizeHeight = minMaxUnit = resizable = nativeResizableAvailable = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = null;
22041
+ me = orgVal = preVal = input = cmdResize = designEditor = designEditorDirty = designEditorDirtyPending /*= htmlWrappedInParagraph*/ = wasAutoChangedToMultiLineMode = minimizeHeight = maximizeHeight = minMaxUnit = resizable = nativeResizableAvailable = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = null;
21716
22042
 
21717
22043
  base();
21718
22044
  });
@@ -22224,6 +22550,16 @@ Fit.Controls.Input = function(ctlId)
22224
22550
  CKEDITOR.config.skin = Fit._internal.Controls.Input.Editor.Skin;
22225
22551
  }
22226
22552
 
22553
+ CKEDITOR.on("instanceReady", function(ev)
22554
+ {
22555
+ // Do not produce XHTML self-closing tags such as <br /> and <img src="img.jpg" />
22556
+ // https://ckeditor.com/docs/ckeditor4/latest/features/output_format.html
22557
+ // NOTICE: The htmlwriter plugin is required for this to work!
22558
+ // Output produced is now both HTML4 and HTML5 compatible, but is not valid
22559
+ // XHTML anymore! Self-closing tags are allowed in HTML5 but not valid in HTML4.
22560
+ ev.editor.dataProcessor.writer.selfClosingEnd = ">"; // Defaults to ' />'
22561
+ });
22562
+
22227
22563
  // Register OnShow and OnHide event handlers when a dialog is opened for the first time.
22228
22564
  // IMPORTANT: These event handlers are shared by all input control instances in Design Mode,
22229
22565
  // so we cannot use 'me' to access the current control for which a dialog is opened.
@@ -22645,6 +22981,7 @@ Fit.Controls.Input = function(ctlId)
22645
22981
  title: "",
22646
22982
  startupFocus: focused === true ? "end" : false,
22647
22983
  extraPlugins: Fit._internal.Controls.Input.Editor.Plugins.join(","), // "justify,pastefromword,base64image,base64imagepaste,dragresize",
22984
+ clipboard_handleImages: false, // Disable native support for image pasting - allow base64imagepaste plugin to handle image data if loaded
22648
22985
  base64image: // Custom property used by base64image plugin if loaded
22649
22986
  {
22650
22987
  storage: "blob", // "base64" (default) or "blob" - base64 will always be provided by browsers not supporting blob storage
@@ -22736,6 +23073,21 @@ Fit.Controls.Input = function(ctlId)
22736
23073
  },
22737
23074
  change: function() // CKEditor bug: not fired in Opera 12 (possibly other old versions as well)
22738
23075
  {
23076
+ if (me._internal.FireOnChangeSuppressed === true)
23077
+ {
23078
+ // Do not process event - it has been fired by CKEditor when HTML
23079
+ // value was initially assigned in Value(..) which happend through
23080
+ // me._internal.ExecuteWithNoOnChange(function() { .. }).
23081
+ // See Value(..) implementation for details.
23082
+ return;
23083
+ }
23084
+
23085
+ // Assume value was changed by user if control has focus
23086
+ if (designEditorDirty === false && me.Focused() === true)
23087
+ {
23088
+ designEditorDirty = true;
23089
+ }
23090
+
22739
23091
  input.onkeyup();
22740
23092
  },
22741
23093
  resize: function() // Fires when size is changed, not just when resized using resize handle in lower right cornor
@@ -22872,7 +23224,9 @@ Fit.Controls.Input = function(ctlId)
22872
23224
 
22873
23225
  if (newVal !== preVal)
22874
23226
  {
22875
- if (designEditor !== null && htmlWrappedInParagraph === false) // A value not wrapped in paragraph(s) was assigned to HTML editor
23227
+ // DISABLED: No longer necessary with the introduction of designEditorDirty which ensures
23228
+ // that we get the initial value set from Value(), unless changed by the user using the editor.
23229
+ /*if (designEditor !== null && htmlWrappedInParagraph === false) // A value not wrapped in paragraph(s) was assigned to HTML editor
22876
23230
  {
22877
23231
  // Do not trigger OnChange if the only difference is that CKEditor
22878
23232
  // wrapped the value initially assigned to control in a paragraph.
@@ -22888,7 +23242,7 @@ Fit.Controls.Input = function(ctlId)
22888
23242
  {
22889
23243
  return; // Do not fire OnChange
22890
23244
  }
22891
- }
23245
+ }*/
22892
23246
 
22893
23247
  preVal = newVal;
22894
23248
  me._internal.FireOnChange();
@@ -23019,10 +23373,10 @@ Fit._internal.Controls.Input.Editor =
23019
23373
  /// </member>
23020
23374
  Skin: null, // Notice: CKEditor does not support multiple different skins on the same page - do not change value once an editor has been created
23021
23375
 
23022
- /// <member container="Fit._internal.Controls.Input.Editor" name="Plugins" access="public" static="true" type="('justify' | 'pastefromword' | 'resize' | 'base64image' | 'base64imagepaste' | 'dragresize')[]">
23376
+ /// <member container="Fit._internal.Controls.Input.Editor" name="Plugins" access="public" static="true" type="('htmlwriter' | 'justify' | 'pastefromword' | 'resize' | 'base64image' | 'base64imagepaste' | 'dragresize')[]">
23023
23377
  /// <description> Additional plugins used with DesignMode </description>
23024
23378
  /// </member>
23025
- Plugins: ["justify", "pastefromword", "resize" /*"base64image", "base64imagepaste", "dragresize"*/], // Regarding base64imagepaste and dragresize: IE11 has native support for pasting images as base64 and IE8+ has native support for image resizing, so plugins are not in effect in IE, even when enabled
23379
+ Plugins: ["htmlwriter", "justify", "pastefromword", "resize" /*"base64image", "base64imagepaste", "dragresize"*/], // Regarding base64imagepaste and dragresize: IE11 has native support for pasting images as base64 and IE8+ has native support for image resizing, so plugins are not in effect in IE, even when enabled
23026
23380
 
23027
23381
  /// <member container="Fit._internal.Controls.Input.Editor" name="Toolbar" access="public" static="true" type="( { name: 'BasicFormatting', items: ('Bold' | 'Italic' | 'Underline')[] } | { name: 'Justify', items: ('JustifyLeft' | 'JustifyCenter' | 'JustifyRight')[] } | { name: 'Lists', items: ('NumberedList' | 'BulletedList' | 'Indent' | 'Outdent')[] } | { name: 'Links', items: ('Link' | 'Unlink')[] } | { name: 'Insert', items: ('base64image')[] } )[]">
23028
23382
  /// <description> Toolbar buttons used with DesignMode - make sure necessary plugins are loaded (see Fit._internal.Controls.Input.EditorPlugins) </description>