fit-ui 2.3.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Fit.UI.js CHANGED
@@ -648,7 +648,7 @@ Fit._internal =
648
648
  {
649
649
  Core:
650
650
  {
651
- VersionInfo: { Major: 2, Minor: 3, Patch: 4 } // 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: 0 } // 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
 
@@ -3048,6 +3048,8 @@ Fit.Controls.Component = function(controlId)
3048
3048
  container = document.createElement("div");
3049
3049
  container.id = id;
3050
3050
  container._internal = { Instance: me };
3051
+
3052
+ Fit.Dom.Data(container, "device", ((Fit.Browser.GetInfo().IsMobile === false) ? "Desktop" : (Fit.Browser.GetInfo().IsPhone === true) ? "Phone" : "Tablet"));
3051
3053
  }
3052
3054
 
3053
3055
  /// <function container="Fit.Controls.Component" name="GetId" access="public" returns="string">
@@ -3278,8 +3280,6 @@ Fit.Controls.ControlBase = function(controlId)
3278
3280
  me._internal.Data("dirty", "false");
3279
3281
  me._internal.Data("enabled", "true");
3280
3282
 
3281
- me._internal.Data("device", ((Fit.Browser.GetInfo().IsMobile === false) ? "Desktop" : (Fit.Browser.GetInfo().IsPhone === true) ? "Phone" : "Tablet"));
3282
-
3283
3283
  // Add hidden inputs which are automatically populated with
3284
3284
  // control value and state information when control is updated.
3285
3285
 
@@ -15793,6 +15793,8 @@ Fit.Controls.DropDown = function(ctlId)
15793
15793
  var focusInputOnMobile = true; // Flag indicating whether control should focus input fields (and potentially bring up a virtual keyboard) based on configuration, platform (computer vs touch) and where user initially clicked/touched DropDown to activate it
15794
15794
  var detectBoundaries = false; // Flag indicating whether drop down menu should detect viewport collision and open upwards when needed
15795
15795
  var detectBoundariesRelToViewPort = false; // Flag indicating whether drop down menu should be positioned relative to viewport (true) or scroll parent (false)
15796
+ var persistView = false; // Flag indicating whether picker controls should remember and restore its scroll position and highlighted item when reopened
15797
+ var highlightFirst = false; // Flag indicating whether picker controls should focus its first node automatically when opened
15796
15798
 
15797
15799
  var onInputChangedHandlers = []; // Invoked when input value is changed - takes two arguments (sender (this), text value)
15798
15800
  var onPasteHandlers = []; // Invoked when a value is pasted - takes two arguments (sender (this), text value)
@@ -15922,8 +15924,15 @@ Fit.Controls.DropDown = function(ctlId)
15922
15924
  {
15923
15925
  var target = Fit.Events.GetTarget(e);
15924
15926
 
15927
+ if (target !== document.documentElement && target !== document.body && Fit.Dom.IsRooted(target) === false)
15928
+ {
15929
+ return; // Do not close DropDown if target no longer exists - this may happen if something is removed within DropDown (e.g. an item in the WSDropDown's action menu)
15930
+ }
15931
+
15925
15932
  if (me.IsDropDownOpen() === true && target !== me.GetDomElement() && Fit.Dom.Contained(me.GetDomElement(), target) === false)
15933
+ {
15926
15934
  me.CloseDropDown();
15935
+ }
15927
15936
  });
15928
15937
  Fit.Array.Add(closeHandlers, eventId);
15929
15938
  }
@@ -15940,6 +15949,11 @@ Fit.Controls.DropDown = function(ctlId)
15940
15949
 
15941
15950
  coords = null;
15942
15951
 
15952
+ if (target !== document.documentElement && target !== document.body && Fit.Dom.IsRooted(target) === false)
15953
+ {
15954
+ return; // Do not close DropDown if target no longer exists - this may happen if something is removed within DropDown (e.g. an item in the WSDropDown's action menu)
15955
+ }
15956
+
15943
15957
  if (me.IsDropDownOpen() === true && target !== me.GetDomElement() && Fit.Dom.Contained(me.GetDomElement(), target) === false)
15944
15958
  {
15945
15959
  coords = Fit.Events.GetPointerState().Coordinates.Document;
@@ -16256,7 +16270,7 @@ Fit.Controls.DropDown = function(ctlId)
16256
16270
  itemDropZones[key].Dispose();
16257
16271
  });
16258
16272
 
16259
- me = itemContainer = itemCollection = itemDropZones = arrow = txtPrimary = txtActive = txtEnabled = dropDownMenu = picker = orgSelections = invalidMessage = invalidMessageChanged = initialFocus = maxHeight = prevValue = focusAssigned = closeHandlers = dropZone = isMobile = focusInputOnMobile = detectBoundaries = onInputChangedHandlers = onPasteHandlers = onOpenHandlers = onCloseHandlers = suppressUpdateItemSelectionState = suppressOnItemSelectionChanged = clearTextSelectionOnInputChange = prevTextSelection = textSelectionCallback = cmdToggleTextMode = null;
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;
16260
16274
 
16261
16275
  base();
16262
16276
  });
@@ -16468,6 +16482,11 @@ Fit.Controls.DropDown = function(ctlId)
16468
16482
  picker.MaxHeight(maxHeight.Value, maxHeight.Unit);
16469
16483
  optimizeDropDownPosition(); // In case dropdown is already open and SetPicker was called async, e.g. initiated from OnOpen event. Function may change MaxHeight on Picker.
16470
16484
 
16485
+ // Persist view and initial focus
16486
+
16487
+ picker.PersistView(persistView);
16488
+ picker.HighlightFirst(highlightFirst);
16489
+
16471
16490
  // Make sure OnItemSelectionChanged is only registered once
16472
16491
 
16473
16492
  if (!picker._internal)
@@ -16525,6 +16544,19 @@ Fit.Controls.DropDown = function(ctlId)
16525
16544
  if (txt.parentElement.parentElement === null)
16526
16545
  txt = txtPrimary;
16527
16546
  }
16547
+ else
16548
+ {
16549
+ // User selected an item which is already selected
16550
+
16551
+ if (me.TextSelectionMode() === true)
16552
+ {
16553
+ updateTextSelection(); // Make sure any search value is removed and text selection is restored
16554
+ }
16555
+ else
16556
+ {
16557
+ me.ClearInput(); // Make sure any search value is removed
16558
+ }
16559
+ }
16528
16560
 
16529
16561
  // DISABLED: Now handled using picker.OnFocus(..) handler further down - see https://github.com/Jemt/Fit.UI/issues/86 for details
16530
16562
  // if (eventArgs.ProgrammaticallyChanged === false && (isMobile === false || focusInputOnMobile === true))
@@ -16845,6 +16877,48 @@ Fit.Controls.DropDown = function(ctlId)
16845
16877
  return null;
16846
16878
  }
16847
16879
 
16880
+ /// <function container="Fit.Controls.DropDown" name="PersistView" access="public" returns="boolean">
16881
+ /// <description> Make DropDown restore scroll position and previously highlighted item when reopened </description>
16882
+ /// <param name="val" type="boolean" default="undefined"> If set, True enables feature, False disables it (default) </param>
16883
+ /// </function>
16884
+ this.PersistView = function(val)
16885
+ {
16886
+ Fit.Validation.ExpectBoolean(val, true);
16887
+
16888
+ if (Fit.Validation.IsSet(val) === true && val !== persistView)
16889
+ {
16890
+ persistView = val;
16891
+
16892
+ if (me.GetPicker() !== null)
16893
+ {
16894
+ me.GetPicker().PersistView(val);
16895
+ }
16896
+ }
16897
+
16898
+ return persistView;
16899
+ }
16900
+
16901
+ /// <function container="Fit.Controls.DropDown" name="HighlightFirst" access="public" returns="boolean">
16902
+ /// <description> Make DropDown highlight first selectable item when opened </description>
16903
+ /// <param name="val" type="boolean" default="undefined"> If set, True enables feature, False disables it (default) </param>
16904
+ /// </function>
16905
+ this.HighlightFirst = function(val)
16906
+ {
16907
+ Fit.Validation.ExpectBoolean(val, true);
16908
+
16909
+ if (Fit.Validation.IsSet(val) === true)
16910
+ {
16911
+ highlightFirst = val;
16912
+
16913
+ if (me.GetPicker() !== null)
16914
+ {
16915
+ me.GetPicker().HighlightFirst(val);
16916
+ }
16917
+ }
16918
+
16919
+ return highlightFirst;
16920
+ }
16921
+
16848
16922
  /// <function container="Fit.Controls.DropDown" name="RenameSelection" access="public">
16849
16923
  /// <description> Rename title of selected item by its value </description>
