lexgui 0.6.4 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/codeeditor.js +10 -10
- package/build/components/docmaker.js +417 -0
- package/build/components/nodegraph.js +12 -12
- package/build/components/timeline.js +170 -175
- package/build/lexgui-docs.css +410 -0
- package/build/lexgui.css +11 -0
- package/build/lexgui.js +1226 -1139
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +1236 -1149
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +21 -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.6",
|
|
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;
|
|
@@ -1520,6 +1151,7 @@ class DropdownMenu {
|
|
|
1520
1151
|
{
|
|
1521
1152
|
const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
|
|
1522
1153
|
const f = item[ 'callback' ];
|
|
1154
|
+
item.checked = v;
|
|
1523
1155
|
if( f )
|
|
1524
1156
|
{
|
|
1525
1157
|
f.call( this, key, v, menuItem );
|
|
@@ -2254,13 +1886,6 @@ class Calendar {
|
|
|
2254
1886
|
|
|
2255
1887
|
LX.Calendar = Calendar;
|
|
2256
1888
|
|
|
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
1889
|
/**
|
|
2265
1890
|
* @class Tabs
|
|
2266
1891
|
*/
|
|
@@ -2386,7 +2011,7 @@ class Tabs {
|
|
|
2386
2011
|
this.thumb.style.transition = "none";
|
|
2387
2012
|
this.thumb.style.transform = "translate( " + ( tabEl.childIndex * tabEl.offsetWidth ) + "px )";
|
|
2388
2013
|
this.thumb.style.width = ( tabEl.offsetWidth ) + "px";
|
|
2389
|
-
flushCss( this.thumb );
|
|
2014
|
+
LX.flushCss( this.thumb );
|
|
2390
2015
|
this.thumb.style.transition = transition;
|
|
2391
2016
|
});
|
|
2392
2017
|
|
|
@@ -3500,7 +3125,7 @@ class CanvasCurve {
|
|
|
3500
3125
|
}
|
|
3501
3126
|
else
|
|
3502
3127
|
{
|
|
3503
|
-
LX.
|
|
3128
|
+
LX.drawSpline( ctx, values, element.smooth );
|
|
3504
3129
|
}
|
|
3505
3130
|
|
|
3506
3131
|
// Draw points
|
|
@@ -4442,254 +4067,6 @@ class CanvasMap2D {
|
|
|
4442
4067
|
|
|
4443
4068
|
LX.CanvasMap2D = CanvasMap2D;
|
|
4444
4069
|
|
|
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
4070
|
Object.defineProperty(String.prototype, 'lastChar', {
|
|
4694
4071
|
get: function() { return this[ this.length - 1 ]; },
|
|
4695
4072
|
enumerable: true,
|
|
@@ -4967,19 +4344,43 @@ function doAsync( fn, ms ) {
|
|
|
4967
4344
|
LX.doAsync = doAsync;
|
|
4968
4345
|
|
|
4969
4346
|
/**
|
|
4970
|
-
* @method
|
|
4971
|
-
* @description
|
|
4972
|
-
*
|
|
4347
|
+
* @method flushCss
|
|
4348
|
+
* @description By reading the offsetHeight property, we are forcing the browser to flush
|
|
4349
|
+
* the pending CSS changes (which it does to ensure the value obtained is accurate).
|
|
4350
|
+
* @param {HTMLElement} element
|
|
4973
4351
|
*/
|
|
4974
|
-
function
|
|
4352
|
+
function flushCss( element )
|
|
4975
4353
|
{
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
let name = text.trim();
|
|
4354
|
+
element.offsetHeight;
|
|
4355
|
+
}
|
|
4979
4356
|
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4357
|
+
LX.flushCss = flushCss;
|
|
4358
|
+
|
|
4359
|
+
/**
|
|
4360
|
+
* @method deleteElement
|
|
4361
|
+
* @param {HTMLElement} element
|
|
4362
|
+
*/
|
|
4363
|
+
function deleteElement( element )
|
|
4364
|
+
{
|
|
4365
|
+
if( element !== undefined ) element.remove();
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4368
|
+
LX.deleteElement = deleteElement;
|
|
4369
|
+
|
|
4370
|
+
/**
|
|
4371
|
+
* @method getSupportedDOMName
|
|
4372
|
+
* @description Convert a text string to a valid DOM name
|
|
4373
|
+
* @param {String} text Original text
|
|
4374
|
+
*/
|
|
4375
|
+
function getSupportedDOMName( text )
|
|
4376
|
+
{
|
|
4377
|
+
console.assert( typeof text == "string", "getSupportedDOMName: Text is not a string!" );
|
|
4378
|
+
|
|
4379
|
+
let name = text.trim();
|
|
4380
|
+
|
|
4381
|
+
// Replace specific known symbols
|
|
4382
|
+
name = name.replace( /@/g, '_at_' ).replace( /\+/g, '_plus_' ).replace( /\./g, '_dot_' );
|
|
4383
|
+
name = name.replace( /[^a-zA-Z0-9_-]/g, '_' );
|
|
4983
4384
|
|
|
4984
4385
|
// prefix with an underscore if needed
|
|
4985
4386
|
if( /^[0-9]/.test( name ) )
|
|
@@ -5036,9 +4437,13 @@ LX.stripHTML = stripHTML;
|
|
|
5036
4437
|
* @param {Number|String} size
|
|
5037
4438
|
* @param {Number} total
|
|
5038
4439
|
*/
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
4440
|
+
function parsePixelSize( size, total )
|
|
4441
|
+
{
|
|
4442
|
+
// Assuming pixels..
|
|
4443
|
+
if( size.constructor === Number )
|
|
4444
|
+
{
|
|
4445
|
+
return size;
|
|
4446
|
+
}
|
|
5042
4447
|
|
|
5043
4448
|
if( size.constructor === String )
|
|
5044
4449
|
{
|
|
@@ -5076,7 +4481,7 @@ const parsePixelSize = ( size, total ) => {
|
|
|
5076
4481
|
}
|
|
5077
4482
|
|
|
5078
4483
|
throw( "Bad size format!" );
|
|
5079
|
-
}
|
|
4484
|
+
}
|
|
5080
4485
|
|
|
5081
4486
|
LX.parsePixelSize = parsePixelSize;
|
|
5082
4487
|
|
|
@@ -5339,7 +4744,7 @@ function measureRealWidth( value, paddingPlusMargin = 8 )
|
|
|
5339
4744
|
i.innerHTML = value;
|
|
5340
4745
|
document.body.appendChild( i );
|
|
5341
4746
|
var rect = i.getBoundingClientRect();
|
|
5342
|
-
LX.
|
|
4747
|
+
LX.deleteElement( i );
|
|
5343
4748
|
return rect.width + paddingPlusMargin;
|
|
5344
4749
|
}
|
|
5345
4750
|
|
|
@@ -5761,206 +5166,852 @@ function makeIcon( iconName, options = { } )
|
|
|
5761
5166
|
svg.classList.add( c );
|
|
5762
5167
|
} );
|
|
5763
5168
|
|
|
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
|
-
}
|
|
5169
|
+
const attrs = data[ 5 ].svgAttributes;
|
|
5170
|
+
attrs?.split( ' ' ).forEach( attr => {
|
|
5171
|
+
const t = attr.split( '=' );
|
|
5172
|
+
svg.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5173
|
+
} );
|
|
5174
|
+
}
|
|
5175
|
+
|
|
5176
|
+
const path = document.createElement( "path" );
|
|
5177
|
+
path.setAttribute( "fill", "currentColor" );
|
|
5178
|
+
path.setAttribute( "d", data[ 4 ] );
|
|
5179
|
+
svg.appendChild( path );
|
|
5180
|
+
|
|
5181
|
+
if( data[ 5 ] )
|
|
5182
|
+
{
|
|
5183
|
+
const classes = data[ 5 ].pathClass;
|
|
5184
|
+
classes?.split( ' ' ).forEach( c => {
|
|
5185
|
+
path.classList.add( c );
|
|
5186
|
+
} );
|
|
5187
|
+
|
|
5188
|
+
const attrs = data[ 5 ].pathAttributes;
|
|
5189
|
+
attrs?.split( ' ' ).forEach( attr => {
|
|
5190
|
+
const t = attr.split( '=' );
|
|
5191
|
+
path.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5192
|
+
} );
|
|
5193
|
+
}
|
|
5194
|
+
|
|
5195
|
+
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. -->`;
|
|
5196
|
+
svg.innerHTML += faLicense;
|
|
5197
|
+
return _createIconFromSVG( svg );
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
|
|
5201
|
+
// Fallback to Lucide icon
|
|
5202
|
+
console.assert( lucideData, `No existing icon named _${ iconName }_` );
|
|
5203
|
+
svg = lucide.createElement( lucideData, options );
|
|
5204
|
+
|
|
5205
|
+
return _createIconFromSVG( svg );
|
|
5206
|
+
}
|
|
5207
|
+
|
|
5208
|
+
LX.makeIcon = makeIcon;
|
|
5209
|
+
|
|
5210
|
+
/**
|
|
5211
|
+
* @method registerIcon
|
|
5212
|
+
* @description Register an SVG icon to LX.ICONS
|
|
5213
|
+
* @param {String} iconName
|
|
5214
|
+
* @param {String} svgString
|
|
5215
|
+
* @param {String} variant
|
|
5216
|
+
* @param {Array} aliases
|
|
5217
|
+
*/
|
|
5218
|
+
function registerIcon( iconName, svgString, variant = "none", aliases = [] )
|
|
5219
|
+
{
|
|
5220
|
+
const svg = new DOMParser().parseFromString( svgString, 'image/svg+xml' ).documentElement;
|
|
5221
|
+
const path = svg.querySelector( "path" );
|
|
5222
|
+
const viewBox = svg.getAttribute( "viewBox" ).split( ' ' );
|
|
5223
|
+
const pathData = path.getAttribute( 'd' );
|
|
5224
|
+
|
|
5225
|
+
let svgAttributes = [];
|
|
5226
|
+
let pathAttributes = [];
|
|
5227
|
+
|
|
5228
|
+
for( const attr of svg.attributes )
|
|
5229
|
+
{
|
|
5230
|
+
switch( attr.name )
|
|
5231
|
+
{
|
|
5232
|
+
case "transform":
|
|
5233
|
+
case "fill":
|
|
5234
|
+
case "stroke-width":
|
|
5235
|
+
case "stroke-linecap":
|
|
5236
|
+
case "stroke-linejoin":
|
|
5237
|
+
svgAttributes.push( `${ attr.name }=${ attr.value }` );
|
|
5238
|
+
break;
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
|
|
5242
|
+
for( const attr of path.attributes )
|
|
5243
|
+
{
|
|
5244
|
+
switch( attr.name )
|
|
5245
|
+
{
|
|
5246
|
+
case "transform":
|
|
5247
|
+
case "fill":
|
|
5248
|
+
case "stroke-width":
|
|
5249
|
+
case "stroke-linecap":
|
|
5250
|
+
case "stroke-linejoin":
|
|
5251
|
+
pathAttributes.push( `${ attr.name }=${ attr.value }` );
|
|
5252
|
+
break;
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
|
|
5256
|
+
const iconData = [
|
|
5257
|
+
parseInt( viewBox[ 2 ] ),
|
|
5258
|
+
parseInt( viewBox[ 3 ] ),
|
|
5259
|
+
aliases,
|
|
5260
|
+
variant,
|
|
5261
|
+
pathData,
|
|
5262
|
+
{
|
|
5263
|
+
svgAttributes: svgAttributes.length ? svgAttributes.join( ' ' ) : null,
|
|
5264
|
+
pathAttributes: pathAttributes.length ? pathAttributes.join( ' ' ) : null
|
|
5265
|
+
}
|
|
5266
|
+
];
|
|
5267
|
+
|
|
5268
|
+
if( LX.ICONS[ iconName ] )
|
|
5269
|
+
{
|
|
5270
|
+
console.warn( `${ iconName } will be added/replaced in LX.ICONS` );
|
|
5271
|
+
}
|
|
5272
|
+
|
|
5273
|
+
LX.ICONS[ iconName ] = iconData;
|
|
5274
|
+
}
|
|
5275
|
+
|
|
5276
|
+
LX.registerIcon = registerIcon;
|
|
5277
|
+
|
|
5278
|
+
/**
|
|
5279
|
+
* @method registerCommandbarEntry
|
|
5280
|
+
* @description Adds an extra command bar entry
|
|
5281
|
+
* @param {String} name
|
|
5282
|
+
* @param {Function} callback
|
|
5283
|
+
*/
|
|
5284
|
+
function registerCommandbarEntry( name, callback )
|
|
5285
|
+
{
|
|
5286
|
+
LX.extraCommandbarEntries.push( { name, callback } );
|
|
5287
|
+
}
|
|
5288
|
+
|
|
5289
|
+
LX.registerCommandbarEntry = registerCommandbarEntry;
|
|
5290
|
+
|
|
5291
|
+
/*
|
|
5292
|
+
Dialog and Notification Elements
|
|
5293
|
+
*/
|
|
5294
|
+
|
|
5295
|
+
/**
|
|
5296
|
+
* @method message
|
|
5297
|
+
* @param {String} text
|
|
5298
|
+
* @param {String} title (Optional)
|
|
5299
|
+
* @param {Object} options
|
|
5300
|
+
* id: Id of the message dialog
|
|
5301
|
+
* position: Dialog position in screen [screen centered]
|
|
5302
|
+
* draggable: Dialog can be dragged [false]
|
|
5303
|
+
*/
|
|
5304
|
+
|
|
5305
|
+
function message( text, title, options = {} )
|
|
5306
|
+
{
|
|
5307
|
+
if( !text )
|
|
5308
|
+
{
|
|
5309
|
+
throw( "No message to show" );
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
options.modal = true;
|
|
5313
|
+
|
|
5314
|
+
return new LX.Dialog( title, p => {
|
|
5315
|
+
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
5316
|
+
}, options );
|
|
5317
|
+
}
|
|
5318
|
+
|
|
5319
|
+
LX.message = message;
|
|
5320
|
+
|
|
5321
|
+
/**
|
|
5322
|
+
* @method popup
|
|
5323
|
+
* @param {String} text
|
|
5324
|
+
* @param {String} title (Optional)
|
|
5325
|
+
* @param {Object} options
|
|
5326
|
+
* id: Id of the message dialog
|
|
5327
|
+
* timeout (Number): Delay time before it closes automatically (ms). Default: [3000]
|
|
5328
|
+
* position (Array): [x,y] Dialog position in screen. Default: [screen centered]
|
|
5329
|
+
* size (Array): [width, height]
|
|
5330
|
+
*/
|
|
5331
|
+
|
|
5332
|
+
function popup( text, title, options = {} )
|
|
5333
|
+
{
|
|
5334
|
+
if( !text )
|
|
5335
|
+
{
|
|
5336
|
+
throw("No message to show");
|
|
5337
|
+
}
|
|
5338
|
+
|
|
5339
|
+
options.size = options.size ?? [ "max-content", "auto" ];
|
|
5340
|
+
options.class = "lexpopup";
|
|
5341
|
+
|
|
5342
|
+
const time = options.timeout || 3000;
|
|
5343
|
+
const dialog = new LX.Dialog( title, p => {
|
|
5344
|
+
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
5345
|
+
}, options );
|
|
5346
|
+
|
|
5347
|
+
setTimeout( () => {
|
|
5348
|
+
dialog.close();
|
|
5349
|
+
}, Math.max( time, 150 ) );
|
|
5350
|
+
|
|
5351
|
+
return dialog;
|
|
5352
|
+
}
|
|
5353
|
+
|
|
5354
|
+
LX.popup = popup;
|
|
5355
|
+
|
|
5356
|
+
/**
|
|
5357
|
+
* @method prompt
|
|
5358
|
+
* @param {String} text
|
|
5359
|
+
* @param {String} title (Optional)
|
|
5360
|
+
* @param {Object} options
|
|
5361
|
+
* id: Id of the prompt dialog
|
|
5362
|
+
* position: Dialog position in screen [screen centered]
|
|
5363
|
+
* draggable: Dialog can be dragged [false]
|
|
5364
|
+
* input: If false, no text input appears
|
|
5365
|
+
* accept: Accept text
|
|
5366
|
+
* required: Input has to be filled [true]. Default: false
|
|
5367
|
+
*/
|
|
5368
|
+
|
|
5369
|
+
function prompt( text, title, callback, options = {} )
|
|
5370
|
+
{
|
|
5371
|
+
options.modal = true;
|
|
5372
|
+
options.className = "prompt";
|
|
5373
|
+
|
|
5374
|
+
let value = "";
|
|
5375
|
+
|
|
5376
|
+
const dialog = new LX.Dialog( title, p => {
|
|
5377
|
+
|
|
5378
|
+
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
5379
|
+
|
|
5380
|
+
if( options.input ?? true )
|
|
5381
|
+
{
|
|
5382
|
+
p.addText( null, options.input || value, v => value = v, { placeholder: "..." } );
|
|
5383
|
+
}
|
|
5384
|
+
|
|
5385
|
+
p.sameLine( 2 );
|
|
5386
|
+
|
|
5387
|
+
p.addButton(null, "Cancel", () => {if(options.on_cancel) options.on_cancel(); dialog.close();} );
|
|
5388
|
+
|
|
5389
|
+
p.addButton( null, options.accept || "Continue", () => {
|
|
5390
|
+
if( options.required && value === '' )
|
|
5391
|
+
{
|
|
5392
|
+
text += text.includes("You must fill the input text.") ? "": "\nYou must fill the input text.";
|
|
5393
|
+
dialog.close();
|
|
5394
|
+
prompt( text, title, callback, options );
|
|
5395
|
+
}
|
|
5396
|
+
else
|
|
5397
|
+
{
|
|
5398
|
+
if( callback ) callback.call( this, value );
|
|
5399
|
+
dialog.close();
|
|
5400
|
+
}
|
|
5401
|
+
}, { buttonClass: "primary" });
|
|
5402
|
+
|
|
5403
|
+
}, options );
|
|
5404
|
+
|
|
5405
|
+
// Focus text prompt
|
|
5406
|
+
if( options.input ?? true )
|
|
5407
|
+
{
|
|
5408
|
+
dialog.root.querySelector( 'input' ).focus();
|
|
5409
|
+
}
|
|
5410
|
+
|
|
5411
|
+
return dialog;
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
LX.prompt = prompt;
|
|
5415
|
+
|
|
5416
|
+
/**
|
|
5417
|
+
* @method toast
|
|
5418
|
+
* @param {String} title
|
|
5419
|
+
* @param {String} description (Optional)
|
|
5420
|
+
* @param {Object} options
|
|
5421
|
+
* action: Data of the custom action { name, callback }
|
|
5422
|
+
* closable: Allow closing the toast
|
|
5423
|
+
* timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
|
|
5424
|
+
*/
|
|
5425
|
+
|
|
5426
|
+
function toast( title, description, options = {} )
|
|
5427
|
+
{
|
|
5428
|
+
if( !title )
|
|
5429
|
+
{
|
|
5430
|
+
throw( "The toast needs at least a title!" );
|
|
5431
|
+
}
|
|
5432
|
+
|
|
5433
|
+
console.assert( this.notifications );
|
|
5434
|
+
|
|
5435
|
+
const toast = document.createElement( "li" );
|
|
5436
|
+
toast.className = "lextoast";
|
|
5437
|
+
toast.style.translate = "0 calc(100% + 30px)";
|
|
5438
|
+
this.notifications.prepend( toast );
|
|
5439
|
+
|
|
5440
|
+
LX.doAsync( () => {
|
|
5441
|
+
|
|
5442
|
+
if( this.notifications.offsetWidth > this.notifications.iWidth )
|
|
5443
|
+
{
|
|
5444
|
+
this.notifications.iWidth = Math.min( this.notifications.offsetWidth, 480 );
|
|
5445
|
+
this.notifications.style.width = this.notifications.iWidth + "px";
|
|
5446
|
+
}
|
|
5447
|
+
|
|
5448
|
+
toast.dataset[ "open" ] = true;
|
|
5449
|
+
}, 10 );
|
|
5450
|
+
|
|
5451
|
+
const content = document.createElement( "div" );
|
|
5452
|
+
content.className = "lextoastcontent";
|
|
5453
|
+
toast.appendChild( content );
|
|
5454
|
+
|
|
5455
|
+
const titleContent = document.createElement( "div" );
|
|
5456
|
+
titleContent.className = "title";
|
|
5457
|
+
titleContent.innerHTML = title;
|
|
5458
|
+
content.appendChild( titleContent );
|
|
5459
|
+
|
|
5460
|
+
if( description )
|
|
5461
|
+
{
|
|
5462
|
+
const desc = document.createElement( "div" );
|
|
5463
|
+
desc.className = "desc";
|
|
5464
|
+
desc.innerHTML = description;
|
|
5465
|
+
content.appendChild( desc );
|
|
5466
|
+
}
|
|
5467
|
+
|
|
5468
|
+
if( options.action )
|
|
5469
|
+
{
|
|
5470
|
+
const panel = new LX.Panel();
|
|
5471
|
+
panel.addButton(null, options.action.name ?? "Accept", options.action.callback.bind( this, toast ), { width: "auto", maxWidth: "150px", className: "right", buttonClass: "border" });
|
|
5472
|
+
toast.appendChild( panel.root.childNodes[ 0 ] );
|
|
5473
|
+
}
|
|
5474
|
+
|
|
5475
|
+
const that = this;
|
|
5476
|
+
|
|
5477
|
+
toast.close = function() {
|
|
5478
|
+
this.dataset[ "closed" ] = true;
|
|
5479
|
+
LX.doAsync( () => {
|
|
5480
|
+
this.remove();
|
|
5481
|
+
if( !that.notifications.childElementCount )
|
|
5482
|
+
{
|
|
5483
|
+
that.notifications.style.width = "unset";
|
|
5484
|
+
that.notifications.iWidth = 0;
|
|
5485
|
+
}
|
|
5486
|
+
}, 500 );
|
|
5487
|
+
};
|
|
5488
|
+
|
|
5489
|
+
if( options.closable ?? true )
|
|
5490
|
+
{
|
|
5491
|
+
const closeIcon = LX.makeIcon( "X", { iconClass: "closer" } );
|
|
5492
|
+
closeIcon.addEventListener( "click", () => {
|
|
5493
|
+
toast.close();
|
|
5494
|
+
} );
|
|
5495
|
+
toast.appendChild( closeIcon );
|
|
5496
|
+
}
|
|
5497
|
+
|
|
5498
|
+
const timeout = options.timeout ?? 3000;
|
|
5499
|
+
|
|
5500
|
+
if( timeout != -1 )
|
|
5501
|
+
{
|
|
5502
|
+
LX.doAsync( () => {
|
|
5503
|
+
toast.close();
|
|
5504
|
+
}, timeout );
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
|
|
5508
|
+
LX.toast = toast;
|
|
5509
|
+
|
|
5510
|
+
/**
|
|
5511
|
+
* @method badge
|
|
5512
|
+
* @param {String} text
|
|
5513
|
+
* @param {String} className
|
|
5514
|
+
* @param {Object} options
|
|
5515
|
+
* style: Style attributes to override
|
|
5516
|
+
* asElement: Returns the badge as HTMLElement [false]
|
|
5517
|
+
*/
|
|
5518
|
+
|
|
5519
|
+
function badge( text, className, options = {} )
|
|
5520
|
+
{
|
|
5521
|
+
const container = document.createElement( "div" );
|
|
5522
|
+
container.innerHTML = text;
|
|
5523
|
+
container.className = "lexbadge " + ( className ?? "" );
|
|
5524
|
+
Object.assign( container.style, options.style ?? {} );
|
|
5525
|
+
return ( options.asElement ?? false ) ? container : container.outerHTML;
|
|
5526
|
+
}
|
|
5527
|
+
|
|
5528
|
+
LX.badge = badge;
|
|
5529
|
+
|
|
5530
|
+
/**
|
|
5531
|
+
* @method makeElement
|
|
5532
|
+
* @param {String} htmlType
|
|
5533
|
+
* @param {String} className
|
|
5534
|
+
* @param {String} innerHTML
|
|
5535
|
+
* @param {HTMLElement} parent
|
|
5536
|
+
* @param {Object} overrideStyle
|
|
5537
|
+
*/
|
|
5538
|
+
|
|
5539
|
+
function makeElement( htmlType, className, innerHTML, parent, overrideStyle = {} )
|
|
5540
|
+
{
|
|
5541
|
+
const element = document.createElement( htmlType );
|
|
5542
|
+
element.className = className ?? "";
|
|
5543
|
+
element.innerHTML = innerHTML ?? "";
|
|
5544
|
+
Object.assign( element.style, overrideStyle );
|
|
5545
|
+
|
|
5546
|
+
if( parent )
|
|
5547
|
+
{
|
|
5548
|
+
if( parent.attach ) // Use attach method if possible
|
|
5549
|
+
{
|
|
5550
|
+
parent.attach( element );
|
|
5551
|
+
}
|
|
5552
|
+
else // its a native HTMLElement
|
|
5553
|
+
{
|
|
5554
|
+
parent.appendChild( element );
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
return element;
|
|
5559
|
+
}
|
|
5560
|
+
|
|
5561
|
+
LX.makeElement = makeElement;
|
|
5562
|
+
|
|
5563
|
+
/**
|
|
5564
|
+
* @method makeContainer
|
|
5565
|
+
* @param {Array} size
|
|
5566
|
+
* @param {String} className
|
|
5567
|
+
* @param {String} innerHTML
|
|
5568
|
+
* @param {HTMLElement} parent
|
|
5569
|
+
* @param {Object} overrideStyle
|
|
5570
|
+
*/
|
|
5571
|
+
|
|
5572
|
+
function makeContainer( size, className, innerHTML, parent, overrideStyle = {} )
|
|
5573
|
+
{
|
|
5574
|
+
const container = LX.makeElement( "div", "lexcontainer " + ( className ?? "" ), innerHTML, parent, overrideStyle );
|
|
5575
|
+
container.style.width = size && size[ 0 ] ? size[ 0 ] : "100%";
|
|
5576
|
+
container.style.height = size && size[ 1 ] ? size[ 1 ] : "100%";
|
|
5577
|
+
return container;
|
|
5578
|
+
}
|
|
5579
|
+
|
|
5580
|
+
LX.makeContainer = makeContainer;
|
|
5581
|
+
|
|
5582
|
+
/**
|
|
5583
|
+
* @method asTooltip
|
|
5584
|
+
* @param {HTMLElement} trigger
|
|
5585
|
+
* @param {String} content
|
|
5586
|
+
* @param {Object} options
|
|
5587
|
+
* side: Side of the tooltip
|
|
5588
|
+
* offset: Tooltip margin offset
|
|
5589
|
+
* active: Tooltip active by default [true]
|
|
5590
|
+
*/
|
|
5591
|
+
|
|
5592
|
+
function asTooltip( trigger, content, options = {} )
|
|
5593
|
+
{
|
|
5594
|
+
console.assert( trigger, "You need a trigger to generate a tooltip!" );
|
|
5595
|
+
|
|
5596
|
+
trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
|
|
5597
|
+
|
|
5598
|
+
let tooltipDom = null;
|
|
5599
|
+
|
|
5600
|
+
trigger.addEventListener( "mouseenter", function(e) {
|
|
5601
|
+
|
|
5602
|
+
if( trigger.dataset[ "disableTooltip" ] == "true" )
|
|
5603
|
+
{
|
|
5604
|
+
return;
|
|
5605
|
+
}
|
|
5606
|
+
|
|
5607
|
+
LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
|
|
5608
|
+
|
|
5609
|
+
tooltipDom = document.createElement( "div" );
|
|
5610
|
+
tooltipDom.className = "lextooltip";
|
|
5611
|
+
tooltipDom.innerHTML = content;
|
|
5612
|
+
|
|
5613
|
+
LX.doAsync( () => {
|
|
5614
|
+
|
|
5615
|
+
const position = [ 0, 0 ];
|
|
5616
|
+
const rect = this.getBoundingClientRect();
|
|
5617
|
+
const offset = options.offset ?? 6;
|
|
5618
|
+
let alignWidth = true;
|
|
5619
|
+
|
|
5620
|
+
switch( options.side ?? "top" )
|
|
5621
|
+
{
|
|
5622
|
+
case "left":
|
|
5623
|
+
position[ 0 ] += ( rect.x - tooltipDom.offsetWidth - offset );
|
|
5624
|
+
alignWidth = false;
|
|
5625
|
+
break;
|
|
5626
|
+
case "right":
|
|
5627
|
+
position[ 0 ] += ( rect.x + rect.width + offset );
|
|
5628
|
+
alignWidth = false;
|
|
5629
|
+
break;
|
|
5630
|
+
case "top":
|
|
5631
|
+
position[ 1 ] += ( rect.y - tooltipDom.offsetHeight - offset );
|
|
5632
|
+
alignWidth = true;
|
|
5633
|
+
break;
|
|
5634
|
+
case "bottom":
|
|
5635
|
+
position[ 1 ] += ( rect.y + rect.height + offset );
|
|
5636
|
+
alignWidth = true;
|
|
5637
|
+
break;
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - tooltipDom.offsetWidth * 0.5; }
|
|
5641
|
+
else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - tooltipDom.offsetHeight * 0.5; }
|
|
5642
|
+
|
|
5643
|
+
// Avoid collisions
|
|
5644
|
+
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - tooltipDom.offsetWidth - 4 );
|
|
5645
|
+
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - tooltipDom.offsetHeight - 4 );
|
|
5646
|
+
|
|
5647
|
+
tooltipDom.style.left = `${ position[ 0 ] }px`;
|
|
5648
|
+
tooltipDom.style.top = `${ position[ 1 ] }px`;
|
|
5649
|
+
} );
|
|
5650
|
+
|
|
5651
|
+
LX.root.appendChild( tooltipDom );
|
|
5652
|
+
} );
|
|
5653
|
+
|
|
5654
|
+
trigger.addEventListener( "mouseleave", function(e) {
|
|
5655
|
+
if( tooltipDom )
|
|
5656
|
+
{
|
|
5657
|
+
tooltipDom.remove();
|
|
5658
|
+
}
|
|
5659
|
+
} );
|
|
5660
|
+
}
|
|
5661
|
+
|
|
5662
|
+
LX.asTooltip = asTooltip;
|
|
5663
|
+
|
|
5664
|
+
/*
|
|
5665
|
+
* Requests
|
|
5666
|
+
*/
|
|
5667
|
+
|
|
5668
|
+
Object.assign(LX, {
|
|
5669
|
+
|
|
5670
|
+
/**
|
|
5671
|
+
* Request file from url (it could be a binary, text, etc.). If you want a simplied version use
|
|
5672
|
+
* @method request
|
|
5673
|
+
* @param {Object} request object with all the parameters like data (for sending forms), dataType, success, error
|
|
5674
|
+
* @param {Function} on_complete
|
|
5675
|
+
**/
|
|
5676
|
+
request( request ) {
|
|
5677
|
+
|
|
5678
|
+
var dataType = request.dataType || "text";
|
|
5679
|
+
if(dataType == "json") //parse it locally
|
|
5680
|
+
dataType = "text";
|
|
5681
|
+
else if(dataType == "xml") //parse it locally
|
|
5682
|
+
dataType = "text";
|
|
5683
|
+
else if (dataType == "binary")
|
|
5684
|
+
{
|
|
5685
|
+
//request.mimeType = "text/plain; charset=x-user-defined";
|
|
5686
|
+
dataType = "arraybuffer";
|
|
5687
|
+
request.mimeType = "application/octet-stream";
|
|
5688
|
+
}
|
|
5689
|
+
|
|
5690
|
+
//regular case, use AJAX call
|
|
5691
|
+
var xhr = new XMLHttpRequest();
|
|
5692
|
+
xhr.open( request.data ? 'POST' : 'GET', request.url, true);
|
|
5693
|
+
if(dataType)
|
|
5694
|
+
xhr.responseType = dataType;
|
|
5695
|
+
if (request.mimeType)
|
|
5696
|
+
xhr.overrideMimeType( request.mimeType );
|
|
5697
|
+
if( request.nocache )
|
|
5698
|
+
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
5699
|
+
|
|
5700
|
+
xhr.onload = function(load)
|
|
5701
|
+
{
|
|
5702
|
+
var response = this.response;
|
|
5703
|
+
if( this.status != 200)
|
|
5704
|
+
{
|
|
5705
|
+
var err = "Error " + this.status;
|
|
5706
|
+
if(request.error)
|
|
5707
|
+
request.error(err);
|
|
5708
|
+
return;
|
|
5709
|
+
}
|
|
5710
|
+
|
|
5711
|
+
if(request.dataType == "json") //chrome doesnt support json format
|
|
5712
|
+
{
|
|
5713
|
+
try
|
|
5714
|
+
{
|
|
5715
|
+
response = JSON.parse(response);
|
|
5716
|
+
}
|
|
5717
|
+
catch (err)
|
|
5718
|
+
{
|
|
5719
|
+
if(request.error)
|
|
5720
|
+
request.error(err);
|
|
5721
|
+
else
|
|
5722
|
+
throw err;
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
else if(request.dataType == "xml")
|
|
5726
|
+
{
|
|
5727
|
+
try
|
|
5728
|
+
{
|
|
5729
|
+
var xmlparser = new DOMParser();
|
|
5730
|
+
response = xmlparser.parseFromString(response,"text/xml");
|
|
5731
|
+
}
|
|
5732
|
+
catch (err)
|
|
5733
|
+
{
|
|
5734
|
+
if(request.error)
|
|
5735
|
+
request.error(err);
|
|
5736
|
+
else
|
|
5737
|
+
throw err;
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
if(request.success)
|
|
5741
|
+
request.success.call(this, response, this);
|
|
5742
|
+
};
|
|
5743
|
+
xhr.onerror = function(err) {
|
|
5744
|
+
if(request.error)
|
|
5745
|
+
request.error(err);
|
|
5746
|
+
};
|
|
5747
|
+
|
|
5748
|
+
var data = new FormData();
|
|
5749
|
+
if( request.data )
|
|
5750
|
+
{
|
|
5751
|
+
for( var i in request.data)
|
|
5752
|
+
data.append(i,request.data[ i ]);
|
|
5753
|
+
}
|
|
5754
|
+
|
|
5755
|
+
xhr.send( data );
|
|
5756
|
+
return xhr;
|
|
5757
|
+
},
|
|
5758
|
+
|
|
5759
|
+
/**
|
|
5760
|
+
* Request file from url
|
|
5761
|
+
* @method requestText
|
|
5762
|
+
* @param {String} url
|
|
5763
|
+
* @param {Function} onComplete
|
|
5764
|
+
* @param {Function} onError
|
|
5765
|
+
**/
|
|
5766
|
+
requestText( url, onComplete, onError ) {
|
|
5767
|
+
return this.request({ url: url, dataType:"text", success: onComplete, error: onError });
|
|
5768
|
+
},
|
|
5769
|
+
|
|
5770
|
+
/**
|
|
5771
|
+
* Request file from url
|
|
5772
|
+
* @method requestJSON
|
|
5773
|
+
* @param {String} url
|
|
5774
|
+
* @param {Function} onComplete
|
|
5775
|
+
* @param {Function} onError
|
|
5776
|
+
**/
|
|
5777
|
+
requestJSON( url, onComplete, onError ) {
|
|
5778
|
+
return this.request({ url: url, dataType:"json", success: onComplete, error: onError });
|
|
5779
|
+
},
|
|
5780
|
+
|
|
5781
|
+
/**
|
|
5782
|
+
* Request binary file from url
|
|
5783
|
+
* @method requestBinary
|
|
5784
|
+
* @param {String} url
|
|
5785
|
+
* @param {Function} onComplete
|
|
5786
|
+
* @param {Function} onError
|
|
5787
|
+
**/
|
|
5788
|
+
requestBinary( url, onComplete, onError ) {
|
|
5789
|
+
return this.request({ url: url, dataType:"binary", success: onComplete, error: onError });
|
|
5790
|
+
},
|
|
5791
|
+
|
|
5792
|
+
/**
|
|
5793
|
+
* Request script and inserts it in the DOM
|
|
5794
|
+
* @method requireScript
|
|
5795
|
+
* @param {String|Array} url the url of the script or an array containing several urls
|
|
5796
|
+
* @param {Function} onComplete
|
|
5797
|
+
* @param {Function} onError
|
|
5798
|
+
* @param {Function} onProgress (if several files are required, onProgress is called after every file is added to the DOM)
|
|
5799
|
+
**/
|
|
5800
|
+
requireScript( url, onComplete, onError, onProgress, version ) {
|
|
5770
5801
|
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
path.setAttribute( "d", data[ 4 ] );
|
|
5774
|
-
svg.appendChild( path );
|
|
5802
|
+
if(!url)
|
|
5803
|
+
throw("invalid URL");
|
|
5775
5804
|
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
const classes = data[ 5 ].pathClass;
|
|
5779
|
-
classes?.split( ' ' ).forEach( c => {
|
|
5780
|
-
path.classList.add( c );
|
|
5781
|
-
} );
|
|
5805
|
+
if( url.constructor === String )
|
|
5806
|
+
url = [url];
|
|
5782
5807
|
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
const t = attr.split( '=' );
|
|
5786
|
-
path.setAttribute( t[ 0 ], t[ 1 ] );
|
|
5787
|
-
} );
|
|
5788
|
-
}
|
|
5808
|
+
var total = url.length;
|
|
5809
|
+
var loaded_scripts = [];
|
|
5789
5810
|
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5811
|
+
for( var i in url)
|
|
5812
|
+
{
|
|
5813
|
+
var script = document.createElement('script');
|
|
5814
|
+
script.num = i;
|
|
5815
|
+
script.type = 'text/javascript';
|
|
5816
|
+
script.src = url[ i ] + ( version ? "?version=" + version : "" );
|
|
5817
|
+
script.original_src = url[ i ];
|
|
5818
|
+
script.async = false;
|
|
5819
|
+
script.onload = function( e ) {
|
|
5820
|
+
total--;
|
|
5821
|
+
loaded_scripts.push(this);
|
|
5822
|
+
if(total)
|
|
5823
|
+
{
|
|
5824
|
+
if( onProgress )
|
|
5825
|
+
{
|
|
5826
|
+
onProgress( this.original_src, this.num );
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
else if(onComplete)
|
|
5830
|
+
onComplete( loaded_scripts );
|
|
5831
|
+
};
|
|
5832
|
+
if(onError)
|
|
5833
|
+
script.onerror = function(err) {
|
|
5834
|
+
onError(err, this.original_src, this.num );
|
|
5835
|
+
};
|
|
5836
|
+
document.getElementsByTagName('head')[ 0 ].appendChild(script);
|
|
5793
5837
|
}
|
|
5794
|
-
}
|
|
5795
|
-
|
|
5796
|
-
// Fallback to Lucide icon
|
|
5797
|
-
console.assert( lucideData, `No existing icon named _${ iconName }_` );
|
|
5798
|
-
svg = lucide.createElement( lucideData, options );
|
|
5838
|
+
},
|
|
5799
5839
|
|
|
5800
|
-
|
|
5801
|
-
|
|
5840
|
+
loadScriptSync( url ) {
|
|
5841
|
+
return new Promise((resolve, reject) => {
|
|
5842
|
+
const script = document.createElement( "script" );
|
|
5843
|
+
script.src = url;
|
|
5844
|
+
script.async = false;
|
|
5845
|
+
script.onload = () => resolve();
|
|
5846
|
+
script.onerror = () => reject(new Error(`Failed to load ${url}`));
|
|
5847
|
+
document.head.appendChild( script );
|
|
5848
|
+
});
|
|
5849
|
+
},
|
|
5802
5850
|
|
|
5803
|
-
|
|
5851
|
+
downloadURL( url, filename ) {
|
|
5804
5852
|
|
|
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' );
|
|
5853
|
+
const fr = new FileReader();
|
|
5819
5854
|
|
|
5820
|
-
|
|
5821
|
-
|
|
5855
|
+
const _download = function(_url) {
|
|
5856
|
+
var link = document.createElement('a');
|
|
5857
|
+
link.href = _url;
|
|
5858
|
+
link.download = filename;
|
|
5859
|
+
document.body.appendChild(link);
|
|
5860
|
+
link.click();
|
|
5861
|
+
document.body.removeChild(link);
|
|
5862
|
+
};
|
|
5822
5863
|
|
|
5823
|
-
|
|
5824
|
-
{
|
|
5825
|
-
switch( attr.name )
|
|
5864
|
+
if( url.includes('http') )
|
|
5826
5865
|
{
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5866
|
+
LX.request({ url: url, dataType: 'blob', success: (f) => {
|
|
5867
|
+
fr.readAsDataURL( f );
|
|
5868
|
+
fr.onload = e => {
|
|
5869
|
+
_download(e.currentTarget.result);
|
|
5870
|
+
};
|
|
5871
|
+
} });
|
|
5872
|
+
}else
|
|
5873
|
+
{
|
|
5874
|
+
_download(url);
|
|
5834
5875
|
}
|
|
5835
|
-
}
|
|
5836
5876
|
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5877
|
+
},
|
|
5878
|
+
|
|
5879
|
+
downloadFile: function( filename, data, dataType ) {
|
|
5880
|
+
if(!data)
|
|
5840
5881
|
{
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
case "stroke-width":
|
|
5844
|
-
case "stroke-linecap":
|
|
5845
|
-
case "stroke-linejoin":
|
|
5846
|
-
pathAttributes.push( `${ attr.name }=${ attr.value }` );
|
|
5847
|
-
break;
|
|
5882
|
+
console.warn("No file provided to download");
|
|
5883
|
+
return;
|
|
5848
5884
|
}
|
|
5849
|
-
}
|
|
5850
5885
|
|
|
5851
|
-
|
|
5852
|
-
parseInt( viewBox[ 2 ] ),
|
|
5853
|
-
parseInt( viewBox[ 3 ] ),
|
|
5854
|
-
aliases,
|
|
5855
|
-
variant,
|
|
5856
|
-
pathData,
|
|
5886
|
+
if(!dataType)
|
|
5857
5887
|
{
|
|
5858
|
-
|
|
5859
|
-
|
|
5888
|
+
if(data.constructor === String )
|
|
5889
|
+
dataType = 'text/plain';
|
|
5890
|
+
else
|
|
5891
|
+
dataType = 'application/octet-stream';
|
|
5860
5892
|
}
|
|
5861
|
-
];
|
|
5862
5893
|
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5894
|
+
var file = null;
|
|
5895
|
+
if(data.constructor !== File && data.constructor !== Blob)
|
|
5896
|
+
file = new Blob( [ data ], {type : dataType});
|
|
5897
|
+
else
|
|
5898
|
+
file = data;
|
|
5899
|
+
|
|
5900
|
+
var url = URL.createObjectURL( file );
|
|
5901
|
+
var element = document.createElement("a");
|
|
5902
|
+
element.setAttribute('href', url);
|
|
5903
|
+
element.setAttribute('download', filename );
|
|
5904
|
+
element.style.display = 'none';
|
|
5905
|
+
document.body.appendChild(element);
|
|
5906
|
+
element.click();
|
|
5907
|
+
document.body.removeChild(element);
|
|
5908
|
+
setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url
|
|
5866
5909
|
}
|
|
5910
|
+
});
|
|
5867
5911
|
|
|
5868
|
-
|
|
5912
|
+
/**
|
|
5913
|
+
* @method compareThreshold
|
|
5914
|
+
* @param {String} url
|
|
5915
|
+
* @param {Function} onComplete
|
|
5916
|
+
* @param {Function} onError
|
|
5917
|
+
**/
|
|
5918
|
+
function compareThreshold( v, p, n, t )
|
|
5919
|
+
{
|
|
5920
|
+
return Math.abs( v - p ) >= t || Math.abs( v - n ) >= t;
|
|
5869
5921
|
}
|
|
5870
5922
|
|
|
5871
|
-
LX.
|
|
5923
|
+
LX.compareThreshold = compareThreshold;
|
|
5872
5924
|
|
|
5873
5925
|
/**
|
|
5874
|
-
* @method
|
|
5875
|
-
* @
|
|
5876
|
-
* @param {
|
|
5877
|
-
* @param {Function}
|
|
5878
|
-
|
|
5879
|
-
function
|
|
5926
|
+
* @method compareThresholdRange
|
|
5927
|
+
* @param {String} url
|
|
5928
|
+
* @param {Function} onComplete
|
|
5929
|
+
* @param {Function} onError
|
|
5930
|
+
**/
|
|
5931
|
+
function compareThresholdRange( v0, v1, t0, t1 )
|
|
5880
5932
|
{
|
|
5881
|
-
|
|
5933
|
+
return v0 >= t0 && v0 <= t1 || v1 >= t0 && v1 <= t1 || v0 <= t0 && v1 >= t1;
|
|
5882
5934
|
}
|
|
5883
5935
|
|
|
5884
|
-
LX.
|
|
5936
|
+
LX.compareThresholdRange = compareThresholdRange;
|
|
5885
5937
|
|
|
5886
5938
|
/**
|
|
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.
|
|
5939
|
+
* @method getControlPoints
|
|
5940
|
+
* @param {String} url
|
|
5941
|
+
* @param {Function} onComplete
|
|
5942
|
+
* @param {Function} onError
|
|
5943
|
+
**/
|
|
5944
|
+
function getControlPoints( x0, y0, x1, y1, x2, y2, t )
|
|
5945
|
+
{
|
|
5946
|
+
// x0,y0,x1,y1 are the coordinates of the end (knot) pts of this segment
|
|
5947
|
+
// x2,y2 is the next knot -- not connected here but needed to calculate p2
|
|
5948
|
+
// p1 is the control point calculated here, from x1 back toward x0.
|
|
5949
|
+
// p2 is the next control point, calculated here and returned to become the
|
|
5950
|
+
// next segment's p1.
|
|
5951
|
+
// t is the 'tension' which controls how far the control points spread.
|
|
5911
5952
|
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5953
|
+
// Scaling factors: distances from this knot to the previous and following knots.
|
|
5954
|
+
var d01=Math.sqrt(Math.pow(x1-x0,2)+Math.pow(y1-y0,2));
|
|
5955
|
+
var d12=Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
|
|
5915
5956
|
|
|
5916
|
-
|
|
5917
|
-
|
|
5957
|
+
var fa=t*d01/(d01+d12);
|
|
5958
|
+
var fb=t-fa;
|
|
5918
5959
|
|
|
5919
|
-
|
|
5920
|
-
|
|
5960
|
+
var p1x=x1+fa*(x0-x2);
|
|
5961
|
+
var p1y=y1+fa*(y0-y2);
|
|
5921
5962
|
|
|
5922
|
-
|
|
5923
|
-
|
|
5963
|
+
var p2x=x1-fb*(x0-x2);
|
|
5964
|
+
var p2y=y1-fb*(y0-y2);
|
|
5924
5965
|
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
drawSpline( ctx, pts, t ) {
|
|
5966
|
+
return [p1x,p1y,p2x,p2y]
|
|
5967
|
+
}
|
|
5928
5968
|
|
|
5929
|
-
|
|
5930
|
-
var cp = []; // array of control points, as x0,y0,x1,y1,...
|
|
5931
|
-
var n = pts.length;
|
|
5969
|
+
LX.getControlPoints = getControlPoints;
|
|
5932
5970
|
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5971
|
+
/**
|
|
5972
|
+
* @method drawSpline
|
|
5973
|
+
* @param {CanvasRenderingContext2D} ctx
|
|
5974
|
+
* @param {Array} pts
|
|
5975
|
+
* @param {Number} t
|
|
5976
|
+
**/
|
|
5977
|
+
function drawSpline( ctx, pts, t )
|
|
5978
|
+
{
|
|
5979
|
+
ctx.save();
|
|
5980
|
+
var cp = []; // array of control points, as x0,y0,x1,y1,...
|
|
5981
|
+
var n = pts.length;
|
|
5938
5982
|
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
ctx.stroke();
|
|
5945
|
-
ctx.closePath();
|
|
5946
|
-
}
|
|
5983
|
+
// Draw an open curve, not connected at the ends
|
|
5984
|
+
for( var i = 0; i < (n - 4); i += 2 )
|
|
5985
|
+
{
|
|
5986
|
+
cp = cp.concat(LX.getControlPoints(pts[ i ],pts[i+1],pts[i+2],pts[i+3],pts[i+4],pts[i+5],t));
|
|
5987
|
+
}
|
|
5947
5988
|
|
|
5948
|
-
|
|
5989
|
+
for( var i = 2; i < ( pts.length - 5 ); i += 2 )
|
|
5990
|
+
{
|
|
5949
5991
|
ctx.beginPath();
|
|
5950
|
-
ctx.moveTo(
|
|
5951
|
-
ctx.
|
|
5992
|
+
ctx.moveTo(pts[ i ], pts[i+1]);
|
|
5993
|
+
ctx.bezierCurveTo(cp[2*i-2],cp[2*i-1],cp[2*i],cp[2*i+1],pts[i+2],pts[i+3]);
|
|
5952
5994
|
ctx.stroke();
|
|
5953
5995
|
ctx.closePath();
|
|
5996
|
+
}
|
|
5954
5997
|
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5998
|
+
// For open curves the first and last arcs are simple quadratics.
|
|
5999
|
+
ctx.beginPath();
|
|
6000
|
+
ctx.moveTo( pts[ 0 ], pts[ 1 ] );
|
|
6001
|
+
ctx.quadraticCurveTo( cp[ 0 ], cp[ 1 ], pts[ 2 ], pts[ 3 ]);
|
|
6002
|
+
ctx.stroke();
|
|
6003
|
+
ctx.closePath();
|
|
5960
6004
|
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
6005
|
+
ctx.beginPath();
|
|
6006
|
+
ctx.moveTo( pts[ n-2 ], pts[ n-1 ] );
|
|
6007
|
+
ctx.quadraticCurveTo( cp[ 2*n-10 ], cp[ 2*n-9 ], pts[ n-4 ], pts[ n-3 ]);
|
|
6008
|
+
ctx.stroke();
|
|
6009
|
+
ctx.closePath();
|
|
6010
|
+
|
|
6011
|
+
ctx.restore();
|
|
6012
|
+
}
|
|
6013
|
+
|
|
6014
|
+
LX.drawSpline = drawSpline;
|
|
5964
6015
|
|
|
5965
6016
|
// area.js @jxarco
|
|
5966
6017
|
|
|
@@ -6685,7 +6736,7 @@ class Area {
|
|
|
6685
6736
|
|
|
6686
6737
|
addSidebar( callback, options = {} ) {
|
|
6687
6738
|
|
|
6688
|
-
let sidebar = new LX.Sidebar( options );
|
|
6739
|
+
let sidebar = new LX.Sidebar( { callback, ...options } );
|
|
6689
6740
|
|
|
6690
6741
|
if( callback )
|
|
6691
6742
|
{
|
|
@@ -13943,6 +13994,7 @@ class Sidebar {
|
|
|
13943
13994
|
|
|
13944
13995
|
this.root = document.createElement( "div" );
|
|
13945
13996
|
this.root.className = "lexsidebar " + ( options.className ?? "" );
|
|
13997
|
+
this.callback = options.callback ?? null;
|
|
13946
13998
|
|
|
13947
13999
|
this._displaySelected = options.displaySelected ?? false;
|
|
13948
14000
|
|
|
@@ -13959,17 +14011,19 @@ class Sidebar {
|
|
|
13959
14011
|
configurable: true
|
|
13960
14012
|
});
|
|
13961
14013
|
|
|
14014
|
+
const mobile = navigator && /Android|iPhone/i.test( navigator.userAgent );
|
|
14015
|
+
|
|
13962
14016
|
this.side = options.side ?? "left";
|
|
13963
14017
|
this.collapsable = options.collapsable ?? true;
|
|
13964
14018
|
this._collapseWidth = ( options.collapseToIcons ?? true ) ? "58px" : "0px";
|
|
13965
|
-
this.collapsed =
|
|
14019
|
+
this.collapsed = options.collapsed ?? mobile;
|
|
13966
14020
|
|
|
13967
14021
|
this.filterString = "";
|
|
13968
14022
|
|
|
13969
14023
|
LX.doAsync( () => {
|
|
13970
14024
|
|
|
13971
14025
|
this.root.parentElement.ogWidth = this.root.parentElement.style.width;
|
|
13972
|
-
this.root.parentElement.style.transition = "width 0.25s ease-out";
|
|
14026
|
+
this.root.parentElement.style.transition = this.collapsed ? "" : "width 0.25s ease-out";
|
|
13973
14027
|
|
|
13974
14028
|
this.resizeObserver = new ResizeObserver( entries => {
|
|
13975
14029
|
for ( const entry of entries )
|
|
@@ -13978,6 +14032,24 @@ class Sidebar {
|
|
|
13978
14032
|
}
|
|
13979
14033
|
});
|
|
13980
14034
|
|
|
14035
|
+
if( this.collapsed )
|
|
14036
|
+
{
|
|
14037
|
+
this.root.classList.toggle( "collapsed", this.collapsed );
|
|
14038
|
+
this.root.parentElement.style.width = this._collapseWidth;
|
|
14039
|
+
|
|
14040
|
+
if( !this.resizeObserver )
|
|
14041
|
+
{
|
|
14042
|
+
throw( "Wait until ResizeObserver has been created!" );
|
|
14043
|
+
}
|
|
14044
|
+
|
|
14045
|
+
this.resizeObserver.observe( this.root.parentElement );
|
|
14046
|
+
|
|
14047
|
+
LX.doAsync( () => {
|
|
14048
|
+
this.resizeObserver.unobserve( this.root.parentElement );
|
|
14049
|
+
this.root.querySelectorAll( ".lexsidebarentrycontent" ).forEach( e => e.dataset[ "disableTooltip" ] = !this.collapsed );
|
|
14050
|
+
}, 10 );
|
|
14051
|
+
}
|
|
14052
|
+
|
|
13981
14053
|
}, 10 );
|
|
13982
14054
|
|
|
13983
14055
|
// Header
|
|
@@ -13993,11 +14065,29 @@ class Sidebar {
|
|
|
13993
14065
|
const icon = LX.makeIcon( this.side == "left" ? "PanelLeft" : "PanelRight", { title: "Toggle Sidebar", iconClass: "toggler" } );
|
|
13994
14066
|
this.header.appendChild( icon );
|
|
13995
14067
|
|
|
13996
|
-
|
|
13997
|
-
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14068
|
+
if( mobile )
|
|
14069
|
+
{
|
|
14070
|
+
// create an area and append a sidebar:
|
|
14071
|
+
const area = new LX.Area({ skipAppend: true });
|
|
14072
|
+
const sheetSidebarOptions = LX.deepCopy( options );
|
|
14073
|
+
sheetSidebarOptions.collapsed = false;
|
|
14074
|
+
sheetSidebarOptions.collapsable = false;
|
|
14075
|
+
area.addSidebar( this.callback, sheetSidebarOptions );
|
|
14076
|
+
|
|
14077
|
+
icon.addEventListener( "click", e => {
|
|
14078
|
+
e.preventDefault();
|
|
14079
|
+
e.stopPropagation();
|
|
14080
|
+
new LX.Sheet("256px", [ area ], { side: this.side } );
|
|
14081
|
+
} );
|
|
14082
|
+
}
|
|
14083
|
+
else
|
|
14084
|
+
{
|
|
14085
|
+
icon.addEventListener( "click", e => {
|
|
14086
|
+
e.preventDefault();
|
|
14087
|
+
e.stopPropagation();
|
|
14088
|
+
this.toggleCollapsed();
|
|
14089
|
+
} );
|
|
14090
|
+
}
|
|
14001
14091
|
}
|
|
14002
14092
|
}
|
|
14003
14093
|
|
|
@@ -14437,20 +14527,17 @@ class Sidebar {
|
|
|
14437
14527
|
return;
|
|
14438
14528
|
}
|
|
14439
14529
|
|
|
14530
|
+
const f = options.callback;
|
|
14531
|
+
if( f ) f.call( this, key, item.value, e );
|
|
14532
|
+
|
|
14440
14533
|
if( isCollapsable )
|
|
14441
14534
|
{
|
|
14442
14535
|
itemDom.querySelector( ".collapser" ).click();
|
|
14443
14536
|
}
|
|
14444
|
-
else
|
|
14537
|
+
else if( item.checkbox )
|
|
14445
14538
|
{
|
|
14446
|
-
|
|
14447
|
-
|
|
14448
|
-
|
|
14449
|
-
if( item.checkbox )
|
|
14450
|
-
{
|
|
14451
|
-
item.value = !item.value;
|
|
14452
|
-
item.checkbox.set( item.value, true );
|
|
14453
|
-
}
|
|
14539
|
+
item.value = !item.value;
|
|
14540
|
+
item.checkbox.set( item.value, true );
|
|
14454
14541
|
}
|
|
14455
14542
|
|
|
14456
14543
|
// Manage selected
|