lexgui 0.6.4 → 0.6.5
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/build/components/codeeditor.js +10 -10
- package/build/components/nodegraph.js +12 -12
- package/build/components/timeline.js +170 -175
- package/build/lexgui.js +1219 -1130
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +1235 -1146
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +13 -1
- package/demo.js +1 -1
- package/examples/all_widgets.html +1 -1
- package/examples/editor.html +1 -1
- package/package.json +1 -1
package/build/lexgui.js
CHANGED
|
@@ -14,7 +14,7 @@ console.warn( 'Script _build/lexgui.js_ is depracated and will be removed soon.
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const LX = {
|
|
17
|
-
version: "0.6.
|
|
17
|
+
version: "0.6.5",
|
|
18
18
|
ready: false,
|
|
19
19
|
components: [], // Specific pre-build components
|
|
20
20
|
signals: {}, // Events and triggers
|
|
@@ -597,549 +597,408 @@ function setCommandbarState( value, resetEntries = true )
|
|
|
597
597
|
|
|
598
598
|
LX.setCommandbarState = setCommandbarState;
|
|
599
599
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
* @param {String} title (Optional)
|
|
604
|
-
* @param {Object} options
|
|
605
|
-
* id: Id of the message dialog
|
|
606
|
-
* position: Dialog position in screen [screen centered]
|
|
607
|
-
* draggable: Dialog can be dragged [false]
|
|
608
|
-
*/
|
|
609
|
-
|
|
610
|
-
function message( text, title, options = {} )
|
|
611
|
-
{
|
|
612
|
-
if( !text )
|
|
613
|
-
{
|
|
614
|
-
throw( "No message to show" );
|
|
615
|
-
}
|
|
600
|
+
/*
|
|
601
|
+
* Events and Signals
|
|
602
|
+
*/
|
|
616
603
|
|
|
617
|
-
|
|
604
|
+
class IEvent {
|
|
618
605
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
606
|
+
constructor( name, value, domEvent ) {
|
|
607
|
+
this.name = name;
|
|
608
|
+
this.value = value;
|
|
609
|
+
this.domEvent = domEvent;
|
|
610
|
+
}
|
|
622
611
|
}
|
|
612
|
+
LX.IEvent = IEvent;
|
|
623
613
|
|
|
624
|
-
|
|
614
|
+
class TreeEvent {
|
|
625
615
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
*/
|
|
616
|
+
static NONE = 0;
|
|
617
|
+
static NODE_SELECTED = 1;
|
|
618
|
+
static NODE_DELETED = 2;
|
|
619
|
+
static NODE_DBLCLICKED = 3;
|
|
620
|
+
static NODE_CONTEXTMENU = 4;
|
|
621
|
+
static NODE_DRAGGED = 5;
|
|
622
|
+
static NODE_RENAMED = 6;
|
|
623
|
+
static NODE_VISIBILITY = 7;
|
|
624
|
+
static NODE_CARETCHANGED = 8;
|
|
636
625
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
626
|
+
constructor( type, node, value ) {
|
|
627
|
+
this.type = type || TreeEvent.NONE;
|
|
628
|
+
this.node = node;
|
|
629
|
+
this.value = value;
|
|
630
|
+
this.multiple = false; // Multiple selection
|
|
631
|
+
this.panel = null;
|
|
642
632
|
}
|
|
643
633
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
634
|
+
string() {
|
|
635
|
+
switch( this.type )
|
|
636
|
+
{
|
|
637
|
+
case TreeEvent.NONE: return "tree_event_none";
|
|
638
|
+
case TreeEvent.NODE_SELECTED: return "tree_event_selected";
|
|
639
|
+
case TreeEvent.NODE_DELETED: return "tree_event_deleted";
|
|
640
|
+
case TreeEvent.NODE_DBLCLICKED: return "tree_event_dblclick";
|
|
641
|
+
case TreeEvent.NODE_CONTEXTMENU: return "tree_event_contextmenu";
|
|
642
|
+
case TreeEvent.NODE_DRAGGED: return "tree_event_dragged";
|
|
643
|
+
case TreeEvent.NODE_RENAMED: return "tree_event_renamed";
|
|
644
|
+
case TreeEvent.NODE_VISIBILITY: return "tree_event_visibility";
|
|
645
|
+
case TreeEvent.NODE_CARETCHANGED: return "tree_event_caretchanged";
|
|
646
|
+
}
|
|
647
|
+
}
|
|
657
648
|
}
|
|
649
|
+
LX.TreeEvent = TreeEvent;
|
|
658
650
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* @method prompt
|
|
663
|
-
* @param {String} text
|
|
664
|
-
* @param {String} title (Optional)
|
|
665
|
-
* @param {Object} options
|
|
666
|
-
* id: Id of the prompt dialog
|
|
667
|
-
* position: Dialog position in screen [screen centered]
|
|
668
|
-
* draggable: Dialog can be dragged [false]
|
|
669
|
-
* input: If false, no text input appears
|
|
670
|
-
* accept: Accept text
|
|
671
|
-
* required: Input has to be filled [true]. Default: false
|
|
672
|
-
*/
|
|
673
|
-
|
|
674
|
-
function prompt( text, title, callback, options = {} )
|
|
651
|
+
function emit( signalName, value, options = {} )
|
|
675
652
|
{
|
|
676
|
-
|
|
677
|
-
options.className = "prompt";
|
|
678
|
-
|
|
679
|
-
let value = "";
|
|
653
|
+
const data = LX.signals[ signalName ];
|
|
680
654
|
|
|
681
|
-
|
|
655
|
+
if( !data )
|
|
656
|
+
{
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
682
659
|
|
|
683
|
-
|
|
660
|
+
const target = options.target;
|
|
684
661
|
|
|
685
|
-
|
|
662
|
+
if( target )
|
|
663
|
+
{
|
|
664
|
+
if( target[ signalName ])
|
|
686
665
|
{
|
|
687
|
-
|
|
666
|
+
target[ signalName ].call( target, value );
|
|
688
667
|
}
|
|
689
668
|
|
|
690
|
-
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
691
671
|
|
|
692
|
-
|
|
672
|
+
for( let obj of data )
|
|
673
|
+
{
|
|
674
|
+
if( obj instanceof LX.Widget )
|
|
675
|
+
{
|
|
676
|
+
obj.set( value, options.skipCallback ?? true );
|
|
677
|
+
}
|
|
678
|
+
else if( obj.constructor === Function )
|
|
679
|
+
{
|
|
680
|
+
const fn = obj;
|
|
681
|
+
fn( null, value );
|
|
682
|
+
}
|
|
683
|
+
else
|
|
684
|
+
{
|
|
685
|
+
// This is an element
|
|
686
|
+
const fn = obj[ signalName ];
|
|
687
|
+
console.assert( fn, `No callback registered with _${ signalName }_ signal` );
|
|
688
|
+
fn.bind( obj )( value );
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
693
692
|
|
|
694
|
-
|
|
695
|
-
if( options.required && value === '' )
|
|
696
|
-
{
|
|
697
|
-
text += text.includes("You must fill the input text.") ? "": "\nYou must fill the input text.";
|
|
698
|
-
dialog.close();
|
|
699
|
-
prompt( text, title, callback, options );
|
|
700
|
-
}
|
|
701
|
-
else
|
|
702
|
-
{
|
|
703
|
-
if( callback ) callback.call( this, value );
|
|
704
|
-
dialog.close();
|
|
705
|
-
}
|
|
706
|
-
}, { buttonClass: "primary" });
|
|
693
|
+
LX.emit = emit;
|
|
707
694
|
|
|
708
|
-
|
|
695
|
+
function addSignal( name, obj, callback )
|
|
696
|
+
{
|
|
697
|
+
obj[ name ] = callback;
|
|
709
698
|
|
|
710
|
-
|
|
711
|
-
if( options.input ?? true )
|
|
699
|
+
if( !LX.signals[ name ] )
|
|
712
700
|
{
|
|
713
|
-
|
|
701
|
+
LX.signals[ name ] = [];
|
|
714
702
|
}
|
|
715
703
|
|
|
716
|
-
|
|
704
|
+
if( LX.signals[ name ].indexOf( obj ) > -1 )
|
|
705
|
+
{
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
LX.signals[ name ].push( obj );
|
|
717
710
|
}
|
|
718
711
|
|
|
719
|
-
LX.
|
|
712
|
+
LX.addSignal = addSignal;
|
|
713
|
+
|
|
714
|
+
/*
|
|
715
|
+
* DOM Elements
|
|
716
|
+
*/
|
|
720
717
|
|
|
721
718
|
/**
|
|
722
|
-
* @
|
|
723
|
-
* @param {String} title
|
|
724
|
-
* @param {String} description (Optional)
|
|
725
|
-
* @param {Object} options
|
|
726
|
-
* action: Data of the custom action { name, callback }
|
|
727
|
-
* closable: Allow closing the toast
|
|
728
|
-
* timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
|
|
719
|
+
* @class Popover
|
|
729
720
|
*/
|
|
730
721
|
|
|
731
|
-
|
|
732
|
-
{
|
|
733
|
-
if( !title )
|
|
734
|
-
{
|
|
735
|
-
throw( "The toast needs at least a title!" );
|
|
736
|
-
}
|
|
722
|
+
class Popover {
|
|
737
723
|
|
|
738
|
-
|
|
724
|
+
static activeElement = false;
|
|
739
725
|
|
|
740
|
-
|
|
741
|
-
toast.className = "lextoast";
|
|
742
|
-
toast.style.translate = "0 calc(100% + 30px)";
|
|
743
|
-
this.notifications.prepend( toast );
|
|
726
|
+
constructor( trigger, content, options = {} ) {
|
|
744
727
|
|
|
745
|
-
|
|
728
|
+
console.assert( trigger, "Popover needs a DOM element as trigger!" );
|
|
746
729
|
|
|
747
|
-
if(
|
|
730
|
+
if( Popover.activeElement )
|
|
748
731
|
{
|
|
749
|
-
|
|
750
|
-
|
|
732
|
+
Popover.activeElement.destroy();
|
|
733
|
+
return;
|
|
751
734
|
}
|
|
752
735
|
|
|
753
|
-
|
|
754
|
-
|
|
736
|
+
this._trigger = trigger;
|
|
737
|
+
trigger.classList.add( "triggered" );
|
|
738
|
+
trigger.active = this;
|
|
755
739
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
740
|
+
this._windowPadding = 4;
|
|
741
|
+
this.side = options.side ?? "bottom";
|
|
742
|
+
this.align = options.align ?? "center";
|
|
743
|
+
this.avoidCollisions = options.avoidCollisions ?? true;
|
|
759
744
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
745
|
+
this.root = document.createElement( "div" );
|
|
746
|
+
this.root.dataset["side"] = this.side;
|
|
747
|
+
this.root.tabIndex = "1";
|
|
748
|
+
this.root.className = "lexpopover";
|
|
749
|
+
LX.root.appendChild( this.root );
|
|
764
750
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
751
|
+
this.root.addEventListener( "keydown", (e) => {
|
|
752
|
+
if( e.key == "Escape" )
|
|
753
|
+
{
|
|
754
|
+
e.preventDefault();
|
|
755
|
+
e.stopPropagation();
|
|
756
|
+
this.destroy();
|
|
757
|
+
}
|
|
758
|
+
} );
|
|
772
759
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
760
|
+
if( content )
|
|
761
|
+
{
|
|
762
|
+
content = [].concat( content );
|
|
763
|
+
content.forEach( e => {
|
|
764
|
+
const domNode = e.root ?? e;
|
|
765
|
+
this.root.appendChild( domNode );
|
|
766
|
+
if( e.onPopover )
|
|
767
|
+
{
|
|
768
|
+
e.onPopover();
|
|
769
|
+
}
|
|
770
|
+
} );
|
|
771
|
+
}
|
|
779
772
|
|
|
780
|
-
|
|
773
|
+
Popover.activeElement = this;
|
|
781
774
|
|
|
782
|
-
toast.close = function() {
|
|
783
|
-
this.dataset[ "closed" ] = true;
|
|
784
775
|
LX.doAsync( () => {
|
|
785
|
-
this.
|
|
786
|
-
if( !that.notifications.childElementCount )
|
|
787
|
-
{
|
|
788
|
-
that.notifications.style.width = "unset";
|
|
789
|
-
that.notifications.iWidth = 0;
|
|
790
|
-
}
|
|
791
|
-
}, 500 );
|
|
792
|
-
};
|
|
776
|
+
this._adjustPosition();
|
|
793
777
|
|
|
794
|
-
|
|
795
|
-
{
|
|
796
|
-
const closeIcon = LX.makeIcon( "X", { iconClass: "closer" } );
|
|
797
|
-
closeIcon.addEventListener( "click", () => {
|
|
798
|
-
toast.close();
|
|
799
|
-
} );
|
|
800
|
-
toast.appendChild( closeIcon );
|
|
801
|
-
}
|
|
778
|
+
this.root.focus();
|
|
802
779
|
|
|
803
|
-
|
|
780
|
+
this._onClick = e => {
|
|
781
|
+
if( e.target && ( this.root.contains( e.target ) || e.target == this._trigger ) )
|
|
782
|
+
{
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
this.destroy();
|
|
786
|
+
};
|
|
804
787
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
toast.close();
|
|
809
|
-
}, timeout );
|
|
788
|
+
document.body.addEventListener( "mousedown", this._onClick, true );
|
|
789
|
+
document.body.addEventListener( "focusin", this._onClick, true );
|
|
790
|
+
}, 10 );
|
|
810
791
|
}
|
|
811
|
-
}
|
|
812
792
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
/**
|
|
816
|
-
* @method badge
|
|
817
|
-
* @param {String} text
|
|
818
|
-
* @param {String} className
|
|
819
|
-
* @param {Object} options
|
|
820
|
-
* style: Style attributes to override
|
|
821
|
-
* asElement: Returns the badge as HTMLElement [false]
|
|
822
|
-
*/
|
|
793
|
+
destroy() {
|
|
823
794
|
|
|
824
|
-
|
|
825
|
-
{
|
|
826
|
-
const container = document.createElement( "div" );
|
|
827
|
-
container.innerHTML = text;
|
|
828
|
-
container.className = "lexbadge " + ( className ?? "" );
|
|
829
|
-
Object.assign( container.style, options.style ?? {} );
|
|
830
|
-
return ( options.asElement ?? false ) ? container : container.outerHTML;
|
|
831
|
-
}
|
|
795
|
+
this._trigger.classList.remove( "triggered" );
|
|
832
796
|
|
|
833
|
-
|
|
797
|
+
delete this._trigger.active;
|
|
834
798
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
* @param {String} htmlType
|
|
838
|
-
* @param {String} className
|
|
839
|
-
* @param {String} innerHTML
|
|
840
|
-
* @param {HTMLElement} parent
|
|
841
|
-
* @param {Object} overrideStyle
|
|
842
|
-
*/
|
|
799
|
+
document.body.removeEventListener( "mousedown", this._onClick, true );
|
|
800
|
+
document.body.removeEventListener( "focusin", this._onClick, true );
|
|
843
801
|
|
|
844
|
-
|
|
845
|
-
{
|
|
846
|
-
const element = document.createElement( htmlType );
|
|
847
|
-
element.className = className ?? "";
|
|
848
|
-
element.innerHTML = innerHTML ?? "";
|
|
849
|
-
Object.assign( element.style, overrideStyle );
|
|
802
|
+
this.root.remove();
|
|
850
803
|
|
|
851
|
-
|
|
852
|
-
{
|
|
853
|
-
if( parent.attach ) // Use attach method if possible
|
|
854
|
-
{
|
|
855
|
-
parent.attach( element );
|
|
856
|
-
}
|
|
857
|
-
else // its a native HTMLElement
|
|
858
|
-
{
|
|
859
|
-
parent.appendChild( element );
|
|
860
|
-
}
|
|
804
|
+
Popover.activeElement = null;
|
|
861
805
|
}
|
|
862
806
|
|
|
863
|
-
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
LX.makeElement = makeElement;
|
|
867
|
-
|
|
868
|
-
/**
|
|
869
|
-
* @method makeContainer
|
|
870
|
-
* @param {Array} size
|
|
871
|
-
* @param {String} className
|
|
872
|
-
* @param {String} innerHTML
|
|
873
|
-
* @param {HTMLElement} parent
|
|
874
|
-
* @param {Object} overrideStyle
|
|
875
|
-
*/
|
|
876
|
-
|
|
877
|
-
function makeContainer( size, className, innerHTML, parent, overrideStyle = {} )
|
|
878
|
-
{
|
|
879
|
-
const container = LX.makeElement( "div", "lexcontainer " + ( className ?? "" ), innerHTML, parent, overrideStyle );
|
|
880
|
-
container.style.width = size && size[ 0 ] ? size[ 0 ] : "100%";
|
|
881
|
-
container.style.height = size && size[ 1 ] ? size[ 1 ] : "100%";
|
|
882
|
-
return container;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
LX.makeContainer = makeContainer;
|
|
886
|
-
|
|
887
|
-
/**
|
|
888
|
-
* @method asTooltip
|
|
889
|
-
* @param {HTMLElement} trigger
|
|
890
|
-
* @param {String} content
|
|
891
|
-
* @param {Object} options
|
|
892
|
-
* side: Side of the tooltip
|
|
893
|
-
* offset: Tooltip margin offset
|
|
894
|
-
* active: Tooltip active by default [true]
|
|
895
|
-
*/
|
|
896
|
-
|
|
897
|
-
function asTooltip( trigger, content, options = {} )
|
|
898
|
-
{
|
|
899
|
-
console.assert( trigger, "You need a trigger to generate a tooltip!" );
|
|
900
|
-
|
|
901
|
-
trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
|
|
902
|
-
|
|
903
|
-
let tooltipDom = null;
|
|
807
|
+
_adjustPosition() {
|
|
904
808
|
|
|
905
|
-
|
|
809
|
+
const position = [ 0, 0 ];
|
|
906
810
|
|
|
907
|
-
|
|
811
|
+
// Place menu using trigger position and user options
|
|
908
812
|
{
|
|
909
|
-
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
|
|
913
|
-
|
|
914
|
-
tooltipDom = document.createElement( "div" );
|
|
915
|
-
tooltipDom.className = "lextooltip";
|
|
916
|
-
tooltipDom.innerHTML = content;
|
|
917
|
-
|
|
918
|
-
LX.doAsync( () => {
|
|
813
|
+
const rect = this._trigger.getBoundingClientRect();
|
|
919
814
|
|
|
920
|
-
const position = [ 0, 0 ];
|
|
921
|
-
const rect = this.getBoundingClientRect();
|
|
922
|
-
const offset = options.offset ?? 6;
|
|
923
815
|
let alignWidth = true;
|
|
924
816
|
|
|
925
|
-
switch(
|
|
817
|
+
switch( this.side )
|
|
926
818
|
{
|
|
927
819
|
case "left":
|
|
928
|
-
position[ 0 ] += ( rect.x -
|
|
820
|
+
position[ 0 ] += ( rect.x - this.root.offsetWidth );
|
|
929
821
|
alignWidth = false;
|
|
930
822
|
break;
|
|
931
823
|
case "right":
|
|
932
|
-
position[ 0 ] += ( rect.x + rect.width
|
|
824
|
+
position[ 0 ] += ( rect.x + rect.width );
|
|
933
825
|
alignWidth = false;
|
|
934
826
|
break;
|
|
935
827
|
case "top":
|
|
936
|
-
position[ 1 ] += ( rect.y -
|
|
828
|
+
position[ 1 ] += ( rect.y - this.root.offsetHeight );
|
|
937
829
|
alignWidth = true;
|
|
938
830
|
break;
|
|
939
831
|
case "bottom":
|
|
940
|
-
position[ 1 ] += ( rect.y + rect.height
|
|
832
|
+
position[ 1 ] += ( rect.y + rect.height );
|
|
941
833
|
alignWidth = true;
|
|
942
834
|
break;
|
|
943
835
|
}
|
|
944
836
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
837
|
+
switch( this.align )
|
|
838
|
+
{
|
|
839
|
+
case "start":
|
|
840
|
+
if( alignWidth ) { position[ 0 ] += rect.x; }
|
|
841
|
+
else { position[ 1 ] += rect.y; }
|
|
842
|
+
break;
|
|
843
|
+
case "center":
|
|
844
|
+
if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - this.root.offsetWidth * 0.5; }
|
|
845
|
+
else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - this.root.offsetHeight * 0.5; }
|
|
846
|
+
break;
|
|
847
|
+
case "end":
|
|
848
|
+
if( alignWidth ) { position[ 0 ] += rect.x - this.root.offsetWidth + rect.width; }
|
|
849
|
+
else { position[ 1 ] += rect.y - this.root.offsetHeight + rect.height; }
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
958
853
|
|
|
959
|
-
|
|
960
|
-
if( tooltipDom )
|
|
854
|
+
if( this.avoidCollisions )
|
|
961
855
|
{
|
|
962
|
-
|
|
856
|
+
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - this.root.offsetWidth - this._windowPadding );
|
|
857
|
+
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
|
|
963
858
|
}
|
|
964
|
-
|
|
859
|
+
|
|
860
|
+
this.root.style.left = `${ position[ 0 ] }px`;
|
|
861
|
+
this.root.style.top = `${ position[ 1 ] }px`;
|
|
862
|
+
}
|
|
965
863
|
}
|
|
864
|
+
LX.Popover = Popover;
|
|
966
865
|
|
|
967
|
-
|
|
866
|
+
/**
|
|
867
|
+
* @class Sheet
|
|
868
|
+
*/
|
|
968
869
|
|
|
969
|
-
|
|
970
|
-
* Events and Signals
|
|
971
|
-
*/
|
|
870
|
+
class Sheet {
|
|
972
871
|
|
|
973
|
-
|
|
872
|
+
constructor( size, content, options = {} ) {
|
|
974
873
|
|
|
975
|
-
|
|
976
|
-
this.name = name;
|
|
977
|
-
this.value = value;
|
|
978
|
-
this.domEvent = domEvent;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
LX.IEvent = IEvent;
|
|
874
|
+
this.side = options.side ?? "left";
|
|
982
875
|
|
|
983
|
-
|
|
876
|
+
this.root = document.createElement( "div" );
|
|
877
|
+
this.root.dataset["side"] = this.side;
|
|
878
|
+
this.root.tabIndex = "1";
|
|
879
|
+
this.root.role = "dialog";
|
|
880
|
+
this.root.className = "lexsheet fixed z-100 bg-primary";
|
|
881
|
+
LX.root.appendChild( this.root );
|
|
984
882
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
static NODE_CARETCHANGED = 8;
|
|
883
|
+
this.root.addEventListener( "keydown", (e) => {
|
|
884
|
+
if( e.key == "Escape" )
|
|
885
|
+
{
|
|
886
|
+
e.preventDefault();
|
|
887
|
+
e.stopPropagation();
|
|
888
|
+
this.destroy();
|
|
889
|
+
}
|
|
890
|
+
} );
|
|
994
891
|
|
|
995
|
-
|
|
996
|
-
this.type = type || TreeEvent.NONE;
|
|
997
|
-
this.node = node;
|
|
998
|
-
this.value = value;
|
|
999
|
-
this.multiple = false; // Multiple selection
|
|
1000
|
-
this.panel = null;
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
string() {
|
|
1004
|
-
switch( this.type )
|
|
892
|
+
if( content )
|
|
1005
893
|
{
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
894
|
+
content = [].concat( content );
|
|
895
|
+
content.forEach( e => {
|
|
896
|
+
const domNode = e.root ?? e;
|
|
897
|
+
this.root.appendChild( domNode );
|
|
898
|
+
if( e.onSheet )
|
|
899
|
+
{
|
|
900
|
+
e.onSheet();
|
|
901
|
+
}
|
|
902
|
+
} );
|
|
1015
903
|
}
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
LX.TreeEvent = TreeEvent;
|
|
1019
904
|
|
|
1020
|
-
|
|
1021
|
-
{
|
|
1022
|
-
const data = LX.signals[ signalName ];
|
|
905
|
+
LX.doAsync( () => {
|
|
1023
906
|
|
|
1024
|
-
|
|
1025
|
-
{
|
|
1026
|
-
return;
|
|
1027
|
-
}
|
|
907
|
+
LX.modal.toggle( false );
|
|
1028
908
|
|
|
1029
|
-
|
|
909
|
+
switch( this.side )
|
|
910
|
+
{
|
|
911
|
+
case "left":
|
|
912
|
+
this.root.style.left = 0;
|
|
913
|
+
this.root.style.width = size;
|
|
914
|
+
this.root.style.height = "100%";
|
|
915
|
+
break;
|
|
916
|
+
case "right":
|
|
917
|
+
this.root.style.right = 0;
|
|
918
|
+
this.root.style.width = size;
|
|
919
|
+
this.root.style.height = "100%";
|
|
920
|
+
break;
|
|
921
|
+
case "top":
|
|
922
|
+
this.root.style.top = 0;
|
|
923
|
+
this.root.style.width = "100%";
|
|
924
|
+
this.root.style.height = size;
|
|
925
|
+
break;
|
|
926
|
+
case "bottom":
|
|
927
|
+
this.root.style.bottom = 0;
|
|
928
|
+
this.root.style.width = "100%";
|
|
929
|
+
this.root.style.height = size;
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
1030
932
|
|
|
1031
|
-
|
|
1032
|
-
{
|
|
1033
|
-
if( target[ signalName ])
|
|
1034
|
-
{
|
|
1035
|
-
target[ signalName ].call( target, value );
|
|
1036
|
-
}
|
|
933
|
+
this.root.focus();
|
|
1037
934
|
|
|
1038
|
-
|
|
1039
|
-
|
|
935
|
+
this._onClick = e => {
|
|
936
|
+
if( e.target && ( this.root.contains( e.target ) ) )
|
|
937
|
+
{
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
this.destroy();
|
|
941
|
+
};
|
|
1040
942
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
{
|
|
1045
|
-
obj.set( value, options.skipCallback ?? true );
|
|
1046
|
-
}
|
|
1047
|
-
else if( obj.constructor === Function )
|
|
1048
|
-
{
|
|
1049
|
-
const fn = obj;
|
|
1050
|
-
fn( null, value );
|
|
1051
|
-
}
|
|
1052
|
-
else
|
|
1053
|
-
{
|
|
1054
|
-
// This is an element
|
|
1055
|
-
const fn = obj[ signalName ];
|
|
1056
|
-
console.assert( fn, `No callback registered with _${ signalName }_ signal` );
|
|
1057
|
-
fn.bind( obj )( value );
|
|
1058
|
-
}
|
|
943
|
+
document.body.addEventListener( "mousedown", this._onClick, true );
|
|
944
|
+
document.body.addEventListener( "focusin", this._onClick, true );
|
|
945
|
+
}, 10 );
|
|
1059
946
|
}
|
|
1060
|
-
}
|
|
1061
947
|
|
|
1062
|
-
|
|
948
|
+
destroy() {
|
|
1063
949
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
obj[ name ] = callback;
|
|
950
|
+
document.body.removeEventListener( "mousedown", this._onClick, true );
|
|
951
|
+
document.body.removeEventListener( "focusin", this._onClick, true );
|
|
1067
952
|
|
|
1068
|
-
|
|
1069
|
-
{
|
|
1070
|
-
LX.signals[ name ] = [];
|
|
1071
|
-
}
|
|
953
|
+
this.root.remove();
|
|
1072
954
|
|
|
1073
|
-
|
|
1074
|
-
{
|
|
1075
|
-
return;
|
|
955
|
+
LX.modal.toggle( true );
|
|
1076
956
|
}
|
|
1077
|
-
|
|
1078
|
-
LX.signals[ name ].push( obj );
|
|
1079
957
|
}
|
|
1080
|
-
|
|
1081
|
-
LX.addSignal = addSignal;
|
|
1082
|
-
|
|
1083
|
-
/*
|
|
1084
|
-
* DOM Elements
|
|
1085
|
-
*/
|
|
958
|
+
LX.Sheet = Sheet;
|
|
1086
959
|
|
|
1087
960
|
/**
|
|
1088
|
-
* @class
|
|
961
|
+
* @class DropdownMenu
|
|
1089
962
|
*/
|
|
1090
963
|
|
|
1091
|
-
class
|
|
964
|
+
class DropdownMenu {
|
|
1092
965
|
|
|
1093
|
-
static
|
|
966
|
+
static currentMenu = false;
|
|
1094
967
|
|
|
1095
|
-
constructor( trigger,
|
|
968
|
+
constructor( trigger, items, options = {} ) {
|
|
1096
969
|
|
|
1097
|
-
console.assert( trigger, "
|
|
970
|
+
console.assert( trigger, "DropdownMenu needs a DOM element as trigger!" );
|
|
1098
971
|
|
|
1099
|
-
if(
|
|
972
|
+
if( DropdownMenu.currentMenu || !items?.length )
|
|
1100
973
|
{
|
|
1101
|
-
|
|
974
|
+
DropdownMenu.currentMenu.destroy();
|
|
975
|
+
this.invalid = true;
|
|
1102
976
|
return;
|
|
1103
977
|
}
|
|
1104
978
|
|
|
1105
979
|
this._trigger = trigger;
|
|
1106
980
|
trigger.classList.add( "triggered" );
|
|
1107
|
-
trigger.
|
|
981
|
+
trigger.ddm = this;
|
|
982
|
+
|
|
983
|
+
this._items = items;
|
|
1108
984
|
|
|
1109
985
|
this._windowPadding = 4;
|
|
1110
986
|
this.side = options.side ?? "bottom";
|
|
1111
987
|
this.align = options.align ?? "center";
|
|
1112
988
|
this.avoidCollisions = options.avoidCollisions ?? true;
|
|
989
|
+
this.onBlur = options.onBlur;
|
|
990
|
+
this.inPlace = false;
|
|
1113
991
|
|
|
1114
992
|
this.root = document.createElement( "div" );
|
|
993
|
+
this.root.id = "root";
|
|
1115
994
|
this.root.dataset["side"] = this.side;
|
|
1116
995
|
this.root.tabIndex = "1";
|
|
1117
|
-
this.root.className = "
|
|
996
|
+
this.root.className = "lexdropdownmenu";
|
|
1118
997
|
LX.root.appendChild( this.root );
|
|
1119
998
|
|
|
1120
|
-
this.
|
|
1121
|
-
if( e.key == "Escape" )
|
|
1122
|
-
{
|
|
1123
|
-
e.preventDefault();
|
|
1124
|
-
e.stopPropagation();
|
|
1125
|
-
this.destroy();
|
|
1126
|
-
}
|
|
1127
|
-
} );
|
|
1128
|
-
|
|
1129
|
-
if( content )
|
|
1130
|
-
{
|
|
1131
|
-
content = [].concat( content );
|
|
1132
|
-
content.forEach( e => {
|
|
1133
|
-
const domNode = e.root ?? e;
|
|
1134
|
-
this.root.appendChild( domNode );
|
|
1135
|
-
if( e.onPopover )
|
|
1136
|
-
{
|
|
1137
|
-
e.onPopover();
|
|
1138
|
-
}
|
|
1139
|
-
} );
|
|
1140
|
-
}
|
|
999
|
+
this._create( this._items );
|
|
1141
1000
|
|
|
1142
|
-
|
|
1001
|
+
DropdownMenu.currentMenu = this;
|
|
1143
1002
|
|
|
1144
1003
|
LX.doAsync( () => {
|
|
1145
1004
|
this._adjustPosition();
|
|
@@ -1147,11 +1006,14 @@ class Popover {
|
|
|
1147
1006
|
this.root.focus();
|
|
1148
1007
|
|
|
1149
1008
|
this._onClick = e => {
|
|
1150
|
-
|
|
1009
|
+
|
|
1010
|
+
// Check if the click is inside a menu or on the trigger
|
|
1011
|
+
if( e.target && ( e.target.closest( ".lexdropdownmenu" ) != undefined || e.target == this._trigger ) )
|
|
1151
1012
|
{
|
|
1152
1013
|
return;
|
|
1153
1014
|
}
|
|
1154
|
-
|
|
1015
|
+
|
|
1016
|
+
this.destroy( true );
|
|
1155
1017
|
};
|
|
1156
1018
|
|
|
1157
1019
|
document.body.addEventListener( "mousedown", this._onClick, true );
|
|
@@ -1159,298 +1021,67 @@ class Popover {
|
|
|
1159
1021
|
}, 10 );
|
|
1160
1022
|
}
|
|
1161
1023
|
|
|
1162
|
-
destroy() {
|
|
1024
|
+
destroy( blurEvent ) {
|
|
1163
1025
|
|
|
1164
1026
|
this._trigger.classList.remove( "triggered" );
|
|
1165
1027
|
|
|
1166
|
-
delete this._trigger.
|
|
1028
|
+
delete this._trigger.ddm;
|
|
1167
1029
|
|
|
1168
1030
|
document.body.removeEventListener( "mousedown", this._onClick, true );
|
|
1169
1031
|
document.body.removeEventListener( "focusin", this._onClick, true );
|
|
1170
1032
|
|
|
1171
|
-
|
|
1033
|
+
LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
|
|
1172
1034
|
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1035
|
+
DropdownMenu.currentMenu = null;
|
|
1175
1036
|
|
|
1176
|
-
|
|
1037
|
+
if( blurEvent && this.onBlur )
|
|
1038
|
+
{
|
|
1039
|
+
this.onBlur();
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1177
1042
|
|
|
1178
|
-
|
|
1043
|
+
_create( items, parentDom ) {
|
|
1179
1044
|
|
|
1180
|
-
|
|
1045
|
+
if( !parentDom )
|
|
1181
1046
|
{
|
|
1182
|
-
|
|
1047
|
+
parentDom = this.root;
|
|
1048
|
+
}
|
|
1049
|
+
else
|
|
1050
|
+
{
|
|
1051
|
+
const parentRect = parentDom.getBoundingClientRect();
|
|
1183
1052
|
|
|
1184
|
-
let
|
|
1053
|
+
let newParent = document.createElement( "div" );
|
|
1054
|
+
newParent.tabIndex = "1";
|
|
1055
|
+
newParent.className = "lexdropdownmenu";
|
|
1056
|
+
newParent.dataset["id"] = parentDom.dataset["id"];
|
|
1057
|
+
newParent.dataset["side"] = "right"; // submenus always come from the right
|
|
1058
|
+
LX.root.appendChild( newParent );
|
|
1185
1059
|
|
|
1186
|
-
|
|
1060
|
+
newParent.currentParent = parentDom;
|
|
1061
|
+
parentDom = newParent;
|
|
1062
|
+
|
|
1063
|
+
LX.doAsync( () => {
|
|
1064
|
+
const position = [ parentRect.x + parentRect.width, parentRect.y ];
|
|
1065
|
+
|
|
1066
|
+
if( this.avoidCollisions )
|
|
1067
|
+
{
|
|
1068
|
+
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - newParent.offsetWidth - this._windowPadding );
|
|
1069
|
+
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - newParent.offsetHeight - this._windowPadding );
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
newParent.style.left = `${ position[ 0 ] }px`;
|
|
1073
|
+
newParent.style.top = `${ position[ 1 ] }px`;
|
|
1074
|
+
}, 10 );
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
let applyIconPadding = items.filter( i => { return ( i?.icon != undefined ) || ( i?.checked != undefined ) } ).length > 0;
|
|
1078
|
+
|
|
1079
|
+
for( let item of items )
|
|
1080
|
+
{
|
|
1081
|
+
if( !item )
|
|
1187
1082
|
{
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
alignWidth = false;
|
|
1191
|
-
break;
|
|
1192
|
-
case "right":
|
|
1193
|
-
position[ 0 ] += ( rect.x + rect.width );
|
|
1194
|
-
alignWidth = false;
|
|
1195
|
-
break;
|
|
1196
|
-
case "top":
|
|
1197
|
-
position[ 1 ] += ( rect.y - this.root.offsetHeight );
|
|
1198
|
-
alignWidth = true;
|
|
1199
|
-
break;
|
|
1200
|
-
case "bottom":
|
|
1201
|
-
position[ 1 ] += ( rect.y + rect.height );
|
|
1202
|
-
alignWidth = true;
|
|
1203
|
-
break;
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
switch( this.align )
|
|
1207
|
-
{
|
|
1208
|
-
case "start":
|
|
1209
|
-
if( alignWidth ) { position[ 0 ] += rect.x; }
|
|
1210
|
-
else { position[ 1 ] += rect.y; }
|
|
1211
|
-
break;
|
|
1212
|
-
case "center":
|
|
1213
|
-
if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - this.root.offsetWidth * 0.5; }
|
|
1214
|
-
else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - this.root.offsetHeight * 0.5; }
|
|
1215
|
-
break;
|
|
1216
|
-
case "end":
|
|
1217
|
-
if( alignWidth ) { position[ 0 ] += rect.x - this.root.offsetWidth + rect.width; }
|
|
1218
|
-
else { position[ 1 ] += rect.y - this.root.offsetHeight + rect.height; }
|
|
1219
|
-
break;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
if( this.avoidCollisions )
|
|
1224
|
-
{
|
|
1225
|
-
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - this.root.offsetWidth - this._windowPadding );
|
|
1226
|
-
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
this.root.style.left = `${ position[ 0 ] }px`;
|
|
1230
|
-
this.root.style.top = `${ position[ 1 ] }px`;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
LX.Popover = Popover;
|
|
1234
|
-
|
|
1235
|
-
/**
|
|
1236
|
-
* @class Sheet
|
|
1237
|
-
*/
|
|
1238
|
-
|
|
1239
|
-
class Sheet {
|
|
1240
|
-
|
|
1241
|
-
constructor( size, content, options = {} ) {
|
|
1242
|
-
|
|
1243
|
-
this.side = options.side ?? "left";
|
|
1244
|
-
|
|
1245
|
-
this.root = document.createElement( "div" );
|
|
1246
|
-
this.root.dataset["side"] = this.side;
|
|
1247
|
-
this.root.tabIndex = "1";
|
|
1248
|
-
this.root.role = "dialog";
|
|
1249
|
-
this.root.className = "lexsheet fixed z-100 bg-primary";
|
|
1250
|
-
LX.root.appendChild( this.root );
|
|
1251
|
-
|
|
1252
|
-
this.root.addEventListener( "keydown", (e) => {
|
|
1253
|
-
if( e.key == "Escape" )
|
|
1254
|
-
{
|
|
1255
|
-
e.preventDefault();
|
|
1256
|
-
e.stopPropagation();
|
|
1257
|
-
this.destroy();
|
|
1258
|
-
}
|
|
1259
|
-
} );
|
|
1260
|
-
|
|
1261
|
-
if( content )
|
|
1262
|
-
{
|
|
1263
|
-
content = [].concat( content );
|
|
1264
|
-
content.forEach( e => {
|
|
1265
|
-
const domNode = e.root ?? e;
|
|
1266
|
-
this.root.appendChild( domNode );
|
|
1267
|
-
if( e.onSheet )
|
|
1268
|
-
{
|
|
1269
|
-
e.onSheet();
|
|
1270
|
-
}
|
|
1271
|
-
} );
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
LX.doAsync( () => {
|
|
1275
|
-
|
|
1276
|
-
LX.modal.toggle( false );
|
|
1277
|
-
|
|
1278
|
-
switch( this.side )
|
|
1279
|
-
{
|
|
1280
|
-
case "left":
|
|
1281
|
-
this.root.style.left = 0;
|
|
1282
|
-
this.root.style.width = size;
|
|
1283
|
-
this.root.style.height = "100%";
|
|
1284
|
-
break;
|
|
1285
|
-
case "right":
|
|
1286
|
-
this.root.style.right = 0;
|
|
1287
|
-
this.root.style.width = size;
|
|
1288
|
-
this.root.style.height = "100%";
|
|
1289
|
-
break;
|
|
1290
|
-
case "top":
|
|
1291
|
-
this.root.style.top = 0;
|
|
1292
|
-
this.root.style.width = "100%";
|
|
1293
|
-
this.root.style.height = size;
|
|
1294
|
-
break;
|
|
1295
|
-
case "bottom":
|
|
1296
|
-
this.root.style.bottom = 0;
|
|
1297
|
-
this.root.style.width = "100%";
|
|
1298
|
-
this.root.style.height = size;
|
|
1299
|
-
break;
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
this.root.focus();
|
|
1303
|
-
|
|
1304
|
-
this._onClick = e => {
|
|
1305
|
-
if( e.target && ( this.root.contains( e.target ) ) )
|
|
1306
|
-
{
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
this.destroy();
|
|
1310
|
-
};
|
|
1311
|
-
|
|
1312
|
-
document.body.addEventListener( "mousedown", this._onClick, true );
|
|
1313
|
-
document.body.addEventListener( "focusin", this._onClick, true );
|
|
1314
|
-
}, 10 );
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
destroy() {
|
|
1318
|
-
|
|
1319
|
-
document.body.removeEventListener( "mousedown", this._onClick, true );
|
|
1320
|
-
document.body.removeEventListener( "focusin", this._onClick, true );
|
|
1321
|
-
|
|
1322
|
-
this.root.remove();
|
|
1323
|
-
|
|
1324
|
-
LX.modal.toggle( true );
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
LX.Sheet = Sheet;
|
|
1328
|
-
|
|
1329
|
-
/**
|
|
1330
|
-
* @class DropdownMenu
|
|
1331
|
-
*/
|
|
1332
|
-
|
|
1333
|
-
class DropdownMenu {
|
|
1334
|
-
|
|
1335
|
-
static currentMenu = false;
|
|
1336
|
-
|
|
1337
|
-
constructor( trigger, items, options = {} ) {
|
|
1338
|
-
|
|
1339
|
-
console.assert( trigger, "DropdownMenu needs a DOM element as trigger!" );
|
|
1340
|
-
|
|
1341
|
-
if( DropdownMenu.currentMenu || !items?.length )
|
|
1342
|
-
{
|
|
1343
|
-
DropdownMenu.currentMenu.destroy();
|
|
1344
|
-
this.invalid = true;
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
this._trigger = trigger;
|
|
1349
|
-
trigger.classList.add( "triggered" );
|
|
1350
|
-
trigger.ddm = this;
|
|
1351
|
-
|
|
1352
|
-
this._items = items;
|
|
1353
|
-
|
|
1354
|
-
this._windowPadding = 4;
|
|
1355
|
-
this.side = options.side ?? "bottom";
|
|
1356
|
-
this.align = options.align ?? "center";
|
|
1357
|
-
this.avoidCollisions = options.avoidCollisions ?? true;
|
|
1358
|
-
this.onBlur = options.onBlur;
|
|
1359
|
-
this.inPlace = false;
|
|
1360
|
-
|
|
1361
|
-
this.root = document.createElement( "div" );
|
|
1362
|
-
this.root.id = "root";
|
|
1363
|
-
this.root.dataset["side"] = this.side;
|
|
1364
|
-
this.root.tabIndex = "1";
|
|
1365
|
-
this.root.className = "lexdropdownmenu";
|
|
1366
|
-
LX.root.appendChild( this.root );
|
|
1367
|
-
|
|
1368
|
-
this._create( this._items );
|
|
1369
|
-
|
|
1370
|
-
DropdownMenu.currentMenu = this;
|
|
1371
|
-
|
|
1372
|
-
LX.doAsync( () => {
|
|
1373
|
-
this._adjustPosition();
|
|
1374
|
-
|
|
1375
|
-
this.root.focus();
|
|
1376
|
-
|
|
1377
|
-
this._onClick = e => {
|
|
1378
|
-
|
|
1379
|
-
// Check if the click is inside a menu or on the trigger
|
|
1380
|
-
if( e.target && ( e.target.closest( ".lexdropdownmenu" ) != undefined || e.target == this._trigger ) )
|
|
1381
|
-
{
|
|
1382
|
-
return;
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
this.destroy( true );
|
|
1386
|
-
};
|
|
1387
|
-
|
|
1388
|
-
document.body.addEventListener( "mousedown", this._onClick, true );
|
|
1389
|
-
document.body.addEventListener( "focusin", this._onClick, true );
|
|
1390
|
-
}, 10 );
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
destroy( blurEvent ) {
|
|
1394
|
-
|
|
1395
|
-
this._trigger.classList.remove( "triggered" );
|
|
1396
|
-
|
|
1397
|
-
delete this._trigger.ddm;
|
|
1398
|
-
|
|
1399
|
-
document.body.removeEventListener( "mousedown", this._onClick, true );
|
|
1400
|
-
document.body.removeEventListener( "focusin", this._onClick, true );
|
|
1401
|
-
|
|
1402
|
-
LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
|
|
1403
|
-
|
|
1404
|
-
DropdownMenu.currentMenu = null;
|
|
1405
|
-
|
|
1406
|
-
if( blurEvent && this.onBlur )
|
|
1407
|
-
{
|
|
1408
|
-
this.onBlur();
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
_create( items, parentDom ) {
|
|
1413
|
-
|
|
1414
|
-
if( !parentDom )
|
|
1415
|
-
{
|
|
1416
|
-
parentDom = this.root;
|
|
1417
|
-
}
|
|
1418
|
-
else
|
|
1419
|
-
{
|
|
1420
|
-
const parentRect = parentDom.getBoundingClientRect();
|
|
1421
|
-
|
|
1422
|
-
let newParent = document.createElement( "div" );
|
|
1423
|
-
newParent.tabIndex = "1";
|
|
1424
|
-
newParent.className = "lexdropdownmenu";
|
|
1425
|
-
newParent.dataset["id"] = parentDom.dataset["id"];
|
|
1426
|
-
newParent.dataset["side"] = "right"; // submenus always come from the right
|
|
1427
|
-
LX.root.appendChild( newParent );
|
|
1428
|
-
|
|
1429
|
-
newParent.currentParent = parentDom;
|
|
1430
|
-
parentDom = newParent;
|
|
1431
|
-
|
|
1432
|
-
LX.doAsync( () => {
|
|
1433
|
-
const position = [ parentRect.x + parentRect.width, parentRect.y ];
|
|
1434
|
-
|
|
1435
|
-
if( this.avoidCollisions )
|
|
1436
|
-
{
|
|
1437
|
-
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - newParent.offsetWidth - this._windowPadding );
|
|
1438
|
-
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - newParent.offsetHeight - this._windowPadding );
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
newParent.style.left = `${ position[ 0 ] }px`;
|
|
1442
|
-
newParent.style.top = `${ position[ 1 ] }px`;
|
|
1443
|
-
}, 10 );
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
let applyIconPadding = items.filter( i => { return ( i?.icon != undefined ) || ( i?.checked != undefined ) } ).length > 0;
|
|
1447
|
-
|
|
1448
|
-
for( let item of items )
|
|
1449
|
-
{
|
|
1450
|
-
if( !item )
|
|
1451
|
-
{
|
|
1452
|
-
this._addSeparator( parentDom );
|
|
1453
|
-
continue;
|
|
1083
|
+
this._addSeparator( parentDom );
|
|
1084
|
+
continue;
|
|
1454
1085
|
}
|
|
1455
1086
|
|
|
1456
1087
|
const key = item.name ?? item;
|
|
@@ -2254,13 +1885,6 @@ class Calendar {
|
|
|
2254
1885
|
|
|
2255
1886
|
LX.Calendar = Calendar;
|
|
2256
1887
|
|
|
2257
|
-
function flushCss(element) {
|
|
2258
|
-
// By reading the offsetHeight property, we are forcing
|
|
2259
|
-
// the browser to flush the pending CSS changes (which it
|
|
2260
|
-
// does to ensure the value obtained is accurate).
|
|
2261
|
-
element.offsetHeight;
|
|
2262
|
-
}
|
|
2263
|
-
|
|
2264
1888
|
/**
|
|
2265
1889
|
* @class Tabs
|
|
2266
1890
|
*/
|
|
@@ -2386,7 +2010,7 @@ class Tabs {
|
|
|
2386
2010
|
this.thumb.style.transition = "none";
|
|
2387
2011
|
this.thumb.style.transform = "translate( " + ( tabEl.childIndex * tabEl.offsetWidth ) + "px )";
|
|
2388
2012
|
this.thumb.style.width = ( tabEl.offsetWidth ) + "px";
|
|
2389
|
-
flushCss( this.thumb );
|
|
2013
|
+
LX.flushCss( this.thumb );
|
|
2390
2014
|
this.thumb.style.transition = transition;
|
|
2391
2015
|
});
|
|
2392
2016
|
|
|
@@ -3500,7 +3124,7 @@ class CanvasCurve {
|
|
|
3500
3124
|
}
|
|
3501
3125
|
else
|
|
3502
3126
|
{
|
|
3503
|
-
LX.
|
|
3127
|
+
LX.drawSpline( ctx, values, element.smooth );
|
|
3504
3128
|
}
|
|
3505
3129
|
|
|
3506
3130
|
// Draw points
|
|
@@ -4442,254 +4066,6 @@ class CanvasMap2D {
|
|
|
4442
4066
|
|
|
4443
4067
|
LX.CanvasMap2D = CanvasMap2D;
|
|
4444
4068
|
|
|
4445
|
-
/*
|
|
4446
|
-
* Requests
|
|
4447
|
-
*/
|
|
4448
|
-
|
|
4449
|
-
Object.assign(LX, {
|
|
4450
|
-
|
|
4451
|
-
/**
|
|
4452
|
-
* Request file from url (it could be a binary, text, etc.). If you want a simplied version use
|
|
4453
|
-
* @method request
|
|
4454
|
-
* @param {Object} request object with all the parameters like data (for sending forms), dataType, success, error
|
|
4455
|
-
* @param {Function} on_complete
|
|
4456
|
-
**/
|
|
4457
|
-
request( request ) {
|
|
4458
|
-
|
|
4459
|
-
var dataType = request.dataType || "text";
|
|
4460
|
-
if(dataType == "json") //parse it locally
|
|
4461
|
-
dataType = "text";
|
|
4462
|
-
else if(dataType == "xml") //parse it locally
|
|
4463
|
-
dataType = "text";
|
|
4464
|
-
else if (dataType == "binary")
|
|
4465
|
-
{
|
|
4466
|
-
//request.mimeType = "text/plain; charset=x-user-defined";
|
|
4467
|
-
dataType = "arraybuffer";
|
|
4468
|
-
request.mimeType = "application/octet-stream";
|
|
4469
|
-
}
|
|
4470
|
-
|
|
4471
|
-
//regular case, use AJAX call
|
|
4472
|
-
var xhr = new XMLHttpRequest();
|
|
4473
|
-
xhr.open( request.data ? 'POST' : 'GET', request.url, true);
|
|
4474
|
-
if(dataType)
|
|
4475
|
-
xhr.responseType = dataType;
|
|
4476
|
-
if (request.mimeType)
|
|
4477
|
-
xhr.overrideMimeType( request.mimeType );
|
|
4478
|
-
if( request.nocache )
|
|
4479
|
-
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
4480
|
-
|
|
4481
|
-
xhr.onload = function(load)
|
|
4482
|
-
{
|
|
4483
|
-
var response = this.response;
|
|
4484
|
-
if( this.status != 200)
|
|
4485
|
-
{
|
|
4486
|
-
var err = "Error " + this.status;
|
|
4487
|
-
if(request.error)
|
|
4488
|
-
request.error(err);
|
|
4489
|
-
return;
|
|
4490
|
-
}
|
|
4491
|
-
|
|
4492
|
-
if(request.dataType == "json") //chrome doesnt support json format
|
|
4493
|
-
{
|
|
4494
|
-
try
|
|
4495
|
-
{
|
|
4496
|
-
response = JSON.parse(response);
|
|
4497
|
-
}
|
|
4498
|
-
catch (err)
|
|
4499
|
-
{
|
|
4500
|
-
if(request.error)
|
|
4501
|
-
request.error(err);
|
|
4502
|
-
else
|
|
4503
|
-
throw err;
|
|
4504
|
-
}
|
|
4505
|
-
}
|
|
4506
|
-
else if(request.dataType == "xml")
|
|
4507
|
-
{
|
|
4508
|
-
try
|
|
4509
|
-
{
|
|
4510
|
-
var xmlparser = new DOMParser();
|
|
4511
|
-
response = xmlparser.parseFromString(response,"text/xml");
|
|
4512
|
-
}
|
|
4513
|
-
catch (err)
|
|
4514
|
-
{
|
|
4515
|
-
if(request.error)
|
|
4516
|
-
request.error(err);
|
|
4517
|
-
else
|
|
4518
|
-
throw err;
|
|
4519
|
-
}
|
|
4520
|
-
}
|
|
4521
|
-
if(request.success)
|
|
4522
|
-
request.success.call(this, response, this);
|
|
4523
|
-
};
|
|
4524
|
-
xhr.onerror = function(err) {
|
|
4525
|
-
if(request.error)
|
|
4526
|
-
request.error(err);
|
|
4527
|
-
};
|
|
4528
|
-
|
|
4529
|
-
var data = new FormData();
|
|
4530
|
-
if( request.data )
|
|
4531
|
-
{
|
|
4532
|
-
for( var i in request.data)
|
|
4533
|
-
data.append(i,request.data[ i ]);
|
|
4534
|
-
}
|
|
4535
|
-
|
|
4536
|
-
xhr.send( data );
|
|
4537
|
-
return xhr;
|
|
4538
|
-
},
|
|
4539
|
-
|
|
4540
|
-
/**
|
|
4541
|
-
* Request file from url
|
|
4542
|
-
* @method requestText
|
|
4543
|
-
* @param {String} url
|
|
4544
|
-
* @param {Function} onComplete
|
|
4545
|
-
* @param {Function} onError
|
|
4546
|
-
**/
|
|
4547
|
-
requestText( url, onComplete, onError ) {
|
|
4548
|
-
return this.request({ url: url, dataType:"text", success: onComplete, error: onError });
|
|
4549
|
-
},
|
|
4550
|
-
|
|
4551
|
-
/**
|
|
4552
|
-
* Request file from url
|
|
4553
|
-
* @method requestJSON
|
|
4554
|
-
* @param {String} url
|
|
4555
|
-
* @param {Function} onComplete
|
|
4556
|
-
* @param {Function} onError
|
|
4557
|
-
**/
|
|
4558
|
-
requestJSON( url, onComplete, onError ) {
|
|
4559
|
-
return this.request({ url: url, dataType:"json", success: onComplete, error: onError });
|
|
4560
|
-
},
|
|
4561
|
-
|
|
4562
|
-
/**
|
|
4563
|
-
* Request binary file from url
|
|
4564
|
-
* @method requestBinary
|
|
4565
|
-
* @param {String} url
|
|
4566
|
-
* @param {Function} onComplete
|
|
4567
|
-
* @param {Function} onError
|
|
4568
|
-
**/
|
|
4569
|
-
requestBinary( url, onComplete, onError ) {
|
|
4570
|
-
return this.request({ url: url, dataType:"binary", success: onComplete, error: onError });
|
|
4571
|
-
},
|
|
4572
|
-
|
|
4573
|
-
/**
|
|
4574
|
-
* Request script and inserts it in the DOM
|
|
4575
|
-
* @method requireScript
|
|
4576
|
-
* @param {String|Array} url the url of the script or an array containing several urls
|
|
4577
|
-
* @param {Function} onComplete
|
|
4578
|
-
* @param {Function} onError
|
|
4579
|
-
* @param {Function} onProgress (if several files are required, onProgress is called after every file is added to the DOM)
|
|
4580
|
-
**/
|
|
4581
|
-
requireScript( url, onComplete, onError, onProgress, version ) {
|
|
4582
|
-
|
|
4583
|
-
if(!url)
|
|
4584
|
-
throw("invalid URL");
|
|
4585
|
-
|
|
4586
|
-
if( url.constructor === String )
|
|
4587
|
-
url = [url];
|
|
4588
|
-
|
|
4589
|
-
var total = url.length;
|
|
4590
|
-
var loaded_scripts = [];
|
|
4591
|
-
|
|
4592
|
-
for( var i in url)
|
|
4593
|
-
{
|
|
4594
|
-
var script = document.createElement('script');
|
|
4595
|
-
script.num = i;
|
|
4596
|
-
script.type = 'text/javascript';
|
|
4597
|
-
script.src = url[ i ] + ( version ? "?version=" + version : "" );
|
|
4598
|
-
script.original_src = url[ i ];
|
|
4599
|
-
script.async = false;
|
|
4600
|
-
script.onload = function( e ) {
|
|
4601
|
-
total--;
|
|
4602
|
-
loaded_scripts.push(this);
|
|
4603
|
-
if(total)
|
|
4604
|
-
{
|
|
4605
|
-
if( onProgress )
|
|
4606
|
-
{
|
|
4607
|
-
onProgress( this.original_src, this.num );
|
|
4608
|
-
}
|
|
4609
|
-
}
|
|
4610
|
-
else if(onComplete)
|
|
4611
|
-
onComplete( loaded_scripts );
|
|
4612
|
-
};
|
|
4613
|
-
if(onError)
|
|
4614
|
-
script.onerror = function(err) {
|
|
4615
|
-
onError(err, this.original_src, this.num );
|
|
4616
|
-
};
|
|
4617
|
-
document.getElementsByTagName('head')[ 0 ].appendChild(script);
|
|
4618
|
-
}
|
|
4619
|
-
},
|
|
4620
|
-
|
|
4621
|
-
loadScriptSync( url ) {
|
|
4622
|
-
return new Promise((resolve, reject) => {
|
|
4623
|
-
const script = document.createElement( "script" );
|
|
4624
|
-
script.src = url;
|
|
4625
|
-
script.async = false;
|
|
4626
|
-
script.onload = () => resolve();
|
|
4627
|
-
script.onerror = () => reject(new Error(`Failed to load ${url}`));
|
|
4628
|
-
document.head.appendChild( script );
|
|
4629
|
-
});
|
|
4630
|
-
},
|
|
4631
|
-
|
|
4632
|
-
downloadURL( url, filename ) {
|
|
4633
|
-
|
|
4634
|
-
const fr = new FileReader();
|
|
4635
|
-
|
|
4636
|
-
const _download = function(_url) {
|
|
4637
|
-
var link = document.createElement('a');
|
|
4638
|
-
link.href = _url;
|
|
4639
|
-
link.download = filename;
|
|
4640
|
-
document.body.appendChild(link);
|
|
4641
|
-
link.click();
|
|
4642
|
-
document.body.removeChild(link);
|
|
4643
|
-
};
|
|
4644
|
-
|
|
4645
|
-
if( url.includes('http') )
|
|
4646
|
-
{
|
|
4647
|
-
LX.request({ url: url, dataType: 'blob', success: (f) => {
|
|
4648
|
-
fr.readAsDataURL( f );
|
|
4649
|
-
fr.onload = e => {
|
|
4650
|
-
_download(e.currentTarget.result);
|
|
4651
|
-
};
|
|
4652
|
-
} });
|
|
4653
|
-
}else
|
|
4654
|
-
{
|
|
4655
|
-
_download(url);
|
|
4656
|
-
}
|
|
4657
|
-
|
|
4658
|
-
},
|
|
4659
|
-
|
|
4660
|
-
downloadFile: function( filename, data, dataType ) {
|
|
4661
|
-
if(!data)
|
|
4662
|
-
{
|
|
4663
|
-
console.warn("No file provided to download");
|
|
4664
|
-
return;
|
|
4665
|
-
}
|
|
4666
|
-
|
|
4667
|
-
if(!dataType)
|
|
4668
|
-
{
|
|
4669
|
-
if(data.constructor === String )
|
|
4670
|
-
dataType = 'text/plain';
|
|
4671
|
-
else
|
|
4672
|
-
dataType = 'application/octet-stream';
|
|
4673
|
-
}
|
|
4674
|
-
|
|
4675
|
-
var file = null;
|
|
4676
|
-
if(data.constructor !== File && data.constructor !== Blob)
|
|
4677
|
-
file = new Blob( [ data ], {type : dataType});
|
|
4678
|
-
else
|
|
4679
|
-
file = data;
|
|
4680
|
-
|
|
4681
|
-
var url = URL.createObjectURL( file );
|
|
4682
|
-
var element = document.createElement("a");
|
|
4683
|
-
element.setAttribute('href', url);
|
|
4684
|
-
element.setAttribute('download', filename );
|
|
4685
|
-
element.style.display = 'none';
|
|
4686
|
-
document.body.appendChild(element);
|
|
4687
|
-
element.click();
|
|
4688
|
-
document.body.removeChild(element);
|
|
4689
|
-
setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url
|
|
4690
|
-
}
|
|
4691
|
-
});
|
|
4692
|
-
|
|
4693
4069
|
Object.defineProperty(String.prototype, 'lastChar', {
|
|
4694
4070
|
get: function() { return this[ this.length - 1 ]; },
|
|
4695
4071
|
enumerable: true,
|
|
@@ -4967,19 +4343,43 @@ function doAsync( fn, ms ) {
|
|
|
4967
4343
|
LX.doAsync = doAsync;
|
|
4968
4344
|
|
|
4969
4345
|
/**
|
|
4970
|
-
* @method
|
|
4971
|
-
* @description
|
|
4972
|
-
*
|
|
4346
|
+
* @method flushCss
|
|
4347
|
+
* @description By reading the offsetHeight property, we are forcing the browser to flush
|
|
4348
|
+
* the pending CSS changes (which it does to ensure the value obtained is accurate).
|
|
4349
|
+
* @param {HTMLElement} element
|
|
4973
4350
|
*/
|
|
4974
|
-
function
|
|
4351
|
+
function flushCss( element )
|
|
4975
4352
|
{
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
let name = text.trim();
|
|
4353
|
+
element.offsetHeight;
|
|
4354
|
+
}
|
|
4979
4355
|
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4356
|
+
LX.flushCss = flushCss;
|
|
4357
|
+
|
|
4358
|
+
/**
|
|
4359
|
+
* @method deleteElement
|
|
4360
|
+
* @param {HTMLElement} element
|
|
4361
|
+
*/
|
|
4362
|
+
function deleteElement( element )
|
|
4363
|
+
{
|
|
4364
|
+
if( element !== undefined ) element.remove();
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
LX.deleteElement = deleteElement;
|
|
4368
|
+
|
|
4369
|
+
/**
|
|
4370
|
+
* @method getSupportedDOMName
|
|
4371
|
+
* @description Convert a text string to a valid DOM name
|
|
4372
|
+
* @param {String} text Original text
|
|
4373
|
+
*/
|
|
4374
|
+
function getSupportedDOMName( text )
|
|
4375
|
+
{
|
|
4376
|
+
console.assert( typeof text == "string", "getSupportedDOMName: Text is not a string!" );
|
|
4377
|
+
|
|
4378
|
+
let name = text.trim();
|
|
4379
|
+
|
|
4380
|
+
// Replace specific known symbols
|
|
4381
|
+
name = name.replace( /@/g, '_at_' ).replace( /\+/g, '_plus_' ).replace( /\./g, '_dot_' );
|
|
4382
|
+
name = name.replace( /[^a-zA-Z0-9_-]/g, '_' );
|
|
4983
4383
|
|
|
4984
4384
|
// prefix with an underscore if needed
|
|
4985
4385
|
if( /^[0-9]/.test( name ) )
|
|
@@ -5036,9 +4436,13 @@ LX.stripHTML = stripHTML;
|
|
|
5036
4436
|
* @param {Number|String} size
|
|
5037
4437
|
* @param {Number} total
|
|
5038
4438
|
*/
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
4439
|
+
function parsePixelSize( size, total )
|
|
4440
|
+
{
|
|
4441
|
+
// Assuming pixels..
|
|
4442
|
+
if( size.constructor === Number )
|
|
4443
|
+
{
|
|
4444
|
+
return size;
|
|
4445
|
+
}
|
|
5042
4446
|
|
|
5043
4447
|
if( size.constructor === String )
|
|
5044
4448
|
{
|
|
@@ -5076,7 +4480,7 @@ const parsePixelSize = ( size, total ) => {
|
|
|
5076
4480
|
}
|
|
5077
4481
|
|
|
5078
4482
|
throw( "Bad size format!" );
|
|
5079
|
-
}
|
|
4483
|
+
}
|
|
5080
4484
|
|
|
5081
4485
|
LX.parsePixelSize = parsePixelSize;
|
|
5082
4486
|
|
|
@@ -5339,7 +4743,7 @@ function measureRealWidth( value, paddingPlusMargin = 8 )
|
|
|
5339
4743
|
i.innerHTML = value;
|
|
5340
4744
|
document.body.appendChild( i );
|
|
5341
4745
|
var rect = i.getBoundingClientRect();
|
|
5342
|
-
LX.
|
|
4746
|
+
LX.deleteElement( i );
|
|
5343
4747
|
return rect.width + paddingPlusMargin;
|
|
5344
4748
|
}
|
|
5345
4749
|
|
|
@@ -5761,206 +5165,852 @@ function makeIcon( iconName, options = { } )
|
|
|
5761
5165
|
svg.classList.add( c );
|
|
5762
5166
|
} );
|
|
5763
5167
|
|
|
5764
|
-
const attrs = data[ 5 ].svgAttributes;
|
|
5765
|
-
attrs?.split( ' ' ).forEach( attr => {
|
|
5766
|
-
const t = attr.split( '=' );
|
|
5767
|
-
svg.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5768
|
-
} );
|
|
5769
|
-
}
|
|
5168
|
+
const attrs = data[ 5 ].svgAttributes;
|
|
5169
|
+
attrs?.split( ' ' ).forEach( attr => {
|
|
5170
|
+
const t = attr.split( '=' );
|
|
5171
|
+
svg.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5172
|
+
} );
|
|
5173
|
+
}
|
|
5174
|
+
|
|
5175
|
+
const path = document.createElement( "path" );
|
|
5176
|
+
path.setAttribute( "fill", "currentColor" );
|
|
5177
|
+
path.setAttribute( "d", data[ 4 ] );
|
|
5178
|
+
svg.appendChild( path );
|
|
5179
|
+
|
|
5180
|
+
if( data[ 5 ] )
|
|
5181
|
+
{
|
|
5182
|
+
const classes = data[ 5 ].pathClass;
|
|
5183
|
+
classes?.split( ' ' ).forEach( c => {
|
|
5184
|
+
path.classList.add( c );
|
|
5185
|
+
} );
|
|
5186
|
+
|
|
5187
|
+
const attrs = data[ 5 ].pathAttributes;
|
|
5188
|
+
attrs?.split( ' ' ).forEach( attr => {
|
|
5189
|
+
const t = attr.split( '=' );
|
|
5190
|
+
path.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5191
|
+
} );
|
|
5192
|
+
}
|
|
5193
|
+
|
|
5194
|
+
const faLicense = `<!-- This icon might belong to a collection from Iconify - https://iconify.design/ - or !Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc. -->`;
|
|
5195
|
+
svg.innerHTML += faLicense;
|
|
5196
|
+
return _createIconFromSVG( svg );
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
|
|
5200
|
+
// Fallback to Lucide icon
|
|
5201
|
+
console.assert( lucideData, `No existing icon named _${ iconName }_` );
|
|
5202
|
+
svg = lucide.createElement( lucideData, options );
|
|
5203
|
+
|
|
5204
|
+
return _createIconFromSVG( svg );
|
|
5205
|
+
}
|
|
5206
|
+
|
|
5207
|
+
LX.makeIcon = makeIcon;
|
|
5208
|
+
|
|
5209
|
+
/**
|
|
5210
|
+
* @method registerIcon
|
|
5211
|
+
* @description Register an SVG icon to LX.ICONS
|
|
5212
|
+
* @param {String} iconName
|
|
5213
|
+
* @param {String} svgString
|
|
5214
|
+
* @param {String} variant
|
|
5215
|
+
* @param {Array} aliases
|
|
5216
|
+
*/
|
|
5217
|
+
function registerIcon( iconName, svgString, variant = "none", aliases = [] )
|
|
5218
|
+
{
|
|
5219
|
+
const svg = new DOMParser().parseFromString( svgString, 'image/svg+xml' ).documentElement;
|
|
5220
|
+
const path = svg.querySelector( "path" );
|
|
5221
|
+
const viewBox = svg.getAttribute( "viewBox" ).split( ' ' );
|
|
5222
|
+
const pathData = path.getAttribute( 'd' );
|
|
5223
|
+
|
|
5224
|
+
let svgAttributes = [];
|
|
5225
|
+
let pathAttributes = [];
|
|
5226
|
+
|
|
5227
|
+
for( const attr of svg.attributes )
|
|
5228
|
+
{
|
|
5229
|
+
switch( attr.name )
|
|
5230
|
+
{
|
|
5231
|
+
case "transform":
|
|
5232
|
+
case "fill":
|
|
5233
|
+
case "stroke-width":
|
|
5234
|
+
case "stroke-linecap":
|
|
5235
|
+
case "stroke-linejoin":
|
|
5236
|
+
svgAttributes.push( `${ attr.name }=${ attr.value }` );
|
|
5237
|
+
break;
|
|
5238
|
+
}
|
|
5239
|
+
}
|
|
5240
|
+
|
|
5241
|
+
for( const attr of path.attributes )
|
|
5242
|
+
{
|
|
5243
|
+
switch( attr.name )
|
|
5244
|
+
{
|
|
5245
|
+
case "transform":
|
|
5246
|
+
case "fill":
|
|
5247
|
+
case "stroke-width":
|
|
5248
|
+
case "stroke-linecap":
|
|
5249
|
+
case "stroke-linejoin":
|
|
5250
|
+
pathAttributes.push( `${ attr.name }=${ attr.value }` );
|
|
5251
|
+
break;
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
|
|
5255
|
+
const iconData = [
|
|
5256
|
+
parseInt( viewBox[ 2 ] ),
|
|
5257
|
+
parseInt( viewBox[ 3 ] ),
|
|
5258
|
+
aliases,
|
|
5259
|
+
variant,
|
|
5260
|
+
pathData,
|
|
5261
|
+
{
|
|
5262
|
+
svgAttributes: svgAttributes.length ? svgAttributes.join( ' ' ) : null,
|
|
5263
|
+
pathAttributes: pathAttributes.length ? pathAttributes.join( ' ' ) : null
|
|
5264
|
+
}
|
|
5265
|
+
];
|
|
5266
|
+
|
|
5267
|
+
if( LX.ICONS[ iconName ] )
|
|
5268
|
+
{
|
|
5269
|
+
console.warn( `${ iconName } will be added/replaced in LX.ICONS` );
|
|
5270
|
+
}
|
|
5271
|
+
|
|
5272
|
+
LX.ICONS[ iconName ] = iconData;
|
|
5273
|
+
}
|
|
5274
|
+
|
|
5275
|
+
LX.registerIcon = registerIcon;
|
|
5276
|
+
|
|
5277
|
+
/**
|
|
5278
|
+
* @method registerCommandbarEntry
|
|
5279
|
+
* @description Adds an extra command bar entry
|
|
5280
|
+
* @param {String} name
|
|
5281
|
+
* @param {Function} callback
|
|
5282
|
+
*/
|
|
5283
|
+
function registerCommandbarEntry( name, callback )
|
|
5284
|
+
{
|
|
5285
|
+
LX.extraCommandbarEntries.push( { name, callback } );
|
|
5286
|
+
}
|
|
5287
|
+
|
|
5288
|
+
LX.registerCommandbarEntry = registerCommandbarEntry;
|
|
5289
|
+
|
|
5290
|
+
/*
|
|
5291
|
+
Dialog and Notification Elements
|
|
5292
|
+
*/
|
|
5293
|
+
|
|
5294
|
+
/**
|
|
5295
|
+
* @method message
|
|
5296
|
+
* @param {String} text
|
|
5297
|
+
* @param {String} title (Optional)
|
|
5298
|
+
* @param {Object} options
|
|
5299
|
+
* id: Id of the message dialog
|
|
5300
|
+
* position: Dialog position in screen [screen centered]
|
|
5301
|
+
* draggable: Dialog can be dragged [false]
|
|
5302
|
+
*/
|
|
5303
|
+
|
|
5304
|
+
function message( text, title, options = {} )
|
|
5305
|
+
{
|
|
5306
|
+
if( !text )
|
|
5307
|
+
{
|
|
5308
|
+
throw( "No message to show" );
|
|
5309
|
+
}
|
|
5310
|
+
|
|
5311
|
+
options.modal = true;
|
|
5312
|
+
|
|
5313
|
+
return new LX.Dialog( title, p => {
|
|
5314
|
+
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
5315
|
+
}, options );
|
|
5316
|
+
}
|
|
5317
|
+
|
|
5318
|
+
LX.message = message;
|
|
5319
|
+
|
|
5320
|
+
/**
|
|
5321
|
+
* @method popup
|
|
5322
|
+
* @param {String} text
|
|
5323
|
+
* @param {String} title (Optional)
|
|
5324
|
+
* @param {Object} options
|
|
5325
|
+
* id: Id of the message dialog
|
|
5326
|
+
* timeout (Number): Delay time before it closes automatically (ms). Default: [3000]
|
|
5327
|
+
* position (Array): [x,y] Dialog position in screen. Default: [screen centered]
|
|
5328
|
+
* size (Array): [width, height]
|
|
5329
|
+
*/
|
|
5330
|
+
|
|
5331
|
+
function popup( text, title, options = {} )
|
|
5332
|
+
{
|
|
5333
|
+
if( !text )
|
|
5334
|
+
{
|
|
5335
|
+
throw("No message to show");
|
|
5336
|
+
}
|
|
5337
|
+
|
|
5338
|
+
options.size = options.size ?? [ "max-content", "auto" ];
|
|
5339
|
+
options.class = "lexpopup";
|
|
5340
|
+
|
|
5341
|
+
const time = options.timeout || 3000;
|
|
5342
|
+
const dialog = new LX.Dialog( title, p => {
|
|
5343
|
+
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
5344
|
+
}, options );
|
|
5345
|
+
|
|
5346
|
+
setTimeout( () => {
|
|
5347
|
+
dialog.close();
|
|
5348
|
+
}, Math.max( time, 150 ) );
|
|
5349
|
+
|
|
5350
|
+
return dialog;
|
|
5351
|
+
}
|
|
5352
|
+
|
|
5353
|
+
LX.popup = popup;
|
|
5354
|
+
|
|
5355
|
+
/**
|
|
5356
|
+
* @method prompt
|
|
5357
|
+
* @param {String} text
|
|
5358
|
+
* @param {String} title (Optional)
|
|
5359
|
+
* @param {Object} options
|
|
5360
|
+
* id: Id of the prompt dialog
|
|
5361
|
+
* position: Dialog position in screen [screen centered]
|
|
5362
|
+
* draggable: Dialog can be dragged [false]
|
|
5363
|
+
* input: If false, no text input appears
|
|
5364
|
+
* accept: Accept text
|
|
5365
|
+
* required: Input has to be filled [true]. Default: false
|
|
5366
|
+
*/
|
|
5367
|
+
|
|
5368
|
+
function prompt( text, title, callback, options = {} )
|
|
5369
|
+
{
|
|
5370
|
+
options.modal = true;
|
|
5371
|
+
options.className = "prompt";
|
|
5372
|
+
|
|
5373
|
+
let value = "";
|
|
5374
|
+
|
|
5375
|
+
const dialog = new LX.Dialog( title, p => {
|
|
5376
|
+
|
|
5377
|
+
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
5378
|
+
|
|
5379
|
+
if( options.input ?? true )
|
|
5380
|
+
{
|
|
5381
|
+
p.addText( null, options.input || value, v => value = v, { placeholder: "..." } );
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
p.sameLine( 2 );
|
|
5385
|
+
|
|
5386
|
+
p.addButton(null, "Cancel", () => {if(options.on_cancel) options.on_cancel(); dialog.close();} );
|
|
5387
|
+
|
|
5388
|
+
p.addButton( null, options.accept || "Continue", () => {
|
|
5389
|
+
if( options.required && value === '' )
|
|
5390
|
+
{
|
|
5391
|
+
text += text.includes("You must fill the input text.") ? "": "\nYou must fill the input text.";
|
|
5392
|
+
dialog.close();
|
|
5393
|
+
prompt( text, title, callback, options );
|
|
5394
|
+
}
|
|
5395
|
+
else
|
|
5396
|
+
{
|
|
5397
|
+
if( callback ) callback.call( this, value );
|
|
5398
|
+
dialog.close();
|
|
5399
|
+
}
|
|
5400
|
+
}, { buttonClass: "primary" });
|
|
5401
|
+
|
|
5402
|
+
}, options );
|
|
5403
|
+
|
|
5404
|
+
// Focus text prompt
|
|
5405
|
+
if( options.input ?? true )
|
|
5406
|
+
{
|
|
5407
|
+
dialog.root.querySelector( 'input' ).focus();
|
|
5408
|
+
}
|
|
5409
|
+
|
|
5410
|
+
return dialog;
|
|
5411
|
+
}
|
|
5412
|
+
|
|
5413
|
+
LX.prompt = prompt;
|
|
5414
|
+
|
|
5415
|
+
/**
|
|
5416
|
+
* @method toast
|
|
5417
|
+
* @param {String} title
|
|
5418
|
+
* @param {String} description (Optional)
|
|
5419
|
+
* @param {Object} options
|
|
5420
|
+
* action: Data of the custom action { name, callback }
|
|
5421
|
+
* closable: Allow closing the toast
|
|
5422
|
+
* timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
|
|
5423
|
+
*/
|
|
5424
|
+
|
|
5425
|
+
function toast( title, description, options = {} )
|
|
5426
|
+
{
|
|
5427
|
+
if( !title )
|
|
5428
|
+
{
|
|
5429
|
+
throw( "The toast needs at least a title!" );
|
|
5430
|
+
}
|
|
5431
|
+
|
|
5432
|
+
console.assert( this.notifications );
|
|
5433
|
+
|
|
5434
|
+
const toast = document.createElement( "li" );
|
|
5435
|
+
toast.className = "lextoast";
|
|
5436
|
+
toast.style.translate = "0 calc(100% + 30px)";
|
|
5437
|
+
this.notifications.prepend( toast );
|
|
5438
|
+
|
|
5439
|
+
LX.doAsync( () => {
|
|
5440
|
+
|
|
5441
|
+
if( this.notifications.offsetWidth > this.notifications.iWidth )
|
|
5442
|
+
{
|
|
5443
|
+
this.notifications.iWidth = Math.min( this.notifications.offsetWidth, 480 );
|
|
5444
|
+
this.notifications.style.width = this.notifications.iWidth + "px";
|
|
5445
|
+
}
|
|
5446
|
+
|
|
5447
|
+
toast.dataset[ "open" ] = true;
|
|
5448
|
+
}, 10 );
|
|
5449
|
+
|
|
5450
|
+
const content = document.createElement( "div" );
|
|
5451
|
+
content.className = "lextoastcontent";
|
|
5452
|
+
toast.appendChild( content );
|
|
5453
|
+
|
|
5454
|
+
const titleContent = document.createElement( "div" );
|
|
5455
|
+
titleContent.className = "title";
|
|
5456
|
+
titleContent.innerHTML = title;
|
|
5457
|
+
content.appendChild( titleContent );
|
|
5458
|
+
|
|
5459
|
+
if( description )
|
|
5460
|
+
{
|
|
5461
|
+
const desc = document.createElement( "div" );
|
|
5462
|
+
desc.className = "desc";
|
|
5463
|
+
desc.innerHTML = description;
|
|
5464
|
+
content.appendChild( desc );
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
if( options.action )
|
|
5468
|
+
{
|
|
5469
|
+
const panel = new LX.Panel();
|
|
5470
|
+
panel.addButton(null, options.action.name ?? "Accept", options.action.callback.bind( this, toast ), { width: "auto", maxWidth: "150px", className: "right", buttonClass: "border" });
|
|
5471
|
+
toast.appendChild( panel.root.childNodes[ 0 ] );
|
|
5472
|
+
}
|
|
5473
|
+
|
|
5474
|
+
const that = this;
|
|
5475
|
+
|
|
5476
|
+
toast.close = function() {
|
|
5477
|
+
this.dataset[ "closed" ] = true;
|
|
5478
|
+
LX.doAsync( () => {
|
|
5479
|
+
this.remove();
|
|
5480
|
+
if( !that.notifications.childElementCount )
|
|
5481
|
+
{
|
|
5482
|
+
that.notifications.style.width = "unset";
|
|
5483
|
+
that.notifications.iWidth = 0;
|
|
5484
|
+
}
|
|
5485
|
+
}, 500 );
|
|
5486
|
+
};
|
|
5487
|
+
|
|
5488
|
+
if( options.closable ?? true )
|
|
5489
|
+
{
|
|
5490
|
+
const closeIcon = LX.makeIcon( "X", { iconClass: "closer" } );
|
|
5491
|
+
closeIcon.addEventListener( "click", () => {
|
|
5492
|
+
toast.close();
|
|
5493
|
+
} );
|
|
5494
|
+
toast.appendChild( closeIcon );
|
|
5495
|
+
}
|
|
5496
|
+
|
|
5497
|
+
const timeout = options.timeout ?? 3000;
|
|
5498
|
+
|
|
5499
|
+
if( timeout != -1 )
|
|
5500
|
+
{
|
|
5501
|
+
LX.doAsync( () => {
|
|
5502
|
+
toast.close();
|
|
5503
|
+
}, timeout );
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
|
|
5507
|
+
LX.toast = toast;
|
|
5508
|
+
|
|
5509
|
+
/**
|
|
5510
|
+
* @method badge
|
|
5511
|
+
* @param {String} text
|
|
5512
|
+
* @param {String} className
|
|
5513
|
+
* @param {Object} options
|
|
5514
|
+
* style: Style attributes to override
|
|
5515
|
+
* asElement: Returns the badge as HTMLElement [false]
|
|
5516
|
+
*/
|
|
5517
|
+
|
|
5518
|
+
function badge( text, className, options = {} )
|
|
5519
|
+
{
|
|
5520
|
+
const container = document.createElement( "div" );
|
|
5521
|
+
container.innerHTML = text;
|
|
5522
|
+
container.className = "lexbadge " + ( className ?? "" );
|
|
5523
|
+
Object.assign( container.style, options.style ?? {} );
|
|
5524
|
+
return ( options.asElement ?? false ) ? container : container.outerHTML;
|
|
5525
|
+
}
|
|
5526
|
+
|
|
5527
|
+
LX.badge = badge;
|
|
5528
|
+
|
|
5529
|
+
/**
|
|
5530
|
+
* @method makeElement
|
|
5531
|
+
* @param {String} htmlType
|
|
5532
|
+
* @param {String} className
|
|
5533
|
+
* @param {String} innerHTML
|
|
5534
|
+
* @param {HTMLElement} parent
|
|
5535
|
+
* @param {Object} overrideStyle
|
|
5536
|
+
*/
|
|
5537
|
+
|
|
5538
|
+
function makeElement( htmlType, className, innerHTML, parent, overrideStyle = {} )
|
|
5539
|
+
{
|
|
5540
|
+
const element = document.createElement( htmlType );
|
|
5541
|
+
element.className = className ?? "";
|
|
5542
|
+
element.innerHTML = innerHTML ?? "";
|
|
5543
|
+
Object.assign( element.style, overrideStyle );
|
|
5544
|
+
|
|
5545
|
+
if( parent )
|
|
5546
|
+
{
|
|
5547
|
+
if( parent.attach ) // Use attach method if possible
|
|
5548
|
+
{
|
|
5549
|
+
parent.attach( element );
|
|
5550
|
+
}
|
|
5551
|
+
else // its a native HTMLElement
|
|
5552
|
+
{
|
|
5553
|
+
parent.appendChild( element );
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
|
|
5557
|
+
return element;
|
|
5558
|
+
}
|
|
5559
|
+
|
|
5560
|
+
LX.makeElement = makeElement;
|
|
5561
|
+
|
|
5562
|
+
/**
|
|
5563
|
+
* @method makeContainer
|
|
5564
|
+
* @param {Array} size
|
|
5565
|
+
* @param {String} className
|
|
5566
|
+
* @param {String} innerHTML
|
|
5567
|
+
* @param {HTMLElement} parent
|
|
5568
|
+
* @param {Object} overrideStyle
|
|
5569
|
+
*/
|
|
5570
|
+
|
|
5571
|
+
function makeContainer( size, className, innerHTML, parent, overrideStyle = {} )
|
|
5572
|
+
{
|
|
5573
|
+
const container = LX.makeElement( "div", "lexcontainer " + ( className ?? "" ), innerHTML, parent, overrideStyle );
|
|
5574
|
+
container.style.width = size && size[ 0 ] ? size[ 0 ] : "100%";
|
|
5575
|
+
container.style.height = size && size[ 1 ] ? size[ 1 ] : "100%";
|
|
5576
|
+
return container;
|
|
5577
|
+
}
|
|
5578
|
+
|
|
5579
|
+
LX.makeContainer = makeContainer;
|
|
5580
|
+
|
|
5581
|
+
/**
|
|
5582
|
+
* @method asTooltip
|
|
5583
|
+
* @param {HTMLElement} trigger
|
|
5584
|
+
* @param {String} content
|
|
5585
|
+
* @param {Object} options
|
|
5586
|
+
* side: Side of the tooltip
|
|
5587
|
+
* offset: Tooltip margin offset
|
|
5588
|
+
* active: Tooltip active by default [true]
|
|
5589
|
+
*/
|
|
5590
|
+
|
|
5591
|
+
function asTooltip( trigger, content, options = {} )
|
|
5592
|
+
{
|
|
5593
|
+
console.assert( trigger, "You need a trigger to generate a tooltip!" );
|
|
5594
|
+
|
|
5595
|
+
trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
|
|
5596
|
+
|
|
5597
|
+
let tooltipDom = null;
|
|
5598
|
+
|
|
5599
|
+
trigger.addEventListener( "mouseenter", function(e) {
|
|
5600
|
+
|
|
5601
|
+
if( trigger.dataset[ "disableTooltip" ] == "true" )
|
|
5602
|
+
{
|
|
5603
|
+
return;
|
|
5604
|
+
}
|
|
5605
|
+
|
|
5606
|
+
LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
|
|
5607
|
+
|
|
5608
|
+
tooltipDom = document.createElement( "div" );
|
|
5609
|
+
tooltipDom.className = "lextooltip";
|
|
5610
|
+
tooltipDom.innerHTML = content;
|
|
5611
|
+
|
|
5612
|
+
LX.doAsync( () => {
|
|
5613
|
+
|
|
5614
|
+
const position = [ 0, 0 ];
|
|
5615
|
+
const rect = this.getBoundingClientRect();
|
|
5616
|
+
const offset = options.offset ?? 6;
|
|
5617
|
+
let alignWidth = true;
|
|
5618
|
+
|
|
5619
|
+
switch( options.side ?? "top" )
|
|
5620
|
+
{
|
|
5621
|
+
case "left":
|
|
5622
|
+
position[ 0 ] += ( rect.x - tooltipDom.offsetWidth - offset );
|
|
5623
|
+
alignWidth = false;
|
|
5624
|
+
break;
|
|
5625
|
+
case "right":
|
|
5626
|
+
position[ 0 ] += ( rect.x + rect.width + offset );
|
|
5627
|
+
alignWidth = false;
|
|
5628
|
+
break;
|
|
5629
|
+
case "top":
|
|
5630
|
+
position[ 1 ] += ( rect.y - tooltipDom.offsetHeight - offset );
|
|
5631
|
+
alignWidth = true;
|
|
5632
|
+
break;
|
|
5633
|
+
case "bottom":
|
|
5634
|
+
position[ 1 ] += ( rect.y + rect.height + offset );
|
|
5635
|
+
alignWidth = true;
|
|
5636
|
+
break;
|
|
5637
|
+
}
|
|
5638
|
+
|
|
5639
|
+
if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - tooltipDom.offsetWidth * 0.5; }
|
|
5640
|
+
else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - tooltipDom.offsetHeight * 0.5; }
|
|
5641
|
+
|
|
5642
|
+
// Avoid collisions
|
|
5643
|
+
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - tooltipDom.offsetWidth - 4 );
|
|
5644
|
+
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - tooltipDom.offsetHeight - 4 );
|
|
5645
|
+
|
|
5646
|
+
tooltipDom.style.left = `${ position[ 0 ] }px`;
|
|
5647
|
+
tooltipDom.style.top = `${ position[ 1 ] }px`;
|
|
5648
|
+
} );
|
|
5649
|
+
|
|
5650
|
+
LX.root.appendChild( tooltipDom );
|
|
5651
|
+
} );
|
|
5652
|
+
|
|
5653
|
+
trigger.addEventListener( "mouseleave", function(e) {
|
|
5654
|
+
if( tooltipDom )
|
|
5655
|
+
{
|
|
5656
|
+
tooltipDom.remove();
|
|
5657
|
+
}
|
|
5658
|
+
} );
|
|
5659
|
+
}
|
|
5660
|
+
|
|
5661
|
+
LX.asTooltip = asTooltip;
|
|
5662
|
+
|
|
5663
|
+
/*
|
|
5664
|
+
* Requests
|
|
5665
|
+
*/
|
|
5666
|
+
|
|
5667
|
+
Object.assign(LX, {
|
|
5668
|
+
|
|
5669
|
+
/**
|
|
5670
|
+
* Request file from url (it could be a binary, text, etc.). If you want a simplied version use
|
|
5671
|
+
* @method request
|
|
5672
|
+
* @param {Object} request object with all the parameters like data (for sending forms), dataType, success, error
|
|
5673
|
+
* @param {Function} on_complete
|
|
5674
|
+
**/
|
|
5675
|
+
request( request ) {
|
|
5676
|
+
|
|
5677
|
+
var dataType = request.dataType || "text";
|
|
5678
|
+
if(dataType == "json") //parse it locally
|
|
5679
|
+
dataType = "text";
|
|
5680
|
+
else if(dataType == "xml") //parse it locally
|
|
5681
|
+
dataType = "text";
|
|
5682
|
+
else if (dataType == "binary")
|
|
5683
|
+
{
|
|
5684
|
+
//request.mimeType = "text/plain; charset=x-user-defined";
|
|
5685
|
+
dataType = "arraybuffer";
|
|
5686
|
+
request.mimeType = "application/octet-stream";
|
|
5687
|
+
}
|
|
5688
|
+
|
|
5689
|
+
//regular case, use AJAX call
|
|
5690
|
+
var xhr = new XMLHttpRequest();
|
|
5691
|
+
xhr.open( request.data ? 'POST' : 'GET', request.url, true);
|
|
5692
|
+
if(dataType)
|
|
5693
|
+
xhr.responseType = dataType;
|
|
5694
|
+
if (request.mimeType)
|
|
5695
|
+
xhr.overrideMimeType( request.mimeType );
|
|
5696
|
+
if( request.nocache )
|
|
5697
|
+
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
5698
|
+
|
|
5699
|
+
xhr.onload = function(load)
|
|
5700
|
+
{
|
|
5701
|
+
var response = this.response;
|
|
5702
|
+
if( this.status != 200)
|
|
5703
|
+
{
|
|
5704
|
+
var err = "Error " + this.status;
|
|
5705
|
+
if(request.error)
|
|
5706
|
+
request.error(err);
|
|
5707
|
+
return;
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5710
|
+
if(request.dataType == "json") //chrome doesnt support json format
|
|
5711
|
+
{
|
|
5712
|
+
try
|
|
5713
|
+
{
|
|
5714
|
+
response = JSON.parse(response);
|
|
5715
|
+
}
|
|
5716
|
+
catch (err)
|
|
5717
|
+
{
|
|
5718
|
+
if(request.error)
|
|
5719
|
+
request.error(err);
|
|
5720
|
+
else
|
|
5721
|
+
throw err;
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
else if(request.dataType == "xml")
|
|
5725
|
+
{
|
|
5726
|
+
try
|
|
5727
|
+
{
|
|
5728
|
+
var xmlparser = new DOMParser();
|
|
5729
|
+
response = xmlparser.parseFromString(response,"text/xml");
|
|
5730
|
+
}
|
|
5731
|
+
catch (err)
|
|
5732
|
+
{
|
|
5733
|
+
if(request.error)
|
|
5734
|
+
request.error(err);
|
|
5735
|
+
else
|
|
5736
|
+
throw err;
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
5739
|
+
if(request.success)
|
|
5740
|
+
request.success.call(this, response, this);
|
|
5741
|
+
};
|
|
5742
|
+
xhr.onerror = function(err) {
|
|
5743
|
+
if(request.error)
|
|
5744
|
+
request.error(err);
|
|
5745
|
+
};
|
|
5746
|
+
|
|
5747
|
+
var data = new FormData();
|
|
5748
|
+
if( request.data )
|
|
5749
|
+
{
|
|
5750
|
+
for( var i in request.data)
|
|
5751
|
+
data.append(i,request.data[ i ]);
|
|
5752
|
+
}
|
|
5753
|
+
|
|
5754
|
+
xhr.send( data );
|
|
5755
|
+
return xhr;
|
|
5756
|
+
},
|
|
5757
|
+
|
|
5758
|
+
/**
|
|
5759
|
+
* Request file from url
|
|
5760
|
+
* @method requestText
|
|
5761
|
+
* @param {String} url
|
|
5762
|
+
* @param {Function} onComplete
|
|
5763
|
+
* @param {Function} onError
|
|
5764
|
+
**/
|
|
5765
|
+
requestText( url, onComplete, onError ) {
|
|
5766
|
+
return this.request({ url: url, dataType:"text", success: onComplete, error: onError });
|
|
5767
|
+
},
|
|
5768
|
+
|
|
5769
|
+
/**
|
|
5770
|
+
* Request file from url
|
|
5771
|
+
* @method requestJSON
|
|
5772
|
+
* @param {String} url
|
|
5773
|
+
* @param {Function} onComplete
|
|
5774
|
+
* @param {Function} onError
|
|
5775
|
+
**/
|
|
5776
|
+
requestJSON( url, onComplete, onError ) {
|
|
5777
|
+
return this.request({ url: url, dataType:"json", success: onComplete, error: onError });
|
|
5778
|
+
},
|
|
5779
|
+
|
|
5780
|
+
/**
|
|
5781
|
+
* Request binary file from url
|
|
5782
|
+
* @method requestBinary
|
|
5783
|
+
* @param {String} url
|
|
5784
|
+
* @param {Function} onComplete
|
|
5785
|
+
* @param {Function} onError
|
|
5786
|
+
**/
|
|
5787
|
+
requestBinary( url, onComplete, onError ) {
|
|
5788
|
+
return this.request({ url: url, dataType:"binary", success: onComplete, error: onError });
|
|
5789
|
+
},
|
|
5790
|
+
|
|
5791
|
+
/**
|
|
5792
|
+
* Request script and inserts it in the DOM
|
|
5793
|
+
* @method requireScript
|
|
5794
|
+
* @param {String|Array} url the url of the script or an array containing several urls
|
|
5795
|
+
* @param {Function} onComplete
|
|
5796
|
+
* @param {Function} onError
|
|
5797
|
+
* @param {Function} onProgress (if several files are required, onProgress is called after every file is added to the DOM)
|
|
5798
|
+
**/
|
|
5799
|
+
requireScript( url, onComplete, onError, onProgress, version ) {
|
|
5770
5800
|
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
path.setAttribute( "d", data[ 4 ] );
|
|
5774
|
-
svg.appendChild( path );
|
|
5801
|
+
if(!url)
|
|
5802
|
+
throw("invalid URL");
|
|
5775
5803
|
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
const classes = data[ 5 ].pathClass;
|
|
5779
|
-
classes?.split( ' ' ).forEach( c => {
|
|
5780
|
-
path.classList.add( c );
|
|
5781
|
-
} );
|
|
5804
|
+
if( url.constructor === String )
|
|
5805
|
+
url = [url];
|
|
5782
5806
|
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
const t = attr.split( '=' );
|
|
5786
|
-
path.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5787
|
-
} );
|
|
5788
|
-
}
|
|
5807
|
+
var total = url.length;
|
|
5808
|
+
var loaded_scripts = [];
|
|
5789
5809
|
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5810
|
+
for( var i in url)
|
|
5811
|
+
{
|
|
5812
|
+
var script = document.createElement('script');
|
|
5813
|
+
script.num = i;
|
|
5814
|
+
script.type = 'text/javascript';
|
|
5815
|
+
script.src = url[ i ] + ( version ? "?version=" + version : "" );
|
|
5816
|
+
script.original_src = url[ i ];
|
|
5817
|
+
script.async = false;
|
|
5818
|
+
script.onload = function( e ) {
|
|
5819
|
+
total--;
|
|
5820
|
+
loaded_scripts.push(this);
|
|
5821
|
+
if(total)
|
|
5822
|
+
{
|
|
5823
|
+
if( onProgress )
|
|
5824
|
+
{
|
|
5825
|
+
onProgress( this.original_src, this.num );
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
else if(onComplete)
|
|
5829
|
+
onComplete( loaded_scripts );
|
|
5830
|
+
};
|
|
5831
|
+
if(onError)
|
|
5832
|
+
script.onerror = function(err) {
|
|
5833
|
+
onError(err, this.original_src, this.num );
|
|
5834
|
+
};
|
|
5835
|
+
document.getElementsByTagName('head')[ 0 ].appendChild(script);
|
|
5793
5836
|
}
|
|
5794
|
-
}
|
|
5795
|
-
|
|
5796
|
-
// Fallback to Lucide icon
|
|
5797
|
-
console.assert( lucideData, `No existing icon named _${ iconName }_` );
|
|
5798
|
-
svg = lucide.createElement( lucideData, options );
|
|
5837
|
+
},
|
|
5799
5838
|
|
|
5800
|
-
|
|
5801
|
-
|
|
5839
|
+
loadScriptSync( url ) {
|
|
5840
|
+
return new Promise((resolve, reject) => {
|
|
5841
|
+
const script = document.createElement( "script" );
|
|
5842
|
+
script.src = url;
|
|
5843
|
+
script.async = false;
|
|
5844
|
+
script.onload = () => resolve();
|
|
5845
|
+
script.onerror = () => reject(new Error(`Failed to load ${url}`));
|
|
5846
|
+
document.head.appendChild( script );
|
|
5847
|
+
});
|
|
5848
|
+
},
|
|
5802
5849
|
|
|
5803
|
-
|
|
5850
|
+
downloadURL( url, filename ) {
|
|
5804
5851
|
|
|
5805
|
-
|
|
5806
|
-
* @method registerIcon
|
|
5807
|
-
* @description Register an SVG icon to LX.ICONS
|
|
5808
|
-
* @param {String} iconName
|
|
5809
|
-
* @param {String} svgString
|
|
5810
|
-
* @param {String} variant
|
|
5811
|
-
* @param {Array} aliases
|
|
5812
|
-
*/
|
|
5813
|
-
function registerIcon( iconName, svgString, variant = "none", aliases = [] )
|
|
5814
|
-
{
|
|
5815
|
-
const svg = new DOMParser().parseFromString( svgString, 'image/svg+xml' ).documentElement;
|
|
5816
|
-
const path = svg.querySelector( "path" );
|
|
5817
|
-
const viewBox = svg.getAttribute( "viewBox" ).split( ' ' );
|
|
5818
|
-
const pathData = path.getAttribute( 'd' );
|
|
5852
|
+
const fr = new FileReader();
|
|
5819
5853
|
|
|
5820
|
-
|
|
5821
|
-
|
|
5854
|
+
const _download = function(_url) {
|
|
5855
|
+
var link = document.createElement('a');
|
|
5856
|
+
link.href = _url;
|
|
5857
|
+
link.download = filename;
|
|
5858
|
+
document.body.appendChild(link);
|
|
5859
|
+
link.click();
|
|
5860
|
+
document.body.removeChild(link);
|
|
5861
|
+
};
|
|
5822
5862
|
|
|
5823
|
-
|
|
5824
|
-
{
|
|
5825
|
-
switch( attr.name )
|
|
5863
|
+
if( url.includes('http') )
|
|
5826
5864
|
{
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5865
|
+
LX.request({ url: url, dataType: 'blob', success: (f) => {
|
|
5866
|
+
fr.readAsDataURL( f );
|
|
5867
|
+
fr.onload = e => {
|
|
5868
|
+
_download(e.currentTarget.result);
|
|
5869
|
+
};
|
|
5870
|
+
} });
|
|
5871
|
+
}else
|
|
5872
|
+
{
|
|
5873
|
+
_download(url);
|
|
5834
5874
|
}
|
|
5835
|
-
}
|
|
5836
5875
|
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5876
|
+
},
|
|
5877
|
+
|
|
5878
|
+
downloadFile: function( filename, data, dataType ) {
|
|
5879
|
+
if(!data)
|
|
5840
5880
|
{
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
case "stroke-width":
|
|
5844
|
-
case "stroke-linecap":
|
|
5845
|
-
case "stroke-linejoin":
|
|
5846
|
-
pathAttributes.push( `${ attr.name }=${ attr.value }` );
|
|
5847
|
-
break;
|
|
5881
|
+
console.warn("No file provided to download");
|
|
5882
|
+
return;
|
|
5848
5883
|
}
|
|
5849
|
-
}
|
|
5850
5884
|
|
|
5851
|
-
|
|
5852
|
-
parseInt( viewBox[ 2 ] ),
|
|
5853
|
-
parseInt( viewBox[ 3 ] ),
|
|
5854
|
-
aliases,
|
|
5855
|
-
variant,
|
|
5856
|
-
pathData,
|
|
5885
|
+
if(!dataType)
|
|
5857
5886
|
{
|
|
5858
|
-
|
|
5859
|
-
|
|
5887
|
+
if(data.constructor === String )
|
|
5888
|
+
dataType = 'text/plain';
|
|
5889
|
+
else
|
|
5890
|
+
dataType = 'application/octet-stream';
|
|
5860
5891
|
}
|
|
5861
|
-
];
|
|
5862
5892
|
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5893
|
+
var file = null;
|
|
5894
|
+
if(data.constructor !== File && data.constructor !== Blob)
|
|
5895
|
+
file = new Blob( [ data ], {type : dataType});
|
|
5896
|
+
else
|
|
5897
|
+
file = data;
|
|
5898
|
+
|
|
5899
|
+
var url = URL.createObjectURL( file );
|
|
5900
|
+
var element = document.createElement("a");
|
|
5901
|
+
element.setAttribute('href', url);
|
|
5902
|
+
element.setAttribute('download', filename );
|
|
5903
|
+
element.style.display = 'none';
|
|
5904
|
+
document.body.appendChild(element);
|
|
5905
|
+
element.click();
|
|
5906
|
+
document.body.removeChild(element);
|
|
5907
|
+
setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url
|
|
5866
5908
|
}
|
|
5909
|
+
});
|
|
5867
5910
|
|
|
5868
|
-
|
|
5911
|
+
/**
|
|
5912
|
+
* @method compareThreshold
|
|
5913
|
+
* @param {String} url
|
|
5914
|
+
* @param {Function} onComplete
|
|
5915
|
+
* @param {Function} onError
|
|
5916
|
+
**/
|
|
5917
|
+
function compareThreshold( v, p, n, t )
|
|
5918
|
+
{
|
|
5919
|
+
return Math.abs( v - p ) >= t || Math.abs( v - n ) >= t;
|
|
5869
5920
|
}
|
|
5870
5921
|
|
|
5871
|
-
LX.
|
|
5922
|
+
LX.compareThreshold = compareThreshold;
|
|
5872
5923
|
|
|
5873
5924
|
/**
|
|
5874
|
-
* @method
|
|
5875
|
-
* @
|
|
5876
|
-
* @param {
|
|
5877
|
-
* @param {Function}
|
|
5878
|
-
|
|
5879
|
-
function
|
|
5925
|
+
* @method compareThresholdRange
|
|
5926
|
+
* @param {String} url
|
|
5927
|
+
* @param {Function} onComplete
|
|
5928
|
+
* @param {Function} onError
|
|
5929
|
+
**/
|
|
5930
|
+
function compareThresholdRange( v0, v1, t0, t1 )
|
|
5880
5931
|
{
|
|
5881
|
-
|
|
5932
|
+
return v0 >= t0 && v0 <= t1 || v1 >= t0 && v1 <= t1 || v0 <= t0 && v1 >= t1;
|
|
5882
5933
|
}
|
|
5883
5934
|
|
|
5884
|
-
LX.
|
|
5935
|
+
LX.compareThresholdRange = compareThresholdRange;
|
|
5885
5936
|
|
|
5886
5937
|
/**
|
|
5887
|
-
*
|
|
5888
|
-
* @
|
|
5889
|
-
* @
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
// does to ensure the value obtained is accurate).
|
|
5901
|
-
element.offsetHeight;
|
|
5902
|
-
},
|
|
5903
|
-
getControlPoints( x0, y0, x1, y1, x2, y2, t ) {
|
|
5904
|
-
|
|
5905
|
-
// x0,y0,x1,y1 are the coordinates of the end (knot) pts of this segment
|
|
5906
|
-
// x2,y2 is the next knot -- not connected here but needed to calculate p2
|
|
5907
|
-
// p1 is the control point calculated here, from x1 back toward x0.
|
|
5908
|
-
// p2 is the next control point, calculated here and returned to become the
|
|
5909
|
-
// next segment's p1.
|
|
5910
|
-
// t is the 'tension' which controls how far the control points spread.
|
|
5938
|
+
* @method getControlPoints
|
|
5939
|
+
* @param {String} url
|
|
5940
|
+
* @param {Function} onComplete
|
|
5941
|
+
* @param {Function} onError
|
|
5942
|
+
**/
|
|
5943
|
+
function getControlPoints( x0, y0, x1, y1, x2, y2, t )
|
|
5944
|
+
{
|
|
5945
|
+
// x0,y0,x1,y1 are the coordinates of the end (knot) pts of this segment
|
|
5946
|
+
// x2,y2 is the next knot -- not connected here but needed to calculate p2
|
|
5947
|
+
// p1 is the control point calculated here, from x1 back toward x0.
|
|
5948
|
+
// p2 is the next control point, calculated here and returned to become the
|
|
5949
|
+
// next segment's p1.
|
|
5950
|
+
// t is the 'tension' which controls how far the control points spread.
|
|
5911
5951
|
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5952
|
+
// Scaling factors: distances from this knot to the previous and following knots.
|
|
5953
|
+
var d01=Math.sqrt(Math.pow(x1-x0,2)+Math.pow(y1-y0,2));
|
|
5954
|
+
var d12=Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
|
|
5915
5955
|
|
|
5916
|
-
|
|
5917
|
-
|
|
5956
|
+
var fa=t*d01/(d01+d12);
|
|
5957
|
+
var fb=t-fa;
|
|
5918
5958
|
|
|
5919
|
-
|
|
5920
|
-
|
|
5959
|
+
var p1x=x1+fa*(x0-x2);
|
|
5960
|
+
var p1y=y1+fa*(y0-y2);
|
|
5921
5961
|
|
|
5922
|
-
|
|
5923
|
-
|
|
5962
|
+
var p2x=x1-fb*(x0-x2);
|
|
5963
|
+
var p2y=y1-fb*(y0-y2);
|
|
5924
5964
|
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
drawSpline( ctx, pts, t ) {
|
|
5965
|
+
return [p1x,p1y,p2x,p2y]
|
|
5966
|
+
}
|
|
5928
5967
|
|
|
5929
|
-
|
|
5930
|
-
var cp = []; // array of control points, as x0,y0,x1,y1,...
|
|
5931
|
-
var n = pts.length;
|
|
5968
|
+
LX.getControlPoints = getControlPoints;
|
|
5932
5969
|
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5970
|
+
/**
|
|
5971
|
+
* @method drawSpline
|
|
5972
|
+
* @param {CanvasRenderingContext2D} ctx
|
|
5973
|
+
* @param {Array} pts
|
|
5974
|
+
* @param {Number} t
|
|
5975
|
+
**/
|
|
5976
|
+
function drawSpline( ctx, pts, t )
|
|
5977
|
+
{
|
|
5978
|
+
ctx.save();
|
|
5979
|
+
var cp = []; // array of control points, as x0,y0,x1,y1,...
|
|
5980
|
+
var n = pts.length;
|
|
5938
5981
|
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
ctx.stroke();
|
|
5945
|
-
ctx.closePath();
|
|
5946
|
-
}
|
|
5982
|
+
// Draw an open curve, not connected at the ends
|
|
5983
|
+
for( var i = 0; i < (n - 4); i += 2 )
|
|
5984
|
+
{
|
|
5985
|
+
cp = cp.concat(LX.getControlPoints(pts[ i ],pts[i+1],pts[i+2],pts[i+3],pts[i+4],pts[i+5],t));
|
|
5986
|
+
}
|
|
5947
5987
|
|
|
5948
|
-
|
|
5988
|
+
for( var i = 2; i < ( pts.length - 5 ); i += 2 )
|
|
5989
|
+
{
|
|
5949
5990
|
ctx.beginPath();
|
|
5950
|
-
ctx.moveTo(
|
|
5951
|
-
ctx.
|
|
5991
|
+
ctx.moveTo(pts[ i ], pts[i+1]);
|
|
5992
|
+
ctx.bezierCurveTo(cp[2*i-2],cp[2*i-1],cp[2*i],cp[2*i+1],pts[i+2],pts[i+3]);
|
|
5952
5993
|
ctx.stroke();
|
|
5953
5994
|
ctx.closePath();
|
|
5995
|
+
}
|
|
5954
5996
|
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5997
|
+
// For open curves the first and last arcs are simple quadratics.
|
|
5998
|
+
ctx.beginPath();
|
|
5999
|
+
ctx.moveTo( pts[ 0 ], pts[ 1 ] );
|
|
6000
|
+
ctx.quadraticCurveTo( cp[ 0 ], cp[ 1 ], pts[ 2 ], pts[ 3 ]);
|
|
6001
|
+
ctx.stroke();
|
|
6002
|
+
ctx.closePath();
|
|
5960
6003
|
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
6004
|
+
ctx.beginPath();
|
|
6005
|
+
ctx.moveTo( pts[ n-2 ], pts[ n-1 ] );
|
|
6006
|
+
ctx.quadraticCurveTo( cp[ 2*n-10 ], cp[ 2*n-9 ], pts[ n-4 ], pts[ n-3 ]);
|
|
6007
|
+
ctx.stroke();
|
|
6008
|
+
ctx.closePath();
|
|
6009
|
+
|
|
6010
|
+
ctx.restore();
|
|
6011
|
+
}
|
|
6012
|
+
|
|
6013
|
+
LX.drawSpline = drawSpline;
|
|
5964
6014
|
|
|
5965
6015
|
// area.js @jxarco
|
|
5966
6016
|
|
|
@@ -6685,7 +6735,7 @@ class Area {
|
|
|
6685
6735
|
|
|
6686
6736
|
addSidebar( callback, options = {} ) {
|
|
6687
6737
|
|
|
6688
|
-
let sidebar = new LX.Sidebar( options );
|
|
6738
|
+
let sidebar = new LX.Sidebar( { callback, ...options } );
|
|
6689
6739
|
|
|
6690
6740
|
if( callback )
|
|
6691
6741
|
{
|
|
@@ -13943,6 +13993,7 @@ class Sidebar {
|
|
|
13943
13993
|
|
|
13944
13994
|
this.root = document.createElement( "div" );
|
|
13945
13995
|
this.root.className = "lexsidebar " + ( options.className ?? "" );
|
|
13996
|
+
this.callback = options.callback ?? null;
|
|
13946
13997
|
|
|
13947
13998
|
this._displaySelected = options.displaySelected ?? false;
|
|
13948
13999
|
|
|
@@ -13959,17 +14010,19 @@ class Sidebar {
|
|
|
13959
14010
|
configurable: true
|
|
13960
14011
|
});
|
|
13961
14012
|
|
|
14013
|
+
const mobile = navigator && /Android|iPhone/i.test( navigator.userAgent );
|
|
14014
|
+
|
|
13962
14015
|
this.side = options.side ?? "left";
|
|
13963
14016
|
this.collapsable = options.collapsable ?? true;
|
|
13964
14017
|
this._collapseWidth = ( options.collapseToIcons ?? true ) ? "58px" : "0px";
|
|
13965
|
-
this.collapsed =
|
|
14018
|
+
this.collapsed = options.collapsed ?? mobile;
|
|
13966
14019
|
|
|
13967
14020
|
this.filterString = "";
|
|
13968
14021
|
|
|
13969
14022
|
LX.doAsync( () => {
|
|
13970
14023
|
|
|
13971
14024
|
this.root.parentElement.ogWidth = this.root.parentElement.style.width;
|
|
13972
|
-
this.root.parentElement.style.transition = "width 0.25s ease-out";
|
|
14025
|
+
this.root.parentElement.style.transition = this.collapsed ? "" : "width 0.25s ease-out";
|
|
13973
14026
|
|
|
13974
14027
|
this.resizeObserver = new ResizeObserver( entries => {
|
|
13975
14028
|
for ( const entry of entries )
|
|
@@ -13978,6 +14031,24 @@ class Sidebar {
|
|
|
13978
14031
|
}
|
|
13979
14032
|
});
|
|
13980
14033
|
|
|
14034
|
+
if( this.collapsed )
|
|
14035
|
+
{
|
|
14036
|
+
this.root.classList.toggle( "collapsed", this.collapsed );
|
|
14037
|
+
this.root.parentElement.style.width = this._collapseWidth;
|
|
14038
|
+
|
|
14039
|
+
if( !this.resizeObserver )
|
|
14040
|
+
{
|
|
14041
|
+
throw( "Wait until ResizeObserver has been created!" );
|
|
14042
|
+
}
|
|
14043
|
+
|
|
14044
|
+
this.resizeObserver.observe( this.root.parentElement );
|
|
14045
|
+
|
|
14046
|
+
LX.doAsync( () => {
|
|
14047
|
+
this.resizeObserver.unobserve( this.root.parentElement );
|
|
14048
|
+
this.root.querySelectorAll( ".lexsidebarentrycontent" ).forEach( e => e.dataset[ "disableTooltip" ] = !this.collapsed );
|
|
14049
|
+
}, 10 );
|
|
14050
|
+
}
|
|
14051
|
+
|
|
13981
14052
|
}, 10 );
|
|
13982
14053
|
|
|
13983
14054
|
// Header
|
|
@@ -13993,11 +14064,29 @@ class Sidebar {
|
|
|
13993
14064
|
const icon = LX.makeIcon( this.side == "left" ? "PanelLeft" : "PanelRight", { title: "Toggle Sidebar", iconClass: "toggler" } );
|
|
13994
14065
|
this.header.appendChild( icon );
|
|
13995
14066
|
|
|
13996
|
-
|
|
13997
|
-
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14067
|
+
if( mobile )
|
|
14068
|
+
{
|
|
14069
|
+
// create an area and append a sidebar:
|
|
14070
|
+
const area = new LX.Area({ skipAppend: true });
|
|
14071
|
+
const sheetSidebarOptions = LX.deepCopy( options );
|
|
14072
|
+
sheetSidebarOptions.collapsed = false;
|
|
14073
|
+
sheetSidebarOptions.collapsable = false;
|
|
14074
|
+
area.addSidebar( this.callback, sheetSidebarOptions );
|
|
14075
|
+
|
|
14076
|
+
icon.addEventListener( "click", e => {
|
|
14077
|
+
e.preventDefault();
|
|
14078
|
+
e.stopPropagation();
|
|
14079
|
+
new LX.Sheet("256px", [ area ], { side: this.side } );
|
|
14080
|
+
} );
|
|
14081
|
+
}
|
|
14082
|
+
else
|
|
14083
|
+
{
|
|
14084
|
+
icon.addEventListener( "click", e => {
|
|
14085
|
+
e.preventDefault();
|
|
14086
|
+
e.stopPropagation();
|
|
14087
|
+
this.toggleCollapsed();
|
|
14088
|
+
} );
|
|
14089
|
+
}
|
|
14001
14090
|
}
|
|
14002
14091
|
}
|
|
14003
14092
|
|