16850
16924
  /// <param name="val" type="string"> Value of selected item to rename </param>
@@ -17354,6 +17428,34 @@ Fit.Controls.DropDown = function(ctlId)
17354
17428
  Fit.Array.Add(onCloseHandlers, cb);
17355
17429
  }
17356
17430
 
17431
+ // ============================================
17432
+ // Protected
17433
+ // ============================================
17434
+
17435
+ this._internal = (this._internal ? this._internal : {});
17436
+
17437
+ // Clear input field without firing OnInputChanged, and make placeholder appear
17438
+ this._internal.ClearInputAndShowPlaceholder = function(forceFocusInput)
17439
+ {
17440
+ Fit.Validation.ExpectBoolean(forceFocusInput, true);
17441
+
17442
+ txtPrimary.value = "";
17443
+ updatePlaceholder(true);
17444
+
17445
+ if (forceFocusInput)
17446
+ {
17447
+ // By default focus is never assigned to input on mobile if user opened the
17448
+ // DropDown by clicking the arrow icon. ForceFocusInput allows us to ignore this
17449
+ // aspect. On desktop the input field always remains focused as it automatically
17450
+ // steals back focus immediately after interacting with a picker control. See
17451
+ // picker.OnFocusIn handler registered in SetPicker(..)
17452
+ var orgFocusInputOnMobile = focusInputOnMobile;
17453
+ focusInputOnMobile = true;
17454
+ focusInput(txtActive);
17455
+ focusInputOnMobile = orgFocusInputOnMobile;
17456
+ }
17457
+ }
17458
+
17357
17459
  // ============================================
17358
17460
  // Private
17359
17461
  // ============================================
@@ -18133,7 +18235,7 @@ Fit.Controls.DropDown = function(ctlId)
18133
18235
 
18134
18236
  if (placeholder !== "" || force === true)
18135
18237
  {
18136
- var setPlaceholder = placeholder !== "" && itemCollectionOrdered.length === 0 && me.GetInputValue() === "" && willAssumeInputValue !== true;
18238
+ var setPlaceholder = placeholder !== "" && (me.TextSelectionMode() === true || itemCollectionOrdered.length === 0) && me.GetInputValue() === "" && willAssumeInputValue !== true;
18137
18239
 
18138
18240
  Fit.Dom.Data(itemContainer, "placeholder", setPlaceholder === true ? placeholder : null);
18139
18241
  Fit.Dom.Data(itemContainer, "placeholder-autoclear", setPlaceholder === true ? Fit.Browser.GetBrowser() === "MSIE" ? "true" : "false" : null);
@@ -18347,6 +18449,12 @@ Fit.Controls.DropDown = function(ctlId)
18347
18449
  Fit.Dom.SetCaretPosition(txtPrimary, 0);
18348
18450
  }
18349
18451
 
18452
+ // Remove placeholder in case it was added using this._internal.ClearInputAndShowPlaceholder()
18453
+
18454
+ updatePlaceholder(true);
18455
+
18456
+ // Have TextSelection removed when input is changed
18457
+
18350
18458
  clearTextSelectionOnInputChange = true;
18351
18459
  }
18352
18460
 
@@ -18829,6 +18937,32 @@ Fit.Controls.PickerBase = function()
18829
18937
  return null;
18830
18938
  }
18831
18939
 
18940
+ /// <function container="Fit.Controls.PickerBase" name="PersistView" access="public" returns="boolean">
18941
+ /// <description>
18942
+ /// Overridden by control developers (optional).
18943
+ /// This function can be used to tell the picker control to persist (remember) its current state between interactions.
18944
+ /// For instance a TreeView control would remembers its scroll position and highlighted node, while a calendar would
18945
+ /// remember the previously selected year and month.
18946
+ /// </description>
18947
+ /// <param name="val" type="boolean" default="undefined"> If set, True enables feature, False disables it (default) </param>
18948
+ /// </function>
18949
+ this.PersistView = function(val)
18950
+ {
18951
+ return false;
18952
+ }
18953
+
18954
+ /// <function container="Fit.Controls.PickerBase" name="HighlightFirst" access="public" returns="boolean">
18955
+ /// <description>
18956
+ /// Overridden by control developers (optional).
18957
+ /// This function can be used to make the picker control automatically highlight the first item.
18958
+ /// </description>
18959
+ /// <param name="val" type="boolean" default="undefined"> If set, True enables feature, False disables it (default) </param>
18960
+ /// </function>
18961
+ this.HighlightFirst = function()
18962
+ {
18963
+ return false;
18964
+ }
18965
+
18832
18966
  /// <function container="Fit.Controls.PickerBase" name="Destroy" access="public">
18833
18967
  /// <description>
18834
18968
  /// Overridden by control developers (required).
@@ -18952,6 +19086,7 @@ Fit.Controls.WSDropDown = function(ctlId)
18952
19086
  var me = this;
18953
19087
  var list = null;
18954
19088
  var tree = null;
19089
+ var actionMenu = null;
18955
19090
 
18956
19091
  var search = "";
18957
19092
  var forceNewSearch = false;
@@ -18965,6 +19100,9 @@ Fit.Controls.WSDropDown = function(ctlId)
18965
19100
  var currentRequest = null;
18966
19101
  var classes = null;
18967
19102
  var autoUpdatedSelections = null; // Cached result from AutoUpdateSelected: [{ Title:string, Value:string, Exists:boolean }, ...]
19103
+ var useActionMenu = false;
19104
+ var useActionMenuForced = false;
19105
+ var translations = null;
18968
19106
 
18969
19107
  var onRequestHandlers = [];
18970
19108
  var onResponseHandlers = [];
@@ -19080,6 +19218,22 @@ Fit.Controls.WSDropDown = function(ctlId)
19080
19218
  {
19081
19219
  fireOnDataLoaded();
19082
19220
  }
19221
+
19222
+ // If no data is returned and DropDown is in TextSelectionMode, the user will
19223
+ // not have an easy way to remove objects from the DropDown, unless SelectionModeToggle
19224
+ // is true. Therefore we allow for items to be removed using an action menu.
19225
+ // EDIT: Now displays action menu in both Visual and Text Selection Mode for consistency.
19226
+
19227
+ if (useActionMenuForced === false)
19228
+ {
19229
+ useActionMenu = eventArgs.Children.length === 0;
19230
+ }
19231
+
19232
+ if (/*me.TextSelectionMode() === true &&*/ useActionMenu === true)
19233
+ {
19234
+ updateActionMenu();
19235
+ me.SetPicker(actionMenu);
19236
+ }
19083
19237
  }
19084
19238
  });
19085
19239
  tree.OnAbort(function(sender, eventArgs)
@@ -19170,6 +19324,104 @@ Fit.Controls.WSDropDown = function(ctlId)
19170
19324
  me.SetPicker(tree);
19171
19325
  });
19172
19326
 
