fit-ui 2.9.1 → 2.9.2
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/Documentation.html +3 -3
- package/dist/Fit.UI.css +519 -10
- package/dist/Fit.UI.js +4267 -77
- package/dist/Fit.UI.min.css +1 -1
- package/dist/Fit.UI.min.js +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +2468 -351
package/dist/Fit.UI.js
CHANGED
|
@@ -682,7 +682,7 @@ Fit._internal =
|
|
|
682
682
|
{
|
|
683
683
|
Core:
|
|
684
684
|
{
|
|
685
|
-
VersionInfo: { Major: 2, Minor: 9, Patch:
|
|
685
|
+
VersionInfo: { Major: 2, Minor: 9, Patch: 2 } // Do NOT modify format - version numbers are programmatically changed when releasing new versions - MUST be on a separate line!
|
|
686
686
|
}
|
|
687
687
|
};
|
|
688
688
|
|
|
@@ -4307,12 +4307,6 @@ Fit.Controls.ControlBase = function(controlId)
|
|
|
4307
4307
|
// focused state, and let the specialized control handle invocation of focus events when needed, and hand
|
|
4308
4308
|
// back control to ControlBase when the modal dialog closes.
|
|
4309
4309
|
|
|
4310
|
-
// Notice regarding Focused(): One could argue that Focused() should return True if focus state is locked,
|
|
4311
|
-
// if control was focused when lock was enabled, and if OnBlur has not been fired. But that means that two
|
|
4312
|
-
// controls could return True from Focused() which is just wrong, and the Focused() state would contradict
|
|
4313
|
-
// what is returned from Fit.Dom.GetFocused() or document.activeElement, which could potentially lead to
|
|
4314
|
-
// incorrect behaviour. So Focused() must give us the truth, even when focus state is locked.
|
|
4315
|
-
|
|
4316
4310
|
if (Fit.Validation.IsSet(value) === true)
|
|
4317
4311
|
{
|
|
4318
4312
|
if (value !== focusStateLocked)
|
|
@@ -4320,7 +4314,10 @@ Fit.Controls.ControlBase = function(controlId)
|
|
|
4320
4314
|
focusStateLocked = value;
|
|
4321
4315
|
|
|
4322
4316
|
// Make sure ControlBase can handle focus in/out properly when focus state is unlocked again
|
|
4323
|
-
|
|
4317
|
+
|
|
4318
|
+
var meElement = me.GetDomElement();
|
|
4319
|
+
var focusElement = Fit.Dom.GetFocused();
|
|
4320
|
+
hasFocus = meElement === focusElement || Fit.Dom.Contained(meElement, focusElement) === true;
|
|
4324
4321
|
}
|
|
4325
4322
|
}
|
|
4326
4323
|
|
|
@@ -6778,6 +6775,7 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
6778
6775
|
var posState = null; // { position: "", left: "", top: "" };
|
|
6779
6776
|
var trgElm = (domTriggerElm ? domTriggerElm : null);
|
|
6780
6777
|
var bringToFrontOnActivation = false;
|
|
6778
|
+
var returnFocus = false;
|
|
6781
6779
|
|
|
6782
6780
|
var onDragStart = null;
|
|
6783
6781
|
var onDragging = null;
|
|
@@ -6807,6 +6805,8 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
6807
6805
|
|
|
6808
6806
|
// Mouse down
|
|
6809
6807
|
|
|
6808
|
+
var focusedBeforeDrag = null;
|
|
6809
|
+
|
|
6810
6810
|
mouseDownEventId = Fit.Events.AddHandler(((trgElm !== null) ? trgElm : elm), (Fit.Browser.IsTouchEnabled() === true ? "touchstart" : "mousedown"), function(e)
|
|
6811
6811
|
{
|
|
6812
6812
|
if (Fit.DragDrop.Draggable._internal.active !== null)
|
|
@@ -6822,6 +6822,8 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
6822
6822
|
}
|
|
6823
6823
|
}
|
|
6824
6824
|
|
|
6825
|
+
focusedBeforeDrag = returnFocus === true ? Fit.Dom.GetFocused() : null;
|
|
6826
|
+
|
|
6825
6827
|
Fit.Dom.AddClass(elm, "FitDragDropDragging");
|
|
6826
6828
|
|
|
6827
6829
|
// Initial positioning (used by Reset())
|
|
@@ -6959,6 +6961,14 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
6959
6961
|
dropzoneState.OnDrop(dropzone, draggable);
|
|
6960
6962
|
}
|
|
6961
6963
|
|
|
6964
|
+
// Restore focus
|
|
6965
|
+
|
|
6966
|
+
if (focusedBeforeDrag !== null)
|
|
6967
|
+
{
|
|
6968
|
+
focusedBeforeDrag.focus();
|
|
6969
|
+
focusedBeforeDrag = null;
|
|
6970
|
+
}
|
|
6971
|
+
|
|
6962
6972
|
// Fire OnDragStop after OnDrop to make sure OnDragStop can act
|
|
6963
6973
|
// depending on whether draggable was dropped on a dropzone or not.
|
|
6964
6974
|
|
|
@@ -6970,6 +6980,11 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
6970
6980
|
});
|
|
6971
6981
|
}
|
|
6972
6982
|
|
|
6983
|
+
Fit.Browser.IsTouchEnabled() === true && Fit.Events.AddHandler(document, "touchcancel", function(e)
|
|
6984
|
+
{
|
|
6985
|
+
focusedBeforeDrag = null;
|
|
6986
|
+
});
|
|
6987
|
+
|
|
6973
6988
|
// Mouse move
|
|
6974
6989
|
|
|
6975
6990
|
if (Fit.DragDrop.Draggable._internal.mouseMoveRegistered === false)
|
|
@@ -7139,13 +7154,31 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
7139
7154
|
return bringToFrontOnActivation;
|
|
7140
7155
|
}
|
|
7141
7156
|
|
|
7157
|
+
/// <function container="Fit.DragDrop.Draggable" name="ReturnFocus" access="public" returns="boolean">
|
|
7158
|
+
/// <description> Get/set flag indicating whether focus is returned/restored after drag operation </description>
|
|
7159
|
+
/// <param name="val" type="boolean" default="undefined">
|
|
7160
|
+
/// A value of True causes draggable to return focus to previously
|
|
7161
|
+
/// focused element when drag operation is completed - defaults to False
|
|
7162
|
+
/// </param>
|
|
7163
|
+
/// </function>
|
|
7164
|
+
this.ReturnFocus = function(val)
|
|
7165
|
+
{
|
|
7166
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
7167
|
+
|
|
7168
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
7169
|
+
{
|
|
7170
|
+
returnFocus = val;
|
|
7171
|
+
}
|
|
7172
|
+
|
|
7173
|
+
return returnFocus;
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7142
7176
|
/// <function container="Fit.DragDrop.Draggable" name="BringToFront" access="public">
|
|
7143
7177
|
/// <description> Bring draggable to front </description>
|
|
7144
7178
|
/// </function>
|
|
7145
7179
|
this.BringToFront = function()
|
|
7146
7180
|
{
|
|
7147
|
-
Fit.DragDrop.Draggable._internal.
|
|
7148
|
-
elm.style.zIndex = Fit.DragDrop.Draggable._internal.zIndex;
|
|
7181
|
+
elm.style.zIndex = Fit.DragDrop.Draggable._internal.getNextZindex();
|
|
7149
7182
|
}
|
|
7150
7183
|
|
|
7151
7184
|
/// <function container="Fit.DragDrop.Draggable" name="Reset" access="public">
|
|
@@ -7197,7 +7230,7 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
7197
7230
|
Fit.DragDrop.Draggable._internal.active = null;
|
|
7198
7231
|
}
|
|
7199
7232
|
|
|
7200
|
-
me = elm = posState = trgElm = bringToFrontOnActivation = onDragStart = onDragging = onDragStop = activationEventId = mouseDownEventId = null;
|
|
7233
|
+
me = elm = posState = trgElm = bringToFrontOnActivation = returnFocus = onDragStart = onDragging = onDragStop = activationEventId = mouseDownEventId = null;
|
|
7201
7234
|
}
|
|
7202
7235
|
|
|
7203
7236
|
// Event handling
|
|
@@ -7248,7 +7281,12 @@ Fit.DragDrop.Draggable = function(domElm, domTriggerElm)
|
|
|
7248
7281
|
Fit.DragDrop.Draggable._internal =
|
|
7249
7282
|
{
|
|
7250
7283
|
/* Shared */
|
|
7251
|
-
|
|
7284
|
+
_zIndex : 10000,
|
|
7285
|
+
getNextZindex: function()
|
|
7286
|
+
{
|
|
7287
|
+
Fit.DragDrop.Draggable._internal._zIndex++;
|
|
7288
|
+
return Fit.DragDrop.Draggable._internal._zIndex + (Fit.Dom.Data(document.documentElement, "fitui-companion") === "fluent-ui" ? 2000000 : 0);
|
|
7289
|
+
},
|
|
7252
7290
|
mouseUpRegistered : false,
|
|
7253
7291
|
mouseMoveRegistered : false,
|
|
7254
7292
|
|
|
@@ -7994,7 +8032,7 @@ Fit.Events.GetModifierKeys = function()
|
|
|
7994
8032
|
|
|
7995
8033
|
/// <container name="Fit.EventTypeDefs.PointerState">
|
|
7996
8034
|
/// <description> Pointer state </description>
|
|
7997
|
-
/// <member name="Buttons" type="{ Primary: boolean, Secondary: boolean }"> Pointer buttons currently activated </member>
|
|
8035
|
+
/// <member name="Buttons" type="{ Primary: boolean, Secondary: boolean, Touch: boolean, Target: DOMElement | null }"> Pointer buttons currently activated </member>
|
|
7998
8036
|
/// <member name="Coordinates" type="{ ViewPort: Fit.TypeDefs.Position, Document: Fit.TypeDefs.Position }"> Pointer position within viewport and document, which might have been scrolled </member>
|
|
7999
8037
|
/// </container>
|
|
8000
8038
|
|
|
@@ -11418,14 +11456,37 @@ Fit.Controls.Button = function(controlId)
|
|
|
11418
11456
|
var label = null;
|
|
11419
11457
|
var width = { Value: -1, Unit: "px" }; // Initial width - a value of -1 indicates that size adjusts to content
|
|
11420
11458
|
var height = { Value: -1, Unit: "px" }; // Initial height - a value of -1 indicates that size adjusts to content
|
|
11459
|
+
var returnFocus = false;
|
|
11421
11460
|
var onClickHandlers = [];
|
|
11422
11461
|
|
|
11423
11462
|
function init()
|
|
11424
11463
|
{
|
|
11425
|
-
|
|
11464
|
+
var invokeClick = true;
|
|
11465
|
+
var focusedBeforeClick = null;
|
|
11466
|
+
|
|
11467
|
+
Fit.Events.AddHandler(element, Fit.Browser.IsTouchEnabled() === true ? "touchstart" : "mousedown", function(e)
|
|
11426
11468
|
{
|
|
11427
|
-
|
|
11469
|
+
invokeClick = true;
|
|
11470
|
+
focusedBeforeClick = returnFocus === true ? Fit.Dom.GetFocused() : null;
|
|
11471
|
+
});
|
|
11472
|
+
Fit.Browser.IsTouchEnabled() === true && Fit.Events.AddHandler(element, "touchmove", function(e)
|
|
11473
|
+
{
|
|
11474
|
+
invokeClick = false;
|
|
11475
|
+
});
|
|
11476
|
+
Fit.Events.AddHandler(element, Fit.Browser.IsTouchEnabled() === true ? "touchend" : "click", function(e)
|
|
11477
|
+
{
|
|
11478
|
+
if (invokeClick === true && me.Enabled() === true)
|
|
11428
11479
|
me.Click();
|
|
11480
|
+
|
|
11481
|
+
if (focusedBeforeClick !== null)
|
|
11482
|
+
{
|
|
11483
|
+
focusedBeforeClick.focus();
|
|
11484
|
+
focusedBeforeClick = null;
|
|
11485
|
+
}
|
|
11486
|
+
});
|
|
11487
|
+
Fit.Browser.IsTouchEnabled() === true && Fit.Events.AddHandler(element, "touchcancel", function(e)
|
|
11488
|
+
{
|
|
11489
|
+
focusedBeforeClick = null;
|
|
11429
11490
|
});
|
|
11430
11491
|
Fit.Events.AddHandler(element, "keydown", function(e)
|
|
11431
11492
|
{
|
|
@@ -11626,6 +11687,24 @@ Fit.Controls.Button = function(controlId)
|
|
|
11626
11687
|
return height;
|
|
11627
11688
|
}
|
|
11628
11689
|
|
|
11690
|
+
/// <function container="Fit.Controls.Button" name="ReturnFocus" access="public" returns="boolean">
|
|
11691
|
+
/// <description> Get/set flag indicating whether button returns focus after click </description>
|
|
11692
|
+
/// <param name="val" type="boolean" default="undefined">
|
|
11693
|
+
/// A value of True causes button to return focus to previously focused element after click - defaults to False
|
|
11694
|
+
/// </param>
|
|
11695
|
+
/// </function>
|
|
11696
|
+
this.ReturnFocus = function(val)
|
|
11697
|
+
{
|
|
11698
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
11699
|
+
|
|
11700
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
11701
|
+
{
|
|
11702
|
+
returnFocus = val;
|
|
11703
|
+
}
|
|
11704
|
+
|
|
11705
|
+
return returnFocus;
|
|
11706
|
+
}
|
|
11707
|
+
|
|
11629
11708
|
/// <function container="Fit.Controls.ButtonTypeDefs" name="ClickEventHandler">
|
|
11630
11709
|
/// <description> OnClick event handler </description>
|
|
11631
11710
|
/// <param name="sender" type="Fit.Controls.Button"> Instance of Button </param>
|
|
@@ -11656,7 +11735,7 @@ Fit.Controls.Button = function(controlId)
|
|
|
11656
11735
|
|
|
11657
11736
|
this.Dispose = Fit.Core.CreateOverride(this.Dispose, function()
|
|
11658
11737
|
{
|
|
11659
|
-
me = id = element = wrapper = icon = label = width = height = onClickHandlers = null;
|
|
11738
|
+
me = id = element = wrapper = icon = label = width = height = returnFocus = onClickHandlers = null;
|
|
11660
11739
|
base();
|
|
11661
11740
|
});
|
|
11662
11741
|
|
|
@@ -15262,6 +15341,7 @@ Fit.Controls.Dialog = function(controlId)
|
|
|
15262
15341
|
{
|
|
15263
15342
|
cmdMaximize = new Fit.Controls.Button();
|
|
15264
15343
|
cmdMaximize.Icon((me.Maximized() === false ? "expand" : "compress"));
|
|
15344
|
+
cmdMaximize.ReturnFocus(true);
|
|
15265
15345
|
cmdMaximize.OnClick(function(sender)
|
|
15266
15346
|
{
|
|
15267
15347
|
if (me.Maximized() === false)
|
|
@@ -15366,7 +15446,8 @@ Fit.Controls.Dialog = function(controlId)
|
|
|
15366
15446
|
|
|
15367
15447
|
draggable = new Fit.DragDrop.Draggable(me.GetDomElement(), titleText);
|
|
15368
15448
|
draggable.BringToFrontOnActivation(true);
|
|
15369
|
-
draggable.
|
|
15449
|
+
draggable.ReturnFocus(true);
|
|
15450
|
+
draggable.OnDragStart(function(elm)
|
|
15370
15451
|
{
|
|
15371
15452
|
if (me.Maximized() === true)
|
|
15372
15453
|
{
|
|
@@ -15638,7 +15719,7 @@ Fit.Controls.Dialog = function(controlId)
|
|
|
15638
15719
|
|
|
15639
15720
|
this.Render = function(toElement) // Override Render() on Fit.Controls.Component
|
|
15640
15721
|
{
|
|
15641
|
-
|
|
15722
|
+
me.Open(toElement);
|
|
15642
15723
|
}
|
|
15643
15724
|
|
|
15644
15725
|
this.Dispose = Fit.Core.CreateOverride(this.Dispose, function()
|
|
@@ -15791,6 +15872,7 @@ Fit.Controls.Dialog = function(controlId)
|
|
|
15791
15872
|
|
|
15792
15873
|
var ev = Fit.Events.GetEvent(e);
|
|
15793
15874
|
|
|
15875
|
+
var focusedBeforeResize = Fit.Dom.GetFocused();
|
|
15794
15876
|
var initPos = Fit.Events.GetPointerState().Coordinates.ViewPort;
|
|
15795
15877
|
var initDim = { Width: me.GetDomElement().offsetWidth, Height: me.GetDomElement().offsetHeight };
|
|
15796
15878
|
|
|
@@ -15807,6 +15889,8 @@ Fit.Controls.Dialog = function(controlId)
|
|
|
15807
15889
|
{
|
|
15808
15890
|
Fit.Events.RemoveHandler(document, cancelHandler);
|
|
15809
15891
|
}
|
|
15892
|
+
|
|
15893
|
+
focusedBeforeResize.focus();
|
|
15810
15894
|
};
|
|
15811
15895
|
|
|
15812
15896
|
moveHandler = Fit.Events.AddHandler(document, (Fit.Browser.IsTouchEnabled() === true ? "touchmove" : "mousemove"), { passive: false /* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener */ }, function(e)
|
|
@@ -22092,7 +22176,9 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22092
22176
|
var designEditorMustDisposeWhenReady = false;
|
|
22093
22177
|
var designEditorUpdateSizeDebouncer = -1;
|
|
22094
22178
|
var designEditorActiveToolbarPanel = null; // { DomElement: HTMLElement, UnlockFocusStateIfEmojiPanelIsClosed: function, CloseEmojiPanel: function }
|
|
22095
|
-
var designEditorDetached = null; // { GetValue: function, Reload: function, Dispose: function }
|
|
22179
|
+
var designEditorDetached = null; // { GetValue: function, Reload: function, Dispose: function, ..... }
|
|
22180
|
+
var designModeDialogModeContent = null;
|
|
22181
|
+
var designModeDialogMode = null; // { DomElement: HTMLElement, AutoOpen: boolean, ..... }
|
|
22096
22182
|
//var htmlWrappedInParagraph = false;
|
|
22097
22183
|
var wasAutoChangedToMultiLineMode = false; // Used to revert to single line if multi line was automatically enabled along with DesignMode(true), Maximizable(true), or Resizable(true)
|
|
22098
22184
|
var minimizeHeight = -1;
|
|
@@ -22195,6 +22281,19 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22195
22281
|
{
|
|
22196
22282
|
restoreHiddenToolbarInDesignEditor(); // Make toolbar appear if currently hidden
|
|
22197
22283
|
updateDesignEditorPlaceholder(true); // Clear placeholder text
|
|
22284
|
+
|
|
22285
|
+
// Make sure control gains focus in case a link received focus when navigating backwards (SHIFT + TAB)
|
|
22286
|
+
if (false && designModeDialogMode !== null && Fit.Dom.GetFocused().tagName === "A")
|
|
22287
|
+
{
|
|
22288
|
+
//me.GetDomElement().focus();
|
|
22289
|
+
//var scrollPos = designModeDialogMode.DomElement.scrollTop;
|
|
22290
|
+
designModeDialogMode.DomElement.focus();
|
|
22291
|
+
//designModeDialogMode.DomElement.scrollTop = scrollPos;
|
|
22292
|
+
}
|
|
22293
|
+
// if (designModeDialogMode !== null && me.GetDomElement() === Fit.Dom.GetFocused())
|
|
22294
|
+
// {
|
|
22295
|
+
// designModeDialogMode.DomElement.focus();
|
|
22296
|
+
// }
|
|
22198
22297
|
});
|
|
22199
22298
|
me.OnBlur(function()
|
|
22200
22299
|
{
|
|
@@ -22212,7 +22311,7 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22212
22311
|
|
|
22213
22312
|
try
|
|
22214
22313
|
{
|
|
22215
|
-
// We rely on the .buttons property to
|
|
22314
|
+
// We rely on the .buttons property to optimize resizing for textarea (MultiLine mode).
|
|
22216
22315
|
// The MouseEvent class might not be available on older browsers or might throw an exception when constructing.
|
|
22217
22316
|
nativeResizableAvailable = window.MouseEvent && new MouseEvent("mousemove", {}).buttons !== undefined || false;
|
|
22218
22317
|
}
|
|
@@ -22223,6 +22322,30 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22223
22322
|
// Public - overrides
|
|
22224
22323
|
// ============================================
|
|
22225
22324
|
|
|
22325
|
+
this.Visible = Fit.Core.CreateOverride(this.Visible, function(val)
|
|
22326
|
+
{
|
|
22327
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
22328
|
+
|
|
22329
|
+
if (Fit.Validation.IsSet(val) && designEditorDetached !== null)
|
|
22330
|
+
{
|
|
22331
|
+
designEditorDetached.SetVisible(val);
|
|
22332
|
+
}
|
|
22333
|
+
|
|
22334
|
+
return base(val);
|
|
22335
|
+
});
|
|
22336
|
+
|
|
22337
|
+
// this.Render = Fit.Core.CreateOverride(this.Render, function(toElement)
|
|
22338
|
+
// {
|
|
22339
|
+
// Fit.Validation.ExpectDomElement(toElement, true);
|
|
22340
|
+
|
|
22341
|
+
// base(toElement);
|
|
22342
|
+
|
|
22343
|
+
// if (designModeDialogMode !== null && designModeDialogMode.AutoOpen === true)
|
|
22344
|
+
// {
|
|
22345
|
+
// openDetachedDesignEditor();
|
|
22346
|
+
// }
|
|
22347
|
+
// });
|
|
22348
|
+
|
|
22226
22349
|
// See documentation on ControlBase
|
|
22227
22350
|
this.Enabled = function(val)
|
|
22228
22351
|
{
|
|
@@ -22255,6 +22378,11 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22255
22378
|
Fit.Dom.Attribute(me.GetDomElement(), "tabindex", input.disabled !== true && me.DesignMode() === true ? "-1" : null); // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons, as it will allow control to gain focus when clicked using the mouse
|
|
22256
22379
|
}
|
|
22257
22380
|
|
|
22381
|
+
if (designEditorDetached !== null)
|
|
22382
|
+
{
|
|
22383
|
+
designEditorDetached.SetEnabled(val);
|
|
22384
|
+
}
|
|
22385
|
+
|
|
22258
22386
|
me._internal.UpdateInternalState();
|
|
22259
22387
|
me._internal.Repaint();
|
|
22260
22388
|
}
|
|
@@ -22267,6 +22395,27 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22267
22395
|
{
|
|
22268
22396
|
Fit.Validation.ExpectBoolean(focus, true);
|
|
22269
22397
|
|
|
22398
|
+
// TODO: This function does not work properly when editor is detached !
|
|
22399
|
+
// What should it do if Focused(true) or Focused(false) is invoked ????
|
|
22400
|
+
// What should it return in detached mode ??
|
|
22401
|
+
|
|
22402
|
+
if (designEditorDetached !== null)
|
|
22403
|
+
{
|
|
22404
|
+
//return true; // Hmm, what if 'focus' is set to false ???
|
|
22405
|
+
if (focus === true)
|
|
22406
|
+
{
|
|
22407
|
+
console.log("Redirecting focus to detached editor");
|
|
22408
|
+
designEditorDetached.Focus();
|
|
22409
|
+
}
|
|
22410
|
+
else
|
|
22411
|
+
{
|
|
22412
|
+
// Should it remove focus? Probably not! Focus is locked and editing experience is modal!
|
|
22413
|
+
}
|
|
22414
|
+
|
|
22415
|
+
return designEditorDetached.GetFocused();
|
|
22416
|
+
//return (Fit.Dom.Contained(designEditorDetached.DomElement, Fit.Dom.GetFocused()) === true);
|
|
22417
|
+
}
|
|
22418
|
+
|
|
22270
22419
|
elm = input;
|
|
22271
22420
|
|
|
22272
22421
|
if (me.DesignMode() === true)
|
|
@@ -22398,7 +22547,12 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22398
22547
|
/*if (val.indexOf("<p>") === 0)
|
|
22399
22548
|
htmlWrappedInParagraph = true; // Indicates that val is comparable with value from CKEditor which wraps content in paragraphs*/
|
|
22400
22549
|
|
|
22401
|
-
if (
|
|
22550
|
+
if (designModeDialogMode !== null)
|
|
22551
|
+
{
|
|
22552
|
+
input.value = val;
|
|
22553
|
+
designModeDialogMode.DomElement.innerHTML = val;
|
|
22554
|
+
}
|
|
22555
|
+
else if (designModeEnabledAndReady() === true)
|
|
22402
22556
|
{
|
|
22403
22557
|
// NOTICE: Invalid HTML is removed, so an all invalid HTML string will be discarded
|
|
22404
22558
|
// by the editor, resulting in the editor's getData() function returning an empty string.
|
|
@@ -22521,7 +22675,11 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22521
22675
|
{
|
|
22522
22676
|
// This will destroy control - it will no longer work!
|
|
22523
22677
|
|
|
22524
|
-
if (me.DesignMode() === true &&
|
|
22678
|
+
if (me.DesignMode() === true && designModeDialogMode !== null)
|
|
22679
|
+
{
|
|
22680
|
+
designModeDialogMode.Dispose();
|
|
22681
|
+
}
|
|
22682
|
+
else if (me.DesignMode() === true && designModeEnabledAndReady() === false) // DesignMode is enabled but editor is not done loading/initializing
|
|
22525
22683
|
{
|
|
22526
22684
|
// WARNING: This has the potential to leak memory if editor never loads and resumes task of disposing control!
|
|
22527
22685
|
designEditorMustDisposeWhenReady = true;
|
|
@@ -22681,11 +22839,20 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22681
22839
|
me.Maximized(false);
|
|
22682
22840
|
}
|
|
22683
22841
|
|
|
22842
|
+
// if (designModeDialogMode !== null)
|
|
22843
|
+
// {
|
|
22844
|
+
// // TODO: Remove autogrow if DesignMode/DialogMode is disabled
|
|
22845
|
+
// me._internal.Data("autogrow", val === -1 ? "true" : null); // Make control container adjust to editor's height
|
|
22846
|
+
// //designModeDialogMode.DomElement.style.height = val === -1 ? "auto" : "";
|
|
22847
|
+
// return base(val, unit);
|
|
22848
|
+
// }
|
|
22849
|
+
|
|
22684
22850
|
me._internal.Data("resized", "false");
|
|
22685
22851
|
me._internal.Data("autogrow", me.DesignMode() === true ? "false" : null);
|
|
22686
22852
|
|
|
22687
22853
|
var autoGrowEnabled = false;
|
|
22688
|
-
if (val === -1 && designModeEnabledAndReady() === true) // Enable auto grow if editor is loaded and ready - otherwise enabled in instanceReady handler
|
|
22854
|
+
//if (val === -1 && (designModeEnabledAndReady() === true || designModeDialogMode !== null)) // Enable auto grow if editor is loaded and ready - otherwise enabled in instanceReady handler
|
|
22855
|
+
if (val === -1 && (designModeEnabledAndReady() === true || designModeDialogModeContent !== null)) // Enable auto grow if editor is loaded and ready - otherwise enabled in instanceReady handler
|
|
22689
22856
|
{
|
|
22690
22857
|
// A value of -1 is used to reset control height (assume default height).
|
|
22691
22858
|
// In DesignMode we want the control height to adjust to the content of the editor in this case.
|
|
@@ -22695,22 +22862,32 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22695
22862
|
autoGrowEnabled = true;
|
|
22696
22863
|
}
|
|
22697
22864
|
|
|
22698
|
-
var
|
|
22699
|
-
|
|
22865
|
+
var h = null;
|
|
22866
|
+
|
|
22867
|
+
//if (designModeDialogMode !== null)
|
|
22868
|
+
if (designModeDialogModeContent !== null)
|
|
22700
22869
|
{
|
|
22701
|
-
|
|
22702
|
-
// When toolbar is hidden, a fixed height is set on the editable area which
|
|
22703
|
-
// prevent changes to control height.
|
|
22704
|
-
restoreHiddenToolbarInDesignEditor();
|
|
22705
|
-
hideToolbarAgain = true;
|
|
22870
|
+
h = base(val, unit);
|
|
22706
22871
|
}
|
|
22872
|
+
else
|
|
22873
|
+
{
|
|
22874
|
+
var hideToolbarAgain = false;
|
|
22875
|
+
if (isToolbarHiddenInDesignEditor() === true)
|
|
22876
|
+
{
|
|
22877
|
+
// If in DesignMode, temporarily restore toolbar to allow update to height.
|
|
22878
|
+
// When toolbar is hidden, a fixed height is set on the editable area which
|
|
22879
|
+
// prevent changes to control height.
|
|
22880
|
+
restoreHiddenToolbarInDesignEditor();
|
|
22881
|
+
hideToolbarAgain = true;
|
|
22882
|
+
}
|
|
22707
22883
|
|
|
22708
|
-
|
|
22709
|
-
|
|
22884
|
+
h = base(val, unit);
|
|
22885
|
+
updateDesignEditorSize();
|
|
22710
22886
|
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
|
|
22887
|
+
if (hideToolbarAgain === true)
|
|
22888
|
+
{
|
|
22889
|
+
hideToolbarInDesignMode();
|
|
22890
|
+
}
|
|
22714
22891
|
}
|
|
22715
22892
|
|
|
22716
22893
|
// Calculate new maximize height if control is maximizable
|
|
@@ -22765,7 +22942,11 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22765
22942
|
{
|
|
22766
22943
|
input.spellcheck = val;
|
|
22767
22944
|
|
|
22768
|
-
if (
|
|
22945
|
+
if (designModeDialogMode !== null)
|
|
22946
|
+
{
|
|
22947
|
+
designModeDialogMode.Reload()
|
|
22948
|
+
}
|
|
22949
|
+
else if (me.DesignMode() === true)
|
|
22769
22950
|
{
|
|
22770
22951
|
reloadEditor();
|
|
22771
22952
|
}
|
|
@@ -22979,6 +23160,25 @@ Fit.Controls.Input = function(ctlId)
|
|
|
22979
23160
|
{
|
|
22980
23161
|
if (val !== resizable)
|
|
22981
23162
|
{
|
|
23163
|
+
// Reset dimensions in case direction is changed - otherwise overflow might occur.
|
|
23164
|
+
// Control container has width:auto when Resizable is Horizontal and
|
|
23165
|
+
// width:height when Resizable is Vertical. Preserving dimensions will
|
|
23166
|
+
// result in resizable element assuming dimensions different from control container.
|
|
23167
|
+
if (designModeDialogMode !== null)
|
|
23168
|
+
{
|
|
23169
|
+
designModeDialogMode.DomElement.style.width = "";
|
|
23170
|
+
designModeDialogMode.DomElement.style.height = "";
|
|
23171
|
+
}
|
|
23172
|
+
else
|
|
23173
|
+
{
|
|
23174
|
+
input.style.width = "";
|
|
23175
|
+
input.style.height = "";
|
|
23176
|
+
input.style.margin = ""; // Chrome adds some odd margin when textarea is resized
|
|
23177
|
+
}
|
|
23178
|
+
|
|
23179
|
+
// Make sure control assumes configured dimensions (disables width:auto and height:auto until resized again)
|
|
23180
|
+
me._internal.Data("resized", "false");
|
|
23181
|
+
|
|
22982
23182
|
if (val !== Fit.Controls.InputResizing.Disabled) // Resizing enabled
|
|
22983
23183
|
{
|
|
22984
23184
|
if (me.Maximizable() === true)
|
|
@@ -23007,15 +23207,6 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23007
23207
|
resizable = val;
|
|
23008
23208
|
me._internal.Data("resizable", val.toLowerCase());
|
|
23009
23209
|
|
|
23010
|
-
if (val === Fit.Controls.InputResizing.Disabled)
|
|
23011
|
-
{
|
|
23012
|
-
me._internal.Data("resized", "false");
|
|
23013
|
-
|
|
23014
|
-
input.style.width = "";
|
|
23015
|
-
input.style.height = "";
|
|
23016
|
-
input.style.margin = ""; // Chrome adds some odd margin when textarea is resized
|
|
23017
|
-
}
|
|
23018
|
-
|
|
23019
23210
|
revertToSingleLineIfNecessary();
|
|
23020
23211
|
|
|
23021
23212
|
if (me.DesignMode() === true)
|
|
@@ -23070,7 +23261,6 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23070
23261
|
minMaxUnit = h.Unit;
|
|
23071
23262
|
maximizeHeightConfigured = heightMax || -1;
|
|
23072
23263
|
|
|
23073
|
-
|
|
23074
23264
|
// Create maximize/minimize button
|
|
23075
23265
|
|
|
23076
23266
|
cmdResize = document.createElement("span");
|
|
@@ -23338,6 +23528,11 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23338
23528
|
/// <member name="MaximumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum height of dialog </member>
|
|
23339
23529
|
/// </container>
|
|
23340
23530
|
|
|
23531
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeDialogMode" extends="Fit.Controls.InputTypeDefs.DesignModeDetachable">
|
|
23532
|
+
/// <description> DialogMode configuration </description>
|
|
23533
|
+
/// <member name="AutoOpen" type="boolean" default="undefined"> Flag indicating whether dialog is automatically opened </member>
|
|
23534
|
+
/// </container>
|
|
23535
|
+
|
|
23341
23536
|
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfig">
|
|
23342
23537
|
/// <description> Configuration for DesignMode </description>
|
|
23343
23538
|
/// <member name="Plugins" type="Fit.Controls.InputTypeDefs.DesignModeConfigPlugins" default="undefined"> Plugins configuration </member>
|
|
@@ -23346,6 +23541,10 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23346
23541
|
/// <member name="Tags" type="Fit.Controls.InputTypeDefs.DesignModeConfigTags" default="undefined"> Tags configuration </member>
|
|
23347
23542
|
/// <member name="AutoGrow" type="Fit.Controls.InputTypeDefs.DesignModeAutoGrow" default="undefined"> Auto grow configuration </member>
|
|
23348
23543
|
/// <member name="Detachable" type="Fit.Controls.InputTypeDefs.DesignModeDetachable" default="undefined"> Detachable configuration </member>
|
|
23544
|
+
/// <member name="DialogMode" type="Fit.Controls.InputTypeDefs.DesignModeDialogMode" default="undefined">
|
|
23545
|
+
/// If set, control opens in dialog when activated (on click, on touch, and on ENTER key).
|
|
23546
|
+
/// Control is initially rendered as a read-only value which becomes editable on activation.
|
|
23547
|
+
/// </member>
|
|
23349
23548
|
/// </container>
|
|
23350
23549
|
|
|
23351
23550
|
/// <function container="Fit.Controls.Input" name="DesignMode" access="public" returns="boolean">
|
|
@@ -23394,29 +23593,36 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23394
23593
|
Fit.Validation.ExpectStringValue((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Unit, true);
|
|
23395
23594
|
Fit.Validation.ExpectBoolean(((editorConfig || {}).AutoGrow || {}).PreventResizeBeyondMaximumHeight, true);
|
|
23396
23595
|
Fit.Validation.ExpectObject((editorConfig || {}).Detachable, true);
|
|
23397
|
-
Fit.Validation.
|
|
23398
|
-
|
|
23399
|
-
Fit.
|
|
23400
|
-
|
|
23401
|
-
|
|
23402
|
-
|
|
23403
|
-
|
|
23404
|
-
|
|
23405
|
-
|
|
23406
|
-
|
|
23407
|
-
|
|
23408
|
-
|
|
23409
|
-
|
|
23410
|
-
|
|
23411
|
-
|
|
23412
|
-
|
|
23413
|
-
|
|
23414
|
-
|
|
23415
|
-
|
|
23416
|
-
|
|
23417
|
-
|
|
23418
|
-
|
|
23419
|
-
|
|
23596
|
+
Fit.Validation.ExpectObject((editorConfig || {}).DialogMode, true);
|
|
23597
|
+
|
|
23598
|
+
Fit.Array.ForEach([(editorConfig || {}).Detachable || null, (editorConfig || {}).DialogMode || null], function(detachableConfig)
|
|
23599
|
+
{
|
|
23600
|
+
if (detachableConfig === null) return;
|
|
23601
|
+
|
|
23602
|
+
Fit.Validation.ExpectString(detachableConfig.Title, true);
|
|
23603
|
+
Fit.Validation.ExpectBoolean(detachableConfig.Maximizable, true);
|
|
23604
|
+
Fit.Validation.ExpectBoolean(detachableConfig.Maximized, true);
|
|
23605
|
+
Fit.Validation.ExpectBoolean(detachableConfig.Draggable, true);
|
|
23606
|
+
Fit.Validation.ExpectBoolean(detachableConfig.Resizable, true);
|
|
23607
|
+
Fit.Validation.ExpectObject(detachableConfig.Width, true);
|
|
23608
|
+
Fit.Validation.ExpectNumber((detachableConfig.Width || {}).Value, true);
|
|
23609
|
+
Fit.Validation.ExpectStringValue((detachableConfig.Width || {}).Unit, true);
|
|
23610
|
+
Fit.Validation.ExpectObject(detachableConfig.MinimumWidth, true);
|
|
23611
|
+
Fit.Validation.ExpectNumber((detachableConfig.MinimumWidth || {}).Value, true);
|
|
23612
|
+
Fit.Validation.ExpectStringValue((detachableConfig.MinimumWidth || {}).Unit, true);
|
|
23613
|
+
Fit.Validation.ExpectObject(detachableConfig.MaximumWidth, true);
|
|
23614
|
+
Fit.Validation.ExpectNumber((detachableConfig.MaximumWidth || {}).Value, true);
|
|
23615
|
+
Fit.Validation.ExpectStringValue((detachableConfig.MaximumWidth || {}).Unit, true);
|
|
23616
|
+
Fit.Validation.ExpectObject(detachableConfig.Height, true);
|
|
23617
|
+
Fit.Validation.ExpectNumber((detachableConfig.Height || {}).Value, true);
|
|
23618
|
+
Fit.Validation.ExpectStringValue((detachableConfig.Height || {}).Unit, true);
|
|
23619
|
+
Fit.Validation.ExpectObject(detachableConfig.MinimumHeight, true);
|
|
23620
|
+
Fit.Validation.ExpectNumber((detachableConfig.MinimumHeight || {}).Value, true);
|
|
23621
|
+
Fit.Validation.ExpectStringValue((detachableConfig.MinimumHeight || {}).Unit, true);
|
|
23622
|
+
Fit.Validation.ExpectObject(detachableConfig.MaximumHeight, true);
|
|
23623
|
+
Fit.Validation.ExpectNumber((detachableConfig.MaximumHeight || {}).Value, true);
|
|
23624
|
+
Fit.Validation.ExpectStringValue((detachableConfig.MaximumHeight || {}).Unit, true);
|
|
23625
|
+
})
|
|
23420
23626
|
|
|
23421
23627
|
if (editorConfig && editorConfig.Tags)
|
|
23422
23628
|
{
|
|
@@ -23436,6 +23642,174 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23436
23642
|
|
|
23437
23643
|
if (Fit.Validation.IsSet(val) === true)
|
|
23438
23644
|
{
|
|
23645
|
+
// TODO: Support reloading using DesignMode(true, {}) !!
|
|
23646
|
+
// What should happen if detached editor is open in dialog mode ??
|
|
23647
|
+
// Normal detached editor remains visible on screen so data is not lost.
|
|
23648
|
+
// Changes to data can still be committed or discarded, which makes sense.
|
|
23649
|
+
|
|
23650
|
+
//if (designEditorConfig !== null && designEditorConfig.DialogMode)
|
|
23651
|
+
var isDialogMode = (editorConfig || designEditorConfig || {}).DialogMode && true || false;
|
|
23652
|
+
|
|
23653
|
+
if (val === true && isDialogMode === true)
|
|
23654
|
+
{
|
|
23655
|
+
if (designModeDialogMode !== null)
|
|
23656
|
+
{
|
|
23657
|
+
designModeDialogMode.Dispose();
|
|
23658
|
+
}
|
|
23659
|
+
|
|
23660
|
+
if (Fit.Validation.IsSet(editorConfig) === true)
|
|
23661
|
+
{
|
|
23662
|
+
designEditorConfig = Fit.Core.Clone(editorConfig); // Clone to prevent external code from making changes later
|
|
23663
|
+
}
|
|
23664
|
+
|
|
23665
|
+
var autoOpen = designEditorConfig.DialogMode.AutoOpen === true;
|
|
23666
|
+
var dialogConfig = designEditorConfig.DialogMode;
|
|
23667
|
+
|
|
23668
|
+
delete designEditorConfig.DialogMode.AutoOpen;
|
|
23669
|
+
delete designEditorConfig.DialogMode;
|
|
23670
|
+
|
|
23671
|
+
designEditorConfig.Detachable = dialogConfig;
|
|
23672
|
+
|
|
23673
|
+
var dimOnMouseDown = { Width: -1, Height: -1 };
|
|
23674
|
+
|
|
23675
|
+
me._internal.Data("designmode", "true")
|
|
23676
|
+
me._internal.Data("dialogmode", "true")
|
|
23677
|
+
var div = document.createElement("div");
|
|
23678
|
+
designModeDialogModeContent = div; // TODO: Nullify if dialog mode is disabled
|
|
23679
|
+
div.tabIndex = 0;
|
|
23680
|
+
//me.GetDomElement().tabIndex = 0;
|
|
23681
|
+
me.GetDomElement().tabIndex = -1; // Necessary ?? Required to keep focus while opening detached editor perhaps ?
|
|
23682
|
+
|
|
23683
|
+
if (designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.MinimumHeight)
|
|
23684
|
+
{
|
|
23685
|
+
div.style.minHeight = designEditorConfig.AutoGrow.MinimumHeight.Value + (designEditorConfig.AutoGrow.MinimumHeight.Unit || "px");
|
|
23686
|
+
}
|
|
23687
|
+
if (designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.MaximumHeight)
|
|
23688
|
+
{
|
|
23689
|
+
div.style.maxHeight = designEditorConfig.AutoGrow.MaximumHeight.Value + (designEditorConfig.AutoGrow.MaximumHeight.Unit || "px");
|
|
23690
|
+
}
|
|
23691
|
+
|
|
23692
|
+
if (nativeResizableAvailable === true)
|
|
23693
|
+
{
|
|
23694
|
+
Fit.Events.AddHandler(div, Fit.Browser.IsTouchEnabled() ? "touchstart" : "mousedown", function(e)
|
|
23695
|
+
{
|
|
23696
|
+
dimOnMouseDown = { Width: div.offsetWidth, Height: div.offsetHeight };
|
|
23697
|
+
});
|
|
23698
|
+
|
|
23699
|
+
Fit.Events.AddHandler(div, Fit.Browser.IsTouchEnabled() ? "touchmove" : "mousemove", function(e)
|
|
23700
|
+
{
|
|
23701
|
+
// var ev = Fit.Events.GetEvent(e);
|
|
23702
|
+
|
|
23703
|
+
// if (ev.buttons !== 1) // The .buttons property does not exist in older browsers (see nativeResizableAvailable)
|
|
23704
|
+
// {
|
|
23705
|
+
// return; // Skip - primary button not held down - not resizing
|
|
23706
|
+
// }
|
|
23707
|
+
|
|
23708
|
+
if (Fit.Events.GetPointerState().Buttons.Primary === false && Fit.Events.GetPointerState().Buttons.Touch === false)
|
|
23709
|
+
{
|
|
23710
|
+
return; // Skip - primary button not held down - not resizing
|
|
23711
|
+
}
|
|
23712
|
+
|
|
23713
|
+
if (me.Resizable() !== Fit.Controls.InputResizing.Disabled && (div.style.width !== "" || div.style.height !== "")) // Div was resized
|
|
23714
|
+
{
|
|
23715
|
+
me._internal.Data("resized", "true");
|
|
23716
|
+
}
|
|
23717
|
+
});
|
|
23718
|
+
}
|
|
23719
|
+
|
|
23720
|
+
// Create synthetic #touchtap" event which executes on touchend, but only if
|
|
23721
|
+
// finger did not travel more than 10px. Otherwise a slide across div will open it which is annoying.
|
|
23722
|
+
|
|
23723
|
+
Fit.Events.AddHandler(div, Fit.Browser.IsTouchEnabled() ? "touchend" : "click", function(e)
|
|
23724
|
+
{
|
|
23725
|
+
console.log("Touch 1");
|
|
23726
|
+
|
|
23727
|
+
if (nativeResizableAvailable === true)
|
|
23728
|
+
{
|
|
23729
|
+
var newDim = { Width: div.offsetWidth, Height: div.offsetHeight };
|
|
23730
|
+
|
|
23731
|
+
console.log(dimOnMouseDown, newDim);
|
|
23732
|
+
if (Fit.Core.IsEqual(dimOnMouseDown, newDim) === false)
|
|
23733
|
+
{
|
|
23734
|
+
return;
|
|
23735
|
+
}
|
|
23736
|
+
}
|
|
23737
|
+
|
|
23738
|
+
console.log("Touch 2");
|
|
23739
|
+
|
|
23740
|
+
var target = Fit.Events.GetTarget(e);
|
|
23741
|
+
if (target.tagName !== "A" && me.Enabled() === true)
|
|
23742
|
+
{
|
|
23743
|
+
console.log("Touch 3");
|
|
23744
|
+
openDetachedDesignEditor();
|
|
23745
|
+
}
|
|
23746
|
+
});
|
|
23747
|
+
div.innerHTML = me.Value();
|
|
23748
|
+
Fit.Dom.InsertAfter(input, div);
|
|
23749
|
+
|
|
23750
|
+
designModeDialogMode = {
|
|
23751
|
+
DomElement: div,
|
|
23752
|
+
AutoOpen: autoOpen,
|
|
23753
|
+
Reload: function()
|
|
23754
|
+
{
|
|
23755
|
+
designEditorDetached.Reload();
|
|
23756
|
+
},
|
|
23757
|
+
Dispose: function()
|
|
23758
|
+
{
|
|
23759
|
+
designEditorDetached.Dispose();
|
|
23760
|
+
|
|
23761
|
+
me._internal.Data("designmode", "false")
|
|
23762
|
+
me._internal.Data("dialogmode", null)
|
|
23763
|
+
Fit.Dom.Remove(designModeDialogMode.DomElement);
|
|
23764
|
+
|
|
23765
|
+
designModeDialogMode = null;
|
|
23766
|
+
}
|
|
23767
|
+
};
|
|
23768
|
+
|
|
23769
|
+
// if (autoOpen)
|
|
23770
|
+
// {
|
|
23771
|
+
// me.Focused(true);
|
|
23772
|
+
// openDetachedDesignEditor();
|
|
23773
|
+
// }
|
|
23774
|
+
|
|
23775
|
+
var keyDownEventId = Fit.Events.AddHandler(me.GetDomElement(), "keydown", function(e)
|
|
23776
|
+
{
|
|
23777
|
+
var ev = Fit.Events.GetEvent(e);
|
|
23778
|
+
var target = Fit.Events.GetTarget(e);
|
|
23779
|
+
|
|
23780
|
+
if (ev.keyCode === 13 && target.tagName !== "A" && me.Enabled() === true)
|
|
23781
|
+
{
|
|
23782
|
+
openDetachedDesignEditor();
|
|
23783
|
+
}
|
|
23784
|
+
});
|
|
23785
|
+
|
|
23786
|
+
// TODO: Remove if disposed !!!
|
|
23787
|
+
var handlerId = Fit.Events.AddHandler(me.GetDomElement(), "#rooted", function()
|
|
23788
|
+
{
|
|
23789
|
+
Fit.Events.RemoveHandler(me.GetDomElement(), handlerId);
|
|
23790
|
+
|
|
23791
|
+
if (designModeDialogMode !== null && designModeDialogMode.AutoOpen === true)
|
|
23792
|
+
{
|
|
23793
|
+
openDetachedDesignEditor();
|
|
23794
|
+
|
|
23795
|
+
// designEditorDetached.DisposeCallback = function()
|
|
23796
|
+
// {
|
|
23797
|
+
// Fit.Events.RemoveHandler(me.GetDomElement(), keyDownEventId);
|
|
23798
|
+
// };
|
|
23799
|
+
}
|
|
23800
|
+
});
|
|
23801
|
+
|
|
23802
|
+
updateDesignEditorPlaceholder();
|
|
23803
|
+
|
|
23804
|
+
return true;
|
|
23805
|
+
}
|
|
23806
|
+
else if (val === false && designModeDialogMode !== null)
|
|
23807
|
+
{
|
|
23808
|
+
designModeDialogMode.Dispose();
|
|
23809
|
+
|
|
23810
|
+
return false;
|
|
23811
|
+
}
|
|
23812
|
+
|
|
23439
23813
|
var designMode = (me._internal.Data("designmode") === "true");
|
|
23440
23814
|
|
|
23441
23815
|
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me && Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed === true)
|
|
@@ -23469,6 +23843,8 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23469
23843
|
designEditorConfig = Fit.Core.Clone(editorConfig); // Clone to prevent external code from making changes later
|
|
23470
23844
|
}
|
|
23471
23845
|
|
|
23846
|
+
// Was here
|
|
23847
|
+
|
|
23472
23848
|
// Notice: Identical logic found in Value(..)!
|
|
23473
23849
|
if (designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeExternalBlobUrlsOnDispose === true)
|
|
23474
23850
|
{
|
|
@@ -23907,7 +24283,7 @@ Fit.Controls.Input = function(ctlId)
|
|
|
23907
24283
|
}
|
|
23908
24284
|
}
|
|
23909
24285
|
|
|
23910
|
-
return (me._internal.Data("designmode") === "true");
|
|
24286
|
+
return (me._internal.Data("designmode") === "true" || designModeDialogMode !== null);
|
|
23911
24287
|
}
|
|
23912
24288
|
|
|
23913
24289
|
/// <function container="Fit.Controls.Input" name="DebounceOnChange" access="public" returns="integer">
|
|
@@ -24413,6 +24789,8 @@ Fit.Controls.Input = function(ctlId)
|
|
|
24413
24789
|
{
|
|
24414
24790
|
instanceReady: function()
|
|
24415
24791
|
{
|
|
24792
|
+
//if (Fit.Dom.Data(Fit.Dom.GetFocused(), "dialogmode") === "true") debugger;
|
|
24793
|
+
|
|
24416
24794
|
designEditorDom = // Object assignment will make designModeEnabledAndReady() return True, so it must be assigned immediately
|
|
24417
24795
|
{
|
|
24418
24796
|
OuterContainer: designEditor.container.$,
|
|
@@ -24770,12 +25148,3748 @@ Fit.Controls.Input = function(ctlId)
|
|
|
24770
25148
|
},
|
|
24771
25149
|
doubleclick: function(ev)
|
|
24772
25150
|
{
|
|
25151
|
+
// Suppress link dialog when double clicking. Use link button instead which
|
|
25152
|
+
// triggers beforeCommandExec below which creates a focus lock to prevent control
|
|
25153
|
+
// from losing focus and firing OnBlur.
|
|
25154
|
+
if (ev.data.element.$.tagName === "A")
|
|
25155
|
+
{
|
|
25156
|
+
ev.cancel();
|
|
25157
|
+
return;
|
|
25158
|
+
}
|
|
25159
|
+
|
|
24773
25160
|
// Suppress link dialog for tags (similar code found in beforeCommandExec handler below)
|
|
24774
|
-
|
|
25161
|
+
// DISABLED: No longer needed since all links now suppress the opening of the link dialog (see above)
|
|
25162
|
+
/*if (Fit.Dom.Data(ev.data.element.$, "tag-id") !== null)
|
|
25163
|
+
{
|
|
25164
|
+
ev.cancel();
|
|
25165
|
+
return;
|
|
25166
|
+
}*/
|
|
25167
|
+
},
|
|
25168
|
+
paste: function(ev)
|
|
25169
|
+
{
|
|
25170
|
+
// Prevent pasting (especially images) into tags.
|
|
25171
|
+
// OnPaste is suppressed using an OnPaste handler in capture phase, which will prevent the operation entirely
|
|
25172
|
+
// on supported browsers. On legacy browsers we handle this by invoking undo on the editor instance instead.
|
|
25173
|
+
//var path = ev.editor.elementPath(); // Null if dialog button is triggered without placing text cursor in editor first
|
|
25174
|
+
//if (Fit.Dom.Data(path.lastElement.$, "tag-id") !== null)
|
|
25175
|
+
if (designEditorSuppressPaste === true) // Also handled in a native OnPaste event handler (capture phase) for supported browsers, which suppresses the event entirely
|
|
25176
|
+
{
|
|
25177
|
+
setTimeout(function() // Postpone - allow editor to create snapshot
|
|
25178
|
+
{
|
|
25179
|
+
ev.editor.execCommand("undo"); // Undo change - paste event cannot be canceled, as it has already happened
|
|
25180
|
+
}, 0);
|
|
25181
|
+
return;
|
|
25182
|
+
}
|
|
25183
|
+
},
|
|
25184
|
+
beforeCommandExec: function(ev)
|
|
25185
|
+
{
|
|
25186
|
+
// Suppress any command (formatting, link dialog etc.) for tags (similar code found in doubleclick handler above).
|
|
25187
|
+
// Commmands can be triggered in multiple ways, e.g. using toolbar buttons, using keyboard shortcuts, and programmatically.
|
|
25188
|
+
var path = ev.editor.elementPath(); // Null if dialog button is triggered without placing text cursor in editor first
|
|
25189
|
+
if (path === null && ev.editor.getData().indexOf("<p><a data-tag-id=") === 0)
|
|
25190
|
+
{
|
|
25191
|
+
// Text cursor has not been placed in editor, but a command such as Bold or "insert image"
|
|
25192
|
+
// has been triggered, and editor content starts with a tag. This results in command being
|
|
25193
|
+
// applied to the tag, which we do not want. Usually this is prevented by the toolbar being
|
|
25194
|
+
// disabled when a tag is selected (see selectionChange event handler further up), but that
|
|
25195
|
+
// is not the case when the user has not yet placed the cursor in the editor.
|
|
25196
|
+
ev.cancel();
|
|
25197
|
+
return;
|
|
25198
|
+
}
|
|
25199
|
+
else if (path !== null && Fit.Dom.Data(path.lastElement.$, "tag-id") !== null && ev.data.name !== "undo") // Allow undo within tag, in case user typed something by mistake
|
|
25200
|
+
{
|
|
25201
|
+
// Cursor is currently placed in a tag - do not allow formatting
|
|
25202
|
+
ev.cancel();
|
|
25203
|
+
return;
|
|
25204
|
+
}
|
|
25205
|
+
|
|
25206
|
+
if (ev && ev.data && ev.data.command && ev.data.command.dialogName)
|
|
25207
|
+
{
|
|
25208
|
+
// Command triggered was a dialog
|
|
25209
|
+
|
|
25210
|
+
// IE9-IE11 does not fire OnFocus when user clicks a dialog button directly,
|
|
25211
|
+
// without placing the text cursor in the editing area first. To avoid this
|
|
25212
|
+
// problem, we simply ignore dialog commands if control does not already
|
|
25213
|
+
// have focus. We target all versions of IE for consistency.
|
|
25214
|
+
if (me.Focused() === false && Fit.Browser.GetBrowser() === "MSIE")
|
|
25215
|
+
{
|
|
25216
|
+
ev.cancel();
|
|
25217
|
+
return;
|
|
25218
|
+
}
|
|
25219
|
+
|
|
25220
|
+
// Prevent multiple control instances from opening a dialog at the same time.
|
|
25221
|
+
// This is very unlikely to happen, as it requires the second dialog to be
|
|
25222
|
+
// triggered programmatically, since a modal layer is immediately placed on top
|
|
25223
|
+
// of the page when clicking a button that opens a dialog, preventing additional
|
|
25224
|
+
// interaction with editors.
|
|
25225
|
+
// Naturally conflicting CSS causing the modal layer to remain hidden could
|
|
25226
|
+
// allow the user to trigger multiple dialogs. Better safe than sorry.
|
|
25227
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog)
|
|
25228
|
+
{
|
|
25229
|
+
ev.cancel();
|
|
25230
|
+
return;
|
|
25231
|
+
}
|
|
25232
|
+
|
|
25233
|
+
// Make sure OnFocus fires before locking focus state
|
|
25234
|
+
|
|
25235
|
+
if (me.Focused() === false)
|
|
25236
|
+
{
|
|
25237
|
+
// Control not focused - make sure OnFocus fires when a button is clicked,
|
|
25238
|
+
// and make sure ControlBase internally considers itself focused, so there is
|
|
25239
|
+
// no risk of OnFocus being fired twice without OnBlur firing in between,
|
|
25240
|
+
// when focus state is unlocked, and focus is perhaps re-assigned to another
|
|
25241
|
+
// DOM element within the control, which will be the case if the design editor
|
|
25242
|
+
// is switched back to an ordinary input field (e.g. using DesignMode(false)).
|
|
25243
|
+
me.Focused(true);
|
|
25244
|
+
}
|
|
25245
|
+
|
|
25246
|
+
// Prevent control from firing OnBlur when dialogs are opened.
|
|
25247
|
+
// Notice that locking the focus state will also prevent OnFocus
|
|
25248
|
+
// from being fired automatically.
|
|
25249
|
+
me._internal.FocusStateLocked(true);
|
|
25250
|
+
|
|
25251
|
+
// Make control available to global dialog event handlers which
|
|
25252
|
+
// cannot access individual control instances otherwise.
|
|
25253
|
+
|
|
25254
|
+
Fit._internal.Controls.Input.ActiveEditorForDialog = me; // Editor instance is needed when OnHide event is fired for dialog on global CKEditor instance
|
|
25255
|
+
Fit._internal.Controls.Input.ActiveDialogForEditor = null; // Dialog instance associated with editor will be set when dialog's OnShow event fires
|
|
25256
|
+
}
|
|
25257
|
+
}
|
|
25258
|
+
}
|
|
25259
|
+
});
|
|
25260
|
+
}
|
|
25261
|
+
|
|
25262
|
+
function disableDesignEditorButtons() // Might be called multiple times, e.g. if navigating from one tag/mention to another - buttons must be disabled every time since CKEditor itself re-enable buttons when navigating elements in editor
|
|
25263
|
+
{
|
|
25264
|
+
var preserveButtonState = designEditorRestoreButtonState === null;
|
|
25265
|
+
|
|
25266
|
+
if (preserveButtonState === true)
|
|
25267
|
+
{
|
|
25268
|
+
designEditorRestoreButtonState = {};
|
|
25269
|
+
}
|
|
25270
|
+
|
|
25271
|
+
Fit.Array.ForEach(designEditor.toolbar, function(toolbarGroup)
|
|
25272
|
+
{
|
|
25273
|
+
var items = toolbarGroup.items;
|
|
25274
|
+
|
|
25275
|
+
Fit.Array.ForEach(toolbarGroup.items, function(item)
|
|
25276
|
+
{
|
|
25277
|
+
if (item.command) // Buttons have a command identifier which can be used to resolve the actual command instance
|
|
25278
|
+
{
|
|
25279
|
+
var cmd = designEditor.getCommand(item.command);
|
|
25280
|
+
|
|
25281
|
+
if (preserveButtonState === true && cmd.state !== CKEDITOR.TRISTATE_DISABLED) // https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_command.html#property-state
|
|
25282
|
+
{
|
|
25283
|
+
designEditorRestoreButtonState[item.command] = true;
|
|
25284
|
+
}
|
|
25285
|
+
|
|
25286
|
+
cmd.disable();
|
|
25287
|
+
}
|
|
25288
|
+
else if (item.setState) // MenuButtons allow for direct manipulation of enabled/disabled state
|
|
25289
|
+
{
|
|
25290
|
+
if (preserveButtonState === true && item.getState() !== CKEDITOR.TRISTATE_DISABLED) // https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_command.html#property-state
|
|
25291
|
+
{
|
|
25292
|
+
designEditorRestoreButtonState[item.name] = item;
|
|
25293
|
+
}
|
|
25294
|
+
|
|
25295
|
+
item.setState(CKEDITOR.TRISTATE_DISABLED);
|
|
25296
|
+
}
|
|
25297
|
+
});
|
|
25298
|
+
});
|
|
25299
|
+
}
|
|
25300
|
+
|
|
25301
|
+
function restoreDesignEditorButtons()
|
|
25302
|
+
{
|
|
25303
|
+
if (designEditorRestoreButtonState !== null)
|
|
25304
|
+
{
|
|
25305
|
+
Fit.Array.ForEach(designEditorRestoreButtonState, function(commandKey)
|
|
25306
|
+
{
|
|
25307
|
+
if (designEditorRestoreButtonState[commandKey] === true) // Command button
|
|
25308
|
+
{
|
|
25309
|
+
var cmd = designEditor.getCommand(commandKey);
|
|
25310
|
+
cmd.enable();
|
|
25311
|
+
}
|
|
25312
|
+
else // MenuButton
|
|
25313
|
+
{
|
|
25314
|
+
designEditorRestoreButtonState[commandKey].setState(CKEDITOR.TRISTATE_OFF); // Enabled but not highlighted/activated like e.g. a bold button would be when selecting bold text
|
|
25315
|
+
}
|
|
25316
|
+
});
|
|
25317
|
+
|
|
25318
|
+
designEditorRestoreButtonState = null;
|
|
25319
|
+
}
|
|
25320
|
+
};
|
|
25321
|
+
|
|
25322
|
+
function updateDesignEditorSize()
|
|
25323
|
+
{
|
|
25324
|
+
if (me.DesignMode() === true && designModeDialogMode === null)
|
|
25325
|
+
{
|
|
25326
|
+
if (designEditorUpdateSizeDebouncer !== -1)
|
|
25327
|
+
{
|
|
25328
|
+
clearTimeout(designEditorUpdateSizeDebouncer);
|
|
25329
|
+
designEditorUpdateSizeDebouncer = -1;
|
|
25330
|
+
}
|
|
25331
|
+
|
|
25332
|
+
if (designModeEnabledAndReady() === false)
|
|
25333
|
+
{
|
|
25334
|
+
// Postpone, editor is not ready yet.
|
|
25335
|
+
// This may happen when editor is created and Width(..) is
|
|
25336
|
+
// immediately set after creating and mounting the control.
|
|
25337
|
+
// https://github.com/Jemt/Fit.UI/issues/34
|
|
25338
|
+
// This is a problem because CKEditor uses setTimeout(..) to for instance
|
|
25339
|
+
// allow early registration of events, and because resources are loaded
|
|
25340
|
+
// in an async. manner.
|
|
25341
|
+
designEditorUpdateSizeDebouncer = setTimeout(function()
|
|
25342
|
+
{
|
|
25343
|
+
designEditorUpdateSizeDebouncer = -1;
|
|
25344
|
+
updateDesignEditorSize();
|
|
25345
|
+
}, 100);
|
|
25346
|
+
|
|
25347
|
+
return;
|
|
25348
|
+
}
|
|
25349
|
+
|
|
25350
|
+
//var w = me.Width();
|
|
25351
|
+
var h = me.Height();
|
|
25352
|
+
|
|
25353
|
+
// Default control width is 200px (defined in Styles.css).
|
|
25354
|
+
// NOTICE: resize does not work reliably when editor is hidden, e.g. behind a tab with display:none.
|
|
25355
|
+
// The height set will not have the height of the toolbar substracted since the height can not be
|
|
25356
|
+
// determined for hidden objects, so the editor will become larger than the value set (height specified + toolbar height).
|
|
25357
|
+
// http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-resize
|
|
25358
|
+
designEditorSuppressOnResize = true;
|
|
25359
|
+
designEditor.resize("100%", h.Value > -1 ? h.Value + h.Unit : "100%"); // A height of 100% allow editor to automatically adjust the height of the editor's content area to the height of its content (data-autogrow="true" must be set to make control container adjust to its content as well)
|
|
25360
|
+
designEditorSuppressOnResize = false;
|
|
25361
|
+
|
|
25362
|
+
// Set mutation observer responsible for updating editor size once it becomes visible
|
|
25363
|
+
|
|
25364
|
+
if (mutationObserverId !== -1) // Cancel any mutation observer previously registered
|
|
25365
|
+
{
|
|
25366
|
+
Fit.Events.RemoveMutationObserver(mutationObserverId);
|
|
25367
|
+
mutationObserverId = -1;
|
|
25368
|
+
}
|
|
25369
|
+
|
|
25370
|
+
var concealer = Fit.Dom.GetConcealer(me.GetDomElement()); // Get element hiding editor
|
|
25371
|
+
|
|
25372
|
+
if (concealer !== null) // Editor is hidden - adjust size when it becomes visible
|
|
25373
|
+
{
|
|
25374
|
+
mutationObserverId = Fit.Events.AddMutationObserver(concealer, function(elm)
|
|
25375
|
+
{
|
|
25376
|
+
if (Fit.Dom.IsVisible(me.GetDomElement()) === true)
|
|
25377
|
+
{
|
|
25378
|
+
designEditorSuppressOnResize = true;
|
|
25379
|
+
designEditor.resize("100%", h.Value > -1 ? h.Value + h.Unit : "100%"); // A height of 100% allow editor to automatically adjust the height of the editor's content area to the height of its content (data-autogrow="true" must be set to make control container adjust to its content as well)
|
|
25380
|
+
designEditorSuppressOnResize = false;
|
|
25381
|
+
|
|
25382
|
+
disconnect(); // Observers are expensive - remove when no longer needed
|
|
25383
|
+
}
|
|
25384
|
+
});
|
|
25385
|
+
}
|
|
25386
|
+
}
|
|
25387
|
+
}
|
|
25388
|
+
|
|
25389
|
+
function isToolbarHiddenInDesignEditor() // Returns True if editor is fully loaded and toolbar is hidden
|
|
25390
|
+
{
|
|
25391
|
+
var toolbarContainer = designModeEnabledAndReady() === true ? designEditorDom.Top || designEditorDom.Bottom : null; // Top is null if editor is placed at the bottom
|
|
25392
|
+
return (toolbarContainer !== null && toolbarContainer.style.display === "none");
|
|
25393
|
+
}
|
|
25394
|
+
|
|
25395
|
+
function hideToolbarInDesignMode() // Editor must be fully loaded before calling this function!
|
|
25396
|
+
{
|
|
25397
|
+
if (designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.HideInitially === true)
|
|
25398
|
+
{
|
|
25399
|
+
var toolbarContainer = designEditorDom.Top || designEditorDom.Bottom; // Top is null if editor is placed at the bottom
|
|
25400
|
+
|
|
25401
|
+
if (toolbarContainer.style.display === "none")
|
|
25402
|
+
{
|
|
25403
|
+
return; // Already hidden
|
|
25404
|
+
}
|
|
25405
|
+
|
|
25406
|
+
// Prevent editor from increasing its height when toolbar is shown.
|
|
25407
|
+
// This is not ideal. We use the top/bottom's (toolbar's) height but it might change
|
|
25408
|
+
// if window is resized, which will cause buttons to "word wrap". But that is
|
|
25409
|
+
// acceptable. In this case the editor might change dimensions when toolbar is
|
|
25410
|
+
// shown and static height on content area is removed in OnFocus handler registered
|
|
25411
|
+
// in init().
|
|
25412
|
+
|
|
25413
|
+
var content = designEditorDom.Editable;
|
|
25414
|
+
content.style.height = toolbarContainer.offsetHeight + content.offsetHeight + "px";
|
|
25415
|
+
|
|
25416
|
+
// Hide toolbar
|
|
25417
|
+
|
|
25418
|
+
toolbarContainer.style.display = "none";
|
|
25419
|
+
|
|
25420
|
+
// Make editable area adjust to take up space previously consumed by toolbar
|
|
25421
|
+
updateDesignEditorSize();
|
|
25422
|
+
}
|
|
25423
|
+
}
|
|
25424
|
+
|
|
25425
|
+
function restoreHiddenToolbarInDesignEditor()
|
|
25426
|
+
{
|
|
25427
|
+
if (designModeEnabledAndReady() === true && designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.HideInitially === true)
|
|
25428
|
+
{
|
|
25429
|
+
// Toolbar has been initially hidden - make it appear again
|
|
25430
|
+
|
|
25431
|
+
var toolbarContainer = designEditorDom.Top || designEditorDom.Bottom; // Top is null if editor is placed at the bottom
|
|
25432
|
+
|
|
25433
|
+
if (toolbarContainer.style.display === "")
|
|
25434
|
+
{
|
|
25435
|
+
return; // Already restored - no longer hidden
|
|
25436
|
+
}
|
|
25437
|
+
|
|
25438
|
+
toolbarContainer.style.display = "";
|
|
25439
|
+
|
|
25440
|
+
// Hiding the toolbar will cause the editor to decrease its height, while displaying the toolbar again
|
|
25441
|
+
// will cause it to increase its height. To avoid this "flickering" a fixed height (toolbar height + content height)
|
|
25442
|
+
// was applied when toolbar was hidden. But now that the toolbar is once again visible, we remove the fixed height
|
|
25443
|
+
// again - otherwise resizing and auto grow will not work.
|
|
25444
|
+
|
|
25445
|
+
var content = designEditorDom.Editable;
|
|
25446
|
+
content.style.height = "";
|
|
25447
|
+
|
|
25448
|
+
me._internal.Data("toolbar", "true");
|
|
25449
|
+
|
|
25450
|
+
// Update size of editable area in case auto grow is not enabled, in which case
|
|
25451
|
+
// toolbar will now have taken up space outside of control's container (overflowing).
|
|
25452
|
+
// Make editable area fit control container again.
|
|
25453
|
+
updateDesignEditorSize();
|
|
25454
|
+
}
|
|
25455
|
+
}
|
|
25456
|
+
|
|
25457
|
+
function updateDesignEditorPlaceholder(clearPlaceholder)
|
|
25458
|
+
{
|
|
25459
|
+
Fit.Validation.ExpectBoolean(clearPlaceholder, true);
|
|
25460
|
+
|
|
25461
|
+
if (designModeDialogModeContent !== null)
|
|
25462
|
+
{
|
|
25463
|
+
var val = clearPlaceholder !== true && me.Value() === "" ? me.Placeholder() : "";
|
|
25464
|
+
Fit.Dom.Data(designModeDialogMode.DomElement, "placeholder", val || null);
|
|
25465
|
+
}
|
|
25466
|
+
else if (designModeEnabledAndReady() === true)
|
|
25467
|
+
{
|
|
25468
|
+
if (Fit.Browser.GetBrowser() === "MSIE" && Fit.Browser.GetVersion() < 10)
|
|
25469
|
+
{
|
|
25470
|
+
// Native support for placeholders (using the real placeholder attribute) was
|
|
25471
|
+
// introduced in IE10, so we want to ensure consistent behaviour for all controls,
|
|
25472
|
+
// as e.g. Input and DatePicker uses the native placeholder implementation.
|
|
25473
|
+
return;
|
|
25474
|
+
}
|
|
25475
|
+
|
|
25476
|
+
// WARNING: Retrieving value from editor is expensive! Do not
|
|
25477
|
+
// call updateDesignEditorPlaceholder() too often (e.g. OnChange).
|
|
25478
|
+
// Simply make sure placeholder is updated OnFocus and OnBlur.
|
|
25479
|
+
|
|
25480
|
+
var val = clearPlaceholder !== true && me.Value() === "" ? me.Placeholder() : "";
|
|
25481
|
+
Fit.Dom.Data(designEditorDom.Editable, "placeholder", val || null);
|
|
25482
|
+
}
|
|
25483
|
+
}
|
|
25484
|
+
|
|
25485
|
+
function openDetachedDesignEditor()
|
|
25486
|
+
{
|
|
25487
|
+
// TODO:
|
|
25488
|
+
// - OK: input.AddValidationRule mappes ikke til detached editor
|
|
25489
|
+
// - OK: input.AutoPostBack(false) fejler når dialogen er åben (DialogMode)
|
|
25490
|
+
// - data-dialogmode="true" er ikke konsistent i sin tilstedeværelse - by design ?
|
|
25491
|
+
// - OK: input.Placeholder("Hejsa") har ingen effekt i DialogMode
|
|
25492
|
+
// - OK: input.AutoPostBack(true) virker ikke i detached editor
|
|
25493
|
+
// - Understøt reload af Dialog Mode med DesignMode(true, newUpdatedConfig) ??
|
|
25494
|
+
// - OK: input.Type() returnerer Textarea for dialog mode
|
|
25495
|
+
// - input.Visible(boolean) viser/skjuler ikke dialogen
|
|
25496
|
+
// - input.Maximizable(true) vises men positioneres forkert og virker ikke
|
|
25497
|
+
// - input.Resizable("enabled") viser ingen resize handle
|
|
25498
|
+
// - input.MultiLine(true) fejler
|
|
25499
|
+
|
|
25500
|
+
me._internal.FocusStateLocked(true);
|
|
25501
|
+
|
|
25502
|
+
var deConfig = null;
|
|
25503
|
+
|
|
25504
|
+
var updateDetachedConfiguration = function()
|
|
25505
|
+
{
|
|
25506
|
+
deConfig = Fit.Core.Clone(designEditorConfig || {});
|
|
25507
|
+
|
|
25508
|
+
// Configure detached editor like original, but with a few required changes.
|
|
25509
|
+
// We need to make sure image blobs are handled properly, that detached editor
|
|
25510
|
+
// cannot created another detached editor, that the toolbar is initially visible
|
|
25511
|
+
// at the top of the dialog, and that auto grow is disabled.
|
|
25512
|
+
|
|
25513
|
+
if (designModeEnableImagePlugin() === true)
|
|
25514
|
+
{
|
|
25515
|
+
deConfig = Fit.Core.Merge(deConfig, // Override image plugin configuration
|
|
25516
|
+
{
|
|
25517
|
+
Plugins:
|
|
25518
|
+
{
|
|
25519
|
+
Images:
|
|
25520
|
+
{
|
|
25521
|
+
Enabled: true,
|
|
25522
|
+
EmbedType: deConfig.Plugins && deConfig.Plugins.Images && deConfig.Plugins.Images.EmbedType,
|
|
25523
|
+
|
|
25524
|
+
// Image blobs added in detached editor must always be disposed if no longer referenced.
|
|
25525
|
+
// Furthermore we make sure image blobs originating from main editor are never disposed.
|
|
25526
|
+
// When detached editor is closed, images transfered from detached editor to main editor
|
|
25527
|
+
// are added to the main editor's index over image blobs so that the main editor becomes
|
|
25528
|
+
// responsible for the memory management of these.
|
|
25529
|
+
// If detached editor is closed without transfering changes (canceled), all images found
|
|
25530
|
+
// in the detached editor, which are not referenced in the main editor, are disposed.
|
|
25531
|
+
// See OnClick handlers for OK and Cancel buttons.
|
|
25532
|
+
|
|
25533
|
+
RevokeBlobUrlsOnDispose: "UnreferencedOnly", // Make dialog editor preserve newly added (and still referenced) image blobs when disposed
|
|
25534
|
+
RevokeExternalBlobUrlsOnDispose: false // Make dialog editor preserve images blobs initially added from main editor
|
|
25535
|
+
}
|
|
25536
|
+
}
|
|
25537
|
+
});
|
|
25538
|
+
}
|
|
25539
|
+
|
|
25540
|
+
deConfig.Toolbar = deConfig.Toolbar || {};
|
|
25541
|
+
deConfig.Toolbar.Detach = false;
|
|
25542
|
+
deConfig.Toolbar.Position = "Top";
|
|
25543
|
+
deConfig.Toolbar.Sticky = false;
|
|
25544
|
+
deConfig.Toolbar.HideInitially = false;
|
|
25545
|
+
|
|
25546
|
+
delete deConfig.AutoGrow;
|
|
25547
|
+
};
|
|
25548
|
+
|
|
25549
|
+
updateDetachedConfiguration();
|
|
25550
|
+
|
|
25551
|
+
// Create dialog and buttons
|
|
25552
|
+
|
|
25553
|
+
var dia = new Fit.Controls.Dialog();
|
|
25554
|
+
Fit.Dom.AddClass(dia.GetDomElement(), "FitUiControlInputDetached");
|
|
25555
|
+
|
|
25556
|
+
var cmdOk = new Fit.Controls.Button();
|
|
25557
|
+
var cmdCancel = new Fit.Controls.Button();
|
|
25558
|
+
|
|
25559
|
+
// Create editor
|
|
25560
|
+
|
|
25561
|
+
var de = new Fit.Controls.Input();
|
|
25562
|
+
|
|
25563
|
+
var setDetachedEditorSettings = function()
|
|
25564
|
+
{
|
|
25565
|
+
de.Width(100, "%");
|
|
25566
|
+
de.Height(-1);
|
|
25567
|
+
de.CheckSpelling(me.CheckSpelling());
|
|
25568
|
+
de.DesignMode(true, deConfig);
|
|
25569
|
+
};
|
|
25570
|
+
|
|
25571
|
+
setDetachedEditorSettings();
|
|
25572
|
+
de.Value(me.Value());
|
|
25573
|
+
|
|
25574
|
+
// Make editor adjust to the dimensions of the dialog
|
|
25575
|
+
|
|
25576
|
+
// Adjust editor size to fit dialog using a timer.
|
|
25577
|
+
// Alternatively expose an OnResize event on Fit.Controls.Dialog, use it in
|
|
25578
|
+
// combination with window.onresize, and adjust editor when these events are triggered.
|
|
25579
|
+
// However, the process of monitoring the dimensions using a timer is practically free,
|
|
25580
|
+
// so even though the timer interupts browser events such as onmousemove, onscroll, etc.,
|
|
25581
|
+
// it creates no lack at all.
|
|
25582
|
+
var height = -1
|
|
25583
|
+
var dimMonitorId = setInterval(function()
|
|
25584
|
+
{
|
|
25585
|
+
if (height === -1 && de._internal.DesignModeEnabledAndReady() === false)
|
|
25586
|
+
{
|
|
25587
|
+
return;
|
|
25588
|
+
}
|
|
25589
|
+
|
|
25590
|
+
if (dia.IsOpen() === false)
|
|
25591
|
+
{
|
|
25592
|
+
return; // Closed because control has been set invisible
|
|
25593
|
+
}
|
|
25594
|
+
|
|
25595
|
+
var newHeight = dia.GetDomElement().offsetHeight;
|
|
25596
|
+
|
|
25597
|
+
if (newHeight !== height)
|
|
25598
|
+
{
|
|
25599
|
+
height = newHeight
|
|
25600
|
+
de.Height(Fit.Dom.GetInnerDimensions(dia.GetContentDomElement()).Height);
|
|
25601
|
+
}
|
|
25602
|
+
}, 250);
|
|
25603
|
+
|
|
25604
|
+
// Configure dialog
|
|
25605
|
+
|
|
25606
|
+
var setDialogSettings = function(update)
|
|
25607
|
+
{
|
|
25608
|
+
var detachConfig = deConfig.Detachable || {};
|
|
25609
|
+
detachConfig = Fit.Core.Merge(detachConfig, // Apply default values - existing properties are preserved (Fit.Core.MergeOverwriteBehaviour.Never)
|
|
25610
|
+
{
|
|
25611
|
+
Title: "",
|
|
25612
|
+
Maximizable: true,
|
|
25613
|
+
Maximized: false,
|
|
25614
|
+
Draggable: true,
|
|
25615
|
+
Resizable: true,
|
|
25616
|
+
Width: detachConfig.Width ? detachConfig.Width : { Value: 850, Unit: "px" },
|
|
25617
|
+
MinimumWidth: detachConfig.MinimumWidth ? detachConfig.MinimumWidth : { Value: 20, Unit: "em" },
|
|
25618
|
+
MaximumWidth: detachConfig.MaximumWidth ? detachConfig.MaximumWidth : { Value: 100, Unit: "%" },
|
|
25619
|
+
Height: detachConfig.Height ? detachConfig.Height : { Value: 550, Unit: "px" },
|
|
25620
|
+
MinimumHeight: detachConfig.MinimumHeight ? detachConfig.MinimumHeight : { Value: 12, Unit: "em" },
|
|
25621
|
+
MaximumHeight: detachConfig.MaximumHeight ? detachConfig.MaximumHeight : { Value: 100, Unit: "%" }
|
|
25622
|
+
}, Fit.Core.MergeOverwriteBehaviour.Never);
|
|
25623
|
+
|
|
25624
|
+
var updateDimensions = (!detachConfig.Resizable || update !== true);
|
|
25625
|
+
|
|
25626
|
+
dia.Title(detachConfig.Title);
|
|
25627
|
+
dia.Modal(true);
|
|
25628
|
+
dia.Draggable(detachConfig.Draggable);
|
|
25629
|
+
dia.Resizable(detachConfig.Resizable);
|
|
25630
|
+
dia.Maximizable(detachConfig.Maximizable);
|
|
25631
|
+
dia.Maximized(detachConfig.Maximized);
|
|
25632
|
+
updateDimensions === true && dia.Width(detachConfig.Width.Value, detachConfig.Width.Unit || "px");
|
|
25633
|
+
updateDimensions === true && dia.Height(detachConfig.Height.Value, detachConfig.Height.Unit || "px");
|
|
25634
|
+
dia.MinimumWidth(detachConfig.MinimumWidth.Value, detachConfig.MinimumWidth.Unit || "px");
|
|
25635
|
+
dia.MinimumHeight(detachConfig.MinimumHeight.Value, detachConfig.MinimumHeight.Unit || "px");
|
|
25636
|
+
dia.MaximumWidth(detachConfig.MaximumWidth.Value, detachConfig.MaximumWidth.Unit || "px");
|
|
25637
|
+
dia.MaximumHeight(detachConfig.MaximumHeight.Value, detachConfig.MaximumHeight.Unit || "px");
|
|
25638
|
+
};
|
|
25639
|
+
|
|
25640
|
+
setDialogSettings();
|
|
25641
|
+
|
|
25642
|
+
// Localization support
|
|
25643
|
+
|
|
25644
|
+
var localizeDetachedEditor = function()
|
|
25645
|
+
{
|
|
25646
|
+
// Editor itself is already localized, so we just need to
|
|
25647
|
+
// localize the dialog. The locale variable will already have
|
|
25648
|
+
// been updated by the OnLocaleChanged handler registered in init().
|
|
25649
|
+
|
|
25650
|
+
cmdOk.Title(locale.Ok);
|
|
25651
|
+
cmdCancel.Title(locale.Cancel);
|
|
25652
|
+
};
|
|
25653
|
+
Fit.Internationalization.OnLocaleChanged(localizeDetachedEditor);
|
|
25654
|
+
|
|
25655
|
+
// Expose detached editor API
|
|
25656
|
+
|
|
25657
|
+
designEditorDetached =
|
|
25658
|
+
{
|
|
25659
|
+
GetValue: function()
|
|
25660
|
+
{
|
|
25661
|
+
return de.Value();
|
|
25662
|
+
},
|
|
25663
|
+
|
|
25664
|
+
SetVisible: function(val)
|
|
25665
|
+
{
|
|
25666
|
+
if (val === true)
|
|
25667
|
+
{
|
|
25668
|
+
dia.Open();
|
|
25669
|
+
}
|
|
25670
|
+
else if (val === false)
|
|
25671
|
+
{
|
|
25672
|
+
dia.Close();
|
|
25673
|
+
}
|
|
25674
|
+
},
|
|
25675
|
+
|
|
25676
|
+
SetEnabled: function(val)
|
|
25677
|
+
{
|
|
25678
|
+
Fit.Validation.ExpectBoolean(val);
|
|
25679
|
+
|
|
25680
|
+
if (val === false)
|
|
25681
|
+
{
|
|
25682
|
+
de.Enabled(false);
|
|
25683
|
+
cmdOk.Enabled(false);
|
|
25684
|
+
cmdCancel.Focused(true);
|
|
25685
|
+
}
|
|
25686
|
+
else
|
|
25687
|
+
{
|
|
25688
|
+
de.Enabled(true);
|
|
25689
|
+
cmdOk.Enabled(true);
|
|
25690
|
+
de.Focused(true);
|
|
25691
|
+
}
|
|
25692
|
+
},
|
|
25693
|
+
|
|
25694
|
+
Focus: function()
|
|
25695
|
+
{
|
|
25696
|
+
if (de.Enabled() === true)
|
|
25697
|
+
{
|
|
25698
|
+
de.Focused(true);
|
|
25699
|
+
}
|
|
25700
|
+
else
|
|
25701
|
+
{
|
|
25702
|
+
cmdCancel.Focused(true);
|
|
25703
|
+
}
|
|
25704
|
+
},
|
|
25705
|
+
|
|
25706
|
+
GetFocused: function()
|
|
25707
|
+
{
|
|
25708
|
+
return de.Focused() === true /* also returns true if e.g. link/image dialog is open */
|
|
25709
|
+
|| cmdOk.Focused() === true || cmdCancel.Focused() === true;
|
|
25710
|
+
},
|
|
25711
|
+
|
|
25712
|
+
Reload: function()
|
|
25713
|
+
{
|
|
25714
|
+
// Update configuration
|
|
25715
|
+
updateDetachedConfiguration();
|
|
25716
|
+
|
|
25717
|
+
// Update editor
|
|
25718
|
+
setDetachedEditorSettings();
|
|
25719
|
+
|
|
25720
|
+
// Update dialog
|
|
25721
|
+
setDialogSettings(true);
|
|
25722
|
+
|
|
25723
|
+
// Make dimension monitor ensure proper editor height (dialog height might have been changed)
|
|
25724
|
+
height = -1;
|
|
25725
|
+
},
|
|
25726
|
+
|
|
25727
|
+
DisposeCallback: null,
|
|
25728
|
+
|
|
25729
|
+
Dispose: function()
|
|
25730
|
+
{
|
|
25731
|
+
clearInterval(dimMonitorId);
|
|
25732
|
+
Fit.Internationalization.RemoveOnLocaleChanged(localizeDetachedEditor);
|
|
25733
|
+
de.Dispose();
|
|
25734
|
+
dia.Dispose(); // Will also dispose associated buttons
|
|
25735
|
+
designEditorDetached.DisposeCallback && designEditorDetached.DisposeCallback();
|
|
25736
|
+
designEditorDetached = null;
|
|
25737
|
+
}
|
|
25738
|
+
};
|
|
25739
|
+
|
|
25740
|
+
cmdOk.Title(locale.Ok);
|
|
25741
|
+
cmdOk.Icon("check");
|
|
25742
|
+
cmdOk.Type(Fit.Controls.ButtonType.Success);
|
|
25743
|
+
cmdOk.OnClick(function(sender)
|
|
25744
|
+
{
|
|
25745
|
+
var referencedBlobUrls = Fit.String.ParseImageBlobUrls(de.Value());
|
|
25746
|
+
Fit.Array.ForEach(referencedBlobUrls, function(blobUrl)
|
|
25747
|
+
{
|
|
25748
|
+
if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
|
|
25749
|
+
{
|
|
25750
|
+
Fit.Array.Add(imageBlobUrls, blobUrl);
|
|
25751
|
+
}
|
|
25752
|
+
});
|
|
25753
|
+
|
|
25754
|
+
me.Value(de.Value());
|
|
25755
|
+
|
|
25756
|
+
designEditorDetached.Dispose();
|
|
25757
|
+
|
|
25758
|
+
me.Focused(true);
|
|
25759
|
+
me._internal.FocusStateLocked(false);
|
|
25760
|
+
});
|
|
25761
|
+
dia.AddButton(cmdOk);
|
|
25762
|
+
|
|
25763
|
+
cmdCancel.Title(locale.Cancel);
|
|
25764
|
+
cmdCancel.Icon("ban");
|
|
25765
|
+
cmdCancel.Type(Fit.Controls.ButtonType.Danger);
|
|
25766
|
+
cmdCancel.OnClick(function(sender)
|
|
25767
|
+
{
|
|
25768
|
+
var closeDialog = function()
|
|
25769
|
+
{
|
|
25770
|
+
var referencedBlobUrls = Fit.String.ParseImageBlobUrls(de.Value());
|
|
25771
|
+
Fit.Array.ForEach(referencedBlobUrls, function(blobUrl)
|
|
25772
|
+
{
|
|
25773
|
+
if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false) // Only remove images added in dialog editor
|
|
25774
|
+
{
|
|
25775
|
+
URL.revokeObjectURL(blobUrl);
|
|
25776
|
+
}
|
|
25777
|
+
});
|
|
25778
|
+
|
|
25779
|
+
var enabled = de.Enabled();
|
|
25780
|
+
designEditorDetached.Dispose(); // Dispose first so e.g. Focused(..) does not behave as if editor is still detached
|
|
25781
|
+
|
|
25782
|
+
if (enabled === true)
|
|
25783
|
+
{
|
|
25784
|
+
me.Focused(true);
|
|
25785
|
+
me._internal.FocusStateLocked(false);
|
|
25786
|
+
}
|
|
25787
|
+
else
|
|
25788
|
+
{
|
|
25789
|
+
me._internal.FocusStateLocked(false);
|
|
25790
|
+
me._internal.FireOnBlur();
|
|
25791
|
+
}
|
|
25792
|
+
};
|
|
25793
|
+
|
|
25794
|
+
if (de.IsDirty() === true)
|
|
25795
|
+
{
|
|
25796
|
+
Fit.Controls.Dialog.Confirm(locale.CancelConfirmTitle + "<br><br>" + locale.CancelConfirmDescription, function(res)
|
|
25797
|
+
{
|
|
25798
|
+
if (res === true)
|
|
25799
|
+
{
|
|
25800
|
+
closeDialog();
|
|
25801
|
+
}
|
|
25802
|
+
else
|
|
25803
|
+
{
|
|
25804
|
+
cmdCancel.Focused(true);
|
|
25805
|
+
}
|
|
25806
|
+
});
|
|
25807
|
+
}
|
|
25808
|
+
else
|
|
25809
|
+
{
|
|
25810
|
+
closeDialog();
|
|
25811
|
+
}
|
|
25812
|
+
});
|
|
25813
|
+
dia.AddButton(cmdCancel);
|
|
25814
|
+
|
|
25815
|
+
dia.Open();
|
|
25816
|
+
de.Render(dia.GetContentDomElement());
|
|
25817
|
+
window.focusingDetachedEditor = true; console.log("Focusing detached editor"); de.Focused(true); window.detachedEditorFocused = true;
|
|
25818
|
+
}
|
|
25819
|
+
|
|
25820
|
+
function revertToSingleLineIfNecessary()
|
|
25821
|
+
{
|
|
25822
|
+
if (wasAutoChangedToMultiLineMode === true && me.Maximizable() === false && me.Resizable() === Fit.Controls.InputResizing.Disabled && me.DesignMode() === false)
|
|
25823
|
+
{
|
|
25824
|
+
me.MultiLine(false); // Changes wasAutoChangedToMultiLineMode to false
|
|
25825
|
+
}
|
|
25826
|
+
}
|
|
25827
|
+
|
|
25828
|
+
function fireOnChange()
|
|
25829
|
+
{
|
|
25830
|
+
var newVal = me.Value();
|
|
25831
|
+
|
|
25832
|
+
if (newVal !== preVal)
|
|
25833
|
+
{
|
|
25834
|
+
// DISABLED: No longer necessary with the introduction of designEditorDirty which ensures
|
|
25835
|
+
// that we get the initial value set from Value(), unless changed by the user using the editor.
|
|
25836
|
+
/*if (designEditor !== null && htmlWrappedInParagraph === false) // A value not wrapped in paragraph(s) was assigned to HTML editor
|
|
25837
|
+
{
|
|
25838
|
+
// Do not trigger OnChange if the only difference is that CKEditor
|
|
25839
|
+
// wrapped the value initially assigned to control in a paragraph.
|
|
25840
|
+
// Only changes made programmatically through the Input control's API
|
|
25841
|
+
// or by the user should be pushed.
|
|
25842
|
+
// This approach is not perfect unfortunately. For instance CKEditor
|
|
25843
|
+
// trims the value, so assigning " hello world" or " <p>Hello world</p>"
|
|
25844
|
+
// to the control will result in OnChange firing if fireOnChange() is called.
|
|
25845
|
+
|
|
25846
|
+
var newValWithoutParagraph = newVal.replace(/^<p>/, "").replace(/<\/p>$/, ""); // Remove <p> and </p> at the beginning and end
|
|
25847
|
+
|
|
25848
|
+
if (newValWithoutParagraph === preVal)
|
|
25849
|
+
{
|
|
25850
|
+
return; // Do not fire OnChange
|
|
25851
|
+
}
|
|
25852
|
+
}*/
|
|
25853
|
+
|
|
25854
|
+
preVal = newVal;
|
|
25855
|
+
me._internal.FireOnChange();
|
|
25856
|
+
}
|
|
25857
|
+
}
|
|
25858
|
+
|
|
25859
|
+
function reloadEditor(force, reloadConfig)
|
|
25860
|
+
{
|
|
25861
|
+
Fit.Validation.ExpectBoolean(force, true);
|
|
25862
|
+
Fit.Validation.ExpectObject(reloadConfig, true); // Not validated further, as it has already been validated in DesignMode(..)
|
|
25863
|
+
|
|
25864
|
+
if (force !== true && (designModeEnabledAndReady() === false || designEditorMustReloadWhenReady === true))
|
|
25865
|
+
{
|
|
25866
|
+
// Attempting to reload editor while initializing - postpone until editor is fully loaded,
|
|
25867
|
+
// since we cannot guarantee reliable behavior with CKEditor if it's disposed while loading.
|
|
25868
|
+
designEditorMustReloadWhenReady = true;
|
|
25869
|
+
designEditorReloadConfig = reloadConfig || designEditorReloadConfig;
|
|
25870
|
+
return;
|
|
25871
|
+
}
|
|
25872
|
+
|
|
25873
|
+
designEditorMustReloadWhenReady = false;
|
|
25874
|
+
|
|
25875
|
+
// Disabling DesignMode brings it back to input or textarea mode.
|
|
25876
|
+
// If reverting to input mode, Height is reset, so we need to preserve that.
|
|
25877
|
+
|
|
25878
|
+
// NOTICE: Custom width/height set using resize handle is not preserved when editor is reloaded
|
|
25879
|
+
|
|
25880
|
+
var height = me.Height();
|
|
25881
|
+
var currentWasAutoChangedToMultiLineMode = wasAutoChangedToMultiLineMode; // DesignMode(false) will result in wasAutoChangedToMultiLineMode being set to false if DesignMode(true) changed the control to MultiLine mode
|
|
25882
|
+
|
|
25883
|
+
// Prevent detached editor from being closed when reloading, e.g. if
|
|
25884
|
+
// CheckSpelling is changed. Detached editor will be reloaded further down.
|
|
25885
|
+
var detachedEditor = null;
|
|
25886
|
+
if (designEditorDetached !== null)
|
|
25887
|
+
{
|
|
25888
|
+
detachedEditor = designEditorDetached;
|
|
25889
|
+
designEditorDetached = null; // Prevent me.DesignMode(false), which in turn calls destroyDesignEditorInstance(), from closing detached editor dialog
|
|
25890
|
+
}
|
|
25891
|
+
|
|
25892
|
+
me.DesignMode(false);
|
|
25893
|
+
me.DesignMode(true, reloadConfig || designEditorReloadConfig || undefined); // Use reloadConfig if set (and if reload was not postponed) or use designEditorReloadConfig if reload was postponed with updated editor config
|
|
25894
|
+
designEditorReloadConfig = null;
|
|
25895
|
+
|
|
25896
|
+
if (detachedEditor !== null)
|
|
25897
|
+
{
|
|
25898
|
+
designEditorDetached = detachedEditor;
|
|
25899
|
+
designEditorDetached.Reload(); // Reload detached editor to reflect any changes made to configuration
|
|
25900
|
+
}
|
|
25901
|
+
|
|
25902
|
+
me.Height(height.Value, height.Unit);
|
|
25903
|
+
wasAutoChangedToMultiLineMode = currentWasAutoChangedToMultiLineMode;
|
|
25904
|
+
}
|
|
25905
|
+
|
|
25906
|
+
function destroyDesignEditorInstance()
|
|
25907
|
+
{
|
|
25908
|
+
// Destroying editor also fires OnHide event for any dialog currently open, which will clean up:
|
|
25909
|
+
// Fit._internal.Controls.Input.ActiveEditorForDialog;
|
|
25910
|
+
// Fit._internal.Controls.Input.ActiveEditorForDialogDestroyed;
|
|
25911
|
+
// Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed;
|
|
25912
|
+
// Fit._internal.Controls.Input.ActiveDialogForEditor;
|
|
25913
|
+
// Fit._internal.Controls.Input.ActiveDialogForEditorCanceled;
|
|
25914
|
+
|
|
25915
|
+
// Calling destroy() fires OnHide for any dialog currently open, which
|
|
25916
|
+
// in turn disables locked focus state and returns focus to the control.
|
|
25917
|
+
|
|
25918
|
+
designEditor.destroy();
|
|
25919
|
+
|
|
25920
|
+
if (designEditorDetached !== null)
|
|
25921
|
+
{
|
|
25922
|
+
designEditorDetached.Dispose();
|
|
25923
|
+
}
|
|
25924
|
+
|
|
25925
|
+
designEditor = null;
|
|
25926
|
+
designEditorDom = null;
|
|
25927
|
+
//designEditorDirty = false; // Do NOT reset this! We need to preserve dirty state in case DesignMode is reloaded!
|
|
25928
|
+
designEditorDirtyPending = false;
|
|
25929
|
+
//designEditorConfig = null; // Do NOT nullify this! We need it, in case DesignMode is toggled!
|
|
25930
|
+
//designEditorReloadConfig = null; // Do NOT nullify this! We need it, in case DesignMode is reloaded!
|
|
25931
|
+
designEditorRestoreButtonState = null;
|
|
25932
|
+
designEditorSuppressPaste = false;
|
|
25933
|
+
designEditorSuppressOnResize = false;
|
|
25934
|
+
designEditorMustReloadWhenReady = false;
|
|
25935
|
+
designEditorMustDisposeWhenReady = false;
|
|
25936
|
+
designEditorActiveToolbarPanel = null;
|
|
25937
|
+
designEditorDetached = null;
|
|
25938
|
+
|
|
25939
|
+
if (designEditorUpdateSizeDebouncer !== -1)
|
|
25940
|
+
{
|
|
25941
|
+
clearTimeout(designEditorUpdateSizeDebouncer);
|
|
25942
|
+
designEditorUpdateSizeDebouncer = -1;
|
|
25943
|
+
}
|
|
25944
|
+
|
|
25945
|
+
if (mutationObserverId !== -1)
|
|
25946
|
+
{
|
|
25947
|
+
Fit.Events.RemoveMutationObserver(mutationObserverId);
|
|
25948
|
+
mutationObserverId = -1;
|
|
25949
|
+
}
|
|
25950
|
+
|
|
25951
|
+
if (rootedEventId !== -1)
|
|
25952
|
+
{
|
|
25953
|
+
Fit.Events.RemoveHandler(me.GetDomElement(), rootedEventId);
|
|
25954
|
+
rootedEventId = -1;
|
|
25955
|
+
}
|
|
25956
|
+
|
|
25957
|
+
if (createWhenReadyIntervalId !== -1)
|
|
25958
|
+
{
|
|
25959
|
+
clearInterval(createWhenReadyIntervalId);
|
|
25960
|
+
createWhenReadyIntervalId = -1;
|
|
25961
|
+
}
|
|
25962
|
+
}
|
|
25963
|
+
|
|
25964
|
+
function designModeEnabledAndReady()
|
|
25965
|
+
{
|
|
25966
|
+
return designEditorDom !== null; // Editor is fully loaded when editor DOM is made available
|
|
25967
|
+
}
|
|
25968
|
+
|
|
25969
|
+
function designModeEnableImagePlugin()
|
|
25970
|
+
{
|
|
25971
|
+
var config = designEditorConfig || {};
|
|
25972
|
+
var enableImagePlugin = (config.Plugins && config.Plugins.Images && config.Plugins.Images.Enabled === true) || (config.Toolbar && config.Toolbar.Images === true) || false;
|
|
25973
|
+
|
|
25974
|
+
// Force enable image support if images are contained in value - otherwise editor will remove them
|
|
25975
|
+
if (enableImagePlugin === false && designEditorDetached !== null && designEditorDetached.GetValue().indexOf("<img ") > -1)
|
|
25976
|
+
{
|
|
25977
|
+
enableImagePlugin = true;
|
|
25978
|
+
}
|
|
25979
|
+
if (enableImagePlugin === false && me.Value().indexOf("<img ") > -1)
|
|
25980
|
+
{
|
|
25981
|
+
enableImagePlugin = true;
|
|
25982
|
+
}
|
|
25983
|
+
|
|
25984
|
+
return enableImagePlugin;
|
|
25985
|
+
}
|
|
25986
|
+
|
|
25987
|
+
function localize()
|
|
25988
|
+
{
|
|
25989
|
+
locale = Fit.Internationalization.GetLocale(me);
|
|
25990
|
+
|
|
25991
|
+
if (me.DesignMode() === true)
|
|
25992
|
+
{
|
|
25993
|
+
// Prevent reloadEditor() from reloading detached editor.
|
|
25994
|
+
// It will automatically reload when locale is changed.
|
|
25995
|
+
// Without this guard the editor would reload twice.
|
|
25996
|
+
var detachedEditor = null;
|
|
25997
|
+
if (designEditorDetached !== null)
|
|
25998
|
+
{
|
|
25999
|
+
detachedEditor = designEditorDetached;
|
|
26000
|
+
designEditorDetached = null; // Prevent reloadEditor() from reloading detached editor
|
|
26001
|
+
}
|
|
26002
|
+
|
|
26003
|
+
// Re-create editor with new language
|
|
26004
|
+
reloadEditor();
|
|
26005
|
+
|
|
26006
|
+
if (detachedEditor !== null)
|
|
26007
|
+
{
|
|
26008
|
+
designEditorDetached = detachedEditor;
|
|
26009
|
+
}
|
|
26010
|
+
}
|
|
26011
|
+
}
|
|
26012
|
+
|
|
26013
|
+
function repaint()
|
|
26014
|
+
{
|
|
26015
|
+
if (isIe8 === true)
|
|
26016
|
+
{
|
|
26017
|
+
me.AddCssClass("FitUi_Non_Existing_Input_Class");
|
|
26018
|
+
me.RemoveCssClass("FitUi_Non_Existing_Input_Class");
|
|
26019
|
+
}
|
|
26020
|
+
}
|
|
26021
|
+
|
|
26022
|
+
init();
|
|
26023
|
+
}
|
|
26024
|
+
|
|
26025
|
+
/// <container name="Fit.Controls.InputType">
|
|
26026
|
+
/// Enum values determining input type
|
|
26027
|
+
/// </container>
|
|
26028
|
+
Fit.Controls.InputType =
|
|
26029
|
+
{
|
|
26030
|
+
/// <member container="Fit.Controls.InputType" name="Textarea" access="public" static="true" type="string" default="Textarea">
|
|
26031
|
+
/// <description> Multi line input field </description>
|
|
26032
|
+
/// </member>
|
|
26033
|
+
Textarea: "Textarea",
|
|
26034
|
+
|
|
26035
|
+
/// <member container="Fit.Controls.InputType" name="Color" access="public" static="true" type="string" default="Color">
|
|
26036
|
+
/// <description> Input control useful for entering a color </description>
|
|
26037
|
+
/// </member>
|
|
26038
|
+
Color: "Color",
|
|
26039
|
+
|
|
26040
|
+
/// <member container="Fit.Controls.InputType" name="Date" access="public" static="true" type="string" default="Date">
|
|
26041
|
+
/// <description> Input control useful for entering a date </description>
|
|
26042
|
+
/// </member>
|
|
26043
|
+
Date: "Date",
|
|
26044
|
+
|
|
26045
|
+
/// <member container="Fit.Controls.InputType" name="DateTime" access="public" static="true" type="string" default="DateTime">
|
|
26046
|
+
/// <description> Input control useful for entering a date and time </description>
|
|
26047
|
+
/// </member>
|
|
26048
|
+
DateTime: "DateTime",
|
|
26049
|
+
|
|
26050
|
+
/// <member container="Fit.Controls.InputType" name="Email" access="public" static="true" type="string" default="Email">
|
|
26051
|
+
/// <description> Input control useful for entering an e-mail address </description>
|
|
26052
|
+
/// </member>
|
|
26053
|
+
Email: "Email",
|
|
26054
|
+
|
|
26055
|
+
/// <member container="Fit.Controls.InputType" name="Month" access="public" static="true" type="string" default="Month">
|
|
26056
|
+
/// <description> Input control useful for entering a month </description>
|
|
26057
|
+
/// </member>
|
|
26058
|
+
Month: "Month",
|
|
26059
|
+
|
|
26060
|
+
/// <member container="Fit.Controls.InputType" name="Number" access="public" static="true" type="string" default="Number">
|
|
26061
|
+
/// <description> Input control useful for entering a number </description>
|
|
26062
|
+
/// </member>
|
|
26063
|
+
Number: "Number",
|
|
26064
|
+
|
|
26065
|
+
/// <member container="Fit.Controls.InputType" name="Password" access="public" static="true" type="string" default="Password">
|
|
26066
|
+
/// <description> Input control useful for entering a password (characters are masked) </description>
|
|
26067
|
+
/// </member>
|
|
26068
|
+
Password: "Password",
|
|
26069
|
+
|
|
26070
|
+
/// <member container="Fit.Controls.InputType" name="PhoneNumber" access="public" static="true" type="string" default="PhoneNumber">
|
|
26071
|
+
/// <description> Input control useful for entering a phone number </description>
|
|
26072
|
+
/// </member>
|
|
26073
|
+
PhoneNumber: "PhoneNumber",
|
|
26074
|
+
|
|
26075
|
+
/// <member container="Fit.Controls.InputType" name="Text" access="public" static="true" type="string" default="Text">
|
|
26076
|
+
/// <description> Input control useful for entering ordinary text </description>
|
|
26077
|
+
/// </member>
|
|
26078
|
+
Text: "Text",
|
|
26079
|
+
|
|
26080
|
+
/// <member container="Fit.Controls.InputType" name="Time" access="public" static="true" type="string" default="Time">
|
|
26081
|
+
/// <description> Input control useful for entering time </description>
|
|
26082
|
+
/// </member>
|
|
26083
|
+
Time: "Time",
|
|
26084
|
+
|
|
26085
|
+
/// <member container="Fit.Controls.InputType" name="Week" access="public" static="true" type="string" default="Week">
|
|
26086
|
+
/// <description> Input control useful for entering a week number </description>
|
|
26087
|
+
/// </member>
|
|
26088
|
+
Week: "Week",
|
|
26089
|
+
|
|
26090
|
+
Unknown: "Unknown"
|
|
26091
|
+
}
|
|
26092
|
+
|
|
26093
|
+
Fit.Controls.Input.Type = Fit.Controls.InputType; // Backward compatibility
|
|
26094
|
+
|
|
26095
|
+
/// <container name="Fit._internal.Controls.Input">
|
|
26096
|
+
/// Allows for manipulating control (appearance, features, and behaviour).
|
|
26097
|
+
/// Features are NOT guaranteed to be backward compatible, and incorrect use might break control!
|
|
26098
|
+
/// </container>
|
|
26099
|
+
Fit._internal.Controls.Input = {};
|
|
26100
|
+
|
|
26101
|
+
/// <container name="Fit._internal.Controls.Input.Editor">
|
|
26102
|
+
/// Internal settings related to HTML Editor (Design Mode)
|
|
26103
|
+
/// </container>
|
|
26104
|
+
Fit._internal.Controls.Input.Editor =
|
|
26105
|
+
{
|
|
26106
|
+
/// <member container="Fit._internal.Controls.Input.Editor" name="Skin" access="public" static="true" type="'bootstrapck' | 'moono-lisa' | null">
|
|
26107
|
+
/// <description> Skin used with DesignMode - must be set before an editor is created and cannot be changed for each individual control </description>
|
|
26108
|
+
/// </member>
|
|
26109
|
+
Skin: null // Notice: CKEditor does not support multiple different skins on the same page - do not change value once an editor has been created
|
|
26110
|
+
};
|
|
26111
|
+
|
|
26112
|
+
/// <container name="Fit.Controls.InputResizing">
|
|
26113
|
+
/// <description> Resizing options </description>
|
|
26114
|
+
/// <member name="Enabled" access="public" static="true" type="string" default="Enabled"> Allow for resizing both vertically and horizontally </member>
|
|
26115
|
+
/// <member name="Disabled" access="public" static="true" type="string" default="Disabled"> Do not allow resizing </member>
|
|
26116
|
+
/// <member name="Horizontal" access="public" static="true" type="string" default="Horizontal"> Allow for horizontal resizing </member>
|
|
26117
|
+
/// <member name="Vertical" access="public" static="true" type="string" default="Vertical"> Allow for vertical resizing </member>
|
|
26118
|
+
/// </container>
|
|
26119
|
+
Fit.Controls.InputResizing = // Enums must exist runtime
|
|
26120
|
+
{
|
|
26121
|
+
Enabled: "Enabled",
|
|
26122
|
+
Disabled: "Disabled",
|
|
26123
|
+
Horizontal: "Horizontal",
|
|
26124
|
+
Vertical: "Vertical"
|
|
26125
|
+
};
|
|
26126
|
+
/// <container name="Fit.Controls.Input" extends="Fit.Controls.ControlBase">
|
|
26127
|
+
/// Input control which allows for one or multiple lines of
|
|
26128
|
+
/// text, and features a Design Mode for rich HTML content.
|
|
26129
|
+
/// Extending from Fit.Controls.ControlBase.
|
|
26130
|
+
/// </container>
|
|
26131
|
+
|
|
26132
|
+
/// <function container="Fit.Controls.Input" name="Input" access="public">
|
|
26133
|
+
/// <description> Create instance of Input control </description>
|
|
26134
|
+
/// <param name="ctlId" type="string" default="undefined"> Unique control ID that can be used to access control using Fit.Controls.Find(..) </param>
|
|
26135
|
+
/// </function>
|
|
26136
|
+
Fit.Controls.Input = function(ctlId)
|
|
26137
|
+
{
|
|
26138
|
+
Fit.Validation.ExpectStringValue(ctlId, true);
|
|
26139
|
+
Fit.Core.Extend(this, Fit.Controls.ControlBase).Apply(ctlId);
|
|
26140
|
+
|
|
26141
|
+
var me = this;
|
|
26142
|
+
var orgVal = ""; // Holds initial value used to determine IsDirty state
|
|
26143
|
+
var preVal = ""; // Holds latest change made by user - used to determine whether OnChange needs to be fired
|
|
26144
|
+
var input = null;
|
|
26145
|
+
var cmdResize = null;
|
|
26146
|
+
var designEditor = null;
|
|
26147
|
+
var designEditorDom = null; // DOM elements within CKEditor which we rely on - some <div> elements become <span> elements in older browsers
|
|
26148
|
+
/*{
|
|
26149
|
+
OuterContainer: null, // <div class="cke">
|
|
26150
|
+
InnerContainer: null, // <div class="cke_inner">
|
|
26151
|
+
Top: null, // <span class="cke_top">
|
|
26152
|
+
Content: null, // <div class="cke_contents">
|
|
26153
|
+
Editable: null, // <div class="cke_editable">
|
|
26154
|
+
Bottom: null // <span class="cke_bottom">
|
|
26155
|
+
}*/
|
|
26156
|
+
var designEditorDirty = false;
|
|
26157
|
+
var designEditorDirtyPending = false;
|
|
26158
|
+
var designEditorConfig = null;
|
|
26159
|
+
var designEditorReloadConfig = null;
|
|
26160
|
+
var designEditorRestoreButtonState = null;
|
|
26161
|
+
var designEditorSuppressPaste = false;
|
|
26162
|
+
var designEditorSuppressOnResize = false;
|
|
26163
|
+
var designEditorMustReloadWhenReady = false;
|
|
26164
|
+
var designEditorMustDisposeWhenReady = false;
|
|
26165
|
+
var designEditorUpdateSizeDebouncer = -1;
|
|
26166
|
+
var designEditorActiveToolbarPanel = null; // { DomElement: HTMLElement, UnlockFocusStateIfEmojiPanelIsClosed: function, CloseEmojiPanel: function }
|
|
26167
|
+
var designEditorDetached = null; // { GetValue: function, Reload: function, Dispose: function }
|
|
26168
|
+
//var htmlWrappedInParagraph = false;
|
|
26169
|
+
var wasAutoChangedToMultiLineMode = false; // Used to revert to single line if multi line was automatically enabled along with DesignMode(true), Maximizable(true), or Resizable(true)
|
|
26170
|
+
var minimizeHeight = -1;
|
|
26171
|
+
var maximizeHeight = -1;
|
|
26172
|
+
var minMaxUnit = null;
|
|
26173
|
+
var maximizeHeightConfigured = -1;
|
|
26174
|
+
var resizable = Fit.Controls.InputResizing.Disabled;
|
|
26175
|
+
var nativeResizableAvailable = false; // Updated in init()
|
|
26176
|
+
var mutationObserverId = -1; // Specific to DesignMode
|
|
26177
|
+
var rootedEventId = -1; // Specific to DesignMode
|
|
26178
|
+
var createWhenReadyIntervalId = -1; // Specific to DesignMode
|
|
26179
|
+
var isIe8 = (Fit.Browser.GetInfo().Name === "MSIE" && Fit.Browser.GetInfo().Version === 8);
|
|
26180
|
+
var debounceOnChangeTimeout = -1;
|
|
26181
|
+
var debouncedOnChange = null;
|
|
26182
|
+
var imageBlobUrls = []; // Specific to DesignMode
|
|
26183
|
+
var locale = null;
|
|
26184
|
+
|
|
26185
|
+
// ============================================
|
|
26186
|
+
// Init
|
|
26187
|
+
// ============================================
|
|
26188
|
+
|
|
26189
|
+
function init()
|
|
26190
|
+
{
|
|
26191
|
+
input = document.createElement("input");
|
|
26192
|
+
input.type = "text";
|
|
26193
|
+
input.autocomplete = "off";
|
|
26194
|
+
input.spellcheck = true;
|
|
26195
|
+
input.onkeyup = function()
|
|
26196
|
+
{
|
|
26197
|
+
if (debounceOnChangeTimeout === -1)
|
|
26198
|
+
{
|
|
26199
|
+
fireOnChange();
|
|
26200
|
+
}
|
|
26201
|
+
else
|
|
26202
|
+
{
|
|
26203
|
+
if (debouncedOnChange === null)
|
|
26204
|
+
{
|
|
26205
|
+
debouncedOnChange = Fit.Core.CreateDebouncer(fireOnChange, debounceOnChangeTimeout);
|
|
26206
|
+
}
|
|
26207
|
+
|
|
26208
|
+
debouncedOnChange.Invoke();
|
|
26209
|
+
}
|
|
26210
|
+
|
|
26211
|
+
if (me.Maximizable() === true)
|
|
26212
|
+
{
|
|
26213
|
+
// Scroll to bottom if nearby, to make sure text does not collide with maximize button.
|
|
26214
|
+
// Extra padding-bottom is added inside control to allow for spacing between text and maximize button.
|
|
26215
|
+
|
|
26216
|
+
var scrollContainer = designEditorDom && designEditorDom.Editable || input;
|
|
26217
|
+
var autoScrollToBottom = scrollContainer.scrollTop + scrollContainer.clientHeight > scrollContainer.scrollHeight - 15; // True when at bottom or very close (15px buffer)
|
|
26218
|
+
|
|
26219
|
+
if (autoScrollToBottom === true)
|
|
26220
|
+
{
|
|
26221
|
+
scrollContainer.scrollTop += 99;
|
|
26222
|
+
}
|
|
26223
|
+
}
|
|
26224
|
+
}
|
|
26225
|
+
input.onchange = function() // OnKeyUp does not catch changes by mouse (e.g. paste or moving selected text)
|
|
26226
|
+
{
|
|
26227
|
+
if (me === null)
|
|
26228
|
+
{
|
|
26229
|
+
// Fix for Chrome which fires OnChange and OnBlur (in both capturering and bubbling phase)
|
|
26230
|
+
// if control has focus while being removed from DOM, e.g. if used in a dialog closed using ESC.
|
|
26231
|
+
// More details here: https://bugs.chromium.org/p/chromium/issues/detail?id=866242
|
|
26232
|
+
return;
|
|
26233
|
+
}
|
|
26234
|
+
|
|
26235
|
+
input.onkeyup();
|
|
26236
|
+
}
|
|
26237
|
+
me._internal.AddDomElement(input);
|
|
26238
|
+
|
|
26239
|
+
me.AddCssClass("FitUiControlInput");
|
|
26240
|
+
|
|
26241
|
+
me._internal.Data("multiline", "false");
|
|
26242
|
+
me._internal.Data("maximizable", "false");
|
|
26243
|
+
me._internal.Data("maximized", "false");
|
|
26244
|
+
me._internal.Data("resizable", resizable.toLowerCase());
|
|
26245
|
+
me._internal.Data("resized", "false");
|
|
26246
|
+
me._internal.Data("designmode", "false");
|
|
26247
|
+
|
|
26248
|
+
Fit.Internationalization.OnLocaleChanged(localize);
|
|
26249
|
+
localize();
|
|
26250
|
+
|
|
26251
|
+
me.OnBlur(function(sender)
|
|
26252
|
+
{
|
|
26253
|
+
// Due to CKEditor and plugins allowing for inconsistency between what is being
|
|
26254
|
+
// pushed via OnChange and the editor's actual value, we ensure that the latest
|
|
26255
|
+
// and actual value is pushed via Input.OnChange when the control lose focus.
|
|
26256
|
+
// See related bug report for CKEditor here: https://github.com/ckeditor/ckeditor4/issues/4856
|
|
26257
|
+
|
|
26258
|
+
if (debouncedOnChange !== null)
|
|
26259
|
+
{
|
|
26260
|
+
debouncedOnChange.Cancel(); // Do not trigger fireOnChange twice (below) if currently scheduled for execution
|
|
26261
|
+
}
|
|
26262
|
+
|
|
26263
|
+
fireOnChange(); // Only fires OnChange if value has actually changed
|
|
26264
|
+
});
|
|
26265
|
+
|
|
26266
|
+
me.OnFocus(function()
|
|
26267
|
+
{
|
|
26268
|
+
restoreHiddenToolbarInDesignEditor(); // Make toolbar appear if currently hidden
|
|
26269
|
+
updateDesignEditorPlaceholder(true); // Clear placeholder text
|
|
26270
|
+
});
|
|
26271
|
+
me.OnBlur(function()
|
|
26272
|
+
{
|
|
26273
|
+
restoreDesignEditorButtons(); // Restore (enable) editor's toolbar buttons in case they were temporarily disabled
|
|
26274
|
+
updateDesignEditorPlaceholder(); // Show placeholder text if control value is empty
|
|
26275
|
+
});
|
|
26276
|
+
|
|
26277
|
+
Fit.Events.AddHandler(me.GetDomElement(), "paste", true, function(e)
|
|
26278
|
+
{
|
|
26279
|
+
if (me.DesignMode() === true && designEditorSuppressPaste === true)
|
|
26280
|
+
{
|
|
26281
|
+
Fit.Events.Stop(e);
|
|
26282
|
+
}
|
|
26283
|
+
});
|
|
26284
|
+
|
|
26285
|
+
try
|
|
26286
|
+
{
|
|
26287
|
+
// We rely on the .buttons property to optimize resizing for textarea (MultiLine mode).
|
|
26288
|
+
// The MouseEvent class might not be available on older browsers or might throw an exception when constructing.
|
|
26289
|
+
nativeResizableAvailable = window.MouseEvent && new MouseEvent("mousemove", {}).buttons !== undefined || false;
|
|
26290
|
+
}
|
|
26291
|
+
catch (err) {}
|
|
26292
|
+
}
|
|
26293
|
+
|
|
26294
|
+
// ============================================
|
|
26295
|
+
// Public - overrides
|
|
26296
|
+
// ============================================
|
|
26297
|
+
|
|
26298
|
+
// See documentation on ControlBase
|
|
26299
|
+
this.Visible = Fit.Core.CreateOverride(this.Visible, function(val)
|
|
26300
|
+
{
|
|
26301
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
26302
|
+
|
|
26303
|
+
if (Fit.Validation.IsSet(val) && designEditorDetached !== null)
|
|
26304
|
+
{
|
|
26305
|
+
designEditorDetached.SetVisible(val);
|
|
26306
|
+
}
|
|
26307
|
+
|
|
26308
|
+
return base(val);
|
|
26309
|
+
});
|
|
26310
|
+
|
|
26311
|
+
// See documentation on ControlBase
|
|
26312
|
+
this.Enabled = function(val)
|
|
26313
|
+
{
|
|
26314
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
26315
|
+
|
|
26316
|
+
if (Fit.Validation.IsSet(val) === true && val !== me.Enabled())
|
|
26317
|
+
{
|
|
26318
|
+
me._internal.Data("enabled", val === true ? "true" : "false");
|
|
26319
|
+
|
|
26320
|
+
if (val === false)
|
|
26321
|
+
{
|
|
26322
|
+
me.Focused(false);
|
|
26323
|
+
}
|
|
26324
|
+
|
|
26325
|
+
input.disabled = val === false;
|
|
26326
|
+
|
|
26327
|
+
if (designModeEnabledAndReady() === true) // ReadOnly mode will be set when instance is ready, if not ready at this time
|
|
26328
|
+
{
|
|
26329
|
+
designEditor.setReadOnly(input.disabled);
|
|
26330
|
+
|
|
26331
|
+
// Set tabindex to allow or disallow focus. Unfortunately there is no editor API for changing the tabindex.
|
|
26332
|
+
// Preventing focus is only possible by nullifying DOM attribute (these does not work: delete elm.tabIndex; elm.tabIndex = null|undefined|-1).
|
|
26333
|
+
Fit.Dom.Attribute(designEditorDom.Editable, "tabindex", input.disabled === true ? null : "0");
|
|
26334
|
+
|
|
26335
|
+
// Prevent control from losing focus when HTML editor is initialized,
|
|
26336
|
+
// e.g. if Design Mode is enabled when ordinary input control gains focus.
|
|
26337
|
+
// This also prevents control from losing focus if toolbar is clicked without
|
|
26338
|
+
// hitting a button. A value of -1 makes it focusable, but keeps it out of
|
|
26339
|
+
// tab flow (keyboard navigation). Also set when DesignMode(true) is called.
|
|
26340
|
+
Fit.Dom.Attribute(me.GetDomElement(), "tabindex", input.disabled !== true && me.DesignMode() === true ? "-1" : null); // Remove tabindex used to prevent control from losing focus when clicking toolbar buttons, as it will allow control to gain focus when clicked using the mouse
|
|
26341
|
+
}
|
|
26342
|
+
|
|
26343
|
+
if (designEditorDetached !== null)
|
|
26344
|
+
{
|
|
26345
|
+
designEditorDetached.SetEnabled(val);
|
|
26346
|
+
}
|
|
26347
|
+
|
|
26348
|
+
me._internal.UpdateInternalState();
|
|
26349
|
+
me._internal.Repaint();
|
|
26350
|
+
}
|
|
26351
|
+
|
|
26352
|
+
return me._internal.Data("enabled") === "true";
|
|
26353
|
+
}
|
|
26354
|
+
|
|
26355
|
+
// See documentation on ControlBase
|
|
26356
|
+
this.Focused = function(focus)
|
|
26357
|
+
{
|
|
26358
|
+
Fit.Validation.ExpectBoolean(focus, true);
|
|
26359
|
+
|
|
26360
|
+
if (designEditorDetached !== null)
|
|
26361
|
+
{
|
|
26362
|
+
if (focus === true)
|
|
26363
|
+
{
|
|
26364
|
+
designEditorDetached.Focus();
|
|
26365
|
+
}
|
|
26366
|
+
else if (focus === false)
|
|
26367
|
+
{
|
|
26368
|
+
Fit.Browser.Debug("WARNING: Unable to remove focus from Input control '" + me.GetId() + "' when modal detached editor is open");
|
|
26369
|
+
}
|
|
26370
|
+
|
|
26371
|
+
return me.Visible(); // Always considered focused if detached editor is open and control (along with detached editor) is visible
|
|
26372
|
+
//return designEditorDetached.GetFocused();
|
|
26373
|
+
}
|
|
26374
|
+
|
|
26375
|
+
elm = input;
|
|
26376
|
+
|
|
26377
|
+
if (me.DesignMode() === true)
|
|
26378
|
+
{
|
|
26379
|
+
if (designModeEnabledAndReady() === true)
|
|
26380
|
+
{
|
|
26381
|
+
elm = designEditor; // Notice: designEditor is an instance of CKEditor, not a DOM element, but it does expose a focus() function
|
|
26382
|
+
}
|
|
26383
|
+
else
|
|
26384
|
+
{
|
|
26385
|
+
elm = me.GetDomElement(); // Editor not loaded yet - focus control container temporarily - focus is later moved to editable area once instanceReady handler is invoked
|
|
26386
|
+
}
|
|
26387
|
+
}
|
|
26388
|
+
|
|
26389
|
+
if (Fit.Validation.IsSet(focus) === true)
|
|
26390
|
+
{
|
|
26391
|
+
if (focus === true)
|
|
26392
|
+
{
|
|
26393
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
|
|
26394
|
+
{
|
|
26395
|
+
// Remove flag used to auto close editor dialog, in case Focused(false)
|
|
26396
|
+
// was called followed by Focused(true), while editor dialog was loading.
|
|
26397
|
+
delete Fit._internal.Controls.Input.ActiveDialogForEditorCanceled;
|
|
26398
|
+
}
|
|
26399
|
+
|
|
26400
|
+
elm.focus();
|
|
26401
|
+
}
|
|
26402
|
+
else // Remove focus
|
|
26403
|
+
{
|
|
26404
|
+
if (designModeEnabledAndReady() === true)
|
|
26405
|
+
{
|
|
26406
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
|
|
26407
|
+
{
|
|
26408
|
+
if (Fit._internal.Controls.Input.ActiveDialogForEditor !== null)
|
|
26409
|
+
{
|
|
26410
|
+
// A dialog (e.g. link or image dialog) is currently open, and will now be closed
|
|
26411
|
+
|
|
26412
|
+
// Hide dialog - fires dialog's OnHide event and returns focus to editor
|
|
26413
|
+
Fit._internal.Controls.Input.ActiveDialogForEditor.hide();
|
|
26414
|
+
|
|
26415
|
+
// CKEditor instance has no blur() function, so we call blur() on DOM element currently focused within CKEditor
|
|
26416
|
+
Fit.Dom.GetFocused().blur();
|
|
26417
|
+
|
|
26418
|
+
// Fire OnBlur manually as blur() above didn't trigger this, as it normally
|
|
26419
|
+
// would. The call to the dialog's hide() function fires its OnHide event
|
|
26420
|
+
// which disables the focus lock, but does so asynchronously, which is
|
|
26421
|
+
// why OnBlur does not fire via ControlBase's onfocusout handler.
|
|
26422
|
+
me._internal.FireOnBlur();
|
|
26423
|
+
}
|
|
26424
|
+
else
|
|
26425
|
+
{
|
|
26426
|
+
// A dialog (e.g. link or image dialog) is currently loading. This situation
|
|
26427
|
+
// can be triggered for debugging purposes by adding the following code in the
|
|
26428
|
+
// beforeCommandExec event handler:
|
|
26429
|
+
// setTimeout(function() { me.Focused(false); }, 0);
|
|
26430
|
+
// Alternatively register an onwheel/onscroll handler on the document that
|
|
26431
|
+
// removes focus from the control, and quickly scroll the document while the
|
|
26432
|
+
// dialog is loading. Use network throttling to increase the load time of the
|
|
26433
|
+
// dialog if necessary.
|
|
26434
|
+
|
|
26435
|
+
// Make dialog close automatically when loaded and shown - handled in dialog's OnShow event handler
|
|
26436
|
+
Fit._internal.Controls.Input.ActiveDialogForEditorCanceled = true;
|
|
26437
|
+
|
|
26438
|
+
// CKEditor instance has no blur() function, so we call blur() on DOM element currently focused within CKEditor.
|
|
26439
|
+
// Notice that OnBlur does not fire immediately (focus state is locked), but does so when dialog's OnHide event fires (async).
|
|
26440
|
+
// While we could fire it immediately and prevent it from firing when the dialog's OnHide event fires, it would prevent
|
|
26441
|
+
// developers from using the OnBlur event to dispose a control in Design Mode, since CKEditor fails when being disposed
|
|
26442
|
+
// while dialogs are open. Focused() will return False after the call to blur() below though - as expected.
|
|
26443
|
+
Fit.Dom.GetFocused().blur();
|
|
26444
|
+
}
|
|
26445
|
+
}
|
|
26446
|
+
else
|
|
26447
|
+
{
|
|
26448
|
+
if (designEditorActiveToolbarPanel !== null)
|
|
26449
|
+
{
|
|
26450
|
+
designEditorActiveToolbarPanel.CloseEmojiPanel(); // Returns focus to editor and nullifies designEditorActiveToolbarPanel
|
|
26451
|
+
}
|
|
26452
|
+
|
|
26453
|
+
// Make sure this control is focused so that one control instance can not
|
|
26454
|
+
// be used to accidentially remove focus from another control instance.
|
|
26455
|
+
if (Fit.Dom.Contained(me.GetDomElement(), Fit.Dom.GetFocused()) === true)
|
|
26456
|
+
{
|
|
26457
|
+
// CKEditor instance has no blur() function, so we call blur() on DOM element currently focused within CKEditor
|
|
26458
|
+
Fit.Dom.GetFocused().blur();
|
|
26459
|
+
}
|
|
26460
|
+
}
|
|
26461
|
+
}
|
|
26462
|
+
else
|
|
26463
|
+
{
|
|
26464
|
+
elm.blur();
|
|
26465
|
+
}
|
|
26466
|
+
}
|
|
26467
|
+
}
|
|
26468
|
+
|
|
26469
|
+
if (me.DesignMode() === true)
|
|
26470
|
+
{
|
|
26471
|
+
// If a dialog is open and it belongs to this control instance, and focus is found within dialog, then control is considered having focus.
|
|
26472
|
+
// However, if <body> is focused while dialog is open, control is also considered to have focus, since dialog temporarily assigns focus to
|
|
26473
|
+
// <body> when tabbing between elements within the dialog. This seems safe as no other control can be considered focused if <body> has focus.
|
|
26474
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me && (Fit.Dom.Contained(Fit._internal.Controls.Input.ActiveDialogForEditor.getElement().$, Fit.Dom.GetFocused()) === true || Fit.Dom.GetFocused() === document.body))
|
|
26475
|
+
return true;
|
|
26476
|
+
|
|
26477
|
+
// If a toolbar dialog/callout is open and contains the element currently having focus, then control is considered having focus.
|
|
26478
|
+
// If the dialog/callout contains an iframe in which an element has focus, then the iframe is considered focused in the main window.
|
|
26479
|
+
if (designEditorActiveToolbarPanel !== null && Fit.Dom.Contained(designEditorActiveToolbarPanel.DomElement, Fit.Dom.GetFocused()) === true)
|
|
26480
|
+
return true;
|
|
26481
|
+
|
|
26482
|
+
return Fit.Dom.GetFocused() === me.GetDomElement() || Fit.Dom.Contained(me.GetDomElement(), Fit.Dom.GetFocused());
|
|
26483
|
+
}
|
|
26484
|
+
|
|
26485
|
+
return (Fit.Dom.GetFocused() === elm);
|
|
26486
|
+
}
|
|
26487
|
+
|
|
26488
|
+
// See documentation on ControlBase
|
|
26489
|
+
this.Value = function(val, preserveDirtyState)
|
|
26490
|
+
{
|
|
26491
|
+
Fit.Validation.ExpectString(val, true);
|
|
26492
|
+
Fit.Validation.ExpectBoolean(preserveDirtyState, true);
|
|
26493
|
+
|
|
26494
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26495
|
+
{
|
|
26496
|
+
var fireOnChange = (me.Value() !== val);
|
|
26497
|
+
|
|
26498
|
+
orgVal = (preserveDirtyState !== true ? val : orgVal);
|
|
26499
|
+
preVal = val;
|
|
26500
|
+
designEditorDirty = designEditorDirtyPending === true ? true : false;
|
|
26501
|
+
designEditorDirtyPending = false;
|
|
26502
|
+
|
|
26503
|
+
/*if (val.indexOf("<p>") === 0)
|
|
26504
|
+
htmlWrappedInParagraph = true; // Indicates that val is comparable with value from CKEditor which wraps content in paragraphs*/
|
|
26505
|
+
|
|
26506
|
+
if (designModeEnabledAndReady() === true)
|
|
26507
|
+
{
|
|
26508
|
+
// NOTICE: Invalid HTML is removed, so an all invalid HTML string will be discarded
|
|
26509
|
+
// by the editor, resulting in the editor's getData() function returning an empty string.
|
|
26510
|
+
|
|
26511
|
+
// Calling setData(..) fires CKEditor's onchange event which in turn fires
|
|
26512
|
+
// Input's OnChange event. Suppress OnChange which is fired further down.
|
|
26513
|
+
me._internal.ExecuteWithNoOnChange(function()
|
|
26514
|
+
{
|
|
26515
|
+
CKEDITOR.instances[me.GetId() + "_DesignMode"].setData(val);
|
|
26516
|
+
});
|
|
26517
|
+
|
|
26518
|
+
updateDesignEditorPlaceholder();
|
|
26519
|
+
}
|
|
26520
|
+
else
|
|
26521
|
+
{
|
|
26522
|
+
input.value = val;
|
|
26523
|
+
}
|
|
26524
|
+
|
|
26525
|
+
// Notice: Identical logic found in DesignMode(true, config)!
|
|
26526
|
+
if (designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeExternalBlobUrlsOnDispose === true)
|
|
26527
|
+
{
|
|
26528
|
+
// Keep track of image blobs added via Value(..) so we can dispose of them automatically.
|
|
26529
|
+
// When RevokeExternalBlobUrlsOnDispose is True it basically means that the Input control
|
|
26530
|
+
// is allowed (and expected) to take control over memory management for these blobs
|
|
26531
|
+
// based on the rule set in RevokeBlobUrlsOnDispose.
|
|
26532
|
+
// This code is also found in DesignMode(true, config) since images might be added before
|
|
26533
|
+
// editor is created, in which case we do not yet have the editor configuration used to determine
|
|
26534
|
+
// the desired behaviour.
|
|
26535
|
+
|
|
26536
|
+
var blobUrls = Fit.String.ParseImageBlobUrls(val);
|
|
26537
|
+
|
|
26538
|
+
Fit.Array.ForEach(blobUrls, function(blobUrl)
|
|
26539
|
+
{
|
|
26540
|
+
if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
|
|
26541
|
+
{
|
|
26542
|
+
Fit.Array.Add(imageBlobUrls, blobUrl);
|
|
26543
|
+
}
|
|
26544
|
+
});
|
|
26545
|
+
}
|
|
26546
|
+
|
|
26547
|
+
if (fireOnChange === true)
|
|
26548
|
+
me._internal.FireOnChange();
|
|
26549
|
+
}
|
|
26550
|
+
|
|
26551
|
+
if (designModeEnabledAndReady() === true)
|
|
26552
|
+
{
|
|
26553
|
+
// If user has not changed value, then return the value initially set.
|
|
26554
|
+
// CKEditor may change (optimize) HTML when applied, but we always want
|
|
26555
|
+
// the value initially set when no changes have been made by the user.
|
|
26556
|
+
// See additional comments regarding this in the IsDirty() implementation.
|
|
26557
|
+
if (designEditorDirty === false)
|
|
26558
|
+
{
|
|
26559
|
+
return orgVal;
|
|
26560
|
+
}
|
|
26561
|
+
|
|
26562
|
+
var curVal = CKEDITOR.instances[me.GetId() + "_DesignMode"].getData();
|
|
26563
|
+
|
|
26564
|
+
// Remove extra line break added by htmlwriter plugin at the end: <p>Hello world</p>\n
|
|
26565
|
+
curVal = curVal.replace(/<\/p>\n$/, "</p>");
|
|
26566
|
+
|
|
26567
|
+
// Remove empty class attribute on <img> tags which may be temporarily set when selecting
|
|
26568
|
+
// an image using the dragresize plugin. This plugin adds a CSS class (ckimgrsz) to the image
|
|
26569
|
+
// tag while being selected, although the class name is removed when calling getData() above.
|
|
26570
|
+
// However, the empty class attribute is useless, so we remove it. It also results in IsDirty()
|
|
26571
|
+
// returning True while the image is selected if we keep it. Actually the class attribute should
|
|
26572
|
+
// never have been returned since the allowedContent option does not allow it - might be a minor bug.
|
|
26573
|
+
curVal = curVal.replace(/(<img.*?) class=""(.*?>)/, "$1$2"); // Not using /g switch as only one image can be selected
|
|
26574
|
+
|
|
26575
|
+
return curVal;
|
|
26576
|
+
}
|
|
26577
|
+
|
|
26578
|
+
return input.value;
|
|
26579
|
+
}
|
|
26580
|
+
|
|
26581
|
+
// See documentation on ControlBase
|
|
26582
|
+
this.UserValue = Fit.Core.CreateOverride(this.UserValue, function(val)
|
|
26583
|
+
{
|
|
26584
|
+
if (Fit.Validation.IsSet(val) === true && me.DesignMode() === true)
|
|
26585
|
+
{
|
|
26586
|
+
designEditorDirtyPending = true;
|
|
26587
|
+
}
|
|
26588
|
+
|
|
26589
|
+
return base(val);
|
|
26590
|
+
});
|
|
26591
|
+
|
|
26592
|
+
// See documentation on ControlBase
|
|
26593
|
+
this.IsDirty = function()
|
|
26594
|
+
{
|
|
26595
|
+
if (me.DesignMode() === true)
|
|
26596
|
+
{
|
|
26597
|
+
// Never do value comparison in DesignMode.
|
|
26598
|
+
// A value such as "Hello world" could have been provided,
|
|
26599
|
+
// which by CKEditor would be returned as "<p>Hello world</p>".
|
|
26600
|
+
// A value such as '<p style="text-align: center;">Hello</p>' could
|
|
26601
|
+
// also have been set, which by CKEditor would be optimized to
|
|
26602
|
+
// '<p style="text-align:center">Hello</p>' via ACF (Advanced Content Filter):
|
|
26603
|
+
// https://ckeditor.com/docs/ckeditor4/latest/guide/dev_advanced_content_filter.html
|
|
26604
|
+
// Furthermore invalid HTML is removed while valid HTML is kept.
|
|
26605
|
+
// All this makes it very difficult to reliably determine dirty state
|
|
26606
|
+
// by comparing values. Therefore, if the user changed anything by interacting
|
|
26607
|
+
// with the editor, or UserValue(..) was called, always consider the value dirty.
|
|
26608
|
+
|
|
26609
|
+
// Another positive of avoiding value comparison to determine dirty state
|
|
26610
|
+
// is that retrieving the value from CKEditor is fairly expensive.
|
|
26611
|
+
|
|
26612
|
+
return designEditorDirty;
|
|
26613
|
+
}
|
|
26614
|
+
|
|
26615
|
+
return (orgVal !== me.Value());
|
|
26616
|
+
}
|
|
26617
|
+
|
|
26618
|
+
// See documentation on ControlBase
|
|
26619
|
+
this.Clear = function()
|
|
26620
|
+
{
|
|
26621
|
+
me.Value("");
|
|
26622
|
+
}
|
|
26623
|
+
|
|
26624
|
+
// See documentation on ControlBase
|
|
26625
|
+
this.Dispose = Fit.Core.CreateOverride(this.Dispose, function()
|
|
26626
|
+
{
|
|
26627
|
+
// This will destroy control - it will no longer work!
|
|
26628
|
+
|
|
26629
|
+
if (me.DesignMode() === true && designModeEnabledAndReady() === false) // DesignMode is enabled but editor is not done loading/initializing
|
|
26630
|
+
{
|
|
26631
|
+
// WARNING: This has the potential to leak memory if editor never loads and resumes task of disposing control!
|
|
26632
|
+
designEditorMustDisposeWhenReady = true;
|
|
26633
|
+
|
|
26634
|
+
// Editor was disposed while loading/initializing.
|
|
26635
|
+
// Postpone destruction of control to make sure we can clean up resources
|
|
26636
|
+
// reliably once editor is ready. We know that CKEditor does not dispose properly
|
|
26637
|
+
// unless fully loaded (it may leave an instance on the global CKEDITOR object or even fail).
|
|
26638
|
+
Fit.Browser.Debug("WARNING: Attempting to dispose Input control '" + me.GetId() + "' while initializing DesignMode! Control will be disposed later.");
|
|
26639
|
+
|
|
26640
|
+
// Do not keep control in user interface when disposed.
|
|
26641
|
+
// Mount control in document root and move it off screen (outside visible
|
|
26642
|
+
// viewport area). CKEditor will fail initialization if not mounted in DOM.
|
|
26643
|
+
me.Render(document.body);
|
|
26644
|
+
me.GetDomElement().style.position = "fixed";
|
|
26645
|
+
me.GetDomElement().style.left = "0px";
|
|
26646
|
+
me.GetDomElement().style.bottom = "-100px";
|
|
26647
|
+
me.GetDomElement().style.maxHeight = "100px";
|
|
26648
|
+
|
|
26649
|
+
// Detect memory leak
|
|
26650
|
+
/* setTimeout(function()
|
|
26651
|
+
{
|
|
26652
|
+
if (me !== null)
|
|
26653
|
+
{
|
|
26654
|
+
Fit.Browser.Debug("WARNING: Input in DesignMode was not properly disposed in time - potential memory leak detected");
|
|
26655
|
+
}
|
|
26656
|
+
}, 5000); // Usually the load time for an editor is barely measurable, so 5 seconds seems sufficient */
|
|
26657
|
+
|
|
26658
|
+
return;
|
|
26659
|
+
}
|
|
26660
|
+
|
|
26661
|
+
var curVal = designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeBlobUrlsOnDispose === "UnreferencedOnly" ? me.Value() : null;
|
|
26662
|
+
|
|
26663
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
|
|
26664
|
+
{
|
|
26665
|
+
if (Fit._internal.Controls.Input.ActiveDialogForEditor === null)
|
|
26666
|
+
{
|
|
26667
|
+
// Dialog is currently loading.
|
|
26668
|
+
// CKEditor will throw an error if disposed while a dialog (e.g. the link dialog) is loading,
|
|
26669
|
+
// leaving a modal layer on the page behind, making it unusable. This may happen if disposed
|
|
26670
|
+
// from e.g. a DOM event handler, a mutation observer, a timer, or an AJAX request. The input control
|
|
26671
|
+
// itself does not fire any events while the dialog is loading which could trigger this situation, so
|
|
26672
|
+
// this can only happen from "external code".
|
|
26673
|
+
|
|
26674
|
+
// WARNING: This has the potential to leak memory if dialog never loads and resumes task of disposing control!
|
|
26675
|
+
Fit._internal.Controls.Input.ActiveEditorForDialogDestroyed = designEditor;
|
|
26676
|
+
Fit.Dom.Remove(me.GetDomElement());
|
|
26677
|
+
|
|
26678
|
+
// Detect memory leak
|
|
26679
|
+
/* setTimeout(function()
|
|
26680
|
+
{
|
|
26681
|
+
if (me !== null)
|
|
26682
|
+
{
|
|
26683
|
+
Fit.Browser.Log("WARNING: Input in DesignMode was not properly disposed in time - potential memory leak detected");
|
|
26684
|
+
}
|
|
26685
|
+
}, 5000); // Usually the load time for a dialog is barely measurable, so 5 seconds seems sufficient */
|
|
26686
|
+
|
|
26687
|
+
return;
|
|
26688
|
+
}
|
|
26689
|
+
else
|
|
26690
|
+
{
|
|
26691
|
+
Fit._internal.Controls.Input.ActiveDialogForEditor.hide(); // Fires dialog's OnHide event
|
|
26692
|
+
}
|
|
26693
|
+
}
|
|
26694
|
+
|
|
26695
|
+
if (designEditor !== null)
|
|
26696
|
+
{
|
|
26697
|
+
// Destroying editor also fires OnHide event for any dialog currently open, which will clean up
|
|
26698
|
+
// Fit._internal.Controls.Input.ActiveEditorForDialog;
|
|
26699
|
+
// Fit._internal.Controls.Input.ActiveEditorForDialogDestroyed;
|
|
26700
|
+
// Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed;
|
|
26701
|
+
// Fit._internal.Controls.Input.ActiveDialogForEditor;
|
|
26702
|
+
// Fit._internal.Controls.Input.ActiveDialogForEditorCanceled;
|
|
26703
|
+
|
|
26704
|
+
destroyDesignEditorInstance(); // Destroys editor and stops related mutation observers, timers, etc.
|
|
26705
|
+
}
|
|
26706
|
+
|
|
26707
|
+
Fit.Internationalization.RemoveOnLocaleChanged(localize);
|
|
26708
|
+
|
|
26709
|
+
/*if (designEditorUpdateSizeDebouncer !== -1)
|
|
26710
|
+
{
|
|
26711
|
+
clearTimeout(designEditorUpdateSizeDebouncer);
|
|
26712
|
+
}
|
|
26713
|
+
|
|
26714
|
+
if (mutationObserverId !== -1)
|
|
26715
|
+
{
|
|
26716
|
+
Fit.Events.RemoveMutationObserver(mutationObserverId);
|
|
26717
|
+
}
|
|
26718
|
+
|
|
26719
|
+
if (rootedEventId !== -1)
|
|
26720
|
+
{
|
|
26721
|
+
Fit.Events.RemoveHandler(me.GetDomElement(), rootedEventId);
|
|
26722
|
+
}
|
|
26723
|
+
|
|
26724
|
+
if (createWhenReadyIntervalId !== -1)
|
|
26725
|
+
{
|
|
26726
|
+
clearInterval(createWhenReadyIntervalId);
|
|
26727
|
+
}*/
|
|
26728
|
+
|
|
26729
|
+
if (debouncedOnChange !== null)
|
|
26730
|
+
{
|
|
26731
|
+
debouncedOnChange.Cancel();
|
|
26732
|
+
}
|
|
26733
|
+
|
|
26734
|
+
if (designEditorConfig === null || !designEditorConfig.Plugins || !designEditorConfig.Plugins.Images || !designEditorConfig.Plugins.Images.RevokeBlobUrlsOnDispose || designEditorConfig.Plugins.Images.RevokeBlobUrlsOnDispose === "All")
|
|
26735
|
+
{
|
|
26736
|
+
Fit.Array.ForEach(imageBlobUrls, function(imageUrl)
|
|
26737
|
+
{
|
|
26738
|
+
URL.revokeObjectURL(imageUrl);
|
|
26739
|
+
});
|
|
26740
|
+
}
|
|
26741
|
+
else // UnreferencedOnly
|
|
26742
|
+
{
|
|
26743
|
+
Fit.Array.ForEach(imageBlobUrls, function(imageUrl)
|
|
26744
|
+
{
|
|
26745
|
+
if (curVal.match(new RegExp("img.*src=([\"'])" + imageUrl + "\\1", "i")) === null)
|
|
26746
|
+
{
|
|
26747
|
+
URL.revokeObjectURL(imageUrl);
|
|
26748
|
+
}
|
|
26749
|
+
});
|
|
26750
|
+
}
|
|
26751
|
+
|
|
26752
|
+
me = orgVal = preVal = input = cmdResize = designEditor = designEditorDom = designEditorDirty = designEditorDirtyPending = designEditorConfig = designEditorReloadConfig = designEditorRestoreButtonState = designEditorSuppressPaste = designEditorSuppressOnResize = designEditorMustReloadWhenReady = designEditorMustDisposeWhenReady = designEditorUpdateSizeDebouncer = designEditorActiveToolbarPanel = designEditorDetached /*= htmlWrappedInParagraph*/ = wasAutoChangedToMultiLineMode = minimizeHeight = maximizeHeight = minMaxUnit = maximizeHeightConfigured = resizable = nativeResizableAvailable = mutationObserverId = rootedEventId = createWhenReadyIntervalId = isIe8 = debounceOnChangeTimeout = debouncedOnChange = imageBlobUrls = locale = null;
|
|
26753
|
+
|
|
26754
|
+
base();
|
|
26755
|
+
});
|
|
26756
|
+
|
|
26757
|
+
// See documentation on ControlBase
|
|
26758
|
+
this.Width = Fit.Core.CreateOverride(this.Width, function(val, unit)
|
|
26759
|
+
{
|
|
26760
|
+
Fit.Validation.ExpectNumber(val, true);
|
|
26761
|
+
Fit.Validation.ExpectStringValue(unit, true);
|
|
26762
|
+
|
|
26763
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26764
|
+
{
|
|
26765
|
+
me._internal.Data("resized", "false");
|
|
26766
|
+
|
|
26767
|
+
base(val, unit);
|
|
26768
|
+
updateDesignEditorSize();
|
|
26769
|
+
}
|
|
26770
|
+
|
|
26771
|
+
return base();
|
|
26772
|
+
});
|
|
26773
|
+
|
|
26774
|
+
// See documentation on ControlBase
|
|
26775
|
+
this.Height = Fit.Core.CreateOverride(this.Height, function(val, unit, suppressMinMax)
|
|
26776
|
+
{
|
|
26777
|
+
Fit.Validation.ExpectNumber(val, true);
|
|
26778
|
+
Fit.Validation.ExpectStringValue(unit, true);
|
|
26779
|
+
Fit.Validation.ExpectBoolean(suppressMinMax, true);
|
|
26780
|
+
|
|
26781
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26782
|
+
{
|
|
26783
|
+
// Restore/minimize control if currently maximized
|
|
26784
|
+
if (me.Maximizable() === true && suppressMinMax !== true)
|
|
26785
|
+
{
|
|
26786
|
+
me.Maximized(false);
|
|
26787
|
+
}
|
|
26788
|
+
|
|
26789
|
+
me._internal.Data("resized", "false");
|
|
26790
|
+
me._internal.Data("autogrow", me.DesignMode() === true ? "false" : null);
|
|
26791
|
+
|
|
26792
|
+
var autoGrowEnabled = false;
|
|
26793
|
+
if (val === -1 && designModeEnabledAndReady() === true) // Enable auto grow if editor is loaded and ready - otherwise enabled in instanceReady handler
|
|
26794
|
+
{
|
|
26795
|
+
// A value of -1 is used to reset control height (assume default height).
|
|
26796
|
+
// In DesignMode we want the control height to adjust to the content of the editor in this case.
|
|
26797
|
+
// The editor's ability to adjust to the HTML content is handled in updateDesignEditorSize() below.
|
|
26798
|
+
// Auto grow can also be enabled using configuration object passed to DesignMode(true, config).
|
|
26799
|
+
me._internal.Data("autogrow", "true"); // Make control container adjust to editor's height
|
|
26800
|
+
autoGrowEnabled = true;
|
|
26801
|
+
}
|
|
26802
|
+
|
|
26803
|
+
var hideToolbarAgain = false;
|
|
26804
|
+
if (isToolbarHiddenInDesignEditor() === true)
|
|
26805
|
+
{
|
|
26806
|
+
// If in DesignMode, temporarily restore toolbar to allow update to height.
|
|
26807
|
+
// When toolbar is hidden, a fixed height is set on the editable area which
|
|
26808
|
+
// prevent changes to control height.
|
|
26809
|
+
restoreHiddenToolbarInDesignEditor();
|
|
26810
|
+
hideToolbarAgain = true;
|
|
26811
|
+
}
|
|
26812
|
+
|
|
26813
|
+
var h = base(val, unit);
|
|
26814
|
+
updateDesignEditorSize();
|
|
26815
|
+
|
|
26816
|
+
if (hideToolbarAgain === true)
|
|
26817
|
+
{
|
|
26818
|
+
hideToolbarInDesignMode();
|
|
26819
|
+
}
|
|
26820
|
+
|
|
26821
|
+
// Calculate new maximize height if control is maximizable
|
|
26822
|
+
if (me.Maximizable() === true && suppressMinMax !== true)
|
|
26823
|
+
{
|
|
26824
|
+
minimizeHeight = h.Value;
|
|
26825
|
+
maximizeHeight = (maximizeHeightConfigured !== -1 ? maximizeHeightConfigured : (minimizeHeight !== -1 ? minimizeHeight * 2 : 300));
|
|
26826
|
+
minMaxUnit = h.Unit;
|
|
26827
|
+
}
|
|
26828
|
+
|
|
26829
|
+
if (autoGrowEnabled === true) // Repaint in case auto grow was enabled above
|
|
26830
|
+
{
|
|
26831
|
+
repaint();
|
|
26832
|
+
}
|
|
26833
|
+
}
|
|
26834
|
+
|
|
26835
|
+
return base();
|
|
26836
|
+
});
|
|
26837
|
+
|
|
26838
|
+
// ============================================
|
|
26839
|
+
// Public
|
|
26840
|
+
// ============================================
|
|
26841
|
+
|
|
26842
|
+
/// <function container="Fit.Controls.Input" name="Placeholder" access="public" returns="string">
|
|
26843
|
+
/// <description> Get/set value used as a placeholder to indicate expected input on supported browsers </description>
|
|
26844
|
+
/// <param name="val" type="string" default="undefined"> If defined, value is set as placeholder </param>
|
|
26845
|
+
/// </function>
|
|
26846
|
+
this.Placeholder = function(val)
|
|
26847
|
+
{
|
|
26848
|
+
Fit.Validation.ExpectString(val, true);
|
|
26849
|
+
|
|
26850
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26851
|
+
{
|
|
26852
|
+
input.placeholder = val;
|
|
26853
|
+
updateDesignEditorPlaceholder();
|
|
26854
|
+
}
|
|
26855
|
+
|
|
26856
|
+
return (input.placeholder ? input.placeholder : "");
|
|
26857
|
+
}
|
|
26858
|
+
|
|
26859
|
+
/// <function container="Fit.Controls.Input" name="CheckSpelling" access="public" returns="boolean">
|
|
26860
|
+
/// <description> Get/set value indicating whether control should have spell checking enabled (default) or disabled </description>
|
|
26861
|
+
/// <param name="val" type="boolean" default="undefined"> If defined, true enables spell checking while false disables it </param>
|
|
26862
|
+
/// </function>
|
|
26863
|
+
this.CheckSpelling = function(val)
|
|
26864
|
+
{
|
|
26865
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
26866
|
+
|
|
26867
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26868
|
+
{
|
|
26869
|
+
if (val !== input.spellcheck)
|
|
26870
|
+
{
|
|
26871
|
+
input.spellcheck = val;
|
|
26872
|
+
|
|
26873
|
+
if (me.DesignMode() === true)
|
|
26874
|
+
{
|
|
26875
|
+
reloadEditor();
|
|
26876
|
+
}
|
|
26877
|
+
}
|
|
26878
|
+
}
|
|
26879
|
+
|
|
26880
|
+
return input.spellcheck;
|
|
26881
|
+
}
|
|
26882
|
+
|
|
26883
|
+
/// <function container="Fit.Controls.Input" name="Type" access="public" returns="Fit.Controls.InputType">
|
|
26884
|
+
/// <description> Get/set input type (e.g. Text, Password, Email, etc.) </description>
|
|
26885
|
+
/// <param name="val" type="Fit.Controls.InputType" default="undefined"> If defined, input type is changed to specified value </param>
|
|
26886
|
+
/// </function>
|
|
26887
|
+
this.Type = function(val)
|
|
26888
|
+
{
|
|
26889
|
+
Fit.Validation.ExpectStringValue(val, true);
|
|
26890
|
+
|
|
26891
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26892
|
+
{
|
|
26893
|
+
if (Fit.Validation.IsSet(Fit.Controls.InputType[val]) === false || val === Fit.Controls.InputType.Unknown)
|
|
26894
|
+
Fit.Validation.ThrowError("Unsupported input type specified - use e.g. Fit.Controls.InputType.Text");
|
|
26895
|
+
|
|
26896
|
+
if (val === Fit.Controls.InputType.Textarea)
|
|
26897
|
+
{
|
|
26898
|
+
me.MultiLine(true);
|
|
26899
|
+
}
|
|
26900
|
+
else
|
|
26901
|
+
{
|
|
26902
|
+
me.MultiLine(false);
|
|
26903
|
+
|
|
26904
|
+
if (val === Fit.Controls.InputType.Color)
|
|
26905
|
+
input.type = "color";
|
|
26906
|
+
else if (val === Fit.Controls.InputType.Date)
|
|
26907
|
+
input.type = "date";
|
|
26908
|
+
else if (val === Fit.Controls.InputType.DateTime)
|
|
26909
|
+
input.type = "datetime";
|
|
26910
|
+
else if (val === Fit.Controls.InputType.Email)
|
|
26911
|
+
input.type = "email";
|
|
26912
|
+
else if (val === Fit.Controls.InputType.Month)
|
|
26913
|
+
input.type = "month";
|
|
26914
|
+
else if (val === Fit.Controls.InputType.Number)
|
|
26915
|
+
input.type = "number";
|
|
26916
|
+
else if (val === Fit.Controls.InputType.Password)
|
|
26917
|
+
input.type = "password";
|
|
26918
|
+
else if (val === Fit.Controls.InputType.PhoneNumber)
|
|
26919
|
+
input.type = "tel";
|
|
26920
|
+
else if (val === Fit.Controls.InputType.Text)
|
|
26921
|
+
input.type = "text";
|
|
26922
|
+
else if (val === Fit.Controls.InputType.Time)
|
|
26923
|
+
input.type = "time";
|
|
26924
|
+
else if (val === Fit.Controls.InputType.Week)
|
|
26925
|
+
input.type = "week";
|
|
26926
|
+
}
|
|
26927
|
+
}
|
|
26928
|
+
|
|
26929
|
+
if (me.MultiLine() === true || me.DesignMode() === true)
|
|
26930
|
+
return Fit.Controls.InputType.Textarea;
|
|
26931
|
+
else if (input.type === "color")
|
|
26932
|
+
return Fit.Controls.InputType.Color;
|
|
26933
|
+
else if (input.type === "date")
|
|
26934
|
+
return Fit.Controls.InputType.Date;
|
|
26935
|
+
else if (input.type === "datetime")
|
|
26936
|
+
return Fit.Controls.InputType.DateTime;
|
|
26937
|
+
else if (input.type === "email")
|
|
26938
|
+
return Fit.Controls.InputType.Email;
|
|
26939
|
+
else if (input.type === "month")
|
|
26940
|
+
return Fit.Controls.InputType.Month;
|
|
26941
|
+
else if (input.type === "number")
|
|
26942
|
+
return Fit.Controls.InputType.Number;
|
|
26943
|
+
else if (input.type === "password")
|
|
26944
|
+
return Fit.Controls.InputType.Password;
|
|
26945
|
+
else if (input.type === "tel")
|
|
26946
|
+
return Fit.Controls.InputType.PhoneNumber;
|
|
26947
|
+
else if (input.type === "text")
|
|
26948
|
+
return Fit.Controls.InputType.Text;
|
|
26949
|
+
else if (input.type === "time")
|
|
26950
|
+
return Fit.Controls.InputType.Time;
|
|
26951
|
+
else if (input.type === "week")
|
|
26952
|
+
return Fit.Controls.InputType.Week;
|
|
26953
|
+
|
|
26954
|
+
return Fit.Controls.InputType.Unknown; // Only happens if someone changed the type to an unsupported value through the DOM (e.g. hidden or checkbox)
|
|
26955
|
+
}
|
|
26956
|
+
|
|
26957
|
+
/// <function container="Fit.Controls.Input" name="MultiLine" access="public" returns="boolean">
|
|
26958
|
+
/// <description> Get/set value indicating whether control is in Multi Line mode (textarea) </description>
|
|
26959
|
+
/// <param name="val" type="boolean" default="undefined"> If defined, True enables Multi Line mode, False disables it </param>
|
|
26960
|
+
/// </function>
|
|
26961
|
+
this.MultiLine = function(val)
|
|
26962
|
+
{
|
|
26963
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
26964
|
+
|
|
26965
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
26966
|
+
{
|
|
26967
|
+
if (me.DesignMode() === true && designModeEnabledAndReady() === false)
|
|
26968
|
+
{
|
|
26969
|
+
console.error("MultiLine(boolean) is not allowed for Input control '" + me.GetId() + "' while DesignMode editor is initializing!");
|
|
26970
|
+
return false; // Return current un-modified state - Input control is not in MultiLine mode when DesignMode is enabled
|
|
26971
|
+
}
|
|
26972
|
+
|
|
26973
|
+
if (me.DesignMode() === true)
|
|
26974
|
+
me.DesignMode(false);
|
|
26975
|
+
|
|
26976
|
+
if (val === true && wasAutoChangedToMultiLineMode === true)
|
|
26977
|
+
{
|
|
26978
|
+
wasAutoChangedToMultiLineMode = false;
|
|
26979
|
+
}
|
|
26980
|
+
|
|
26981
|
+
if (val === true && input.tagName === "INPUT")
|
|
26982
|
+
{
|
|
26983
|
+
var focused = me.Focused();
|
|
26984
|
+
|
|
26985
|
+
var oldInput = input;
|
|
26986
|
+
me._internal.RemoveDomElement(oldInput);
|
|
26987
|
+
|
|
26988
|
+
input = document.createElement("textarea");
|
|
26989
|
+
input.value = oldInput.value;
|
|
26990
|
+
input.spellcheck = oldInput.spellcheck;
|
|
26991
|
+
input.placeholder = oldInput.placeholder;
|
|
26992
|
+
input.disabled = oldInput.disabled;
|
|
26993
|
+
input.onkeyup = oldInput.onkeyup;
|
|
26994
|
+
input.onchange = oldInput.onchange;
|
|
26995
|
+
me._internal.AddDomElement(input);
|
|
26996
|
+
|
|
26997
|
+
if (nativeResizableAvailable === true)
|
|
26998
|
+
{
|
|
26999
|
+
Fit.Events.AddHandler(input, "mousemove", function(e)
|
|
27000
|
+
{
|
|
27001
|
+
var ev = Fit.Events.GetEvent(e);
|
|
27002
|
+
|
|
27003
|
+
if (ev.buttons !== 1) // The .buttons property does not exist in older browsers (see nativeResizableAvailable)
|
|
27004
|
+
{
|
|
27005
|
+
return; // Skip - primary button not held down - not resizing
|
|
27006
|
+
}
|
|
27007
|
+
|
|
27008
|
+
if (me.Resizable() !== Fit.Controls.InputResizing.Disabled && (input.style.width !== "" || input.style.height !== "")) // Textarea was resized
|
|
27009
|
+
{
|
|
27010
|
+
me._internal.Data("resized", "true");
|
|
27011
|
+
}
|
|
27012
|
+
});
|
|
27013
|
+
}
|
|
27014
|
+
|
|
27015
|
+
if (focused === true)
|
|
27016
|
+
input.focus();
|
|
27017
|
+
|
|
27018
|
+
me._internal.Data("multiline", "true");
|
|
27019
|
+
repaint();
|
|
27020
|
+
}
|
|
27021
|
+
else if (val === false && input.tagName === "TEXTAREA")
|
|
27022
|
+
{
|
|
27023
|
+
var focused = me.Focused();
|
|
27024
|
+
|
|
27025
|
+
var oldInput = input;
|
|
27026
|
+
me._internal.RemoveDomElement(oldInput);
|
|
27027
|
+
|
|
27028
|
+
if (cmdResize !== null)
|
|
27029
|
+
{
|
|
27030
|
+
me._internal.RemoveDomElement(cmdResize);
|
|
27031
|
+
cmdResize = null;
|
|
27032
|
+
|
|
27033
|
+
me._internal.Data("maximized", "false");
|
|
27034
|
+
me._internal.Data("maximizable", "false");
|
|
27035
|
+
repaint();
|
|
27036
|
+
}
|
|
27037
|
+
else if (resizable !== Fit.Controls.InputResizing.Disabled)
|
|
27038
|
+
{
|
|
27039
|
+
resizable = Fit.Controls.InputResizing.Disabled;
|
|
27040
|
+
me._internal.Data("resizable", resizable.toLowerCase());
|
|
27041
|
+
me._internal.Data("resized", "false");
|
|
27042
|
+
}
|
|
27043
|
+
|
|
27044
|
+
input = document.createElement("input");
|
|
27045
|
+
input.autocomplete = "off";
|
|
27046
|
+
input.type = "text";
|
|
27047
|
+
input.value = oldInput.value;
|
|
27048
|
+
input.spellcheck = oldInput.spellcheck;
|
|
27049
|
+
input.placeholder = oldInput.placeholder;
|
|
27050
|
+
input.disabled = oldInput.disabled;
|
|
27051
|
+
input.onkeyup = oldInput.onkeyup;
|
|
27052
|
+
input.onchange = oldInput.onchange;
|
|
27053
|
+
me._internal.AddDomElement(input);
|
|
27054
|
+
|
|
27055
|
+
me.Height(-1);
|
|
27056
|
+
|
|
27057
|
+
if (focused === true)
|
|
27058
|
+
input.focus();
|
|
27059
|
+
|
|
27060
|
+
wasAutoChangedToMultiLineMode = false;
|
|
27061
|
+
|
|
27062
|
+
me._internal.Data("multiline", "false");
|
|
27063
|
+
repaint();
|
|
27064
|
+
}
|
|
27065
|
+
}
|
|
27066
|
+
|
|
27067
|
+
return (input.tagName === "TEXTAREA" && me.DesignMode() === false);
|
|
27068
|
+
}
|
|
27069
|
+
|
|
27070
|
+
/// <function container="Fit.Controls.Input" name="Resizable" access="public" returns="Fit.Controls.InputResizing">
|
|
27071
|
+
/// <description>
|
|
27072
|
+
/// Get/set value indicating whether control is resizable on supported
|
|
27073
|
+
/// (modern) browsers. Making control resizable will disable Maximizable.
|
|
27074
|
+
/// </description>
|
|
27075
|
+
/// <param name="val" type="Fit.Controls.InputResizing" default="undefined">
|
|
27076
|
+
/// If defined, determines whether control resizes, and in what direction(s).
|
|
27077
|
+
/// </param>
|
|
27078
|
+
/// </function>
|
|
27079
|
+
this.Resizable = function(val)
|
|
27080
|
+
{
|
|
27081
|
+
Fit.Validation.ExpectStringValue(val, true);
|
|
27082
|
+
|
|
27083
|
+
if (val === Fit.Controls.InputResizing.Enabled || val === Fit.Controls.InputResizing.Disabled || val === Fit.Controls.InputResizing.Horizontal || val === Fit.Controls.InputResizing.Vertical)
|
|
27084
|
+
{
|
|
27085
|
+
if (val !== resizable)
|
|
27086
|
+
{
|
|
27087
|
+
if (val !== Fit.Controls.InputResizing.Disabled) // Resizing enabled
|
|
27088
|
+
{
|
|
27089
|
+
if (me.Maximizable() === true)
|
|
27090
|
+
{
|
|
27091
|
+
me.Maximizable(false);
|
|
27092
|
+
//Fit.Browser.Log("Maximizable disabled as Resizable was enabled!");
|
|
27093
|
+
}
|
|
27094
|
+
|
|
27095
|
+
if (me.MultiLine() === false && me.DesignMode() === false)
|
|
27096
|
+
{
|
|
27097
|
+
me.MultiLine(true);
|
|
27098
|
+
wasAutoChangedToMultiLineMode = true;
|
|
27099
|
+
}
|
|
27100
|
+
}
|
|
27101
|
+
else // Resizing disabled
|
|
27102
|
+
{
|
|
27103
|
+
// Reset custom width/height set by user
|
|
27104
|
+
|
|
27105
|
+
var w = me.Width();
|
|
27106
|
+
me.Width(w.Value, w.Unit);
|
|
27107
|
+
|
|
27108
|
+
var h = me.Height();
|
|
27109
|
+
me.Height(h.Value, h.Unit);
|
|
27110
|
+
}
|
|
27111
|
+
|
|
27112
|
+
resizable = val;
|
|
27113
|
+
me._internal.Data("resizable", val.toLowerCase());
|
|
27114
|
+
|
|
27115
|
+
if (val === Fit.Controls.InputResizing.Disabled)
|
|
27116
|
+
{
|
|
27117
|
+
me._internal.Data("resized", "false");
|
|
27118
|
+
|
|
27119
|
+
input.style.width = "";
|
|
27120
|
+
input.style.height = "";
|
|
27121
|
+
input.style.margin = ""; // Chrome adds some odd margin when textarea is resized
|
|
27122
|
+
}
|
|
27123
|
+
|
|
27124
|
+
revertToSingleLineIfNecessary();
|
|
27125
|
+
|
|
27126
|
+
if (me.DesignMode() === true)
|
|
27127
|
+
{
|
|
27128
|
+
reloadEditor();
|
|
27129
|
+
}
|
|
27130
|
+
}
|
|
27131
|
+
}
|
|
27132
|
+
|
|
27133
|
+
return resizable;
|
|
27134
|
+
}
|
|
27135
|
+
|
|
27136
|
+
/// <function container="Fit.Controls.Input" name="Maximizable" access="public" returns="boolean">
|
|
27137
|
+
/// <description>
|
|
27138
|
+
/// Get/set value indicating whether control is maximizable.
|
|
27139
|
+
/// Making control maximizable will disable Resizable.
|
|
27140
|
+
/// </description>
|
|
27141
|
+
/// <param name="val" type="boolean" default="undefined"> If defined, True enables maximize button, False disables it </param>
|
|
27142
|
+
/// <param name="heightMax" type="number" default="undefined">
|
|
27143
|
+
/// If defined, this becomes the height of the input control when maximized.
|
|
27144
|
+
/// The value is considered the same unit set using Height(..) which defaults to px.
|
|
27145
|
+
/// If not set, the value assumes twice the height set using Height(..).
|
|
27146
|
+
/// </param>
|
|
27147
|
+
/// </function>
|
|
27148
|
+
this.Maximizable = function(val, heightMax)
|
|
27149
|
+
{
|
|
27150
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
27151
|
+
Fit.Validation.ExpectNumber(heightMax, true);
|
|
27152
|
+
|
|
27153
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
27154
|
+
{
|
|
27155
|
+
if (val === true && cmdResize === null)
|
|
27156
|
+
{
|
|
27157
|
+
if (me.Resizable() !== Fit.Controls.InputResizing.Disabled)
|
|
27158
|
+
{
|
|
27159
|
+
me.Resizable(Fit.Controls.InputResizing.Disabled);
|
|
27160
|
+
//Fit.Browser.Log("Resizable disabled as Maximizable was enabled!");
|
|
27161
|
+
}
|
|
27162
|
+
|
|
27163
|
+
if (me.MultiLine() === false && me.DesignMode() === false)
|
|
27164
|
+
{
|
|
27165
|
+
me.MultiLine(true);
|
|
27166
|
+
wasAutoChangedToMultiLineMode = true;
|
|
27167
|
+
}
|
|
27168
|
+
|
|
27169
|
+
// Determine height to use when maximizing and minimizing
|
|
27170
|
+
|
|
27171
|
+
var h = me.Height();
|
|
27172
|
+
|
|
27173
|
+
minimizeHeight = h.Value;
|
|
27174
|
+
maximizeHeight = ((Fit.Validation.IsSet(heightMax) === true) ? heightMax : ((minimizeHeight !== -1) ? minimizeHeight * 2 : 300));
|
|
27175
|
+
minMaxUnit = h.Unit;
|
|
27176
|
+
maximizeHeightConfigured = heightMax || -1;
|
|
27177
|
+
|
|
27178
|
+
// Create maximize/minimize button
|
|
27179
|
+
|
|
27180
|
+
cmdResize = document.createElement("span");
|
|
27181
|
+
cmdResize.tabIndex = -1; // Allow button to temporarily gain focus so control does not fire OnBlur
|
|
27182
|
+
cmdResize.onclick = function()
|
|
27183
|
+
{
|
|
27184
|
+
me.Maximized(!me.Maximized());
|
|
27185
|
+
me.Focused(true);
|
|
27186
|
+
}
|
|
27187
|
+
Fit.Dom.AddClass(cmdResize, "fa");
|
|
27188
|
+
Fit.Dom.AddClass(cmdResize, "fa-chevron-down");
|
|
27189
|
+
me._internal.AddDomElement(cmdResize);
|
|
27190
|
+
|
|
27191
|
+
// Update UI
|
|
27192
|
+
|
|
27193
|
+
me._internal.Data("maximizable", "true");
|
|
27194
|
+
repaint();
|
|
27195
|
+
}
|
|
27196
|
+
else if (val === false && cmdResize !== null)
|
|
27197
|
+
{
|
|
27198
|
+
me._internal.RemoveDomElement(cmdResize);
|
|
27199
|
+
cmdResize = null;
|
|
27200
|
+
|
|
27201
|
+
me.Height(minimizeHeight, minMaxUnit);
|
|
27202
|
+
minimizeHeight = -1;
|
|
27203
|
+
maximizeHeight = -1;
|
|
27204
|
+
minMaxUnit = null;
|
|
27205
|
+
|
|
27206
|
+
me._internal.Data("maximizable", "false"); // Also set in MultiLine(..)
|
|
27207
|
+
|
|
27208
|
+
revertToSingleLineIfNecessary();
|
|
27209
|
+
|
|
27210
|
+
repaint();
|
|
27211
|
+
}
|
|
27212
|
+
else if (val === true && cmdResize !== null && Fit.Validation.IsSet(heightMax) === true)
|
|
27213
|
+
{
|
|
27214
|
+
// Already enabled - just update maximize height
|
|
27215
|
+
maximizeHeight = heightMax !== -1 ? heightMax : minimizeHeight * 2;
|
|
27216
|
+
maximizeHeightConfigured = heightMax;
|
|
27217
|
+
}
|
|
27218
|
+
}
|
|
27219
|
+
|
|
27220
|
+
return (cmdResize !== null);
|
|
27221
|
+
}
|
|
27222
|
+
|
|
27223
|
+
/// <function container="Fit.Controls.Input" name="Maximized" access="public" returns="boolean">
|
|
27224
|
+
/// <description> Get/set value indicating whether control is maximized </description>
|
|
27225
|
+
/// <param name="val" type="boolean" default="undefined"> If defined, True maximizes control, False minimizes it </param>
|
|
27226
|
+
/// </function>
|
|
27227
|
+
this.Maximized = function(val)
|
|
27228
|
+
{
|
|
27229
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
27230
|
+
|
|
27231
|
+
var autoGrowEnabled = me.Height().Value === -1 && me.DesignMode() === true;
|
|
27232
|
+
|
|
27233
|
+
if (Fit.Validation.IsSet(val) === true && me.Maximizable() === true && autoGrowEnabled === false)
|
|
27234
|
+
{
|
|
27235
|
+
if (val === true && Fit.Dom.HasClass(cmdResize, "fa-chevron-up") === false)
|
|
27236
|
+
{
|
|
27237
|
+
me.Height(maximizeHeight, minMaxUnit, true);
|
|
27238
|
+
Fit.Dom.RemoveClass(cmdResize, "fa-chevron-down");
|
|
27239
|
+
Fit.Dom.AddClass(cmdResize, "fa-chevron-up");
|
|
27240
|
+
|
|
27241
|
+
me._internal.Data("maximized", "true");
|
|
27242
|
+
repaint();
|
|
27243
|
+
}
|
|
27244
|
+
else if (val === false && Fit.Dom.HasClass(cmdResize, "fa-chevron-down") === false)
|
|
27245
|
+
{
|
|
27246
|
+
me.Height(minimizeHeight, minMaxUnit, true);
|
|
27247
|
+
Fit.Dom.RemoveClass(cmdResize, "fa-chevron-up");
|
|
27248
|
+
Fit.Dom.AddClass(cmdResize, "fa-chevron-down");
|
|
27249
|
+
|
|
27250
|
+
me._internal.Data("maximized", "false"); // Also set in MultiLine(..)
|
|
27251
|
+
repaint();
|
|
27252
|
+
}
|
|
27253
|
+
}
|
|
27254
|
+
|
|
27255
|
+
return (cmdResize !== null && Fit.Dom.HasClass(cmdResize, "fa-chevron-up") === true);
|
|
27256
|
+
}
|
|
27257
|
+
|
|
27258
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfigPluginsImagesConfig">
|
|
27259
|
+
/// <description> Configuration for image plugins </description>
|
|
27260
|
+
/// <member name="Enabled" type="boolean"> Flag indicating whether to enable image plugins or not (defaults to False) </member>
|
|
27261
|
+
/// <member name="EmbedType" type="'base64' | 'blob'" default="undefined">
|
|
27262
|
+
/// How to store and embed images. Base64 (default) is persistent while blob is temporary
|
|
27263
|
+
/// and must be extracted from memory and uploaded/stored to be permanantly persisted.
|
|
27264
|
+
/// References to blobs can be parsed from the HTML value produced by the editor.
|
|
27265
|
+
/// </member>
|
|
27266
|
+
/// <member name="RevokeBlobUrlsOnDispose" type="'All' | 'UnreferencedOnly'" default="undefined">
|
|
27267
|
+
/// This option is in effect when EmbedType is blob.
|
|
27268
|
+
/// Dispose images from blob storage (revoke blob URLs) added though image plugins when control is disposed.
|
|
27269
|
+
/// If "UnreferencedOnly" is specified, the component using Fit.UI's input control will be responsible for
|
|
27270
|
+
/// disposing referenced blobs. Failing to do so may cause a memory leak. Defaults to All.
|
|
27271
|
+
/// </member>
|
|
27272
|
+
/// <member name="RevokeExternalBlobUrlsOnDispose" type="boolean" default="undefined">
|
|
27273
|
+
/// This option is in effect when EmbedType is blob.
|
|
27274
|
+
/// Dispose images from blob storage (revoke blob URLs) added through Value(..)
|
|
27275
|
+
/// function when control is disposed. Basically ownership of these blobs are handed
|
|
27276
|
+
/// over to the control for the duration of its life time.
|
|
27277
|
+
/// These images are furthermore subject to the rule set in RevokeBlobUrlsOnDispose.
|
|
27278
|
+
/// Defaults to False.
|
|
27279
|
+
/// </member>
|
|
27280
|
+
/// </container>
|
|
27281
|
+
|
|
27282
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfigPlugins">
|
|
27283
|
+
/// <description> Additional plugins enabled in DesignMode </description>
|
|
27284
|
+
/// <member name="Emojis" type="boolean" default="undefined"> Plugin(s) related to emoji support (defaults to False) </member>
|
|
27285
|
+
/// <member name="Images" type="Fit.Controls.InputTypeDefs.DesignModeConfigPluginsImagesConfig" default="undefined"> Plugin(s) related to support for images (defaults to False) </member>
|
|
27286
|
+
/// </container>
|
|
27287
|
+
|
|
27288
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfigToolbar">
|
|
27289
|
+
/// <description> Toolbar buttons enabled in DesignMode </description>
|
|
27290
|
+
/// <member name="Formatting" type="boolean" default="undefined"> Enable text formatting (bold, italic, underline) (defaults to True) </member>
|
|
27291
|
+
/// <member name="Justify" type="boolean" default="undefined"> Enable text alignment (defaults to True) </member>
|
|
27292
|
+
/// <member name="Lists" type="boolean" default="undefined"> Enable ordered and unordered lists with indentation (defaults to True) </member>
|
|
27293
|
+
/// <member name="Links" type="boolean" default="undefined"> Enable links (defaults to True) </member>
|
|
27294
|
+
/// <member name="Emojis" type="boolean" default="undefined"> Enable emoji button (defaults to False) </member>
|
|
27295
|
+
/// <member name="Images" type="boolean" default="undefined"> Enable image button (defaults to false) </member>
|
|
27296
|
+
/// <member name="Detach" type="boolean" default="undefined"> Enable detach button (defaults to false) </member>
|
|
27297
|
+
/// <member name="Position" type="'Top' | 'Bottom'" default="undefined"> Toolbar position (defaults to Top) </member>
|
|
27298
|
+
/// <member name="Sticky" type="boolean" default="undefined"> Make toolbar stick to edge of scroll container on supported browsers when scrolling (defaults to False) </member>
|
|
27299
|
+
/// <member name="HideInitially" type="boolean" default="undefined"> Hide toolbar until control gains focus (defaults to False) </member>
|
|
27300
|
+
/// </container>
|
|
27301
|
+
|
|
27302
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfigInfoPanel">
|
|
27303
|
+
/// <description> Information panel at the top or bottom of the editor, depending on the location of the toolbar </description>
|
|
27304
|
+
/// <member name="Text" type="string" default="undefined"> Text to display </member>
|
|
27305
|
+
/// <member name="Alignment" type="'Left' | 'Center' | 'Right'" default="undefined"> Text alignment - defaults to Center </member>
|
|
27306
|
+
/// </container>
|
|
27307
|
+
|
|
27308
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsOnRequestEventHandlerArgs">
|
|
27309
|
+
/// <description> Request handler event arguments </description>
|
|
27310
|
+
/// <member name="Sender" type="Fit.Controls.Input"> Instance of control </member>
|
|
27311
|
+
/// <member name="Request" type="Fit.Http.JsonRequest | Fit.Http.JsonpRequest"> Instance of JsonRequest or JsonpRequest </member>
|
|
27312
|
+
/// <member name="Query" type="{ Marker: string, Query: string }"> Query information </member>
|
|
27313
|
+
/// </container>
|
|
27314
|
+
/// <function container="Fit.Controls.InputTypeDefs" name="DesignModeTagsOnRequest" returns="boolean | void">
|
|
27315
|
+
/// <description> Cancelable request event handler </description>
|
|
27316
|
+
/// <param name="sender" type="Fit.Controls.Input"> Instance of control </param>
|
|
27317
|
+
/// <param name="eventArgs" type="Fit.Controls.InputTypeDefs.DesignModeTagsOnRequestEventHandlerArgs"> Event arguments </param>
|
|
27318
|
+
/// </function>
|
|
27319
|
+
|
|
27320
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponseJsonTag">
|
|
27321
|
+
/// <description> JSON object representing tag </description>
|
|
27322
|
+
/// <member name="Value" type="string"> Unique value </member>
|
|
27323
|
+
/// <member name="Title" type="string"> Title </member>
|
|
27324
|
+
/// <member name="Icon" type="string" default="undefined"> Optional URL to icon/image </member>
|
|
27325
|
+
/// <member name="Url" type="string" default="undefined"> Optional URL to associate with tag </member>
|
|
27326
|
+
/// <member name="Data" type="string" default="undefined"> Optional data to associate with tag </member>
|
|
27327
|
+
/// <member name="Context" type="string" default="undefined"> Optional context information to associate with tag </member>
|
|
27328
|
+
/// </container>
|
|
27329
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponseEventHandlerArgs">
|
|
27330
|
+
/// <description> Response handler event arguments </description>
|
|
27331
|
+
/// <member name="Sender" type="Fit.Controls.Input"> Instance of control </member>
|
|
27332
|
+
/// <member name="Request" type="Fit.Http.JsonRequest | Fit.Http.JsonpRequest"> Instance of JsonRequest or JsonpRequest </member>
|
|
27333
|
+
/// <member name="Query" type="{ Marker: string, Query: string }"> Query information </member>
|
|
27334
|
+
/// <member name="Tags" type="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponseJsonTag[]"> Tags received from WebService </member>
|
|
27335
|
+
/// </container>
|
|
27336
|
+
/// <function container="Fit.Controls.InputTypeDefs" name="DesignModeTagsOnResponse">
|
|
27337
|
+
/// <description> Response event handler </description>
|
|
27338
|
+
/// <param name="sender" type="Fit.Controls.Input"> Instance of control </param>
|
|
27339
|
+
/// <param name="eventArgs" type="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponseEventHandlerArgs"> Event arguments </param>
|
|
27340
|
+
/// </function>
|
|
27341
|
+
|
|
27342
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsTagCreatorReturnType">
|
|
27343
|
+
/// <description> JSON object representing tag to be inserted into editor </description>
|
|
27344
|
+
/// <member name="Title" type="string"> Tag title </member>
|
|
27345
|
+
/// <member name="Value" type="string"> Tag value (ID) </member>
|
|
27346
|
+
/// <member name="Type" type="string"> Tag type (marker) </member>
|
|
27347
|
+
/// <member name="Url" type="string" default="undefined"> Optional tag URL </member>
|
|
27348
|
+
/// <member name="Data" type="string" default="undefined"> Optional tag data </member>
|
|
27349
|
+
/// <member name="Context" type="string" default="undefined"> Optional tag context </member>
|
|
27350
|
+
/// </container>
|
|
27351
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeTagsTagCreatorCallbackArgs">
|
|
27352
|
+
/// <description> TagCreator event arguments </description>
|
|
27353
|
+
/// <member name="Sender" type="Fit.Controls.Input"> Instance of control </member>
|
|
27354
|
+
/// <member name="QueryMarker" type="string"> Query marker </member>
|
|
27355
|
+
/// <member name="Tag" type="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponseJsonTag"> Tag received from WebService </member>
|
|
27356
|
+
/// </container>
|
|
27357
|
+
/// <function container="Fit.Controls.InputTypeDefs" name="DesignModeTagsTagCreator" returns="Fit.Controls.InputTypeDefs.DesignModeTagsTagCreatorReturnType | null | void">
|
|
27358
|
+
/// <description>
|
|
27359
|
+
/// Function producing JSON object representing tag to be inserted into editor.
|
|
27360
|
+
/// Returning nothing or Null results in default tag being inserted into editor.
|
|
27361
|
+
/// </description>
|
|
27362
|
+
/// <param name="sender" type="Fit.Controls.Input"> Instance of control </param>
|
|
27363
|
+
/// <param name="eventArgs" type="Fit.Controls.InputTypeDefs.DesignModeTagsTagCreatorCallbackArgs"> Event arguments </param>
|
|
27364
|
+
/// </function>
|
|
27365
|
+
|
|
27366
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfigTags">
|
|
27367
|
+
/// <description> Configuration for tags in DesignMode </description>
|
|
27368
|
+
/// <member name="Triggers" type="{ Marker: string, MinimumCharacters?: integer, DebounceQuery?: integer }[]"> Markers triggering tags request and context menu </member>
|
|
27369
|
+
/// <member name="QueryUrl" type="string">
|
|
27370
|
+
/// URL to request data from. Endpoint receives the following payload:
|
|
27371
|
+
/// { Marker: "@", Query: "search" }
|
|
27372
|
+
///
|
|
27373
|
+
/// Data is expected to be returned in the following format:
|
|
27374
|
+
/// [
|
|
27375
|
+
/// { Value: "t-1", Title: "Tag 1", Icon: "images/img1.jpeg", Url: "show/1", Data: "..." },
|
|
27376
|
+
/// { Value: "t-2", Title: "Tag 2", Icon: "images/img2.jpeg", Url: "show/2", Data: "..." }, ...
|
|
27377
|
+
/// ]
|
|
27378
|
+
///
|
|
27379
|
+
/// The Value and Title properties are required. The Icon property is optional and must specify the path to an image.
|
|
27380
|
+
/// The Url property is optional and must specify a path to a related page/resource.
|
|
27381
|
+
/// The Data property is optional and allows for additional data to be associated with the tag.
|
|
27382
|
+
/// To hold multiple values, consider using a base64 encoded JSON object:
|
|
27383
|
+
/// btoa(JSON.stringify({ creationDate: new Date(), active: true }))
|
|
27384
|
+
///
|
|
27385
|
+
/// The data eventuelly results in a tag being added to the editor with the following format:
|
|
27386
|
+
/// <a data-tag-type="@" data-tag-id="unique id 1" data-tag-data="..." data-tag-context="..." href="show/1">Tag name 1</a>
|
|
27387
|
+
/// The data-tag-data and data-tag-context attributes are only declared if the corresponding Data and Context properties are defined in data.
|
|
27388
|
+
/// </member>
|
|
27389
|
+
/// <member name="JsonpCallback" type="string" default="undefined"> Name of URL parameter receiving name of JSONP callback function (only for JSONP services) </member>
|
|
27390
|
+
/// <member name="JsonpTimeout" type="integer" default="undefined"> Number of milliseconds to allow JSONP request to wait for a response before aborting (only for JSONP services) </member>
|
|
27391
|
+
/// <member name="OnRequest" type="Fit.Controls.InputTypeDefs.DesignModeTagsOnRequest" default="undefined">
|
|
27392
|
+
/// Event handler invoked when tags are requested. Request may be canceled by returning False.
|
|
27393
|
+
/// Function receives two arguments:
|
|
27394
|
+
/// Sender (Fit.Controls.Input) and EventArgs object.
|
|
27395
|
+
/// EventArgs object contains the following properties:
|
|
27396
|
+
/// - Sender: Fit.Controls.Input instance
|
|
27397
|
+
/// - Request: Fit.Http.JsonpRequest or Fit.Http.JsonRequest instance
|
|
27398
|
+
/// - Query: Contains query information in its Marker and Query property
|
|
27399
|
+
/// </member>
|
|
27400
|
+
/// <member name="OnResponse" type="Fit.Controls.InputTypeDefs.DesignModeTagsOnResponse" default="undefined">
|
|
27401
|
+
/// Event handler invoked when tags data is received, allowing for data transformation.
|
|
27402
|
+
/// Function receives two arguments:
|
|
27403
|
+
/// Sender (Fit.Controls.Input) and EventArgs object.
|
|
27404
|
+
/// EventArgs object contains the following properties:
|
|
27405
|
+
/// - Sender: Fit.Controls.Input instance
|
|
27406
|
+
/// - Request: Fit.Http.JsonpRequest or Fit.Http.JsonRequest instance
|
|
27407
|
+
/// - Query: Contains query information in its Marker and Query property
|
|
27408
|
+
/// - Tags: JSON tags array received from WebService
|
|
27409
|
+
/// </member>
|
|
27410
|
+
/// <member name="TagCreator" type="Fit.Controls.InputTypeDefs.DesignModeTagsTagCreator" default="undefined">
|
|
27411
|
+
/// Callback invoked when a tag is being inserted into editor, allowing
|
|
27412
|
+
/// for customization to the title and attributes associated with the tag.
|
|
27413
|
+
/// Function receives two arguments:
|
|
27414
|
+
/// Sender (Fit.Controls.Input) and EventArgs object.
|
|
27415
|
+
/// EventArgs object contains the following properties:
|
|
27416
|
+
/// - Sender: Fit.Controls.Input instance
|
|
27417
|
+
/// - QueryMarker: String containing query marker
|
|
27418
|
+
/// - Tag: JSON tag received from WebService
|
|
27419
|
+
/// </member>
|
|
27420
|
+
/// </container>
|
|
27421
|
+
|
|
27422
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeAutoGrow">
|
|
27423
|
+
/// <description> Auto grow configuration </description>
|
|
27424
|
+
/// <member name="Enabled" type="boolean"> Flag indicating whether auto grow feature is enabled or not - on by default if no height is set, or if Height(-1) is set </member>
|
|
27425
|
+
/// <member name="MinimumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum height of editable area </member>
|
|
27426
|
+
/// <member name="MaximumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum height of editable area </member>
|
|
27427
|
+
/// <member name="PreventResizeBeyondMaximumHeight" type="boolean" default="undefined"> Prevent user from resizing editor beyond maximum height (see MaximumHeight property - defaults to False) </member>
|
|
27428
|
+
/// </container>
|
|
27429
|
+
|
|
27430
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeDetachable">
|
|
27431
|
+
/// <description> Detachable configuration </description>
|
|
27432
|
+
/// <member name="Title" type="string" default="undefined"> Dialog title </member>
|
|
27433
|
+
/// <member name="Maximizable" type="boolean" default="undefined"> Flag indicating whether dialog is maximizable </member>
|
|
27434
|
+
/// <member name="Maximized" type="boolean" default="undefined"> Flag indicating whether dialog is initially maximized </member>
|
|
27435
|
+
/// <member name="Draggable" type="boolean" default="undefined"> Flag indicating whether dialog is draggable </member>
|
|
27436
|
+
/// <member name="Resizable" type="boolean" default="undefined"> Flag indicating whether dialog is resizable </member>
|
|
27437
|
+
/// <member name="Width" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Dialog width </member>
|
|
27438
|
+
/// <member name="MinimumWidth" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum width of dialog </member>
|
|
27439
|
+
/// <member name="MaximumWidth" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum Width of dialog </member>
|
|
27440
|
+
/// <member name="Height" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Dialog height </member>
|
|
27441
|
+
/// <member name="MinimumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Minimum height of dialog </member>
|
|
27442
|
+
/// <member name="MaximumHeight" type="{ Value: number, Unit?: Fit.TypeDefs.CssUnit }" default="undefined"> Maximum height of dialog </member>
|
|
27443
|
+
/// </container>
|
|
27444
|
+
|
|
27445
|
+
/// <container name="Fit.Controls.InputTypeDefs.DesignModeConfig">
|
|
27446
|
+
/// <description> Configuration for DesignMode </description>
|
|
27447
|
+
/// <member name="Plugins" type="Fit.Controls.InputTypeDefs.DesignModeConfigPlugins" default="undefined"> Plugins configuration </member>
|
|
27448
|
+
/// <member name="Toolbar" type="Fit.Controls.InputTypeDefs.DesignModeConfigToolbar" default="undefined"> Toolbar configuration </member>
|
|
27449
|
+
/// <member name="InfoPanel" type="Fit.Controls.InputTypeDefs.DesignModeConfigInfoPanel" default="undefined"> Information panel configuration </member>
|
|
27450
|
+
/// <member name="Tags" type="Fit.Controls.InputTypeDefs.DesignModeConfigTags" default="undefined"> Tags configuration </member>
|
|
27451
|
+
/// <member name="AutoGrow" type="Fit.Controls.InputTypeDefs.DesignModeAutoGrow" default="undefined"> Auto grow configuration </member>
|
|
27452
|
+
/// <member name="Detachable" type="Fit.Controls.InputTypeDefs.DesignModeDetachable" default="undefined"> Detachable configuration </member>
|
|
27453
|
+
/// </container>
|
|
27454
|
+
|
|
27455
|
+
/// <function container="Fit.Controls.Input" name="DesignMode" access="public" returns="boolean">
|
|
27456
|
+
/// <description>
|
|
27457
|
+
/// Get/set value indicating whether control is in Design Mode allowing for rich HTML content.
|
|
27458
|
+
/// Notice that this control type requires dimensions (Width/Height) to be specified in pixels.
|
|
27459
|
+
/// </description>
|
|
27460
|
+
/// <param name="val" type="boolean" default="undefined"> If defined, True enables Design Mode, False disables it </param>
|
|
27461
|
+
/// <param name="editorConfig" type="Fit.Controls.InputTypeDefs.DesignModeConfig" default="undefined">
|
|
27462
|
+
/// If provided and DesignMode is enabled, configuration is applied when editor is created.
|
|
27463
|
+
/// </param>
|
|
27464
|
+
/// </function>
|
|
27465
|
+
this.DesignMode = function(val, editorConfig)
|
|
27466
|
+
{
|
|
27467
|
+
Fit.Validation.ExpectBoolean(val, true);
|
|
27468
|
+
Fit.Validation.ExpectObject(editorConfig, true);
|
|
27469
|
+
Fit.Validation.ExpectObject((editorConfig || {}).Plugins, true);
|
|
27470
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Plugins || {}).Emojis, true);
|
|
27471
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Plugins || {}).Images, true);
|
|
27472
|
+
Fit.Validation.ExpectBoolean((((editorConfig || {}).Plugins || {}).Images || {}).Enabled, true);
|
|
27473
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Plugins || {}).Images || {}).EmbedType, true);
|
|
27474
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Plugins || {}).Images || {}).RevokeBlobUrlsOnDispose, true);
|
|
27475
|
+
Fit.Validation.ExpectBoolean((((editorConfig || {}).Plugins || {}).Images || {}).RevokeExternalBlobUrlsOnDispose, true);
|
|
27476
|
+
Fit.Validation.ExpectObject((editorConfig || {}).Toolbar, true);
|
|
27477
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Formatting, true);
|
|
27478
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Justify, true);
|
|
27479
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Lists, true);
|
|
27480
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Links, true);
|
|
27481
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Emojis, true);
|
|
27482
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Images, true);
|
|
27483
|
+
Fit.Validation.ExpectStringValue(((editorConfig || {}).Toolbar || {}).Position, true);
|
|
27484
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Sticky, true);
|
|
27485
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).HideInitially, true);
|
|
27486
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Toolbar || {}).Detach, true);
|
|
27487
|
+
Fit.Validation.ExpectObject((editorConfig || {}).InfoPanel, true);
|
|
27488
|
+
Fit.Validation.ExpectString(((editorConfig || {}).InfoPanel || {}).Text, true);
|
|
27489
|
+
Fit.Validation.ExpectString(((editorConfig || {}).InfoPanel || {}).Alignment, true);
|
|
27490
|
+
Fit.Validation.ExpectObject((editorConfig || {}).Tags, true);
|
|
27491
|
+
Fit.Validation.ExpectObject((editorConfig || {}).AutoGrow, true);
|
|
27492
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).AutoGrow || {}).Enabled, true);
|
|
27493
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).AutoGrow || {}).MinimumHeight, true);
|
|
27494
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).AutoGrow || {}).MinimumHeight || {}).Value, true);
|
|
27495
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).AutoGrow || {}).MinimumHeight || {}).Unit, true);
|
|
27496
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).AutoGrow || {}).MaximumHeight, true);
|
|
27497
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Value, true);
|
|
27498
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).AutoGrow || {}).MaximumHeight || {}).Unit, true);
|
|
27499
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).AutoGrow || {}).PreventResizeBeyondMaximumHeight, true);
|
|
27500
|
+
Fit.Validation.ExpectObject((editorConfig || {}).Detachable, true);
|
|
27501
|
+
Fit.Validation.ExpectString(((editorConfig || {}).Detachable || {}).Title, true);
|
|
27502
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Maximizable, true);
|
|
27503
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Maximized, true);
|
|
27504
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Draggable, true);
|
|
27505
|
+
Fit.Validation.ExpectBoolean(((editorConfig || {}).Detachable || {}).Resizable, true);
|
|
27506
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).Width, true);
|
|
27507
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).Width || {}).Value, true);
|
|
27508
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).Width || {}).Unit, true);
|
|
27509
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MinimumWidth, true);
|
|
27510
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MinimumWidth || {}).Value, true);
|
|
27511
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MinimumWidth || {}).Unit, true);
|
|
27512
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MaximumWidth, true);
|
|
27513
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MaximumWidth || {}).Value, true);
|
|
27514
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MaximumWidth || {}).Unit, true);
|
|
27515
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).Height, true);
|
|
27516
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).Height || {}).Value, true);
|
|
27517
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).Height || {}).Unit, true);
|
|
27518
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MinimumHeight, true);
|
|
27519
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MinimumHeight || {}).Value, true);
|
|
27520
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MinimumHeight || {}).Unit, true);
|
|
27521
|
+
Fit.Validation.ExpectObject(((editorConfig || {}).Detachable || {}).MaximumHeight, true);
|
|
27522
|
+
Fit.Validation.ExpectNumber((((editorConfig || {}).Detachable || {}).MaximumHeight || {}).Value, true);
|
|
27523
|
+
Fit.Validation.ExpectStringValue((((editorConfig || {}).Detachable || {}).MaximumHeight || {}).Unit, true);
|
|
27524
|
+
|
|
27525
|
+
if (editorConfig && editorConfig.Tags)
|
|
27526
|
+
{
|
|
27527
|
+
Fit.Validation.ExpectTypeArray(editorConfig.Tags.Triggers, function(trigger)
|
|
27528
|
+
{
|
|
27529
|
+
Fit.Validation.ExpectStringValue(trigger.Marker);
|
|
27530
|
+
Fit.Validation.ExpectInteger(trigger.MinimumCharacters, true);
|
|
27531
|
+
Fit.Validation.ExpectInteger(trigger.DebounceQuery, true);
|
|
27532
|
+
});
|
|
27533
|
+
Fit.Validation.ExpectStringValue(editorConfig.Tags.QueryUrl);
|
|
27534
|
+
Fit.Validation.ExpectStringValue(editorConfig.Tags.JsonpCallback, true);
|
|
27535
|
+
Fit.Validation.ExpectInteger(editorConfig.Tags.JsonpTimeout, true);
|
|
27536
|
+
Fit.Validation.ExpectFunction(editorConfig.Tags.OnRequest, true);
|
|
27537
|
+
Fit.Validation.ExpectFunction(editorConfig.Tags.OnResponse, true);
|
|
27538
|
+
Fit.Validation.ExpectFunction(editorConfig.Tags.TagCreator, true);
|
|
27539
|
+
}
|
|
27540
|
+
|
|
27541
|
+
if (Fit.Validation.IsSet(val) === true)
|
|
27542
|
+
{
|
|
27543
|
+
var designMode = (me._internal.Data("designmode") === "true");
|
|
27544
|
+
|
|
27545
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me && Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed === true)
|
|
27546
|
+
designMode = false; // Not considered in Design Mode if scheduled to be disabled (postponed because a dialog is currently loading)
|
|
27547
|
+
|
|
27548
|
+
if ((val === true && designMode === false) || (val === true && Fit.Validation.IsSet(editorConfig) === true && Fit.Core.IsEqual(editorConfig, designEditorConfig) === false))
|
|
27549
|
+
{
|
|
27550
|
+
var configUpdated = designMode === true; // Already in DesignMode which means editorConfig was changed
|
|
27551
|
+
|
|
27552
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
|
|
27553
|
+
{
|
|
27554
|
+
// Control is actually already in Design Mode, but waiting
|
|
27555
|
+
// for dialog to finish loading, so DesignMode can be disabled (scheduled).
|
|
27556
|
+
// Remove flag responsible for disabling DesignMode so it remains an editor.
|
|
27557
|
+
delete Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed;
|
|
27558
|
+
|
|
27559
|
+
if (configUpdated === false)
|
|
27560
|
+
{
|
|
27561
|
+
return true;
|
|
27562
|
+
}
|
|
27563
|
+
}
|
|
27564
|
+
|
|
27565
|
+
if (configUpdated === true)
|
|
27566
|
+
{
|
|
27567
|
+
reloadEditor(false, Fit.Core.Clone(editorConfig)); // Clone to prevent external code from making changes later
|
|
27568
|
+
return true;
|
|
27569
|
+
}
|
|
27570
|
+
|
|
27571
|
+
if (Fit.Validation.IsSet(editorConfig) === true)
|
|
27572
|
+
{
|
|
27573
|
+
designEditorConfig = Fit.Core.Clone(editorConfig); // Clone to prevent external code from making changes later
|
|
27574
|
+
}
|
|
27575
|
+
|
|
27576
|
+
// Notice: Identical logic found in Value(..)!
|
|
27577
|
+
if (designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.RevokeExternalBlobUrlsOnDispose === true)
|
|
27578
|
+
{
|
|
27579
|
+
// Keep track of image blobs added via Value(..) so we can dispose of them automatically.
|
|
27580
|
+
// When RevokeExternalBlobUrlsOnDispose is True it basically means that the Input control
|
|
27581
|
+
// is allowed (and expected) to take control over memory management for these blobs
|
|
27582
|
+
// based on the rule set in RevokeBlobUrlsOnDispose.
|
|
27583
|
+
// This code is also found in Value(..) since images might be added after editor has been created.
|
|
27584
|
+
|
|
27585
|
+
var blobUrls = Fit.String.ParseImageBlobUrls(me.Value());
|
|
27586
|
+
|
|
27587
|
+
Fit.Array.ForEach(blobUrls, function(blobUrl)
|
|
27588
|
+
{
|
|
27589
|
+
if (Fit.Array.Contains(imageBlobUrls, blobUrl) === false)
|
|
27590
|
+
{
|
|
27591
|
+
Fit.Array.Add(imageBlobUrls, blobUrl);
|
|
27592
|
+
}
|
|
27593
|
+
});
|
|
27594
|
+
}
|
|
27595
|
+
|
|
27596
|
+
if (me.MultiLine() === false)
|
|
27597
|
+
{
|
|
27598
|
+
me.MultiLine(true);
|
|
27599
|
+
wasAutoChangedToMultiLineMode = true;
|
|
27600
|
+
}
|
|
27601
|
+
|
|
27602
|
+
me._internal.Data("designmode", "true");
|
|
27603
|
+
me._internal.Data("toolbar", designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.HideInitially === true ? "false" : "true");
|
|
27604
|
+
me._internal.Data("toolbar-position", designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.Position === "Bottom" ? "bottom" : "top");
|
|
27605
|
+
me._internal.Data("toolbar-sticky", designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.Sticky === true ? "true" : "false");
|
|
27606
|
+
|
|
27607
|
+
// Prevent control from losing focus when HTML editor is initialized,
|
|
27608
|
+
// e.g. if Design Mode is enabled when ordinary input control gains focus.
|
|
27609
|
+
// This also prevents control from losing focus if toolbar is clicked without
|
|
27610
|
+
// hitting a button. A value of -1 makes it focusable, but keeps it out of
|
|
27611
|
+
// tab flow (keyboard navigation). Also set when Enabled(true) is called.
|
|
27612
|
+
me.GetDomElement().tabIndex = -1; // TabIndex is removed if DesignMode is disabled (DesignMode(false)) or if control is disabled (Enabled(false))
|
|
27613
|
+
|
|
27614
|
+
if (me.Focused() === true)
|
|
27615
|
+
{
|
|
27616
|
+
// Move focus from input to control's outer container (tabindex
|
|
27617
|
+
// set above) to keep focus while editor is loading/initializing.
|
|
27618
|
+
// Focus is moved to editor once initialization is complete - see
|
|
27619
|
+
// instanceReady handler.
|
|
27620
|
+
me.GetDomElement().focus();
|
|
27621
|
+
}
|
|
27622
|
+
|
|
27623
|
+
input.id = me.GetId() + "_DesignMode";
|
|
27624
|
+
|
|
27625
|
+
if (window.CKEDITOR === undefined)
|
|
27626
|
+
{
|
|
27627
|
+
window.CKEDITOR = null;
|
|
27628
|
+
|
|
27629
|
+
Fit.Loader.LoadScript(Fit.GetUrl() + "/Resources/CKEditor/ckeditor.js?cacheKey=" + Fit.GetVersion().Version, function(src) // Using Fit.GetUrl() rather than Fit.GetPath() to allow editor to be used on e.g. JSFiddle (Cross-Origin Resource Sharing policy)
|
|
27630
|
+
{
|
|
27631
|
+
// WARNING: Control could potentially have been disposed at this point, but
|
|
27632
|
+
// we still need to finalize the configuration of CKEditor which is global.
|
|
27633
|
+
|
|
27634
|
+
if (Fit.Validation.IsSet(Fit._internal.Controls.Input.Editor.Skin) === true)
|
|
27635
|
+
{
|
|
27636
|
+
CKEDITOR.config.skin = Fit._internal.Controls.Input.Editor.Skin;
|
|
27637
|
+
}
|
|
27638
|
+
|
|
27639
|
+
CKEDITOR.on("instanceReady", function(ev)
|
|
27640
|
+
{
|
|
27641
|
+
// Do not produce XHTML self-closing tags such as <br /> and <img src="img.jpg" />
|
|
27642
|
+
// https://ckeditor.com/docs/ckeditor4/latest/features/output_format.html
|
|
27643
|
+
// NOTICE: The htmlwriter plugin is required for this to work!
|
|
27644
|
+
// Output produced is now both HTML4 and HTML5 compatible, but is not valid
|
|
27645
|
+
// XHTML anymore! Self-closing tags are allowed in HTML5 but not valid in HTML4.
|
|
27646
|
+
ev.editor.dataProcessor.writer.selfClosingEnd = ">"; // Defaults to ' />'
|
|
27647
|
+
});
|
|
27648
|
+
|
|
27649
|
+
// Register OnShow and OnHide event handlers when a dialog is opened for the first time.
|
|
27650
|
+
// IMPORTANT: These event handlers are shared by all input control instances in Design Mode,
|
|
27651
|
+
// so we cannot use 'me' to access the current control for which a dialog is opened.
|
|
27652
|
+
// Naturally 'me' will always be a reference to the first control that opened a given dialog.
|
|
27653
|
+
CKEDITOR.on("dialogDefinition", function(e) // OnDialogDefinition fires only once
|
|
27654
|
+
{
|
|
27655
|
+
//var dialogName = e.data.name;
|
|
27656
|
+
var dialog = e.data.definition.dialog;
|
|
27657
|
+
|
|
27658
|
+
dialog.on("show", function(ev)
|
|
27659
|
+
{
|
|
27660
|
+
if (Fit._internal.Controls.Input.ActiveDialogForEditorCanceled)
|
|
27661
|
+
{
|
|
27662
|
+
// Focused(false) was called on control while dialog was loading - close dialog
|
|
27663
|
+
|
|
27664
|
+
if (Fit.Browser.GetBrowser() === "MSIE" && Fit.Browser.GetVersion() < 9)
|
|
27665
|
+
{
|
|
27666
|
+
// CKEditor uses setTimeout(..) to focus an input field in the dialog, but if the dialog is
|
|
27667
|
+
// closed immediately, that input field will be removed from DOM along with the dialog of course,
|
|
27668
|
+
// which in IE8 results in an error:
|
|
27669
|
+
// "Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."
|
|
27670
|
+
// Other browsers simply ignore the request to focus a control that is no longer found in DOM.
|
|
27671
|
+
setTimeout(function()
|
|
27672
|
+
{
|
|
27673
|
+
ev.sender.hide(); // Fires OnHide
|
|
27674
|
+
}, 100);
|
|
27675
|
+
}
|
|
27676
|
+
else
|
|
27677
|
+
{
|
|
27678
|
+
ev.sender.hide(); // Fires OnHide
|
|
27679
|
+
}
|
|
27680
|
+
|
|
27681
|
+
return;
|
|
27682
|
+
}
|
|
27683
|
+
|
|
27684
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === undefined)
|
|
27685
|
+
return; // Control was disposed while waiting for dialog to load and open
|
|
27686
|
+
|
|
27687
|
+
Fit.Dom.Data(ev.sender.getElement().$, "skin", CKEDITOR.config.skin); // Add e.g. data-skin="bootstrapck" to dialog - used in Input.css
|
|
27688
|
+
|
|
27689
|
+
// Keep instance to dialog so we can close it if e.g. Focused(false) is invoked
|
|
27690
|
+
Fit._internal.Controls.Input.ActiveDialogForEditor = ev.sender;
|
|
27691
|
+
|
|
27692
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialogDestroyed)
|
|
27693
|
+
{
|
|
27694
|
+
// Dispose() was called on control while dialog was loading.
|
|
27695
|
+
// Since destroying editor while a dialog is loading would cause
|
|
27696
|
+
// an error in CKEditor, the operation has been postponed til dialog's
|
|
27697
|
+
// OnShow event fires, and the dialog is ready.
|
|
27698
|
+
setTimeout(function()
|
|
27699
|
+
{
|
|
27700
|
+
// Dispose() calls destroy() on editor which closes dialog and causes the dialog's OnHide event to fire.
|
|
27701
|
+
// Dispose() uses Fit._internal.Controls.Input.ActiveDialogForEditor, which is why it is set above, before
|
|
27702
|
+
// checking whether control has been destroyed (scheduled for destruction).
|
|
27703
|
+
Fit._internal.Controls.Input.ActiveEditorForDialog.Dispose();
|
|
27704
|
+
}, 0); // Postponed - CKEditor throws an error if destroyed from OnShow event handler
|
|
27705
|
+
|
|
27706
|
+
return;
|
|
27707
|
+
}
|
|
27708
|
+
|
|
27709
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed)
|
|
27710
|
+
{
|
|
27711
|
+
setTimeout(function()
|
|
27712
|
+
{
|
|
27713
|
+
delete Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed;
|
|
27714
|
+
|
|
27715
|
+
// DesignMode(false) calls destroy() on editor which closes dialog and causes the dialog's OnHide event to fire.
|
|
27716
|
+
Fit._internal.Controls.Input.ActiveEditorForDialog.DesignMode(false);
|
|
27717
|
+
}, 0); // Postponed - CKEditor throws an error if destroyed from OnShow event handler
|
|
27718
|
+
|
|
27719
|
+
return;
|
|
27720
|
+
}
|
|
27721
|
+
|
|
27722
|
+
// Allow light dismissable panels/callouts to prevent close/dismiss
|
|
27723
|
+
// when interacting with editor dialogs hosted outside of these panels/callouts,
|
|
27724
|
+
// by detecting the presence of the data-disable-light-dismiss="true" attribute.
|
|
27725
|
+
|
|
27726
|
+
var ckeDialogElement = this.getElement().$;
|
|
27727
|
+
Fit.Dom.Data(ckeDialogElement, "disable-light-dismiss", "true");
|
|
27728
|
+
|
|
27729
|
+
var bgModalLayer = document.querySelector("div.cke_dialog_background_cover"); // Shared among instances
|
|
27730
|
+
if (bgModalLayer !== null) // Better safe than sorry
|
|
27731
|
+
{
|
|
27732
|
+
Fit.Dom.Data(bgModalLayer, "disable-light-dismiss", "true");
|
|
27733
|
+
}
|
|
27734
|
+
|
|
27735
|
+
// Reduce pollution of document root
|
|
27736
|
+
|
|
27737
|
+
if (Fit._internal.ControlBase.ReduceDocumentRootPollution === true)
|
|
27738
|
+
{
|
|
27739
|
+
// Move dialog to control - otherwise placed in the root of the document where it pollutes,
|
|
27740
|
+
// and makes it impossible to interact with the dialog in light dismissable panels and callouts.
|
|
27741
|
+
// Dialog is placed alongside control and not within the control's container, to prevent Fit.UI
|
|
27742
|
+
// styling from affecting the dialog.
|
|
27743
|
+
// DISABLED: It breaks file picker controls in dialogs which are hosted in iframes.
|
|
27744
|
+
// When an iframe is re-rooted in DOM it reloads, and any dynamically created content is lost.
|
|
27745
|
+
// We will have to increase the z-index to make sure dialogs open on top of modal layers.
|
|
27746
|
+
// EDIT 2021-08-20: Enabled again. The base64image plugin has now been altered so it no longer
|
|
27747
|
+
// uses CKEditor's built-in file picker which is wrapped in an iFrame. Therefore the dialog can
|
|
27748
|
+
// once again be mounted next to the Input control.
|
|
27749
|
+
|
|
27750
|
+
var ckeDialogElement = this.getElement().$;
|
|
27751
|
+
Fit.Dom.InsertAfter(Fit._internal.Controls.Input.ActiveEditorForDialog.GetDomElement(), ckeDialogElement);
|
|
27752
|
+
|
|
27753
|
+
// 2nd+ time dialog is opened it remains invisible - make it appear and position it
|
|
27754
|
+
ckeDialogElement.style.display = !CKEDITOR.env.ie || CKEDITOR.env.edge ? "flex" : ""; // https://github.com/ckeditor/ckeditor4/blob/8b208d05d1338d046cdc8f971c9faf21604dd75d/plugins/dialog/plugin.js#L152
|
|
27755
|
+
this.layout(); // 'this' is the dialog instance - layout() positions dialog
|
|
27756
|
+
|
|
27757
|
+
// Temporarily move modal background layer next to control to ensure it is part of same
|
|
27758
|
+
// stacking context as control and dialog. Otherwise it might stay on top of a panel containing
|
|
27759
|
+
// the editor, if that panel has position:fixed (which creates a separate stacking context)
|
|
27760
|
+
// and a lower z-index than the background layer.
|
|
27761
|
+
// Be aware that the background layer also has position:fixed so it will "escape" a parent
|
|
27762
|
+
// container that also has position:fixed, still making it stretch from the upper left corner
|
|
27763
|
+
// of the screen to the lower right corner of the screen. However, if a parent is using
|
|
27764
|
+
// CSS transform, it will prevent the background layer from escaping, instead positioning it
|
|
27765
|
+
// and making it stretch from the upper left corner of the container using transform, to the
|
|
27766
|
+
// lower right corner of that container.
|
|
27767
|
+
// Also be aware that the use of transform will cause minor problems when moving/dragging dialog,
|
|
27768
|
+
// although that is not related to remounting of the background layer.
|
|
27769
|
+
var bgModalLayer = document.querySelector("div.cke_dialog_background_cover");
|
|
27770
|
+
if (bgModalLayer !== null) // Better safe than sorry
|
|
27771
|
+
{
|
|
27772
|
+
Fit.Dom.InsertAfter(Fit._internal.Controls.Input.ActiveEditorForDialog.GetDomElement(), bgModalLayer);
|
|
27773
|
+
}
|
|
27774
|
+
|
|
27775
|
+
if (ev.sender.definition.title === "Image")
|
|
27776
|
+
{
|
|
27777
|
+
// Hide image resize handles placed in the root of the document.
|
|
27778
|
+
// When the modal background layer above is rooted next to the control,
|
|
27779
|
+
// it becomes impossible to ensure the resize handles remains hidden behind
|
|
27780
|
+
// the background layer, since the control may be part of a stacking context
|
|
27781
|
+
// different from the one containing the image resize handles (document root).
|
|
27782
|
+
// It would require dynamic z-index values to achieve this. Therefore the
|
|
27783
|
+
// resize handles are temporarily hidden instead.
|
|
27784
|
+
|
|
27785
|
+
var imageResizeHandlersContainer = document.querySelector("#ckimgrsz");
|
|
27786
|
+
if (imageResizeHandlersContainer !== null) // Better safe than sorry
|
|
27787
|
+
{
|
|
27788
|
+
imageResizeHandlersContainer.style.display = "none";
|
|
27789
|
+
}
|
|
27790
|
+
}
|
|
27791
|
+
}
|
|
27792
|
+
});
|
|
27793
|
+
|
|
27794
|
+
dialog.on("hide", function(ev) // Fires when user closes dialog, or when hide() is called on dialog, or if destroy() is called on editor instance from Dispose() or DesignMode(false)
|
|
27795
|
+
{
|
|
27796
|
+
var inputControl = Fit._internal.Controls.Input.ActiveEditorForDialog;
|
|
27797
|
+
var showCanceledDueToBlur = Fit._internal.Controls.Input.ActiveDialogForEditorCanceled === true;
|
|
27798
|
+
|
|
27799
|
+
// Clean up global references accessible while dialog is open
|
|
27800
|
+
delete Fit._internal.Controls.Input.ActiveEditorForDialog;
|
|
27801
|
+
delete Fit._internal.Controls.Input.ActiveEditorForDialogDestroyed;
|
|
27802
|
+
delete Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed;
|
|
27803
|
+
delete Fit._internal.Controls.Input.ActiveDialogForEditor;
|
|
27804
|
+
delete Fit._internal.Controls.Input.ActiveDialogForEditorCanceled;
|
|
27805
|
+
|
|
27806
|
+
if (Fit._internal.ControlBase.ReduceDocumentRootPollution === true)
|
|
27807
|
+
{
|
|
27808
|
+
// Return modal background layer to document root - it was temporarily moved
|
|
27809
|
+
// next to control to ensure it works properly with current stacking context.
|
|
27810
|
+
// See comments regarding this in dialog "show" handler registered above.
|
|
27811
|
+
// The background layer will not work for other editor instances if not moved back.
|
|
27812
|
+
var bgModalLayer = document.querySelector("div.cke_dialog_background_cover");
|
|
27813
|
+
if (bgModalLayer !== null) // Better safe than sorry
|
|
27814
|
+
{
|
|
27815
|
+
Fit.Dom.Add(document.body, bgModalLayer);
|
|
27816
|
+
}
|
|
27817
|
+
|
|
27818
|
+
// Allow image resize handlers to show up again (hidden in "show" handler registered above)
|
|
27819
|
+
if (ev.sender.definition.title === "Image")
|
|
27820
|
+
{
|
|
27821
|
+
var imageResizeHandlersContainer = document.querySelector("#ckimgrsz");
|
|
27822
|
+
if (imageResizeHandlersContainer !== null) // Better safe than sorry
|
|
27823
|
+
{
|
|
27824
|
+
imageResizeHandlersContainer.style.display = "";
|
|
27825
|
+
}
|
|
27826
|
+
}
|
|
27827
|
+
}
|
|
27828
|
+
|
|
27829
|
+
// Disable focus lock - let ControlBase handle OnFocus and OnBlur automatically again.
|
|
27830
|
+
// This is done postponed since unlocking it immediately will cause OnFocus to fire when
|
|
27831
|
+
// dialog returns focus to the editor.
|
|
27832
|
+
setTimeout(function()
|
|
27833
|
+
{
|
|
27834
|
+
if (inputControl.GetDomElement() === null)
|
|
27835
|
+
return; // Control was disposed - OnHide was fired because destroy() was called on editor instance from Dispose()
|
|
27836
|
+
|
|
27837
|
+
inputControl._internal.FocusStateLocked(false);
|
|
27838
|
+
|
|
27839
|
+
if (showCanceledDueToBlur === true)
|
|
27840
|
+
{
|
|
27841
|
+
// Undo focus which dialog returned to editor.
|
|
27842
|
+
// ControlBase fires OnBlur because focus state was unlocked above.
|
|
27843
|
+
Fit.Dom.GetFocused().blur();
|
|
27844
|
+
}
|
|
27845
|
+
}, 0);
|
|
27846
|
+
});
|
|
27847
|
+
});
|
|
27848
|
+
|
|
27849
|
+
if (me === null)
|
|
27850
|
+
return; // Control was disposed while waiting for jQuery UI to load
|
|
27851
|
+
|
|
27852
|
+
if (me.DesignMode() === false)
|
|
27853
|
+
return; // DesignMode was disabled while waiting for resources to load
|
|
27854
|
+
|
|
27855
|
+
createEditor();
|
|
27856
|
+
});
|
|
27857
|
+
}
|
|
27858
|
+
else if (window.CKEDITOR === null)
|
|
27859
|
+
{
|
|
27860
|
+
if (createWhenReadyIntervalId === -1) // Make sure DesignMode has not been enabled multiple times - e.g. DesignMode(true); DesignMode(false); DesignMode(true); - in which case an interval timer may already be "waiting" for CKEditor resources to finish loading
|
|
27861
|
+
{
|
|
27862
|
+
createWhenReadyIntervalId = setInterval(function()
|
|
27863
|
+
{
|
|
27864
|
+
/*if (me === null)
|
|
27865
|
+
{
|
|
27866
|
+
// Control was disposed while waiting for CKEditor to finish loading
|
|
27867
|
+
clearInterval(iId);
|
|
27868
|
+
return;
|
|
27869
|
+
}*/
|
|
27870
|
+
|
|
27871
|
+
if (window.CKEDITOR !== null)
|
|
27872
|
+
{
|
|
27873
|
+
clearInterval(createWhenReadyIntervalId);
|
|
27874
|
+
createWhenReadyIntervalId = -1;
|
|
27875
|
+
|
|
27876
|
+
// Create editor if still in DesignMode (might have been disabled while waiting for
|
|
27877
|
+
// CKEditor resources to finish loading), and if editor has not already been created.
|
|
27878
|
+
// Editor may already exist if control had DesignMode enabled, then disabled, and then
|
|
27879
|
+
// enabled once again.
|
|
27880
|
+
// If the control is the first one to enabled DesignMode, it will start loading CKEditor
|
|
27881
|
+
// resources and postpone editor creation until resources have finished loading.
|
|
27882
|
+
// When disabled and re-enabled, the control will realize that resources are being loaded,
|
|
27883
|
+
// and postpone editor creation once again, this time using the interval timer here.
|
|
27884
|
+
// When resources are loaded, it will create the editor instances, and when the interval
|
|
27885
|
+
// timer here executes, it will also create the editor instance, unless we prevent it by
|
|
27886
|
+
// making sure only to do it if designEditor is null. Without this check we might experience
|
|
27887
|
+
// the following warning in the browser console, when editor is being created on the same
|
|
27888
|
+
// textarea control multiple times:
|
|
27889
|
+
// [CKEDITOR] Error code: editor-element-conflict. {editorName: "64992ea4-bd01-4081-b606-aa9ff23f417b_DesignMode"}
|
|
27890
|
+
// [CKEDITOR] For more information about this error go to https://ckeditor.com/docs/ckeditor4/latest/guide/dev_errors.html#editor-element-conflict
|
|
27891
|
+
if (me.DesignMode() === true && designEditor === null)
|
|
27892
|
+
{
|
|
27893
|
+
createEditor();
|
|
27894
|
+
}
|
|
27895
|
+
}
|
|
27896
|
+
}, 500);
|
|
27897
|
+
}
|
|
27898
|
+
}
|
|
27899
|
+
else
|
|
27900
|
+
{
|
|
27901
|
+
createEditor();
|
|
27902
|
+
}
|
|
27903
|
+
|
|
27904
|
+
if (me.Resizable() !== Fit.Controls.InputResizing.Disabled) // Undo any resizing done in ordinary MultiLine mode
|
|
27905
|
+
{
|
|
27906
|
+
Fit.Dom.Data(me.GetDomElement(), "resized", "false");
|
|
27907
|
+
|
|
27908
|
+
input.style.width = "";
|
|
27909
|
+
input.style.height = "";
|
|
27910
|
+
input.style.margin = ""; // Chrome adds some odd margin when textarea is resized
|
|
27911
|
+
}
|
|
27912
|
+
|
|
27913
|
+
var enableAutoGrow = me.Height().Value === -1 || (designEditorConfig !== null && designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.Enabled === true);
|
|
27914
|
+
|
|
27915
|
+
if (enableAutoGrow === true && me.Maximizable() === true)
|
|
27916
|
+
{
|
|
27917
|
+
// Maximize button is disabled when auto grow is enabled, but we make sure to "minimize" control
|
|
27918
|
+
// so maximize button returns to its initial state, in case control was maximized prior to enabling
|
|
27919
|
+
// DesignMode with auto grow. Otherwise the button indicates that the control is maximized, and so
|
|
27920
|
+
// does calls to Maximized() which will incorrectly return True.
|
|
27921
|
+
me.Maximized(false);
|
|
27922
|
+
}
|
|
27923
|
+
|
|
27924
|
+
repaint();
|
|
27925
|
+
}
|
|
27926
|
+
else if (val === false && designMode === true)
|
|
27927
|
+
{
|
|
27928
|
+
if (designModeEnabledAndReady() === false)
|
|
27929
|
+
{
|
|
27930
|
+
console.error("DesignMode(false) is not allowed for Input control '" + me.GetId() + "' while DesignMode editor is initializing!");
|
|
27931
|
+
return true; // Return current un-modified state - DesignMode remains enabled
|
|
27932
|
+
}
|
|
27933
|
+
|
|
27934
|
+
var focused = me.Focused();
|
|
27935
|
+
|
|
27936
|
+
if (focused === true) // Make sure focus is preserved when editor is destroyed
|
|
27937
|
+
{
|
|
27938
|
+
me.GetDomElement().focus();
|
|
27939
|
+
}
|
|
27940
|
+
|
|
27941
|
+
if (Fit._internal.Controls.Input.ActiveEditorForDialog === me)
|
|
27942
|
+
{
|
|
27943
|
+
if (Fit._internal.Controls.Input.ActiveDialogForEditor !== null)
|
|
27944
|
+
{
|
|
27945
|
+
Fit._internal.Controls.Input.ActiveDialogForEditor.hide(); // Fires dialog's OnHide event
|
|
27946
|
+
}
|
|
27947
|
+
else
|
|
27948
|
+
{
|
|
27949
|
+
// Dialog is still loading - calling designEditor.destroy() below will cause an error,
|
|
27950
|
+
// leaving a modal layer on the page behind, making it unusable. This may happen if Design Mode is disabled
|
|
27951
|
+
// from e.g. a DOM event handler, a mutation observer, a timer, or an AJAX request. The input control
|
|
27952
|
+
// itself does not fire any events while the dialog is loading which could trigger this situation, so
|
|
27953
|
+
// this can only happen from "external code".
|
|
27954
|
+
|
|
27955
|
+
// WARNING: This has the potential to leak memory if dialog never loads and resumes task of destroying control!
|
|
27956
|
+
Fit._internal.Controls.Input.ActiveEditorForDialogDisabledPostponed = true;
|
|
27957
|
+
|
|
27958
|
+
// Detect memory leak
|
|
27959
|
+
/* setTimeout(function()
|
|
27960
|
+
{
|
|
27961
|
+
if (me !== null && me.DesignMode() === false && Fit._internal.Controls.Input.ActiveEditorForDialog === me)
|
|
27962
|
+
{
|
|
27963
|
+
Fit.Browser.Log("WARNING: Input in DesignMode was not properly disposed in time - potential memory leak detected");
|
|
27964
|
+
}
|
|
27965
|
+
}, 5000); // Usually the load time for a dialog is barely measurable, so 5 seconds seems sufficient */
|
|
27966
|
+
|
|
27967
|
+
return true; // Return current un-modified state - DesignMode remains enabled until dialog is done loading
|
|
27968
|
+
}
|
|
27969
|
+
}
|
|
27970
|
+
|
|
27971
|
+
if (designEditorActiveToolbarPanel !== null)
|
|
27972
|
+
{
|
|
27973
|
+
designEditorActiveToolbarPanel.CloseEmojiPanel();
|
|
27974
|
+
}
|
|
27975
|
+
|
|
27976
|
+
// Destroy editor - content is automatically synchronized to input control.
|
|
27977
|
+
// Calling destroy() fires OnHide for any dialog currently open, which in turn
|
|
27978
|
+
// disables locked focus state and returns focus to the control.
|
|
27979
|
+
destroyDesignEditorInstance();
|
|
27980
|
+
|
|
27981
|
+
me._internal.Data("designmode", "false");
|
|
27982
|
+
Fit.Dom.Data(me.GetDomElement(), "resized", "false");
|
|
27983
|
+
|
|
27984
|
+
// Remove DesignMode specific data attributes
|
|
27985
|
+
me._internal.Data("autogrow", null);
|
|
27986
|
+
me._internal.Data("toolbar", null);
|
|
27987
|
+
me._internal.Data("toolbar-position", null);
|
|
27988
|
+
me._internal.Data("toolbar-sticky", null);
|
|
27989
|
+
|
|
27990
|
+
revertToSingleLineIfNecessary();
|
|
27991
|
+
if (focused === true)
|
|
27992
|
+
{
|
|
27993
|
+
// On IE8 input.focus() does not work if input field is switched to a traditional input field,
|
|
27994
|
+
// or if input field is hidden/invisible. It's just not reliable and not worth it. Remove focus
|
|
27995
|
+
// from the control in IE8 when DesignMode is disabled and preserve focus in every other browser.
|
|
27996
|
+
|
|
27997
|
+
if (isIe8 === false)
|
|
27998
|
+
{
|
|
27999
|
+
input.focus();
|
|
28000
|
+
}
|
|
28001
|
+
else
|
|
28002
|
+
{
|
|
28003
|
+
me.GetDomElement().blur(); // Control container was given focus further up - this will fire OnBlur as expected
|
|
28004
|
+
}
|
|
28005
|
+
}
|
|
28006
|
+
|
|
28007
|
+
// Remove tabindex used to prevent control from losing focus when clicking toolbar buttons
|
|
28008
|
+
Fit.Dom.Attribute(me.GetDomElement(), "tabindex", null);
|
|
28009
|
+
|
|
28010
|
+
repaint();
|
|
28011
|
+
}
|
|
28012
|
+
}
|
|
28013
|
+
|
|
28014
|
+
return (me._internal.Data("designmode") === "true");
|
|
28015
|
+
}
|
|
28016
|
+
|
|
28017
|
+
/// <function container="Fit.Controls.Input" name="DebounceOnChange" access="public" returns="integer">
|
|
28018
|
+
/// <description>
|
|
28019
|
+
/// Get/set number of milliseconds used to postpone onchange event.
|
|
28020
|
+
/// Every new keystroke/change resets the timer. Debouncing can
|
|
28021
|
+
/// improve performance when working with large amounts of data.
|
|
28022
|
+
/// </description>
|
|
28023
|
+
/// <param name="timeout" type="integer"> If defined, timeout value (milliseconds) is updated - a value of -1 disables debouncing </param>
|
|
28024
|
+
/// </function>
|
|
28025
|
+
this.DebounceOnChange = function(timeout)
|
|
28026
|
+
{
|
|
28027
|
+
Fit.Validation.ExpectInteger(timeout);
|
|
28028
|
+
|
|
28029
|
+
if (Fit.Validation.IsSet(debounceOnChangeTimeout) === true && timeout !== debounceOnChangeTimeout)
|
|
28030
|
+
{
|
|
28031
|
+
debounceOnChangeTimeout = timeout;
|
|
28032
|
+
|
|
28033
|
+
if (debouncedOnChange !== null)
|
|
28034
|
+
{
|
|
28035
|
+
debouncedOnChange.Flush();
|
|
28036
|
+
debouncedOnChange = null; // Re-created when needed with new timeout value
|
|
28037
|
+
}
|
|
28038
|
+
}
|
|
28039
|
+
|
|
28040
|
+
return debounceOnChangeTimeout;
|
|
28041
|
+
}
|
|
28042
|
+
|
|
28043
|
+
// ============================================
|
|
28044
|
+
// Protected
|
|
28045
|
+
// ============================================
|
|
28046
|
+
|
|
28047
|
+
this._internal = (this._internal ? this._internal : {});
|
|
28048
|
+
|
|
28049
|
+
this._internal.DesignModeEnabledAndReady = function()
|
|
28050
|
+
{
|
|
28051
|
+
return designModeEnabledAndReady();
|
|
28052
|
+
}
|
|
28053
|
+
|
|
28054
|
+
// ============================================
|
|
28055
|
+
// Private
|
|
28056
|
+
// ============================================
|
|
28057
|
+
|
|
28058
|
+
function createEditor()
|
|
28059
|
+
{
|
|
28060
|
+
// NOTICE: CKEDITOR requires input control to be rooted in DOM.
|
|
28061
|
+
// Creating the editor when Render(..) is called is not the solution, since the programmer
|
|
28062
|
+
// may call GetDomElement() instead and root the element at any given time which is out of our control.
|
|
28063
|
+
// It may be possible to temporarily root the control and make it invisible while the control
|
|
28064
|
+
// is being created, and remove it from the DOM when instanceReady is fired. However, since creating
|
|
28065
|
+
// the editor is an asynchronous operation, we need to detect whether the element has been rooted
|
|
28066
|
+
// elsewhere when instanceCreated is fired, and only remove it from the DOM if this is not the case.
|
|
28067
|
+
// This problem needs to be solved some other time as it may spawn other problems, such as determining
|
|
28068
|
+
// the size of objects while being invisible. The CKEditor team may also solve the bug in an update.
|
|
28069
|
+
if (Fit.Dom.IsRooted(me.GetDomElement()) === false)
|
|
28070
|
+
{
|
|
28071
|
+
//Fit.Validation.ThrowError("Control must be appended/rendered to DOM before DesignMode can be initialized");
|
|
28072
|
+
|
|
28073
|
+
var retry = function()
|
|
28074
|
+
{
|
|
28075
|
+
if (Fit.Dom.IsRooted(me.GetDomElement()) === true)
|
|
28076
|
+
{
|
|
28077
|
+
if (me.DesignMode() === true)
|
|
28078
|
+
{
|
|
28079
|
+
createEditor();
|
|
28080
|
+
}
|
|
28081
|
+
|
|
28082
|
+
return true;
|
|
28083
|
+
}
|
|
28084
|
+
|
|
28085
|
+
// Return False to indicate that we still need to keep retrying (still in DesignMode).
|
|
28086
|
+
// Otherwie return True to indicate success - retrying is no longer relevant.
|
|
28087
|
+
return (me.DesignMode() === true ? false : true);
|
|
28088
|
+
};
|
|
28089
|
+
|
|
28090
|
+
setTimeout(function() // Queue to allow control to be rooted
|
|
28091
|
+
{
|
|
28092
|
+
if (me === null)
|
|
28093
|
+
{
|
|
28094
|
+
return; // Control was disposed
|
|
28095
|
+
}
|
|
28096
|
+
|
|
28097
|
+
if (retry() === false)
|
|
28098
|
+
{
|
|
28099
|
+
// Still not rooted - add observer to create editor instance once control is rooted
|
|
28100
|
+
|
|
28101
|
+
rootedEventId = Fit.Events.AddHandler(me.GetDomElement(), "#rooted", function(e)
|
|
28102
|
+
{
|
|
28103
|
+
if (retry() === true || me.DesignMode() === false)
|
|
28104
|
+
{
|
|
28105
|
+
Fit.Events.RemoveHandler(me.GetDomElement(), rootedEventId);
|
|
28106
|
+
rootedEventId = -1;
|
|
28107
|
+
}
|
|
28108
|
+
});
|
|
28109
|
+
}
|
|
28110
|
+
}, 0);
|
|
28111
|
+
|
|
28112
|
+
return;
|
|
28113
|
+
}
|
|
28114
|
+
|
|
28115
|
+
var langSupport = ["da", "de", "en"];
|
|
28116
|
+
var localeCode = Fit.Internationalization.Locale().length === 2 ? Fit.Internationalization.Locale() : Fit.Internationalization.Locale().substring(0, 2);
|
|
28117
|
+
var lang = Fit.Array.Contains(langSupport, localeCode) === true ? localeCode : "en";
|
|
28118
|
+
var plugins = [];
|
|
28119
|
+
var toolbar = [];
|
|
28120
|
+
var mentions = [];
|
|
28121
|
+
|
|
28122
|
+
var config = designEditorConfig || {};
|
|
28123
|
+
|
|
28124
|
+
// Enable additional plugins not compiled into CKEditor by default
|
|
28125
|
+
|
|
28126
|
+
if ((config.Plugins && config.Plugins.Emojis === true) || (config.Toolbar && config.Toolbar.Emojis === true))
|
|
28127
|
+
{
|
|
28128
|
+
Fit.Array.Add(plugins, "emoji");
|
|
28129
|
+
}
|
|
28130
|
+
|
|
28131
|
+
if (designModeEnableImagePlugin() === true)
|
|
28132
|
+
{
|
|
28133
|
+
if (config.Toolbar && config.Toolbar.Images === true)
|
|
28134
|
+
{
|
|
28135
|
+
Fit.Array.Add(plugins, "base64image");
|
|
28136
|
+
}
|
|
28137
|
+
|
|
28138
|
+
plugins = Fit.Array.Merge(plugins, ["base64imagepaste", "dragresize"]);
|
|
28139
|
+
}
|
|
28140
|
+
|
|
28141
|
+
Fit.Array.Add(plugins, "custombuttons");
|
|
28142
|
+
|
|
28143
|
+
// Add toolbar buttons
|
|
28144
|
+
|
|
28145
|
+
if (!config.Toolbar || config.Toolbar.Formatting !== false)
|
|
28146
|
+
{
|
|
28147
|
+
Fit.Array.Add(toolbar,
|
|
28148
|
+
{
|
|
28149
|
+
name: "BasicFormatting",
|
|
28150
|
+
items: [ "Bold", "Italic", "Underline" ]
|
|
28151
|
+
});
|
|
28152
|
+
}
|
|
28153
|
+
|
|
28154
|
+
if (!config.Toolbar || config.Toolbar.Justify !== false)
|
|
28155
|
+
{
|
|
28156
|
+
Fit.Array.Add(toolbar,
|
|
28157
|
+
{
|
|
28158
|
+
name: "Justify",
|
|
28159
|
+
items: [ "JustifyLeft", "JustifyCenter", "JustifyRight" ]
|
|
28160
|
+
});
|
|
28161
|
+
}
|
|
28162
|
+
|
|
28163
|
+
if (!config.Toolbar || config.Toolbar.Lists !== false)
|
|
28164
|
+
{
|
|
28165
|
+
Fit.Array.Add(toolbar,
|
|
28166
|
+
{
|
|
28167
|
+
name: "Lists",
|
|
28168
|
+
items: [ "NumberedList", "BulletedList", "Indent", "Outdent" ]
|
|
28169
|
+
});
|
|
28170
|
+
}
|
|
28171
|
+
|
|
28172
|
+
if (!config.Toolbar || config.Toolbar.Links !== false)
|
|
28173
|
+
{
|
|
28174
|
+
Fit.Array.Add(toolbar,
|
|
28175
|
+
{
|
|
28176
|
+
name: "Links",
|
|
28177
|
+
items: [ "Link", "Unlink" ]
|
|
28178
|
+
});
|
|
28179
|
+
}
|
|
28180
|
+
|
|
28181
|
+
if (config.Toolbar)
|
|
28182
|
+
{
|
|
28183
|
+
var insert = [];
|
|
28184
|
+
|
|
28185
|
+
if (config.Toolbar.Emojis === true)
|
|
28186
|
+
{
|
|
28187
|
+
Fit.Array.Add(insert, "EmojiPanel");
|
|
28188
|
+
}
|
|
28189
|
+
|
|
28190
|
+
if (config.Toolbar.Images === true)
|
|
28191
|
+
{
|
|
28192
|
+
Fit.Array.Add(insert, "base64image");
|
|
28193
|
+
}
|
|
28194
|
+
|
|
28195
|
+
if (insert.length > 0)
|
|
28196
|
+
{
|
|
28197
|
+
Fit.Array.Add(toolbar,
|
|
28198
|
+
{
|
|
28199
|
+
name: "Insert",
|
|
28200
|
+
items: insert
|
|
28201
|
+
});
|
|
28202
|
+
}
|
|
28203
|
+
|
|
28204
|
+
var customButtons = [];
|
|
28205
|
+
var customToolbarGroups = [];
|
|
28206
|
+
|
|
28207
|
+
if (config.Toolbar.Detach === true)
|
|
28208
|
+
{
|
|
28209
|
+
Fit.Array.Add(customButtons,
|
|
28210
|
+
{
|
|
28211
|
+
Label: locale.Detach,
|
|
28212
|
+
Command: "Detach",
|
|
28213
|
+
Icon: Fit.GetUrl() + "/Controls/Input/" + (window.devicePixelRatio === 2 ? "maximize-highres.png" : "maximize.png"),
|
|
28214
|
+
Callback: function(args)
|
|
28215
|
+
{
|
|
28216
|
+
//console.log("Command " + args.Command.name + " executed", args);
|
|
28217
|
+
openDetachedDesignEditor();
|
|
28218
|
+
}
|
|
28219
|
+
});
|
|
28220
|
+
|
|
28221
|
+
/*Fit.Array.Add(customButtons,
|
|
28222
|
+
{
|
|
28223
|
+
Label: "Testing 1-2-3",
|
|
28224
|
+
Command: "TestButton",
|
|
28225
|
+
Icon: "/files/images/Bird.png",
|
|
28226
|
+
Callback: function(args)
|
|
28227
|
+
{
|
|
28228
|
+
alert("Hello world");
|
|
28229
|
+
}
|
|
28230
|
+
});*/
|
|
28231
|
+
|
|
28232
|
+
Fit.Array.Add(customToolbarGroups,
|
|
28233
|
+
{
|
|
28234
|
+
name: "DetachableEditor",
|
|
28235
|
+
items: ["Detach"/*, "TestButton"*/]
|
|
28236
|
+
});
|
|
28237
|
+
}
|
|
28238
|
+
|
|
28239
|
+
toolbar = Fit.Array.Merge(toolbar, customToolbarGroups);
|
|
28240
|
+
}
|
|
28241
|
+
|
|
28242
|
+
// Configure tags/mentions plugin
|
|
28243
|
+
|
|
28244
|
+
if (config.Tags)
|
|
28245
|
+
{
|
|
28246
|
+
var requestAwaiting = null;
|
|
28247
|
+
|
|
28248
|
+
var createEventArgs = function(marker, query, request) // EventsArgs for OnRequest and OnResponse
|
|
28249
|
+
{
|
|
28250
|
+
return { Sender: me, Query: { Marker: marker, Query: query }, Request: request };
|
|
28251
|
+
};
|
|
28252
|
+
|
|
28253
|
+
Fit.Array.ForEach(config.Tags.Triggers, function(trigger)
|
|
28254
|
+
{
|
|
28255
|
+
var mention =
|
|
28256
|
+
{
|
|
28257
|
+
marker: trigger.Marker,
|
|
28258
|
+
minChars: trigger.MinimumCharacters || 0,
|
|
28259
|
+
throttle: 0, // Throttling is not debouncing - it merely ensures that no more than 1 request is made every X milliseconds when value is changed (defaults to 200ms) - real debouncing implemented further down, which reduce and cancel network calls as user types - also a work around for https://github.com/ckeditor/ckeditor4/issues/5036
|
|
28260
|
+
feed: function(args, resolve)
|
|
28261
|
+
{
|
|
28262
|
+
// WebService is expected to return tag items in an array like so:
|
|
28263
|
+
// [ { Title: string, Value: string, Icon?: string, Url?: string, Data?: string }, { ... }, ... ]
|
|
28264
|
+
|
|
28265
|
+
var req = null;
|
|
28266
|
+
|
|
28267
|
+
if (config.Tags.JsonpCallback)
|
|
28268
|
+
{
|
|
28269
|
+
req = new Fit.Http.JsonpRequest(config.Tags.QueryUrl, config.Tags.JsonpCallback);
|
|
28270
|
+
config.Tags.JsonpTimeout && req.Timeout(config.Tags.JsonpTimeout);
|
|
28271
|
+
req.SetParameter("Marker", args.marker);
|
|
28272
|
+
req.SetParameter("Query", args.query);
|
|
28273
|
+
}
|
|
28274
|
+
else
|
|
28275
|
+
{
|
|
28276
|
+
req = new Fit.Http.JsonRequest(config.Tags.QueryUrl);
|
|
28277
|
+
req.SetData({ Marker: args.marker, Query: args.query });
|
|
28278
|
+
}
|
|
28279
|
+
|
|
28280
|
+
if (config.Tags.OnRequest)
|
|
28281
|
+
{
|
|
28282
|
+
var eventArgs = createEventArgs(args.marker, args.query, req);
|
|
28283
|
+
|
|
28284
|
+
if (config.Tags.OnRequest(me, eventArgs) === false)
|
|
28285
|
+
{
|
|
28286
|
+
resolve([]);
|
|
28287
|
+
return;
|
|
28288
|
+
}
|
|
28289
|
+
|
|
28290
|
+
if (eventArgs.Request !== req)
|
|
28291
|
+
{
|
|
28292
|
+
// Support for changing request instans to
|
|
28293
|
+
// take control over webservice communication.
|
|
28294
|
+
|
|
28295
|
+
// Restrict to support for Fit.Http.Request or classes derived from this
|
|
28296
|
+
Fit.Validation.ExpectInstance(eventArgs.Request, Fit.Http.Request);
|
|
28297
|
+
|
|
28298
|
+
req = eventArgs.Request;
|
|
28299
|
+
}
|
|
28300
|
+
}
|
|
28301
|
+
|
|
28302
|
+
var processDataAndResolve = function(items)
|
|
28303
|
+
{
|
|
28304
|
+
if (config.Tags.OnResponse) // OnResponse is allowed to manipulate tags
|
|
28305
|
+
{
|
|
28306
|
+
var eventArgs = Fit.Core.Merge(createEventArgs(args.marker, args.query, req), { Tags: items });
|
|
28307
|
+
config.Tags.OnResponse(me, eventArgs);
|
|
28308
|
+
|
|
28309
|
+
items = eventArgs.Tags; // In case OnResponse event handler assigned new collection
|
|
28310
|
+
}
|
|
28311
|
+
|
|
28312
|
+
Fit.Array.ForEach(items, function(item)
|
|
28313
|
+
{
|
|
28314
|
+
// Set properties required by mentions plugin
|
|
28315
|
+
item.id = item.Value;
|
|
28316
|
+
item.name = item.Title;
|
|
28317
|
+
});
|
|
28318
|
+
|
|
28319
|
+
resolve(items); // Opens context menu immediately if array contain elements, unless user managed to add a space after the search value while waiting for a response, in which case the context menu will not be opened
|
|
28320
|
+
|
|
28321
|
+
if (items.length > 0)
|
|
28322
|
+
{
|
|
28323
|
+
// Calling resolve(..) above immediately opens the context menu from which a tag can be selected
|
|
28324
|
+
|
|
28325
|
+
// Get the autocomplete context menu currently open. There can be only one
|
|
28326
|
+
// such menu open at any time. Each editor can declare multiple autocomplete
|
|
28327
|
+
// context menus since each tag marker is associated with its own context menu.
|
|
28328
|
+
var ctm = document.querySelector("ul.cke_autocomplete_opened");
|
|
28329
|
+
|
|
28330
|
+
if (ctm !== null) // Null if user managed to enter a space after tag search value, before response was received - in this case resolve(..) above will not open the context menu
|
|
28331
|
+
{
|
|
28332
|
+
// Allow light dismissable panels/callouts to prevent close/dismiss
|
|
28333
|
+
// when interacting with tags context menu hosted outside of these panels/callouts,
|
|
28334
|
+
// by detecting the presence of the data-disable-light-dismiss="true" attribute.
|
|
28335
|
+
Fit.Dom.Data(ctm, "disable-light-dismiss", "true");
|
|
28336
|
+
|
|
28337
|
+
if (Fit._internal.ControlBase.ReduceDocumentRootPollution === true)
|
|
28338
|
+
{
|
|
28339
|
+
// Tags context menu is placed in the root of the document where
|
|
28340
|
+
// it pollutes the global scope. Move it next to the Fit.UI control.
|
|
28341
|
+
// We do not mount it within the Fit.UI control as it could cause Fit.UI styles
|
|
28342
|
+
// to take effect on the context menu.
|
|
28343
|
+
|
|
28344
|
+
// Has position:absolute by default, but this may be affected by a positioned
|
|
28345
|
+
// container (offsetParent), so we change it to position:fixed. Downside:
|
|
28346
|
+
// It no longer sticks to the editor when scrolling. However, if a container has CSS
|
|
28347
|
+
// transform set, the context menu's position will be affected and become inaccurate.
|
|
28348
|
+
ctm.style.position = "fixed";
|
|
28349
|
+
Fit.Dom.InsertAfter(me.GetDomElement(), ctm);
|
|
28350
|
+
}
|
|
28351
|
+
}
|
|
28352
|
+
}
|
|
28353
|
+
};
|
|
28354
|
+
|
|
28355
|
+
if (Fit.Core.InstanceOf(req, Fit.Http.JsonpRequest) === true)
|
|
28356
|
+
{
|
|
28357
|
+
req.OnSuccess(function(sender)
|
|
28358
|
+
{
|
|
28359
|
+
var response = req.GetResponse();
|
|
28360
|
+
var items = ((response instanceof Array) ? response : []);
|
|
28361
|
+
|
|
28362
|
+
processDataAndResolve(items);
|
|
28363
|
+
});
|
|
28364
|
+
|
|
28365
|
+
req.OnTimeout(function(sender)
|
|
28366
|
+
{
|
|
28367
|
+
resolve([]);
|
|
28368
|
+
Fit.Validation.ThrowError("Unable to get tags - request did not return data in time (JSONP timeout reached)");
|
|
28369
|
+
});
|
|
28370
|
+
}
|
|
28371
|
+
else
|
|
28372
|
+
{
|
|
28373
|
+
req.OnSuccess(function(sender)
|
|
28374
|
+
{
|
|
28375
|
+
var response = req.GetResponseJson();
|
|
28376
|
+
var items = ((response instanceof Array) ? response : []);
|
|
28377
|
+
|
|
28378
|
+
processDataAndResolve(items);
|
|
28379
|
+
});
|
|
28380
|
+
|
|
28381
|
+
req.OnFailure(function(sender)
|
|
28382
|
+
{
|
|
28383
|
+
resolve([]);
|
|
28384
|
+
Fit.Validation.ThrowError("Unable to get tags - request failed with HTTP Status code " + req.GetHttpStatus());
|
|
28385
|
+
});
|
|
28386
|
+
}
|
|
28387
|
+
|
|
28388
|
+
if (requestAwaiting !== null)
|
|
28389
|
+
{
|
|
28390
|
+
requestAwaiting.Abort();
|
|
28391
|
+
}
|
|
28392
|
+
|
|
28393
|
+
requestAwaiting = req;
|
|
28394
|
+
req.Start();
|
|
28395
|
+
},
|
|
28396
|
+
itemTemplate: function(item) // Item must define "name" and "id" properties - the {name} placeholder is replaced by "@" + the value of the "name" property - to get rid of "@" simply use an alternative property such as nameWithoutTag:"Some username"
|
|
28397
|
+
{
|
|
28398
|
+
if (item.Icon)
|
|
28399
|
+
{
|
|
28400
|
+
return '<li data-id="' + item.Value + '"><img src="' + item.Icon + '" style="width: 24px; height: 24px; border-radius: 24px; vertical-align: middle" alt=""><span style="display: inline-block; width: 165px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; margin-left: 5px">' + item.Title + '</span></li>';
|
|
28401
|
+
}
|
|
28402
|
+
else
|
|
28403
|
+
{
|
|
28404
|
+
return '<li data-id="' + item.Value + '">' + item.Title + '</li>';
|
|
28405
|
+
}
|
|
28406
|
+
},
|
|
28407
|
+
outputTemplate: function(item)
|
|
28408
|
+
{
|
|
28409
|
+
// IMPORTANT: Output produced must respect ACF (Advanced Content Filter).
|
|
28410
|
+
// So the tag produced must be allowed, and any attributes contained must be allowed.
|
|
28411
|
+
|
|
28412
|
+
var alternativeItem = null;
|
|
28413
|
+
|
|
28414
|
+
if (config.Tags.TagCreator)
|
|
28415
|
+
{
|
|
28416
|
+
var callbackArgs = { Sender: me, QueryMarker: trigger.Marker, Tag: Fit.Core.Clone(item) };
|
|
28417
|
+
alternativeItem = config.Tags.TagCreator(me, callbackArgs) || null;
|
|
28418
|
+
}
|
|
28419
|
+
|
|
28420
|
+
// Function should return a link for tags to "just work". Returning a <span> requires the span to be whitelisted in
|
|
28421
|
+
// extraAllowedContent configuration, but even then the editor will continue writing text within the <span> element,
|
|
28422
|
+
// rather than next to it. So one would expect something like: We will assign <span data-tag-type="@" ..>@James Bond</span> to this mission.
|
|
28423
|
+
// But what we get instead is something like: We will assign <span data-tag-type="@" ..>@James Bond to this mission</span>.
|
|
28424
|
+
// The same happens to link tags if the href attribute is removed, which is why we always add it, even when no URL is defined.
|
|
28425
|
+
|
|
28426
|
+
if (alternativeItem !== null)
|
|
28427
|
+
{
|
|
28428
|
+
return '<a data-tag-type="' + (alternativeItem.Type || trigger.Marker) + '" data-tag-id="' + (alternativeItem.Value || item.Value) + '"' + (alternativeItem.Data || item.Data ? ' data-tag-data="' + (alternativeItem.Data || item.Data) + '"' : '') + (alternativeItem.Context || item.Context ? ' data-tag-context="' + (alternativeItem.Context || item.Context) + '"' : '') + (alternativeItem.Url || item.Url ? ' href="' + (alternativeItem.Url || item.Url) + '"' : 'href=""') + '>' + (alternativeItem.Title || (trigger.Marker + item.Title)) + '</a>';
|
|
28429
|
+
}
|
|
28430
|
+
else
|
|
28431
|
+
{
|
|
28432
|
+
return '<a data-tag-type="' + trigger.Marker + '" data-tag-id="' + item.Value + '"' + (item.Data ? ' data-tag-data="' + item.Data + '"' : '') + (item.Context ? ' data-tag-context="' + item.Context + '"' : '') + (item.Url ? ' href="' + item.Url + '"' : 'href=""') + '>' + trigger.Marker + item.Title + '</a>';
|
|
28433
|
+
}
|
|
28434
|
+
}
|
|
28435
|
+
};
|
|
28436
|
+
|
|
28437
|
+
if (trigger.DebounceQuery !== 0) // A value of 0 (zero) disables debouncing
|
|
28438
|
+
{
|
|
28439
|
+
// Wrap feed handler in debounce function so that every time it gets invoked, it cancels the previous invocation
|
|
28440
|
+
mention.feed = Fit.Core.CreateDebouncer(mention.feed, trigger.DebounceQuery || 300).Invoke;
|
|
28441
|
+
}
|
|
28442
|
+
|
|
28443
|
+
Fit.Array.Add(mentions, mention);
|
|
28444
|
+
});
|
|
28445
|
+
}
|
|
28446
|
+
|
|
28447
|
+
var onImageAdded = function(args)
|
|
28448
|
+
{
|
|
28449
|
+
if (args.type === "blob")
|
|
28450
|
+
{
|
|
28451
|
+
// For a list of blobs in Chrome see: chrome://blob-internals/
|
|
28452
|
+
// Be aware that garbage is NOT being collected unless needed, so
|
|
28453
|
+
// don't expect the list to update immediately. Garbage collection
|
|
28454
|
+
// can be triggered in Dev Tools > Memory: click the trash can icon.
|
|
28455
|
+
// Make sure to garbage collect from the tab/window running Fit.UI,
|
|
28456
|
+
// NOT from the tab/window listing blobs!
|
|
28457
|
+
// Use https://jsfiddle.net/ute87p1m/6/ to test garbage collection.
|
|
28458
|
+
|
|
28459
|
+
imageBlobUrls.push(args.url);
|
|
28460
|
+
}
|
|
28461
|
+
|
|
28462
|
+
/*// Image data can be retrieved from a blob like this:
|
|
28463
|
+
if (img.src.indexOf("blob:") === 0)
|
|
28464
|
+
{
|
|
28465
|
+
var r = new Fit.Http.Request(img.src); // E.g. "blob:http://localhost:8080/0c5aa2ae-f2ea-414a-af42-53047959ad1b"
|
|
28466
|
+
r.RequestProperties({ responseType: "blob" });
|
|
28467
|
+
r.OnSuccess(function(sender)
|
|
28468
|
+
{
|
|
28469
|
+
var blob = sender.GetResponse();
|
|
28470
|
+
|
|
28471
|
+
var reader = new FileReader();
|
|
28472
|
+
reader.onload = function(ev)
|
|
28473
|
+
{
|
|
28474
|
+
var base64 = ev.target.result;
|
|
28475
|
+
console.log(base64);
|
|
28476
|
+
};
|
|
28477
|
+
reader.readAsDataURL(blob);
|
|
28478
|
+
});
|
|
28479
|
+
r.Start();
|
|
28480
|
+
}*/
|
|
28481
|
+
};
|
|
28482
|
+
|
|
28483
|
+
designEditor = CKEDITOR.replace(me.GetId() + "_DesignMode",
|
|
28484
|
+
{
|
|
28485
|
+
toolbarLocation: designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.Position === "Bottom" ? "bottom" : "top",
|
|
28486
|
+
uiColor: Fit._internal.Controls.Input.Editor.Skin === "moono-lisa" || Fit._internal.Controls.Input.Editor.Skin === null ? "#FFFFFF" : undefined,
|
|
28487
|
+
//allowedContent: true, // http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules and http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent
|
|
28488
|
+
extraAllowedContent: "a[data-tag-type,data-tag-id,data-tag-data,data-tag-context]", // https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-extraAllowedContent
|
|
28489
|
+
language: lang,
|
|
28490
|
+
disableNativeSpellChecker: me.CheckSpelling() === false,
|
|
28491
|
+
readOnly: me.Enabled() === false,
|
|
28492
|
+
tabIndex: me.Enabled() === false ? -1 : 0,
|
|
28493
|
+
title: "",
|
|
28494
|
+
width: "100%", // Assume width of container
|
|
28495
|
+
height: me.Height().Value > -1 ? me.Height().Value + me.Height().Unit : "100%", // Height of content area - toolbar and bottom panel takes up additional space - once editor is loaded, the outer dimensions are accurately set using updateDesignEditorSize() - a height of 100% enables auto grow
|
|
28496
|
+
startupFocus: me.Focused() === true ? "end" : false, // Doesn't work when editor is hidden while initializing to avoid flickering - focus is set again when editor is made visible in instanceReady handler, at which point it will place cursor at the end of the editor if startupFocus is set to "end"
|
|
28497
|
+
extraPlugins: plugins.join(","),
|
|
28498
|
+
clipboard_handleImages: false, // Disable native support for image pasting - allow base64imagepaste plugin to handle image data if loaded
|
|
28499
|
+
base64image: // Custom property used by base64image plugin if loaded
|
|
28500
|
+
{
|
|
28501
|
+
storage: designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.EmbedType === "blob" ? "blob" : "base64", // "base64" (default) or "blob" - base64 will always be provided by browsers not supporting blob storage
|
|
28502
|
+
onImageAdded: onImageAdded
|
|
28503
|
+
},
|
|
28504
|
+
base64imagepaste: // Custom property used by base64imagepaste plugin if loaded - notice that IE has native support for image pasting as base64 so plugin is not triggered in IE
|
|
28505
|
+
{
|
|
28506
|
+
storage: designEditorConfig !== null && designEditorConfig.Plugins && designEditorConfig.Plugins.Images && designEditorConfig.Plugins.Images.EmbedType === "blob" ? "blob" : "base64", // "base64" (default) or "blob" - base64 will always be provided by browsers not supporting blob storage
|
|
28507
|
+
onImageAdded: onImageAdded
|
|
28508
|
+
},
|
|
28509
|
+
resize_enabled: resizable !== Fit.Controls.InputResizing.Disabled,
|
|
28510
|
+
resize_dir: resizable === Fit.Controls.InputResizing.Enabled ? "both" : resizable === Fit.Controls.InputResizing.Vertical ? "vertical" : resizable === Fit.Controls.InputResizing.Horizontal ? "horizontal" : "none", // Specific to resize plugin (horizontal | vertical | both - https://ckeditor.com/docs/ckeditor4/latest/features/resize.html)
|
|
28511
|
+
toolbar: toolbar,
|
|
28512
|
+
removeButtons: "", // Set to empty string to prevent CKEditor from removing buttons such as Underline
|
|
28513
|
+
customButtons: customButtons,
|
|
28514
|
+
mentions: mentions,
|
|
28515
|
+
emoji_minChars: 9999, // Impossible requirement to number of search characters to "disable" emoji auto complete menu - we cannot make it work properly with light dismissable panels/callouts since we have no event available for registering the data-disable-light-dismiss="true" attribute, and it's not very useful in any case
|
|
28516
|
+
on:
|
|
28517
|
+
{
|
|
28518
|
+
instanceReady: function()
|
|
28519
|
+
{
|
|
28520
|
+
designEditorDom = // Object assignment will make designModeEnabledAndReady() return True, so it must be assigned immediately
|
|
28521
|
+
{
|
|
28522
|
+
OuterContainer: designEditor.container.$,
|
|
28523
|
+
InnerContainer: designEditor.container.$.querySelector(".cke_inner"),
|
|
28524
|
+
Top: designEditor.container.$.querySelector(".cke_top"), // Null if toolbar is placed at the bottom
|
|
28525
|
+
Content: designEditor.container.$.querySelector(".cke_contents"),
|
|
28526
|
+
Editable: designEditor.container.$.querySelector(".cke_editable"),
|
|
28527
|
+
Bottom: designEditor.container.$.querySelector(".cke_bottom") // Only exist if editor is resizable or toolbar is placed at the bottom
|
|
28528
|
+
}
|
|
28529
|
+
|
|
28530
|
+
// Make sure expected DOM elements are present, so we do not have to perform null checks anywhere else in the code.
|
|
28531
|
+
// Notice that designEditorDom.Top is null if toolbar is placed at the bottom, and designEditorDom.Bottom is null
|
|
28532
|
+
// unless toolbar is placed at the bottom, or editor is resizable, which adds a resize handled in the lower right corner.
|
|
28533
|
+
if (designEditorDom.InnerContainer === null || designEditorDom.Content === null || designEditorDom.Editable === null || (designEditorDom.Top === null && designEditorDom.Bottom === null))
|
|
28534
|
+
{
|
|
28535
|
+
throw "One or more editor DOM elements are missing"; // This should only happen if CKEditor changes its DOM structure
|
|
28536
|
+
}
|
|
28537
|
+
|
|
28538
|
+
if (designEditorMustDisposeWhenReady === true)
|
|
28539
|
+
{
|
|
28540
|
+
Fit.Browser.Debug("WARNING: Input control '" + me.GetId() + "' was disposed while initializing DesignMode - now resuming disposal");
|
|
28541
|
+
me.Dispose();
|
|
28542
|
+
return;
|
|
28543
|
+
}
|
|
28544
|
+
|
|
28545
|
+
if (designEditorMustReloadWhenReady === true)
|
|
28546
|
+
{
|
|
28547
|
+
Fit.Browser.Debug("WARNING: Editor for Input control '" + me.GetId() + "' finished loading, but properties affecting editor has changed while initializing - reloading to adjust to changes");
|
|
28548
|
+
reloadEditor(true);
|
|
28549
|
+
return;
|
|
28550
|
+
}
|
|
28551
|
+
|
|
28552
|
+
updateDesignEditorPlaceholder(); // Show/hide placeholder - value might have been set/removed while initializing editor
|
|
28553
|
+
|
|
28554
|
+
Fit.Dom.Data(designEditorDom.OuterContainer, "skin", CKEDITOR.config.skin); // Add e.g. data-skin="bootstrapck" to editor - used in Input.css
|
|
28555
|
+
|
|
28556
|
+
// Enabled state might have been changed while loading.
|
|
28557
|
+
// Unfortunately there is no API for changing the tabIndex.
|
|
28558
|
+
// Similar logic is found in Enabled(..) function.
|
|
28559
|
+
designEditor.setReadOnly(me.Enabled() === false);
|
|
28560
|
+
Fit.Dom.Attribute(designEditorDom.Editable, "tabindex", me.Enabled() === false ? null : "0"); // Preventing focus is only possible by nullifying DOM attribute (these does not work: delete elm.tabIndex; elm.tabIndex = null|undefined|-1)
|
|
28561
|
+
|
|
28562
|
+
if (me.Height().Value === -1 || (designEditorConfig !== null && designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.Enabled === true))
|
|
28563
|
+
{
|
|
28564
|
+
// Enable auto grow - this is done late to allow an initial static height on the outer control which prevents "flickering" while loading
|
|
28565
|
+
|
|
28566
|
+
me.Height(-1); // Make sure auto grow is enabled since it is unlikely external code has done so explicitely by calling Height(-1)
|
|
28567
|
+
|
|
28568
|
+
// Make necessary adjustments to editor DOM for auto grow's min/max height to work
|
|
28569
|
+
|
|
28570
|
+
var editableDiv = designEditorDom.Editable;
|
|
28571
|
+
editableDiv.style.minHeight = designEditorConfig !== null && designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.MinimumHeight ? designEditorConfig.AutoGrow.MinimumHeight.Value + (designEditorConfig.AutoGrow.MinimumHeight.Unit || "px") : ""; // NOTICE: Minimum height of editable area, not control
|
|
28572
|
+
editableDiv.style.maxHeight = designEditorConfig !== null && designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.MaximumHeight ? designEditorConfig.AutoGrow.MaximumHeight.Value + (designEditorConfig.AutoGrow.MaximumHeight.Unit || "px") : ""; // NOTICE: Maximum height of editable area, not control
|
|
28573
|
+
|
|
28574
|
+
// Restrict resizing:
|
|
28575
|
+
// Make sure user cannot resize editor beyond max height of editable area.
|
|
28576
|
+
// Editable area will not "follow" since it is restricted using maxHeight set above.
|
|
28577
|
+
// If we do not want resizing to be restricted, then unset minHeight and maxHeight set
|
|
28578
|
+
// above, when resizing occur (see CKEditor's resize event handler).
|
|
28579
|
+
if (editableDiv.style.maxHeight !== "" && designEditorConfig.AutoGrow.PreventResizeBeyondMaximumHeight === true)
|
|
28580
|
+
{
|
|
28581
|
+
var contents = designEditorDom.Content;
|
|
28582
|
+
contents.style.maxHeight = editableDiv.style.maxHeight;
|
|
28583
|
+
}
|
|
28584
|
+
}
|
|
28585
|
+
|
|
28586
|
+
if (designEditorConfig !== null && designEditorConfig.InfoPanel && designEditorConfig.InfoPanel.Text)
|
|
28587
|
+
{
|
|
28588
|
+
var infoPanel = document.createElement("div");
|
|
28589
|
+
infoPanel.className = "FitUiControlInputInfoPanel";
|
|
28590
|
+
infoPanel.innerHTML = designEditorConfig.InfoPanel.Text;
|
|
28591
|
+
infoPanel.style.cssText = "text-align: " + (designEditorConfig.InfoPanel.Alignment ? designEditorConfig.InfoPanel.Alignment.toLowerCase() : "center");
|
|
28592
|
+
|
|
28593
|
+
if (designEditorConfig !== null && designEditorConfig.Toolbar && designEditorConfig.Toolbar.Position === "Bottom")
|
|
28594
|
+
{
|
|
28595
|
+
Fit.Dom.InsertBefore(designEditorDom.Content, infoPanel);
|
|
28596
|
+
}
|
|
28597
|
+
else
|
|
28598
|
+
{
|
|
28599
|
+
Fit.Dom.InsertAfter(designEditorDom.Content, infoPanel);
|
|
28600
|
+
}
|
|
28601
|
+
}
|
|
28602
|
+
|
|
28603
|
+
// Sticky toolbar - compensate for padding in scroll parent
|
|
28604
|
+
|
|
28605
|
+
if (me._internal.Data("toolbar-sticky") === "true")
|
|
28606
|
+
{
|
|
28607
|
+
var toolbarContainer = designEditorDom.Top || designEditorDom.Bottom;
|
|
28608
|
+
|
|
28609
|
+
if (Fit.Dom.GetComputedStyle(toolbarContainer, "position") === "sticky") // False on non-supported browsers as position:sticky is applied via the @supports CSS rule
|
|
28610
|
+
{
|
|
28611
|
+
var scrollParent = Fit.Dom.GetScrollParent(me.GetDomElement());
|
|
28612
|
+
|
|
28613
|
+
if (scrollParent !== null) // In case editor is hosted in a container with position:fixed with overflow:hidden, in which case Fit.Dom.GetScrollParent(..) returns null
|
|
28614
|
+
{
|
|
28615
|
+
var toolbarPosition = me._internal.Data("toolbar-position"); // top | bottom
|
|
28616
|
+
|
|
28617
|
+
if (toolbarPosition === "top")
|
|
28618
|
+
{
|
|
28619
|
+
var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-top"); // E.g. "28px"
|
|
28620
|
+
toolbarContainer.style.top = paddingOffset !== "0px" ? "-" + paddingOffset : "";
|
|
28621
|
+
}
|
|
28622
|
+
else
|
|
28623
|
+
{
|
|
28624
|
+
var paddingOffset = Fit.Dom.GetComputedStyle(scrollParent, "padding-bottom"); // E.g. "28px"
|
|
28625
|
+
toolbarContainer.style.bottom = paddingOffset !== "0px" ? "-" + paddingOffset : "";
|
|
28626
|
+
}
|
|
28627
|
+
}
|
|
28628
|
+
}
|
|
28629
|
+
}
|
|
28630
|
+
|
|
28631
|
+
// Register necessary events with emoji panel when opened
|
|
28632
|
+
|
|
28633
|
+
var emojiButton = designEditor.container.$.querySelector("a.cke_button__emojipanel");
|
|
28634
|
+
|
|
28635
|
+
if (emojiButton !== null) // Better safe than sorry
|
|
28636
|
+
{
|
|
28637
|
+
Fit.Events.AddHandler(emojiButton, "click", function(e)
|
|
28638
|
+
{
|
|
28639
|
+
// Make sure OnFocus fires before locking focus state
|
|
28640
|
+
|
|
28641
|
+
if (me.Focused() === false)
|
|
28642
|
+
{
|
|
28643
|
+
// Control not focused - make sure OnFocus fires when emoji button is clicked,
|
|
28644
|
+
// and make sure ControlBase internally considers itself focused, so there is
|
|
28645
|
+
// no risk of OnFocus being fired twice without OnBlur firing in between,
|
|
28646
|
+
// when focus state is unlocked, and focus is perhaps re-assigned to another
|
|
28647
|
+
// DOM element within the control, which will be the case if the design editor
|
|
28648
|
+
// is switched back to an ordinary input field (e.g. using DesignMode(false)).
|
|
28649
|
+
me.Focused(true);
|
|
28650
|
+
}
|
|
28651
|
+
|
|
28652
|
+
// Prevent control from firing OnBlur when emoji dialog is opened.
|
|
28653
|
+
// Notice that locking the focus state will also prevent OnFocus
|
|
28654
|
+
// from being fired automatically.
|
|
28655
|
+
me._internal.FocusStateLocked(true);
|
|
28656
|
+
|
|
28657
|
+
setTimeout(function() // Postpone - emoji panel is made visible after click event
|
|
28658
|
+
{
|
|
28659
|
+
// Allow light dismissable panels/callouts to prevent close/dismiss
|
|
28660
|
+
// when interacting with emoji widget hosted outside of panels/callouts,
|
|
28661
|
+
// by detecting the presence of a data-disable-light-dismiss="true" attribute.
|
|
28662
|
+
var emojiPanel = document.querySelector("div.cke_emoji-panel"); // Shared among instances
|
|
28663
|
+
|
|
28664
|
+
if (emojiPanel !== null) // Better safe than sorry
|
|
28665
|
+
{
|
|
28666
|
+
Fit.Dom.Data(emojiPanel, "disable-light-dismiss", "true");
|
|
28667
|
+
|
|
28668
|
+
emojiPanel._associatedFitUiControl = me;
|
|
28669
|
+
|
|
28670
|
+
designEditorActiveToolbarPanel =
|
|
28671
|
+
{
|
|
28672
|
+
DomElement: emojiPanel,
|
|
28673
|
+
UnlockFocusStateIfEmojiPanelIsClosed: function() // Function called regularly via interval timer while emoji panel is open to make sure focus state is unlocked when emoji panel is closed, e.g. by pressing ESC, clicking outside of emoji panel, or by choosing an emoji
|
|
28674
|
+
{
|
|
28675
|
+
if (designModeEnabledAndReady() === false /* No longer in DesignMode */ || Fit.Dom.IsVisible(emojiPanel) === false /* Emoji panel closed */ || emojiPanel._associatedFitUiControl !== me /* Emoji panel now opened from another editor */)
|
|
28676
|
+
{
|
|
28677
|
+
designEditorActiveToolbarPanel = null;
|
|
28678
|
+
|
|
28679
|
+
// Disable focus lock - let ControlBase handle OnFocus and OnBlur automatically again
|
|
28680
|
+
me._internal.FocusStateLocked(false);
|
|
28681
|
+
|
|
28682
|
+
// Fire OnBlur in case user changed focus while emoji panel was open.
|
|
28683
|
+
// OnBlur does not fire automatically when focus state is locked.
|
|
28684
|
+
if (me.Focused() === false)
|
|
28685
|
+
{
|
|
28686
|
+
me._internal.FireOnBlur();
|
|
28687
|
+
}
|
|
28688
|
+
}
|
|
28689
|
+
},
|
|
28690
|
+
CloseEmojiPanel: function()
|
|
28691
|
+
{
|
|
28692
|
+
if (emojiPanel._associatedFitUiControl === me && Fit.Dom.IsVisible(emojiPanel) === true && Fit.Dom.Contained(emojiPanel, Fit.Dom.GetFocused()) === true)
|
|
28693
|
+
{
|
|
28694
|
+
designEditor.focus();
|
|
28695
|
+
designEditorActiveToolbarPanel.UnlockFocusStateIfEmojiPanelIsClosed();
|
|
28696
|
+
}
|
|
28697
|
+
}
|
|
28698
|
+
}
|
|
28699
|
+
}
|
|
28700
|
+
|
|
28701
|
+
var checkClosedId = setInterval(function()
|
|
28702
|
+
{
|
|
28703
|
+
// Invoke cleanup function regularly to make sure
|
|
28704
|
+
// focus lock is relased when emoji panel is closed,
|
|
28705
|
+
// and to fire OnBlur if another control was focused
|
|
28706
|
+
// while emoji panel was open.
|
|
28707
|
+
|
|
28708
|
+
if (me === null)
|
|
28709
|
+
{
|
|
28710
|
+
clearInterval(checkClosedId);
|
|
28711
|
+
return;
|
|
28712
|
+
}
|
|
28713
|
+
|
|
28714
|
+
if (designEditorActiveToolbarPanel !== null)
|
|
28715
|
+
{
|
|
28716
|
+
designEditorActiveToolbarPanel.UnlockFocusStateIfEmojiPanelIsClosed(); // Nullfies designEditorActiveToolbarPanel if emoji panel is closed
|
|
28717
|
+
}
|
|
28718
|
+
|
|
28719
|
+
if (designEditorActiveToolbarPanel === null)
|
|
28720
|
+
{
|
|
28721
|
+
clearInterval(checkClosedId);
|
|
28722
|
+
}
|
|
28723
|
+
}, 250);
|
|
28724
|
+
}, 0);
|
|
28725
|
+
});
|
|
28726
|
+
}
|
|
28727
|
+
|
|
28728
|
+
// DISABLED: Doesn't work! Emoji panel contains an iFrame. When it is re-mounted
|
|
28729
|
+
// in DOM, the iframe reloads, and dynamically added content is lost. Also, this makes
|
|
28730
|
+
// CKEditor throw errors and the dialog never appears.
|
|
28731
|
+
/*if (Fit._internal.ControlBase.ReduceDocumentRootPollution === true)
|
|
28732
|
+
{
|
|
28733
|
+
// Move emoji dialog to control - otherwise placed in the root of the document where it pollutes,
|
|
28734
|
+
// and makes it impossible to interact with the dialog in light dismissable panels and callouts.
|
|
28735
|
+
// Dialog is placed alongside control and not within the control's container, to prevent Fit.UI
|
|
28736
|
+
// styling from affecting the dialog.
|
|
28737
|
+
if (config.Toolbar && config.Toolbar.Emojis === true)
|
|
28738
|
+
{
|
|
28739
|
+
var emojiButton = designEditor.container.$.querySelector("a.cke_button__emojipanel");
|
|
28740
|
+
|
|
28741
|
+
if (emojiButton !== null)
|
|
28742
|
+
{
|
|
28743
|
+
Fit.Events.AddHandler(emojiButton, "click", function(e)
|
|
28744
|
+
{
|
|
28745
|
+
setTimeout(function() // Postpone - made visible after click event
|
|
28746
|
+
{
|
|
28747
|
+
var emojiPanel = document.querySelector("div.cke_emoji-panel:not([style*='display: none'])");
|
|
28748
|
+
|
|
28749
|
+
if (emojiPanel !== null)
|
|
28750
|
+
{
|
|
28751
|
+
Fit.Dom.InsertAfter(me.GetDomElement(), emojiPanel);
|
|
28752
|
+
}
|
|
28753
|
+
}, 0);
|
|
28754
|
+
});
|
|
28755
|
+
}
|
|
28756
|
+
}
|
|
28757
|
+
}*/
|
|
28758
|
+
|
|
28759
|
+
// Make editor assume configured width and height.
|
|
28760
|
+
// Notice that using config.width and config.height
|
|
28761
|
+
// (https://ckeditor.com/docs/ckeditor4/latest/features/size.html)
|
|
28762
|
+
// results in editor becoming too high since the toolbar height is not
|
|
28763
|
+
// substracted. This problem does not occur when using updateDesignEditorSize().
|
|
28764
|
+
updateDesignEditorSize();
|
|
28765
|
+
|
|
28766
|
+
if (me.Focused() === false)
|
|
28767
|
+
{
|
|
28768
|
+
// Hide editor toolbar if configured to do so
|
|
28769
|
+
hideToolbarInDesignMode();
|
|
28770
|
+
}
|
|
28771
|
+
else
|
|
28772
|
+
{
|
|
28773
|
+
// Remove placeholder if initially focused
|
|
28774
|
+
updateDesignEditorPlaceholder(true);
|
|
28775
|
+
}
|
|
28776
|
+
|
|
28777
|
+
// Make editor visible - postpone to allow editor to first calculate auto grow height
|
|
28778
|
+
// so the user will not see the chrome (borders) of the editor increase its height.
|
|
28779
|
+
setTimeout(function()
|
|
28780
|
+
{
|
|
28781
|
+
designEditorDom.OuterContainer.style.visibility = "visible";
|
|
28782
|
+
|
|
28783
|
+
// Because editor is hidden while initializing, startupFocus
|
|
28784
|
+
// (https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-startupFocus)
|
|
28785
|
+
// won't be able to place focus in the editor. We resolve this by assigning focus again
|
|
28786
|
+
// once editor is visible (visibility set above). Because startupFocus was set, it will
|
|
28787
|
+
// place focus at the end of the editor as expected.
|
|
28788
|
+
if (me.Focused() === true)
|
|
28789
|
+
{
|
|
28790
|
+
designEditor.focus(); // Won't work on iOS as assigning focus must be the result of a direct user interaction, which this is not since it is postponed using setTimeout(..) and async. loading of the editor
|
|
28791
|
+
}
|
|
28792
|
+
}, 0);
|
|
28793
|
+
},
|
|
28794
|
+
change: function() // CKEditor bug: not fired in Opera 12 (possibly other old versions as well)
|
|
28795
|
+
{
|
|
28796
|
+
if (me._internal.FireOnChangeSuppressed === true)
|
|
28797
|
+
{
|
|
28798
|
+
// Do not process event - it has been fired by CKEditor when HTML
|
|
28799
|
+
// value was initially assigned in Value(..) which happend through
|
|
28800
|
+
// me._internal.ExecuteWithNoOnChange(function() { .. }).
|
|
28801
|
+
// See Value(..) implementation for details.
|
|
28802
|
+
return;
|
|
28803
|
+
}
|
|
28804
|
+
|
|
28805
|
+
// Assume value was changed by user if control has focus
|
|
28806
|
+
if (designEditorDirty === false && me.Focused() === true)
|
|
28807
|
+
{
|
|
28808
|
+
designEditorDirty = true;
|
|
28809
|
+
}
|
|
28810
|
+
|
|
28811
|
+
input.onkeyup();
|
|
28812
|
+
},
|
|
28813
|
+
resize: function() // Fires when size is changed (except via auto grow), not just when resized using resize handle in lower right cornor
|
|
28814
|
+
{
|
|
28815
|
+
if (designEditorSuppressOnResize === false) // Only set data-resized="true" when resized using resize handle
|
|
28816
|
+
{
|
|
28817
|
+
// Disable Min/Max height configured with auto grow feature so user can resize it freely, unless PreventResizeBeyondMaximumHeight is enabled
|
|
28818
|
+
if (designEditorConfig !== null && designEditorConfig.AutoGrow && designEditorConfig.AutoGrow.Enabled === true && designEditorConfig.AutoGrow.PreventResizeBeyondMaximumHeight !== true)
|
|
28819
|
+
{
|
|
28820
|
+
var editableDiv = designEditorDom.Editable;
|
|
28821
|
+
editableDiv.style.minHeight = "";
|
|
28822
|
+
editableDiv.style.maxHeight = "";
|
|
28823
|
+
|
|
28824
|
+
var contents = designEditorDom.Content;
|
|
28825
|
+
contents.style.maxHeight = "";
|
|
28826
|
+
}
|
|
28827
|
+
|
|
28828
|
+
me._internal.Data("resized", "true");
|
|
28829
|
+
repaint();
|
|
28830
|
+
}
|
|
28831
|
+
},
|
|
28832
|
+
selectionChange: function(ev)
|
|
28833
|
+
{
|
|
28834
|
+
var elm = ev.data.selection.getStartElement().$;
|
|
28835
|
+
|
|
28836
|
+
// Allow light dismissable panels/callouts to prevent close/dismiss
|
|
28837
|
+
// when interacting with image resize handles hosted outside of panels/callouts,
|
|
28838
|
+
// by detecting the presence of a data-disable-light-dismiss="true" attribute.
|
|
28839
|
+
|
|
28840
|
+
if (elm.tagName === "IMG")
|
|
28841
|
+
{
|
|
28842
|
+
setTimeout(function() // Postpone - wait for image resize plugin to add image resize handles
|
|
28843
|
+
{
|
|
28844
|
+
var imageResizeHandlesContainer = document.querySelector("#ckimgrsz");
|
|
28845
|
+
if (imageResizeHandlesContainer !== null) // Better safe than sorry
|
|
28846
|
+
{
|
|
28847
|
+
Fit.Dom.Data(imageResizeHandlesContainer, "disable-light-dismiss", "true");
|
|
28848
|
+
}
|
|
28849
|
+
}, 0);
|
|
28850
|
+
}
|
|
28851
|
+
|
|
28852
|
+
// Disable/enable toolbar buttons, depending on whether a tag/mention is selected
|
|
28853
|
+
|
|
28854
|
+
if (elm.tagName === "A" && Fit.Dom.Data(elm, "tag-id") !== null)
|
|
28855
|
+
{
|
|
28856
|
+
// Notice that selectionChange handler is invoked while editor is loading if control was given initial focus.
|
|
28857
|
+
// But at this point the toolbar buttons are not yet available to be disabled, so disableDesignEditorButtons()
|
|
28858
|
+
// won't work. However, as soon as the editor is done loading, focus is re-assigned to the editable area
|
|
28859
|
+
// which will trigger selectionChange handler once again, at which point designModeEnabledAndReady() returns true.
|
|
28860
|
+
if (designModeEnabledAndReady() === true)
|
|
28861
|
+
{
|
|
28862
|
+
designEditorSuppressPaste = true;
|
|
28863
|
+
setTimeout(function() // Postpone - otherwise we won't be able to temporarily disable some of the buttons (https://jsfiddle.net/ymv56znq/14/)
|
|
28864
|
+
{
|
|
28865
|
+
disableDesignEditorButtons();
|
|
28866
|
+
}, 0);
|
|
28867
|
+
}
|
|
28868
|
+
}
|
|
28869
|
+
else
|
|
28870
|
+
{
|
|
28871
|
+
designEditorSuppressPaste = false;
|
|
28872
|
+
restoreDesignEditorButtons();
|
|
28873
|
+
}
|
|
28874
|
+
},
|
|
28875
|
+
doubleclick: function(ev)
|
|
28876
|
+
{
|
|
28877
|
+
// Suppress link dialog when double clicking. User must use link
|
|
28878
|
+
// button instead which triggers beforeCommandExec below - it creates
|
|
28879
|
+
// a focus lock to prevent control from losing focus and firing OnBlur.
|
|
28880
|
+
if (ev.data.element.$.tagName === "A")
|
|
24775
28881
|
{
|
|
24776
28882
|
ev.cancel();
|
|
24777
28883
|
return;
|
|
24778
28884
|
}
|
|
28885
|
+
|
|
28886
|
+
// Suppress link dialog for tags (similar code found in beforeCommandExec handler below)
|
|
28887
|
+
// DISABLED: No longer needed since link dialog is now suppressed for all links (see code above)
|
|
28888
|
+
/*if (Fit.Dom.Data(ev.data.element.$, "tag-id") !== null)
|
|
28889
|
+
{
|
|
28890
|
+
ev.cancel();
|
|
28891
|
+
return;
|
|
28892
|
+
}*/
|
|
24779
28893
|
},
|
|
24780
28894
|
paste: function(ev)
|
|
24781
28895
|
{
|
|
@@ -25142,11 +29256,14 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25142
29256
|
|
|
25143
29257
|
updateDetachedConfiguration();
|
|
25144
29258
|
|
|
25145
|
-
// Create dialog
|
|
29259
|
+
// Create dialog and buttons
|
|
25146
29260
|
|
|
25147
29261
|
var dia = new Fit.Controls.Dialog();
|
|
25148
29262
|
Fit.Dom.AddClass(dia.GetDomElement(), "FitUiControlInputDetached");
|
|
25149
29263
|
|
|
29264
|
+
var cmdOk = new Fit.Controls.Button();
|
|
29265
|
+
var cmdCancel = new Fit.Controls.Button();
|
|
29266
|
+
|
|
25150
29267
|
// Create editor
|
|
25151
29268
|
|
|
25152
29269
|
var de = new Fit.Controls.Input();
|
|
@@ -25171,8 +29288,14 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25171
29288
|
// so even though the timer interupts browser events such as onmousemove, onscroll, etc.,
|
|
25172
29289
|
// it creates no lack at all.
|
|
25173
29290
|
var height = -1
|
|
29291
|
+
var suspendDimensionMonitor = false;
|
|
25174
29292
|
var dimMonitorId = setInterval(function()
|
|
25175
29293
|
{
|
|
29294
|
+
if (suspendDimensionMonitor === true)
|
|
29295
|
+
{
|
|
29296
|
+
return;
|
|
29297
|
+
}
|
|
29298
|
+
|
|
25176
29299
|
if (height === -1 && de._internal.DesignModeEnabledAndReady() === false)
|
|
25177
29300
|
{
|
|
25178
29301
|
return;
|
|
@@ -25247,6 +29370,62 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25247
29370
|
return de.Value();
|
|
25248
29371
|
},
|
|
25249
29372
|
|
|
29373
|
+
SetVisible: function(val)
|
|
29374
|
+
{
|
|
29375
|
+
// Focus state remains locked when toggling visibility.
|
|
29376
|
+
// We merely make sure to invoke OnBlur and OnFocus events.
|
|
29377
|
+
// Focus lock is only released when detached editor is closed
|
|
29378
|
+
// or if control is disposed.
|
|
29379
|
+
|
|
29380
|
+
if (val === false && dia.IsOpen() === true)
|
|
29381
|
+
{
|
|
29382
|
+
dia.Close();
|
|
29383
|
+
me._internal.FireOnBlur();
|
|
29384
|
+
suspendDimensionMonitor = true;
|
|
29385
|
+
}
|
|
29386
|
+
else if (val === true && dia.IsOpen() === false)
|
|
29387
|
+
{
|
|
29388
|
+
dia.Open();
|
|
29389
|
+
de.Focused(true);
|
|
29390
|
+
me._internal.FireOnFocus();
|
|
29391
|
+
suspendDimensionMonitor = false;
|
|
29392
|
+
}
|
|
29393
|
+
},
|
|
29394
|
+
|
|
29395
|
+
SetEnabled: function(val)
|
|
29396
|
+
{
|
|
29397
|
+
if (val === false && de.Enabled() === true)
|
|
29398
|
+
{
|
|
29399
|
+
de.Enabled(false);
|
|
29400
|
+
cmdOk.Enabled(false);
|
|
29401
|
+
cmdCancel.Focused(true);
|
|
29402
|
+
}
|
|
29403
|
+
else if (val === true && de.Enabled() === false)
|
|
29404
|
+
{
|
|
29405
|
+
de.Enabled(true);
|
|
29406
|
+
cmdOk.Enabled(true);
|
|
29407
|
+
de.Focused(true);
|
|
29408
|
+
}
|
|
29409
|
+
},
|
|
29410
|
+
|
|
29411
|
+
Focus: function()
|
|
29412
|
+
{
|
|
29413
|
+
if (de.Enabled() === true)
|
|
29414
|
+
{
|
|
29415
|
+
de.Focused(true);
|
|
29416
|
+
}
|
|
29417
|
+
else
|
|
29418
|
+
{
|
|
29419
|
+
cmdCancel.Focused(true);
|
|
29420
|
+
}
|
|
29421
|
+
},
|
|
29422
|
+
|
|
29423
|
+
// GetFocused: function()
|
|
29424
|
+
// {
|
|
29425
|
+
// return de.Focused() === true /* also returns true if e.g. link/image dialog is open */
|
|
29426
|
+
// || cmdOk.Focused() === true || cmdCancel.Focused() === true;
|
|
29427
|
+
// },
|
|
29428
|
+
|
|
25250
29429
|
Reload: function()
|
|
25251
29430
|
{
|
|
25252
29431
|
// Update configuration
|
|
@@ -25272,7 +29451,6 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25272
29451
|
}
|
|
25273
29452
|
};
|
|
25274
29453
|
|
|
25275
|
-
var cmdOk = new Fit.Controls.Button();
|
|
25276
29454
|
cmdOk.Title(locale.Ok);
|
|
25277
29455
|
cmdOk.Icon("check");
|
|
25278
29456
|
cmdOk.Type(Fit.Controls.ButtonType.Success);
|
|
@@ -25296,7 +29474,6 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25296
29474
|
});
|
|
25297
29475
|
dia.AddButton(cmdOk);
|
|
25298
29476
|
|
|
25299
|
-
var cmdCancel = new Fit.Controls.Button();
|
|
25300
29477
|
cmdCancel.Title(locale.Cancel);
|
|
25301
29478
|
cmdCancel.Icon("ban");
|
|
25302
29479
|
cmdCancel.Type(Fit.Controls.ButtonType.Danger);
|
|
@@ -25313,10 +29490,19 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25313
29490
|
}
|
|
25314
29491
|
});
|
|
25315
29492
|
|
|
25316
|
-
|
|
29493
|
+
var enabled = me.Enabled();
|
|
29494
|
+
designEditorDetached.Dispose(); // Dispose first so me.Focused(true) below does not redirect focus to detached editor
|
|
25317
29495
|
|
|
25318
|
-
|
|
25319
|
-
|
|
29496
|
+
if (enabled === true) // Return focus to control if it is still enabled - if not, do not return focus and fire OnBlur
|
|
29497
|
+
{
|
|
29498
|
+
me.Focused(true);
|
|
29499
|
+
me._internal.FocusStateLocked(false);
|
|
29500
|
+
}
|
|
29501
|
+
else
|
|
29502
|
+
{
|
|
29503
|
+
me._internal.FocusStateLocked(false);
|
|
29504
|
+
me._internal.FireOnBlur();
|
|
29505
|
+
}
|
|
25320
29506
|
};
|
|
25321
29507
|
|
|
25322
29508
|
if (de.IsDirty() === true)
|
|
@@ -25327,6 +29513,10 @@ Fit.Controls.Input = function(ctlId)
|
|
|
25327
29513
|
{
|
|
25328
29514
|
closeDialog();
|
|
25329
29515
|
}
|
|
29516
|
+
else
|
|
29517
|
+
{
|
|
29518
|
+
cmdCancel.Focused(true);
|
|
29519
|
+
}
|
|
25330
29520
|
});
|
|
25331
29521
|
}
|
|
25332
29522
|
else
|