19327
+ // Create action menu
19328
+
19329
+ var skipUpdateActionMenuOnChange = false;
19330
+
19331
+ actionMenu = new Fit.Controls.ListView(ctlId + "__ActionsListView");
19332
+ 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
19333
+ {
19334
+ if (item.Value === "SearchMore")
19335
+ {
19336
+ me._internal.ClearInputAndShowPlaceholder(true); // NOTICE: TextSelectionMode only - Visual Selection Mode cannot be temporarily cleared to display the place holder
19337
+ }
19338
+ else if (item.Value === "ShowAll")
19339
+ {
19340
+ me.SetPicker(tree);
19341
+ }
19342
+ else if (item.Value === "RemoveAll")
19343
+ {
19344
+ skipUpdateActionMenuOnChange = true;
19345
+ me.ClearSelections(); // Fires OnChange
19346
+ skipUpdateActionMenuOnChange = false;
19347
+
19348
+ updateActionMenu(); // Update action menu (remove all "Remove: xyz" options)
19349
+ }
19350
+ else if (item.Value.indexOf("Remove:") === 0)
19351
+ {
19352
+ var dataValue = item.Value.replace("Remove:", "");
19353
+
19354
+ // Find item below if using keyboard to remove item
19355
+
19356
+ var above = null;
19357
+ var below = null;
19358
+
19359
+ if (Fit.Browser.GetInfo().IsMobile === false && Fit.Events.GetPointerState().Buttons.Primary === false) // Do not find and highlight item below on touch devices, or if using the mouse
19360
+ {
19361
+ var items = actionMenu.GetItems();
19362
+ var stopNext = false;
19363
+
19364
+ for (var i = 0 ; i < items.length ; i++)
19365
+ {
19366
+ if (items[i].Value.indexOf("Remove:") !== 0)
19367
+ {
19368
+ continue; // Skip items that do not remove individual selections
19369
+ }
19370
+
19371
+ if (items[i].Value.replace("Remove:", "") === dataValue)
19372
+ {
19373
+ stopNext = true;
19374
+ continue;
19375
+ }
19376
+
19377
+ if (stopNext === false)
19378
+ {
19379
+ above = items[i].Value;
19380
+ }
19381
+ else
19382
+ {
19383
+ below = items[i].Value;
19384
+ break;
19385
+ }
19386
+ }
19387
+ }
19388
+
19389
+ // Remove item
19390
+
19391
+ skipUpdateActionMenuOnChange = true;
19392
+ me.RemoveSelection(dataValue); // Fires OnChange
19393
+ skipUpdateActionMenuOnChange = false;
19394
+
19395
+ // Update list of removable items
19396
+
19397
+ actionMenu.RemoveItem(item.Value);
19398
+
19399
+ if (me.GetSelections().length === 1)
19400
+ {
19401
+ updateActionMenu(); // Update action menu to get rid of "Remove all" item when only one item is left
19402
+ }
19403
+
19404
+ // Highlight item below (or above) the item that was just removed
19405
+
19406
+ if (above !== null || below !== null)
19407
+ {
19408
+ actionMenu.RevealItemInView(below !== null ? below : above);
19409
+ }
19410
+ }
19411
+
19412
+ return false; // Prevent selection of behavioural item
19413
+ });
19414
+
19415
+ me.OnChange(function(sender)
19416
+ {
19417
+ if (skipUpdateActionMenuOnChange === false)
19418
+ {
19419
+ updateActionMenu();
19420
+ }
19421
+ });
19422
+
19423
+ // Misc
19424
+
19173
19425
  me.OnOpen(function()
19174
19426
  {
19175
19427
  if (suppressTreeOnOpen === true)
@@ -19178,9 +19430,19 @@ Fit.Controls.WSDropDown = function(ctlId)
19178
19430
  return;
19179
19431
  }
19180
19432
 
19181
- me.SetPicker(tree);
19182
- ensureTreeViewData();
19433
+ if (useActionMenu === true)
19434
+ {
19435
+ me.SetPicker(actionMenu);
19436
+ }
19437
+ else
19438
+ {
19439
+ me.SetPicker(tree);
19440
+ ensureTreeViewData();
19441
+ }
19183
19442
  });
19443
+
19444
+ Fit.Internationalization.OnLocaleChanged(localize);
19445
+ localize();
19184
19446
  }
19185
19447
 
19186
19448
  // ============================================
@@ -19405,7 +19667,6 @@ Fit.Controls.WSDropDown = function(ctlId)
19405
19667
  return base(val);
19406
19668
  });
19407
19669
 
19408
-
19409
19670
  /// <function container="Fit.Controls.WSDropDown" name="GetListView" access="public" returns="Fit.Controls.WSListView">
19410
19671
  /// <description> Get WSListView control used to display data in a flat list view </description>
19411
19672
  /// </function>
@@ -19422,6 +19683,44 @@ Fit.Controls.WSDropDown = function(ctlId)
19422
19683
  return tree;
19423
19684
  }
19424
19685
 
19686
+ /// <function container="Fit.Controls.WSDropDown" name="UseActionMenu" access="public">
19687
+ /// <description> Get/set value indicating whether control uses the built-in action menu to ease addition and removal of items </description>
19688
+ /// <param name="val" type="boolean" default="undefined"> If defined, True enables the action menu, False disables it </param>
19689
+ /// </function>
19690
+ this.UseActionMenu = function(val)
19691
+ {
19692
+ Fit.Validation.ExpectBoolean(val, true);
19693
+
19694
+ if (Fit.Validation.IsSet(val) === true)
19695
+ {
19696
+ useActionMenuForced = true;
19697
+
19698
+ if (val !== useActionMenu)
19699
+ {
19700
+ useActionMenu = val;
19701
+
19702
+ if (val === true)
19703
+ {
19704
+ updateActionMenu();
19705
+
19706
+ if (me.IsDropDownOpen() === true)
19707
+ {
19708
+ me.SetPicker(actionMenu);
19709
+ }
19710
+ }
19711
+ else
19712
+ {
19713
+ if (me.GetPicker() === actionMenu)
19714
+ {
19715
+ me.SetPicker(tree.GetChildren().length > 0 ? tree : list);
19716
+ }
19717
+ }
19718
+ }
19719
+ }
19720
+
19721
+ return useActionMenu;
19722
+ }
19723
+
19425
19724
  // See documentation on ControlBase
19426
19725
  this.Dispose = Fit.Core.CreateOverride(this.Dispose, function()
19427
19726
  {
@@ -19429,10 +19728,13 @@ Fit.Controls.WSDropDown = function(ctlId)
19429
19728
 
19430
19729
  list.Destroy();
19431
19730
  tree.Destroy();
19731
+ actionMenu.Destroy();
19432
19732
 
19433
19733
  cancelSearch();
19434
19734
 
19435
- me = list = tree = search = forceNewSearch = hideLinesForFlatData = dataRequested = dataLoading = requestCount = onDataLoadedCallback = suppressTreeOnOpen = timeOut = currentRequest = classes = autoUpdatedSelections = onRequestHandlers = onResponseHandlers = null;
19735
+ Fit.Internationalization.RemoveOnLocaleChanged(localize);
19736
+
19737
+ me = list = tree = actionMenu = search = forceNewSearch = hideLinesForFlatData = dataRequested = dataLoading = requestCount = onDataLoadedCallback = suppressTreeOnOpen = timeOut = currentRequest = classes = autoUpdatedSelections = useActionMenu = useActionMenuForced = translations = onRequestHandlers = onResponseHandlers = null;
19436
19738
 
19437
19739
  base();
19438
19740
  });
@@ -19623,6 +19925,48 @@ Fit.Controls.WSDropDown = function(ctlId)
19623
19925
  }
19624
19926
  }
19625
19927
 
19928
+ function updateActionMenu()
19929
+ {
19930
+ if (useActionMenu === false)
19931
+ {
19932
+ return;
19933
+ }
19934
+
19935
+ var searchIcon = "<span class='FitUiControlDropDownActionMenuItem FitUiControlDropDownActionMenuItemSearch fa fa-search'></span> ";
19936
+ var showAllIcon = "<span class='FitUiControlDropDownActionMenuItem FitUiControlDropDownActionMenuItemShowAll fa fa-sitemap'></span> ";
19937
+ var delIcon = "<span class='FitUiControlDropDownActionMenuItem FitUiControlDropDownActionMenuItemDelete fa fa-times'></span> ";
19938
+
19939
+ var selectedItems = me.GetSelections();
19940
+ var addRemoveAll = selectedItems.length > 1;
19941
+
19942
+ actionMenu.RemoveItems();
19943
+
19944
+ actionMenu.AddItem(searchIcon + translations.SearchMore, "SearchMore");
19945
+
19946
+ if (dataRequested === false || tree.GetChildren().length > 0)
19947
+ {
19948
+ actionMenu.AddItem(showAllIcon + translations.ShowAllOptions, "ShowAll");
19949
+ }
19950
+
19951
+ if (addRemoveAll === true)
19952
+ {
19953
+ actionMenu.AddItem(delIcon + translations.RemoveAll, "RemoveAll");
19954
+ }
19955
+
19956
+ Fit.Array.ForEach(selectedItems, function(item)
19957
+ {
19958
+ actionMenu.AddItem((addRemoveAll === true ? " &nbsp; &nbsp; &nbsp; " : "") + delIcon + translations.Remove + " " + item.Title, "Remove:" + item.Value);
19959
+ });
19960
+ }
19961
+
19962
+ function localize()
19963
+ {
19964
+ var locale = Fit.Internationalization.GetLocale(me);
19965
+ translations = locale.Translations;
19966
+
19967
+ updateActionMenu();
19968
+ }
19969
+
19626
19970
  function onDataLoaded(cb)
19627
19971
  {
19628
19972
  Fit.Validation.ExpectFunction(cb);
@@ -19700,31 +20044,46 @@ Fit.Controls.WSDropDown = function(ctlId)
19700
20044
  // All locales inherit from en. All country specific overrides inherit
19701
20045
  // from their primary locale (e.g. de_AT inherits from de).
19702
20046
  // English (en) MUST be defined!
19703
-
20047
+
19704
20048
  "en": // US
19705
20049
  {
19706
20050
  Translations:
19707
20051
  {
19708
- InvalidSelection : "Invalid selection"
20052
+ InvalidSelection : "Invalid selection",
20053
+
20054
+ SearchMore : "Search for more options",
20055
+ ShowAllOptions : "Show all available options",
20056
+ RemoveAll : "Remove all selected",
20057
+ Remove : "Remove"
19709
20058
  }
19710
20059
  },
19711
20060
  "da":
19712
20061
  {
19713
20062
  Translations:
19714
20063
  {
19715
- InvalidSelection : "Ugyldigt valg"
20064
+ InvalidSelection : "Ugyldigt valg",
20065
+
20066
+ SearchMore : "Søg efter flere valgmuligheder",
20067
+ ShowAllOptions : "Vis alle tilgængelige valgmuligheder",
20068
+ RemoveAll : "Fjern alle valgte",
20069
+ Remove : "Fjern"
19716
20070
  }
19717
20071
  },
19718
20072
  "de":
19719
20073
  {
19720
20074
  Translations:
19721
20075
  {
19722
- InvalidSelection : "Ungültige Auswahl"
20076
+ InvalidSelection : "Ungültige Auswahl",
20077
+
20078
+ SearchMore : "Nach weiteren Optionen suchen",
20079
+ ShowAllOptions : "Alle verfügbaren Optionen anzeigen",
20080
+ RemoveAll : "Alle ausgewählten entfernen",
20081
+ Remove : "Entfernen"
19723
20082
  }
19724
20083
  }
19725
20084
  }
19726
20085
  Fit.Internationalization.AddLocalization(Fit.Controls.DropDown, locale);
19727
- Fit.Internationalization.AddLocalization(Fit.Controls.WSDropDown, locale);
20086
+ Fit.Internationalization.AddLocalization(Fit.Controls.WSDropDown, locale);
19728
20087
  })();
19729
20088
  /// <container name="Fit.Controls.FilePicker" extends="Fit.Controls.ControlBase">
19730
20089
  /// Control allowing for files to be selected locally and uploaded asynchronously.
@@ -20843,19 +21202,18 @@ Fit.Controls.Input = function(ctlId)
20843
21202
  Fit.Core.Extend(this, Fit.Controls.ControlBase).Apply(ctlId);
20844
21203
 
20845
21204
  var me = this;
20846
- var width = { Value: 200, Unit: "px" }; // Any changes to this line must be dublicated to Width(..)
20847
- var height = { Value: -1, Unit: "px" };
20848
21205
  var orgVal = "";
20849
21206
  var preVal = "";
20850
21207
  var input = null;
20851
21208
  var cmdResize = null;
20852
21209
  var designEditor = null;
20853
21210
  var htmlWrappedInParagraph = false;
20854
- var wasMultiLineBefore = false;
21211
+ var wasAutoChangedToMultiLineMode = false; // Used to revert to single line if multi line was automatically enabled along with DesignMode(true), Maximizable(true), or Resizable(true)
20855
21212
  var minimizeHeight = -1;
20856
21213
  var maximizeHeight = -1;
20857
21214
  var minMaxUnit = null;
20858
21215
  var resizable = Fit.Controls.InputResizing.Disabled;
21216
+ var nativeResizableAvailable = false; // Updated in init()
20859
21217
  var mutationObserverId = -1;
20860
21218
  var rootedEventId = -1;
20861
21219
  var createWhenReadyIntervalId = -1;
@@ -20924,6 +21282,7 @@ Fit.Controls.Input = function(ctlId)
20924
21282
  me._internal.Data("maximizable", "false");
20925
21283
  me._internal.Data("maximized", "false");
20926
21284
  me._internal.Data("resizable", resizable.toLowerCase());
21285
+ me._internal.Data("resized", "false");
20927
21286
  me._internal.Data("designmode", "false");
20928
21287
 
20929
21288
  Fit.Internationalization.OnLocaleChanged(localize);
@@ -20942,6 +21301,14 @@ Fit.Controls.Input = function(ctlId)
20942
21301
 
20943
21302
  fireOnChange(); // Only fires OnChange if value has actually changed
20944
21303
  });
21304
+
21305
+ try
21306
+ {
21307
+ // We rely on the .buttons property to optimization resizing for textarea (MultiLine mode).
21308
+ // The MouseEvent class might not be available on older browsers or might throw an exception when constructing.
21309
+ nativeResizableAvailable = window.MouseEvent && new MouseEvent("mousemove", {}).buttons !== undefined || false;
21310
+ }
21311
+ catch (err) {}
20945
21312
  }
20946
21313
 
20947
21314
  // ============================================
@@ -21224,41 +21591,28 @@ Fit.Controls.Input = function(ctlId)
21224
21591
  });
21225
21592
  }
21226
21593
 
21227
- me = orgVal = preVal = input = cmdResize = designEditor = htmlWrappedInParagraph = wasMultiLineBefore = minimizeHeight = maximizeHeight = minMaxUnit = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = null;
21594
+ me = orgVal = preVal = input = cmdResize = designEditor = htmlWrappedInParagraph = wasAutoChangedToMultiLineMode = minimizeHeight = maximizeHeight = minMaxUnit = resizable = nativeResizableAvailable = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = null;
21228
21595
 
21229
21596
  base();
21230
21597
  });
21231
21598
 
21232
21599
  // See documentation on ControlBase
21233
- this.Width = function(val, unit)
21600
+ this.Width = Fit.Core.CreateOverride(this.Width, function(val, unit)
21234
21601
  {
21235
21602
  Fit.Validation.ExpectNumber(val, true);
21236
21603
  Fit.Validation.ExpectStringValue(unit, true);
21237
21604
 
21238
21605
  if (Fit.Validation.IsSet(val) === true)
21239
21606
  {
21240
- // Contrary to other controls, width is not applied to the control's container,
21241
- // but directly on the input element, as this allows for textarea resizing.
21242
-
21243
- if (val > -1)
21244
- {
21245
- width = { Value: val, Unit: ((Fit.Validation.IsSet(unit) === true) ? unit : "px") };
21246
- input.style.width = width.Value + width.Unit;
21247
- }
21248
- else
21249
- {
21250
- width = { Value: 200, Unit: "px" }; // Any changes to this line must be dublicated to line declaring the width variable !
21251
- input.style.width = "";
21252
- }
21253
-
21607
+ base(val, unit);
21254
21608
  updateDesignEditorSize();
21255
21609
  }
21256
21610
 
21257
- return width;
21258
- }
21611
+ return base();
21612
+ });
21259
21613
 
21260
21614
  // See documentation on ControlBase
21261
- this.Height = function(val, unit, suppressMinMax)
21615
+ this.Height = Fit.Core.CreateOverride(this.Height, function(val, unit, suppressMinMax)
21262
21616
  {
21263
21617
  Fit.Validation.ExpectNumber(val, true);
21264
21618
  Fit.Validation.ExpectStringValue(unit, true);
@@ -21266,30 +21620,21 @@ Fit.Controls.Input = function(ctlId)
21266
21620
 
21267
21621
  if (Fit.Validation.IsSet(val) === true)
21268
21622
  {
21269
- // Contrary to other controls, height is not applied to the control's container,
21270
- // but directly on the input element, as this allows for textarea resizing.
21271
-
21272
- height = { Value: val, Unit: ((Fit.Validation.IsSet(unit) === true && val !== -1) ? unit : "px") };
21273
-
21274
- if (height.Value > -1)
21275
- input.style.height = height.Value + height.Unit;
21276
- else
21277
- input.style.height = "";
21278
-
21623
+ var h = base(val, unit);
21279
21624
  updateDesignEditorSize(); // Throws error if in DesignMode and unit is not px
21280
21625
 
21281
21626
  if (me.Maximizable() === true && suppressMinMax !== true)
21282
21627
  {
21283
- minimizeHeight = height.Value;
21284
- maximizeHeight = ((maximizeHeight > height.Value && height.Unit === minMaxUnit) ? maximizeHeight : height.Value * 2)
21285
- minMaxUnit = height.Unit;
21628
+ minimizeHeight = h.Value;
21629
+ maximizeHeight = ((maximizeHeight > h.Value && h.Unit === minMaxUnit) ? maximizeHeight : h.Value * 2)
21630
+ minMaxUnit = h.Unit;
21286
21631
 
21287
21632
  me.Maximized(false);
21288
21633
  }
21289
21634
  }
21290
21635
 
21291
- return height;
21292
- }
21636
+ return base();
21637
+ });
21293
21638
 
21294
21639
  // ============================================
21295
21640
  // Public
@@ -21422,6 +21767,11 @@ Fit.Controls.Input = function(ctlId)
21422
21767
  if (me.DesignMode() === true)
21423
21768
  me.DesignMode(false);
21424
21769
 
21770
+ if (val === true && wasAutoChangedToMultiLineMode === true)
21771
+ {
21772
+ wasAutoChangedToMultiLineMode = false;
21773
+ }
21774
+
21425
21775
  if (val === true && input.tagName === "INPUT")
21426
21776
  {
21427
21777
  var focused = me.Focused();
@@ -21430,7 +21780,6 @@ Fit.Controls.Input = function(ctlId)
21430
21780
  me._internal.RemoveDomElement(oldInput);
21431
21781
 
21432
21782
  input = document.createElement("textarea");
21433
- input.style.width = oldInput.style.width;
21434
21783
  input.value = oldInput.value;
21435
21784
  input.spellcheck = oldInput.spellcheck;
21436
21785
  input.placeholder = oldInput.placeholder;
@@ -21439,6 +21788,24 @@ Fit.Controls.Input = function(ctlId)
21439
21788
  input.onchange = oldInput.onchange;
21440
21789
  me._internal.AddDomElement(input);
21441
21790
 
21791
+ if (nativeResizableAvailable === true)
21792
+ {
21793
+ Fit.Events.AddHandler(input, "mousemove", function(e)
21794
+ {
21795
+ var ev = Fit.Events.GetEvent(e);
21796
+
21797
+ if (ev.buttons !== 1) // The .buttons property does not exist in older browsers (see nativeResizableAvailable)
21798
+ {
21799
+ return; // Skip - primary button not held down - not resizing
21800
+ }
21801
+
21802
+ if (me.Resizable() !== Fit.Controls.InputResizing.Disabled && (input.style.width !== "" || input.style.height !== "")) // Textarea was resized
21803
+ {
21804
+ me._internal.Data("resized", "true");
21805
+ }
21806
+ });
21807
+ }
21808
+
21442
21809
  if (me.Height().Value === -1)
21443
21810
  me.Height(150);
21444
21811
 
@@ -21464,9 +21831,14 @@ Fit.Controls.Input = function(ctlId)
21464
21831
  me._internal.Data("maximizable", "false");
21465
21832
  repaint();
21466
21833
  }
21834
+ else if (resizable !== Fit.Controls.InputResizing.Disabled)
21835
+ {
21836
+ resizable = Fit.Controls.InputResizing.Disabled;
21837
+ me._internal.Data("resizable", resizable.toLowerCase());
21838
+ me._internal.Data("resized", "false");
21839
+ }
21467
21840
 
21468
21841
  input = document.createElement("input");
21469
- input.style.width = oldInput.style.width;
21470
21842
  input.autocomplete = "off";
21471
21843
  input.type = "text";
21472
21844
  input.value = oldInput.value;
@@ -21482,7 +21854,7 @@ Fit.Controls.Input = function(ctlId)
21482
21854
  if (focused === true)
21483
21855
  input.focus();
21484
21856
 
21485
- wasMultiLineBefore = false;
21857
+ wasAutoChangedToMultiLineMode = false;
21486
21858
 
21487
21859
  me._internal.Data("multiline", "false");
21488
21860
  repaint();
@@ -21509,15 +21881,45 @@ Fit.Controls.Input = function(ctlId)
21509
21881
  {
21510
21882
  if (val !== resizable)
21511
21883
  {
21884
+ if (val !== Fit.Controls.InputResizing.Disabled) // Resizing enabled
21885
+ {
21886
+ if (me.Maximizable() === true)
21887
+ {
21888
+ me.Maximizable(false);
21889
+ //Fit.Browser.Log("Maximizable disabled as Resizable was enabled!");
21890
+ }
21891
+
21892
+ if (me.MultiLine() === false && designEditor === null)
21893
+ {
21894
+ me.MultiLine(true);
21895
+ wasAutoChangedToMultiLineMode = true;
21896
+ }
21897
+ }
21898
+ else // Resizing disabled
21899
+ {
21900
+ // Reset custom width/height set by user
21901
+
21902
+ var w = me.Width();
21903
+ me.Width(w.Value, w.Unit);
21904
+
21905
+ var h = me.Height();
21906
+ me.Height(h.Value, h.Unit);
21907
+ }
21908
+
21512
21909
  resizable = val;
21513
21910
  me._internal.Data("resizable", val.toLowerCase());
21514
21911
 
21515
- if (val !== Fit.Controls.InputResizing.Disabled && me.Maximizable() === true)
21912
+ if (val === Fit.Controls.InputResizing.Disabled)
21516
21913
  {
21517
- me.Maximizable(false);
21518
- //Fit.Browser.Log("Maximizable disabled as Resizable was enabled!");
21914
+ me._internal.Data("resized", "false");
21915
+
21916
+ input.style.width = "";
21917
+ input.style.height = "";
21918
+ input.style.margin = ""; // Chrome adds some odd margin when textarea is resized
21519
21919
  }
21520
21920
 
21921
+ revertToSingleLineIfNecessary();
21922
+
21521
21923
  if (me.DesignMode() === true)
21522
21924
  {
21523
21925
  reloadEditor();
@@ -21549,11 +21951,17 @@ Fit.Controls.Input = function(ctlId)
21549
21951
  {
21550
21952
  if (val === true && cmdResize === null)
21551
21953
  {
21552
- if (me.MultiLine() === true)
21553
- wasMultiLineBefore = true;
21954
+ if (me.Resizable() !== Fit.Controls.InputResizing.Disabled)
21955
+ {
21956
+ me.Resizable(Fit.Controls.InputResizing.Disabled);
21957
+ //Fit.Browser.Log("Resizable disabled as Maximizable was enabled!");
21958
+ }
21554
21959
 
21555
21960
  if (me.MultiLine() === false && designEditor === null)
21961
+ {
21556
21962
  me.MultiLine(true);
21963
+ wasAutoChangedToMultiLineMode = true;
21964
+ }
21557
21965
 
21558
21966
  // Determine height to use when maximizing and minimizing
21559
21967
 
@@ -21585,14 +21993,6 @@ Fit.Controls.Input = function(ctlId)
21585
21993
  Fit.Dom.AddClass(cmdResize, "fa-chevron-down");
21586
21994
  me._internal.AddDomElement(cmdResize);
21587
21995
 
21588
- // Disable Resizable
21589
-
21590
- if (me.Resizable() !== Fit.Controls.InputResizing.Disabled)
21591
- {
21592
- me.Resizable(Fit.Controls.InputResizing.Disabled);
21593
- //Fit.Browser.Log("Resizable disabled as Maximizable was enabled!");
21594
- }
21595
-
21596
21996
  // Update UI
21597
21997
 
21598
21998
  me._internal.Data("maximizable", "true");
@@ -21603,12 +22003,15 @@ Fit.Controls.Input = function(ctlId)
21603
22003
  me._internal.RemoveDomElement(cmdResize);
21604
22004
  cmdResize = null;
21605
22005
 
21606
- if (wasMultiLineBefore === true)
21607
- me.Height(minimizeHeight, minMaxUnit);
21608
- else
21609
- me.MultiLine(false);
22006
+ me.Height(minimizeHeight, minMaxUnit);
22007
+ minimizeHeight = -1;
22008
+ maximizeHeight = -1;
22009
+ minMaxUnit = null;
21610
22010
 
21611
22011
  me._internal.Data("maximizable", "false"); // Also set in MultiLine(..)
22012
+
22013
+ revertToSingleLineIfNecessary();
22014
+
21612
22015
  repaint();
21613
22016
  }
21614
22017
  }
@@ -21678,10 +22081,11 @@ Fit.Controls.Input = function(ctlId)
21678
22081
  return;
21679
22082
  }
21680
22083
 
21681
- if (me.MultiLine() === true)
21682
- wasMultiLineBefore = true;
21683
- else
22084
+ if (me.MultiLine() === false)
22085
+ {
21684
22086
  me.MultiLine(true);
22087
+ wasAutoChangedToMultiLineMode = true;
22088
+ }
21685
22089
 
21686
22090
  input.id = me.GetId() + "_DesignMode";
21687
22091
 
@@ -21878,6 +22282,15 @@ Fit.Controls.Input = function(ctlId)
21878
22282
  createEditor();
21879
22283
  }
21880
22284
 
22285
+ if (val !== Fit.Controls.InputResizing.Disabled)
22286
+ {
22287
+ Fit.Dom.Data(me.GetDomElement(), "resized", "false");
22288
+
22289
+ input.style.width = "";
22290
+ input.style.height = "";
22291
+ input.style.margin = ""; // Chrome adds some odd margin when textarea is resized
22292
+ }
22293
+
21881
22294
  me._internal.Data("designmode", "true");
21882
22295
  repaint();
21883
22296
  }
@@ -21926,9 +22339,9 @@ Fit.Controls.Input = function(ctlId)
21926
22339
  }
21927
22340
 
21928
22341
  me._internal.Data("designmode", "false");
22342
+ Fit.Dom.Data(me.GetDomElement(), "resized", "false");
21929
22343
 
21930
- if (wasMultiLineBefore === false)
21931
- me.MultiLine(false);
22344
+ revertToSingleLineIfNecessary();
21932
22345
 
21933
22346
  // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons
21934
22347
  Fit.Dom.Attribute(me.GetDomElement(), "tabindex", null);
@@ -22171,7 +22584,20 @@ Fit.Controls.Input = function(ctlId)
22171
22584
  }
22172
22585
 
22173
22586
  var h = me.Height();
22174
- me.Height(((h.Value >= 150 && h.Unit === "px") ? h.Value : 150));
22587
+ var useConfiguredHeight = h.Value >= 150 && h.Unit === "px"; // Only pixels are supported in DesignMode, and a minimum height of 150px must be applied for editor to be usable
22588
+
22589
+ if (useConfiguredHeight === false)
22590
+ {
22591
+ var defaultHeight = 150;
22592
+ me.Height(defaultHeight);
22593
+
22594
+ if (me.Maximizable() === true)
22595
+ {
22596
+ minimizeHeight = defaultHeight;
22597
+ maximizeHeight = defaultHeight * 2;
22598
+ minMaxUnit = "px";
22599
+ }
22600
+ }
22175
22601
 
22176
22602
  if (maximized === true)
22177
22603
  {
@@ -22179,13 +22605,21 @@ Fit.Controls.Input = function(ctlId)
22179
22605
  }
22180
22606
 
22181
22607
  designEditor._isReadyForInteraction = true;
22608
+
22609
+ // Make editor assume configured width and height.
22610
+ // Notice that using config.width and config.height
22611
+ // (https://ckeditor.com/docs/ckeditor4/latest/features/size.html)
22612
+ // results in editor becoming too high since the toolbar height is not
22613
+ // substracted. This problem does not occur when using updateDesignEditorSize().
22614
+ updateDesignEditorSize(); // Important: Make sure designEditor._isReadyForInteraction is set first (see above)
22182
22615
  },
22183
22616
  change: function() // CKEditor bug: not fired in Opera 12 (possibly other old versions as well)
22184
22617
  {
22185
22618
  input.onkeyup();
22186
22619
  },
22187
- resize: function()
22620
+ resize: function() // Fires when size is changed, not just when resized using resize handle in lower right cornor
22188
22621
  {
22622
+ me._internal.Data("resized", "true");
22189
22623
  repaint();
22190
22624
  },
22191
22625
  beforeCommandExec: function(ev)
@@ -22303,6 +22737,14 @@ Fit.Controls.Input = function(ctlId)
22303
22737
  }
22304
22738
  }
22305
22739
 
22740
+ function revertToSingleLineIfNecessary()
22741
+ {
22742
+ if (wasAutoChangedToMultiLineMode === true && me.Maximizable() === false && me.Resizable() === Fit.Controls.InputResizing.Disabled && me.DesignMode() === false)
22743
+ {
22744
+ me.MultiLine(false); // Changes wasAutoChangedToMultiLineMode to false
22745
+ }
22746
+ }
22747
+
22306
22748
  function fireOnChange()
22307
22749
  {
22308
22750
  var newVal = me.Value();
@@ -22335,12 +22777,18 @@ Fit.Controls.Input = function(ctlId)
22335
22777
  function reloadEditor()
22336
22778
  {
22337
22779
  // Disabling DesignMode brings it back to input or textarea mode.
22338
- // If reverting to input mode, the Height is reset, so we need to preserve that.
22780
+ // If reverting to input mode, Height is reset, so we need to preserve that.
22781
+
22782
+ // NOTICE: Custom width/height set using resize handle is not preserved when editor is reloaded
22339
22783
 
22340
22784
  var height = me.Height();
22785
+ var currentWasAutoChangedToMultiLineMode = wasAutoChangedToMultiLineMode; // DesignMode(false) will result in wasAutoChangedToMultiLineMode being set to false if DesignMode(true) changed the control to MultiLine mode
22786
+
22341
22787
  me.DesignMode(false);
22342
- me.Height(height.Value, height.Unit);
22343
22788
  me.DesignMode(true);
22789
+
22790
+ me.Height(height.Value, height.Unit);
22791
+ wasAutoChangedToMultiLineMode = currentWasAutoChangedToMultiLineMode;
22344
22792
  }
22345
22793
 
22346
22794
  function localize()
@@ -22540,8 +22988,15 @@ Fit.Controls.ListView = function(controlId)
22540
22988
  var me = this;
22541
22989
  var list = me.GetDomElement();
22542
22990
  var active = null;
22991
+ var persistView = false;
22992
+ var scrollPositionTop = 0;
22993
+ var highlightFirst = false;
22994
+ var firstWasHighlighted = false;
22543
22995
  var isIe8 = (Fit.Browser.GetInfo().Name === "MSIE" && Fit.Browser.GetInfo().Version === 8);
22544
22996
 
22997
+ var onSelectHandlers = [];
22998
+ var onSelectedHandlers = [];
22999
+
22545
23000
  function init()
22546
23001
  {
22547
23002
  list.tabIndex = "0";
@@ -22549,10 +23004,37 @@ Fit.Controls.ListView = function(controlId)
22549
23004
 
22550
23005
  me.OnShow(function()
22551
23006
  {
22552
- list.scrollTop = 0;
22553
- setActive(null);
23007
+ if (persistView === false) // Reset selection and scroll position
23008
+ {
23009
+ if (highlightFirst === true)
23010
+ {
23011
+ focusFirstItem();
23012
+ }
23013
+ else
23014
+ {
23015
+ setActive(null);
23016
+ }
23017
+
23018
+ list.scrollTop = 0;
23019
+ }
23020
+ else // View persisted
23021
+ {
23022
+ if (highlightFirst === true && firstWasHighlighted === false)
23023
+ {
23024
+ focusFirstItem();
23025
+ firstWasHighlighted = true;
23026
+ }
23027
+
23028
+ list.scrollTop = scrollPositionTop;
23029
+ }
22554
23030
  });
22555
23031
 
23032
+ list.onscroll = function(e)
23033
+ {
23034
+ // Preserve scroll position which is lost if picker control is removed from DOM
23035
+ scrollPositionTop = list.scrollTop;
23036
+ };
23037
+
22556
23038
  list.onclick = function(e)
22557
23039
  {
22558
23040
  var ev = Fit.Events.GetEvent(e);
@@ -22568,12 +23050,17 @@ Fit.Controls.ListView = function(controlId)
22568
23050
 
22569
23051
  // Fire OnChanging and OnChange events
22570
23052
 
23053
+ var item = convertItemElementToObject(elm);
23054
+ var selectionCanceled = fireOnSelectHandlers(item) === false;
23055
+
22571
23056
  // Notice: We always pass False as current selection state to OnItemSelectionChanging since ListView does
22572
23057
  // not keep track of selection state. In theory item could very well already be selected in host control.
22573
23058
  // Event handlers should not trust boolean to reveal selection in host control, only in picker.
22574
- if (me._internal.FireOnItemSelectionChanging(Fit.Dom.Text(elm), decode(Fit.Dom.Data(elm, "value")), false, false) === true)
23059
+ if (selectionCanceled === false && me._internal.FireOnItemSelectionChanging(item.Title, item.Value, false, false) === true)
22575
23060
  {
22576
- me._internal.FireOnItemSelectionChanged(Fit.Dom.Text(elm), decode(Fit.Dom.Data(elm, "value")), true, false);
23061
+ fireOnSelectedHandlers(item);
23062
+
23063
+ me._internal.FireOnItemSelectionChanged(item.Title, item.Value, true, false);
22577
23064
  me._internal.FireOnItemSelectionComplete();
22578
23065
  }
22579
23066
  }
@@ -22693,6 +23180,21 @@ Fit.Controls.ListView = function(controlId)
22693
23180
  return null;
22694
23181
  }
22695
23182
 
23183
+ /// <function container="Fit.Controls.ListView" name="GetItems" access="public" returns="Fit.Controls.ListViewTypeDefs.ListViewItem[]">
23184
+ /// <description> Get all items - returns array containing objects with Title (string) and Value (string) properties </description>
23185
+ /// </function>
23186
+ this.GetItems = function()
23187
+ {
23188
+ var items = [];
23189
+
23190
+ Fit.Array.ForEach(list.children, function(child)
23191
+ {
23192
+ Fit.Array.Add(items, convertItemElementToObject(child));
23193
+ });
23194
+
23195
+ return items;
23196
+ }
23197
+
22696
23198
  /// <function container="Fit.Controls.ListView" name="HasItem" access="public" returns="boolean">
22697
23199
  /// <description> Returns value indicating whether control contains item with specified value </description>
22698
23200
  /// <param name="value" type="string"> Value of item to check for </param>
@@ -22716,6 +23218,11 @@ Fit.Controls.ListView = function(controlId)
22716
23218
  if (item !== null)
22717
23219
  {
22718
23220
  Fit.Dom.Remove(item);
23221
+
23222
+ if (item === active)
23223
+ {
23224
+ active = null;
23225
+ }
22719
23226
  }
22720
23227
  }
22721
23228
 
@@ -22779,12 +23286,17 @@ Fit.Controls.ListView = function(controlId)
22779
23286
 
22780
23287
  if (active !== null)
22781
23288
  {
23289
+ var item = convertItemElementToObject(active);
23290
+ var selectionCanceled = fireOnSelectHandlers(item) === false;
23291
+
22782
23292
  // Notice: We always pass False as current selection state to OnItemSelectionChanging since ListView does
22783
23293
  // not keep track of selection state. In theory item could very well already be selected in host control.
22784
23294
  // Event handlers should not trust boolean to reveal selection in host control, only in picker.
22785
- if (me._internal.FireOnItemSelectionChanging(Fit.Dom.Text(active), decode(Fit.Dom.Data(active, "value")), false, false) === true)
23295
+ if (selectionCanceled === false && me._internal.FireOnItemSelectionChanging(item.Title, item.Value, false, false) === true)
22786
23296
  {
22787
- me._internal.FireOnItemSelectionChanged(Fit.Dom.Text(active), decode(Fit.Dom.Data(active, "value")), true, false);
23297
+ fireOnSelectedHandlers(item);
23298
+
23299
+ me._internal.FireOnItemSelectionChanged(item.Title, item.Value, true, false);
22788
23300
  me._internal.FireOnItemSelectionComplete();
22789
23301
  }
22790
23302
  }
@@ -22805,6 +23317,85 @@ Fit.Controls.ListView = function(controlId)
22805
23317
  return null;
22806
23318
  }
22807
23319
 
23320
+ this.PersistView = function(val)
23321
+ {
23322
+ Fit.Validation.ExpectBoolean(val, true);
23323
+
23324
+ if (Fit.Validation.IsSet(val) === true && val !== persistView)
23325
+ {
23326
+ persistView = val;
23327
+ scrollPositionTop = 0;
23328
+ }
23329
+
23330
+ return persistView;
23331
+ }
23332
+
23333
+ this.HighlightFirst = function(val)
23334
+ {
23335
+ Fit.Validation.ExpectBoolean(val, true);
23336
+
23337
+ if (Fit.Validation.IsSet(val) === true)
23338
+ {
23339
+ if (val !== highlightFirst)
23340
+ {
23341
+ highlightFirst = val;
23342
+ firstWasHighlighted = false;
23343
+ }
23344
+
23345
+ // Allow external code to force focus first item if picker is visible.
23346
+ // Usually first item is automatically highlighted when the host control
23347
+ // is opened (see OnShow handler in init()).
23348
+ if (val === true && Fit.Dom.IsVisible(me.GetDomElement()) === true)
23349
+ {
23350
+ focusFirstItem();
23351
+ firstWasHighlighted = true;
23352
+ }
23353
+ }
23354
+
23355
+ return highlightFirst;
23356
+ }
23357
+
23358
+ /// <function container="Fit.Controls.ListViewTypeDefs" name="OnSelectEventHandler" returns="boolean | void">
23359
+ /// <description> OnSelect event handler </description>
23360
+ /// <param name="sender" type="$TypeOfThis"> Instance of control </param>
23361
+ /// <param name="item" type="{ Title: string, Value: string }"> Selected item </param>
23362
+ /// </function>
23363
+
23364
+ /// <function container="Fit.Controls.ListView" name="OnSelect" access="public">
23365
+ /// <description>
23366
+ /// Register event handler fired when item is being selected.
23367
+ /// Selection can be canceled by returning False.
23368
+ /// The following arguments are passed to event handler function:
23369
+ /// Sender (ListView) and Item (with Title (string) and Value (string) properties).
23370
+ /// </description>
23371
+ /// <param name="cb" type="Fit.Controls.ListViewTypeDefs.OnSelectEventHandler"> Event handler function </param>
23372
+ /// </function>
23373
+ this.OnSelect = function(cb)
23374
+ {
23375
+ Fit.Validation.ExpectFunction(cb);
23376
+ Fit.Array.Add(onSelectHandlers, cb);
23377
+ }
23378
+
23379
+ /// <function container="Fit.Controls.ListViewTypeDefs" name="OnSelectedEventHandler">
23380
+ /// <description> OnSelected event handler </description>
23381
+ /// <param name="sender" type="$TypeOfThis"> Instance of control </param>
23382
+ /// <param name="item" type="{ Title: string, Value: string }"> Selected item </param>
23383
+ /// </function>
23384
+
23385
+ /// <function container="Fit.Controls.ListView" name="OnSelected" access="public">
23386
+ /// <description>
23387
+ /// Register event handler fired when item is selected.
23388
+ /// The following arguments are passed to event handler function:
23389
+ /// Sender (ListView) and Item (with Title (string) and Value (string) properties).
23390
+ /// </description>
23391
+ /// <param name="cb" type="Fit.Controls.ListViewTypeDefs.OnSelectedEventHandler"> Event handler function </param>
23392
+ /// </function>
23393
+ this.OnSelected = function(cb)
23394
+ {
23395
+ Fit.Validation.ExpectFunction(cb);
23396
+ Fit.Array.Add(onSelectedHandlers, cb);
23397
+ }
23398
+
22808
23399
  this.Destroy = Fit.Core.CreateOverride(this.Destroy, function(calledInternally)
22809
23400
  {
22810
23401
  Fit.Validation.ExpectBoolean(calledInternally, true);
@@ -22830,9 +23421,26 @@ Fit.Controls.ListView = function(controlId)
22830
23421
  me.Destroy(true); // PickerBase.Destroy()
22831
23422
  }
22832
23423
 
22833
- me = list = active = isIe8 = null;
23424
+ me = list = active = persistView = scrollPositionTop = highlightFirst = firstWasHighlighted = isIe8 = onSelectHandlers = onSelectedHandlers = null;
22834
23425
  });
22835
23426
 
23427
+ // ============================================
23428
+ // Protected
23429
+ // ============================================
23430
+
23431
+ this._internal = (this._internal ? this._internal : {});
23432
+
23433
+ this._internal.FocusFirstItem = function() // Similar implementation in Fit.Controls.TreeView._internal.FocusFirstNode()
23434
+ {
23435
+ // If picker is still visible (it might have been hidden if user closed
23436
+ // host control while data was being loaded/populated async.) then focus first item.
23437
+ if (Fit.Dom.IsVisible(me.GetDomElement()) === true)
23438
+ {
23439
+ focusFirstItem();
23440
+ firstWasHighlighted = true;
23441
+ }
23442
+ }
23443
+
22836
23444
  // ============================================
22837
23445
  // Private
22838
23446
  // ============================================
@@ -22883,6 +23491,14 @@ Fit.Controls.ListView = function(controlId)
22883
23491
  }
22884
23492
  }
22885
23493
 
23494
+ function focusFirstItem()
23495
+ {
23496
+ if (list.children.length > 0)
23497
+ {
23498
+ setActive(list.children[0]);
23499
+ }
23500
+ }
23501
+
22886
23502
  function moveUp()
22887
23503
  {
22888
23504
  if (list.children.length === 0)
@@ -22917,6 +23533,37 @@ Fit.Controls.ListView = function(controlId)
22917
23533
  }
22918
23534
  }
22919
23535
 
23536
+ function fireOnSelectHandlers(item)
23537
+ {
23538
+ Fit.Validation.ExpectObject(item);
23539
+ Fit.Validation.ExpectString(item.Title);
23540
+ Fit.Validation.ExpectString(item.Value);
23541
+
23542
+ var selectionCanceled = false;
23543
+
23544
+ Fit.Array.ForEach(onSelectHandlers, function(handler)
23545
+ {
23546
+ if (handler(me, item) === false)
23547
+ {
23548
+ selectionCanceled = true;
23549
+ }
23550
+ });
23551
+
23552
+ return selectionCanceled === true ? false : true; // Return False if canceled
23553
+ }
23554
+
23555
+ function fireOnSelectedHandlers(item)
23556
+ {
23557
+ Fit.Validation.ExpectObject(item);
23558
+ Fit.Validation.ExpectString(item.Title);
23559
+ Fit.Validation.ExpectString(item.Value);
23560
+
23561
+ Fit.Array.ForEach(onSelectedHandlers, function(handler)
23562
+ {
23563
+ handler(me, item);
23564
+ });
23565
+ }
23566
+
22920
23567
  function repaint()
22921
23568
  {
22922
23569
  if (isIe8 === true)
@@ -23254,6 +23901,26 @@ Fit.Controls.WSListView = function(ctlId)
23254
23901
  populate(item);
23255
23902
  });
23256
23903
 
23904
+ // Highlight first item
23905
+
23906
+ if (me.PersistView() === true)
23907
+ {
23908
+ // Reset PersistView to make sure it does not reuse state
23909
+ // for newly loaded items, in case item list is changed.
23910
+ me.PersistView(false);
23911
+ me.PersistView(true);
23912
+ }
23913
+
23914
+ if (me.HighlightFirst() === true)
23915
+ {
23916
+ // Reset HighlightFirst to make sure it takes effect again
23917
+ // for newly loaded items, in case item list is changed.
23918
+ me.HighlightFirst(false);
23919
+ me.HighlightFirst(true);
23920
+
23921
+ me._internal.FocusFirstItem();
23922
+ }
23923
+
23257
23924
  // Invoke callback
23258
23925
 
23259
23926
  if (Fit.Validation.IsSet(cb) === true)
@@ -23778,6 +24445,10 @@ Fit.Controls.TreeView = function(ctlId)
23778
24445
  var showSelectAll = false; // TBD: Never implemented - can be achieved using ContextMenu. Remove or implement?
23779
24446
  var allowDeselect = true;
23780
24447
  var revealExpandedNodes = false;
24448
+ var persistView = false;
24449
+ var scrollPosition = { X: 0, Y: 0 };
24450
+ var highlightFirst = false;
24451
+ var firstWasHighlighted = false;
23781
24452
 
23782
24453
  var selected = createInternalCollection();
23783
24454
  var selectedOrg = [];
@@ -24178,6 +24849,13 @@ Fit.Controls.TreeView = function(ctlId)
24178
24849
  touchTimeout = -1;
24179
24850
  }
24180
24851
  });
24852
+
24853
+ Fit.Events.AddHandler(me.GetDomElement(), "scroll", function(e)
24854
+ {
24855
+ // Preserve scroll position which is lost if picker control is removed from DOM
24856
+ scrollPosition.X = me.GetDomElement().scrollLeft;
24857
+ scrollPosition.Y = me.GetDomElement().scrollTop;
24858
+ });
24181
24859
  }
24182
24860
 
24183
24861
  // ============================================
@@ -24776,7 +25454,7 @@ Fit.Controls.TreeView = function(ctlId)
24776
25454
  me.Destroy(true); // PickerBase.Destroy()
24777
25455
  }
24778
25456
 
24779
- me = rootContainer = rootNode = focusInEventId = keyNavigationEnabled = selectable = multiSelect = showSelectAll = allowDeselect = revealExpandedNodes = selected = selectedOrg = ctx = onSelectHandlers = onSelectedHandlers = onToggleHandlers = onToggledHandlers = onSelectAllHandlers = onSelectAllCompleteHandlers = onContextMenuHandlers = forceClear = isIe8 = isPicker = activeNode = hostFocused = null;
25457
+ me = rootContainer = rootNode = focusInEventId = keyNavigationEnabled = selectable = multiSelect = showSelectAll = allowDeselect = revealExpandedNodes = persistView = scrollPosition = highlightFirst = firstWasHighlighted = selected = selectedOrg = ctx = onSelectHandlers = onSelectedHandlers = onToggleHandlers = onToggledHandlers = onSelectAllHandlers = onSelectAllCompleteHandlers = onContextMenuHandlers = forceClear = isIe8 = isPicker = activeNode = hostFocused = null;
24780
25458
  });
24781
25459
 
24782
25460
  // ============================================
@@ -24952,10 +25630,31 @@ Fit.Controls.TreeView = function(ctlId)
24952
25630
 
24953
25631
  this.OnShow(function(sender)
24954
25632
  {
24955
- // Reset selection and scroll
24956
- unsetActiveNode();
24957
- me.GetDomElement().scrollTop = 0;
24958
- me.GetDomElement().scrollLeft = 0;
25633
+ if (persistView === false) // Reset selection and scroll position
25634
+ {
25635
+ if (highlightFirst === true)
25636
+ {
25637
+ focusFirstNode();
25638
+ }
25639
+ else
25640
+ {
25641
+ unsetActiveNode();
25642
+ }
25643
+
25644
+ me.GetDomElement().scrollTop = 0;
25645
+ me.GetDomElement().scrollLeft = 0;
25646
+ }
25647
+ else // View persisted
25648
+ {
25649
+ if (highlightFirst === true && firstWasHighlighted === false)
25650
+ {
25651
+ focusFirstNode();
25652
+ firstWasHighlighted = true;
25653
+ }
25654
+
25655
+ me.GetDomElement().scrollTop = scrollPosition.Y;
25656
+ me.GetDomElement().scrollLeft = scrollPosition.X;
25657
+ }
24959
25658
  });
24960
25659
 
24961
25660
  this.OnSelect(function(sender, node)
@@ -25084,6 +25783,45 @@ Fit.Controls.TreeView = function(ctlId)
25084
25783
  return null;
25085
25784
  }
25086
25785
 
25786
+ this.PersistView = function(val)
25787
+ {
25788
+ Fit.Validation.ExpectBoolean(val, true);
25789
+
25790
+ if (Fit.Validation.IsSet(val) === true && val !== persistView)
25791
+ {
25792
+ persistView = val;
25793
+ scrollPosition.X = 0;
25794
+ scrollPosition.Y = 0;
25795
+ }
25796
+
25797
+ return persistView;
25798
+ }
25799
+
25800
+ this.HighlightFirst = function(val)
25801
+ {
25802
+ Fit.Validation.ExpectBoolean(val, true);
25803
+
25804
+ if (Fit.Validation.IsSet(val) === true)
25805
+ {
25806
+ if (val !== highlightFirst)
25807
+ {
25808
+ highlightFirst = val;
25809
+ firstWasHighlighted = false;
25810
+ }
25811
+
25812
+ // Allow external code to force focus first item if picker is visible.
25813
+ // Usually first item is automatically highlighted when the host control
25814
+ // is opened (see OnShow handler in init()).
25815
+ if (val === true && Fit.Dom.IsVisible(me.GetDomElement()) === true)
25816
+ {
25817
+ focusFirstNode();
25818
+ firstWasHighlighted = true;
25819
+ }
25820
+ }
25821
+
25822
+ return highlightFirst;
25823
+ }
25824
+
25087
25825
  this.UpdateItemSelection = function(itemValue, selected, programmaticallyChanged)
25088
25826
  {
25089
25827
  Fit.Validation.ExpectString(itemValue);
@@ -25134,6 +25872,17 @@ Fit.Controls.TreeView = function(ctlId)
25134
25872
  fireEventHandlers(onSelectAllCompleteHandlers, { Selected: selected, Node: node || null });
25135
25873
  }
25136
25874
 
25875
+ this._internal.FocusFirstNode = function() // Similar implementation in Fit.Controls.ListView._internal.FocusFirstItem()
25876
+ {
25877
+ // If picker is still visible (it might have been hidden if user closed
25878
+ // host control while data was being loaded/populated async.) then focus first node.
25879
+ if (Fit.Dom.IsVisible(me.GetDomElement()) === true)
25880
+ {
25881
+ focusFirstNode();
25882
+ firstWasHighlighted = true;
25883
+ }
25884
+ }
25885
+
25137
25886
  this.HandleEvent = function(e)
25138
25887
  {
25139
25888
  Fit.Validation.ExpectEvent(e, true);
@@ -27361,6 +28110,29 @@ Fit.Controls.WSTreeView = function(ctlId)
27361
28110
  delete request._loadingIndicator;
27362
28111
  }
27363
28112
 
28113
+ // Highlight first node
28114
+
28115
+ if (node === null) // Only do this for root nodes
28116
+ {
28117
+ if (me.PersistView() === true)
28118
+ {
28119
+ // Reset PersistView to make sure it does not reuse state
28120
+ // for newly loaded nodes, in case node list is changed.
28121
+ me.PersistView(false);
28122
+ me.PersistView(true);
28123
+ }
28124
+
28125
+ if (me.HighlightFirst() === true)
28126
+ {
28127
+ // Reset HighlightFirst to make sure it takes effect again
28128
+ // for newly loaded nodes, in case node list is changed.
28129
+ me.HighlightFirst(false);
28130
+ me.HighlightFirst(true);
28131
+
28132
+ me._internal.FocusFirstNode();
28133
+ }
28134
+ }
28135
+
27364
28136
  // Select nodes found in preselections
27365
28137
 
27366
28138
  var hasBeenDisposedOrDetached = node !== null && nodeDisposedOrDetached(node);