/** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * A simple Color class for rgb set color values. * */ class Color { #red; #green; #blue; #hex; /** * * @param {number} red Red-Value for RGB Color definition * @param {number} green Green-Value for RGB * @param {number} blue Blue-Value */ constructor(red, green, blue) { this.#red = red; this.#green = green; this.#blue = blue; } /** * * @returns {string} an rgb object string */ cssRGBString() { return `rgb(${this.#red}, ${this.#green}, ${this.#blue})`; } /** * * @param {string} hexcode * @returns */ hex(hexcode) { this.#hex = hexcode; return this; } } const Colors = Object.freeze({ indian_red: new Color(176, 23, 31).hex('#B0171F'), crimson: new Color(220, 20, 60).hex('#DC143C'), lightpink: new Color(255, 182, 193).hex('#FFB6C1'), lightpink_1: new Color(255, 174, 185).hex('#FFAEB9'), lightpink_2: new Color(238, 162, 173).hex('#EEA2AD'), lightpink_3: new Color(205, 140, 149).hex('#CD8C95'), lightpink_4: new Color(139, 95, 101).hex('#8B5F65'), pink: new Color(255, 192, 203).hex('#FFC0CB'), pink_1: new Color(255, 181, 197).hex('#FFB5C5'), pink_2: new Color(238, 169, 184).hex('#EEA9B8'), pink_3: new Color(205, 145, 158).hex('#CD919E'), pink_4: new Color(139, 99, 108).hex('#8B636C'), palevioletred: new Color(219, 112, 147).hex('#DB7093'), palevioletred_1: new Color(255, 130, 171).hex('#FF82AB'), palevioletred_2: new Color(238, 121, 159).hex('#EE799F'), palevioletred_3: new Color(205, 104, 137).hex('#CD6889'), palevioletred_4: new Color(139, 71, 93).hex('#8B475D'), lavenderblush_1: new Color(255, 240, 245).hex('#FFF0F5'), //(lavenderblush) lavenderblush_2: new Color(238, 224, 229).hex('#EEE0E5'), lavenderblush_3: new Color(205, 193, 197).hex('#CDC1C5'), lavenderblush_4: new Color(139, 131, 134).hex('#8B8386'), violetred_1: new Color(255, 62, 150).hex('#FF3E96'), violetred_2: new Color(238, 58, 140).hex('#EE3A8C'), violetred_3: new Color(205, 50, 120).hex('#CD3278'), violetred_4: new Color(139, 34, 82).hex('#8B2252'), hotpink: new Color(255, 105, 180).hex('#FF69B4'), hotpink_1: new Color(255, 110, 180).hex('#FF6EB4'), hotpink_2: new Color(238, 106, 167).hex('#EE6AA7'), hotpink_3: new Color(205, 96, 144).hex('#CD6090'), hotpink_4: new Color(139, 58, 98).hex('#8B3A62'), raspberry: new Color(135, 38, 87).hex('#872657'), deeppink_1: new Color(255, 20, 147).hex('#FF1493'), // (deeppink) deeppink_2: new Color(238, 18, 137).hex('#EE1289'), deeppink_3: new Color(205, 16, 118).hex('#CD1076'), deeppink_4: new Color(139, 10, 80).hex('#8B0A50'), maroon_1: new Color(255, 52, 179).hex('#FF34B3'), maroon_2: new Color(238, 48, 167).hex('#EE30A7'), maroon_3: new Color(205, 41, 144).hex('#CD2990'), maroon_4: new Color(139, 28, 98).hex('#8B1C62'), mediumvioletred: new Color(199, 21, 133).hex('#C71585'), violetred: new Color(208, 32, 144).hex('#D02090'), orchid: new Color(218, 112, 214).hex('#DA70D6'), orchid_1: new Color(255, 131, 250).hex('#FF83FA'), orchid_2: new Color(238, 122, 233).hex('#EE7AE9'), orchid_3: new Color(205, 105, 201).hex('#CD69C9'), orchid_4: new Color(139, 71, 137).hex('#8B4789'), thistle: new Color(216, 191, 216).hex('#D8BFD8'), thistle_1: new Color(255, 225, 255).hex('#FFE1FF'), thistle_2: new Color(238, 210, 238).hex('#EED2EE'), thistle_3: new Color(205, 181, 205).hex('#CDB5CD'), thistle_4: new Color(139, 123, 139).hex('#8B7B8B'), plum_1: new Color(255, 187, 255).hex('#FFBBFF'), plum_2: new Color(238, 174, 238).hex('#EEAEEE'), plum_3: new Color(205, 150, 205).hex('#CD96CD'), plum_4: new Color(139, 102, 139).hex('#8B668B'), plum: new Color(221, 160, 221).hex('#DDA0DD'), violet: new Color(238, 130, 238).hex('#EE82EE'), magenta: new Color(255, 0, 255).hex('#FF00FF'), // (fuchsia*) magenta_2: new Color(238, 0, 238).hex('#EE00EE'), magenta_3: new Color(205, 0, 205).hex('#CD00CD'), magenta_4: new Color(139, 0, 139).hex('#8B008B'), //(darkmagenta) purple: new Color(128, 0, 128).hex('#800080'), mediumorchid: new Color(186, 85, 211).hex('#BA55D3'), mediumorchid_1: new Color(224, 102, 255).hex('#E066FF'), mediumorchid_2: new Color(209, 95, 238).hex('#D15FEE'), mediumorchid_3: new Color(180, 82, 205).hex('#B452CD'), mediumorchid_4: new Color(122, 55, 139).hex('#7A378B'), darkviolet: new Color(148, 0, 211).hex('#9400D3'), darkorchid: new Color(153, 50, 204).hex('#9932CC'), darkorchid_1: new Color(191, 62, 255).hex('#BF3EFF'), darkorchid_2: new Color(178, 58, 238).hex('#B23AEE'), darkorchid_3: new Color(154, 50, 205).hex('#9A32CD'), darkorchid_4: new Color(104, 34, 139).hex('#68228B'), indigo: new Color(75, 0, 130).hex('#4B0082'), blueviolet: new Color(138, 43, 226).hex('#8A2BE2'), purple_1: new Color(155, 48, 255).hex('#9B30FF'), purple_2: new Color(145, 44, 238).hex('#912CEE'), purple_3: new Color(125, 38, 205).hex('#7D26CD'), purple_4: new Color(85, 26, 139).hex('#551A8B'), mediumpurple: new Color(147, 112, 219).hex('#9370DB'), mediumpurple_1: new Color(171, 130, 255).hex('#AB82FF'), mediumpurple_2: new Color(159, 121, 238).hex('#9F79EE'), mediumpurple_3: new Color(137, 104, 205).hex('#8968CD'), mediumpurple_4: new Color(93, 71, 139).hex('#5D478B'), darkslateblue: new Color(72, 61, 139).hex('#483D8B'), lightslateblue: new Color(132, 112, 255).hex('#8470FF'), mediumslateblue: new Color(123, 104, 238).hex('#7B68EE'), slateblue: new Color(106, 90, 205).hex('#6A5ACD'), slateblue_1: new Color(131, 111, 255).hex('#836FFF'), slateblue_2: new Color(122, 103, 238).hex('#7A67EE'), slateblue_3: new Color(105, 89, 205).hex('#6959CD'), slateblue_4: new Color(71, 60, 139).hex('#473C8B'), ghostwhite: new Color(248, 248, 255).hex('#F8F8FF'), lavender: new Color(230, 230, 250).hex('#E6E6FA'), blue: new Color(0, 0, 255).hex('#0000FF'), blue_2: new Color(0, 0, 238).hex('#0000EE'), blue_3: new Color(0, 0, 205).hex('#0000CD'), //(mediumblue) blue_4: new Color(0, 0, 139).hex('#00008B'), //(darkblue) navy: new Color(0, 0, 128).hex('#000080'), midnightblue: new Color(25, 25, 112).hex('#191970'), cobalt: new Color(61, 89, 171).hex('#3D59AB'), royalblue: new Color(65, 105, 225).hex('#4169E1'), royalblue_1: new Color(72, 118, 255).hex('#4876FF'), royalblue_2: new Color(67, 110, 238).hex('#436EEE'), royalblue_3: new Color(58, 95, 205).hex('#3A5FCD'), royalblue_4: new Color(39, 64, 139).hex('#27408B'), cornflowerblue: new Color(100, 149, 237).hex('#6495ED'), lightsteelblue: new Color(176, 196, 222).hex('#B0C4DE'), lightsteelblue_1: new Color(202, 225, 255).hex('#CAE1FF'), lightsteelblue_2: new Color(188, 210, 238).hex('#BCD2EE'), lightsteelblue_3: new Color(162, 181, 205).hex('#A2B5CD'), lightsteelblue_4: new Color(110, 123, 139).hex('#6E7B8B'), lightslategray: new Color(119, 136, 153).hex('#778899'), slategray: new Color(112, 128, 144).hex('#708090'), slategray_1: new Color(198, 226, 255).hex('#C6E2FF'), slategray_2: new Color(185, 211, 238).hex('#B9D3EE'), slategray_3: new Color(159, 182, 205).hex('#9FB6CD'), slategray_4: new Color(108, 123, 139).hex('#6C7B8B'), dodgerblue_1: new Color(30, 144, 255).hex('#1E90FF'), //(dodgerblue) dodgerblue_2: new Color(28, 134, 238).hex('#1C86EE'), dodgerblue_3: new Color(24, 116, 205).hex('#1874CD'), dodgerblue_4: new Color(16, 78, 139).hex('#104E8B'), aliceblue: new Color(240, 248, 255).hex('#F0F8FF'), steelblue: new Color(70, 130, 180).hex('#4682B4'), steelblue_1: new Color(99, 184, 255).hex('#63B8FF'), steelblue_2: new Color(92, 172, 238).hex('#5CACEE'), steelblue_3: new Color(79, 148, 205).hex('#4F94CD'), steelblue_4: new Color(54, 100, 139).hex('#36648B'), lightskyblue: new Color(135, 206, 250).hex('#87CEFA'), lightskyblue_1: new Color(176, 226, 255).hex('#B0E2FF'), lightskyblue_2: new Color(164, 211, 238).hex('#A4D3EE'), lightskyblue_3: new Color(141, 182, 205).hex('#8DB6CD'), lightskyblue_4: new Color(96, 123, 139).hex('#607B8B'), skyblue_1: new Color(135, 206, 255).hex('#87CEFF'), skyblue_2: new Color(126, 192, 238).hex('#7EC0EE'), skyblue_3: new Color(108, 166, 205).hex('#6CA6CD'), skyblue_4: new Color(74, 112, 139).hex('#4A708B'), skyblue: new Color(135, 206, 235).hex('#87CEEB'), deepskyblue_1: new Color(0, 191, 255).hex('#00BFFF'), //(deepskyblue) deepskyblue_2: new Color(0, 178, 238).hex('#00B2EE'), deepskyblue_3: new Color(0, 154, 205).hex('#009ACD'), deepskyblue_4: new Color(0, 104, 139).hex('#00688B'), peacock: new Color(51, 161, 201).hex('#33A1C9'), lightblue: new Color(173, 216, 230).hex('#ADD8E6'), lightblue_1: new Color(191, 239, 255).hex('#BFEFFF'), lightblue_2: new Color(178, 223, 238).hex('#B2DFEE'), lightblue_3: new Color(154, 192, 205).hex('#9AC0CD'), lightblue_4: new Color(104, 131, 139).hex('#68838B'), powderblue: new Color(176, 224, 230).hex('#B0E0E6'), cadetblue_1: new Color(152, 245, 255).hex('#98F5FF'), cadetblue_2: new Color(142, 229, 238).hex('#8EE5EE'), cadetblue_3: new Color(122, 197, 205).hex('#7AC5CD'), cadetblue_4: new Color(83, 134, 139).hex('#53868B'), turquoise_1: new Color(0, 245, 255).hex('#00F5FF'), turquoise_2: new Color(0, 229, 238).hex('#00E5EE'), turquoise_3: new Color(0, 197, 205).hex('#00C5CD'), turquoise_4: new Color(0, 134, 139).hex('#00868B'), cadetblue: new Color(95, 158, 160).hex('#5F9EA0'), darkturquoise: new Color(0, 206, 209).hex('#00CED1'), azure_1: new Color(240, 255, 255).hex('#F0FFFF'), //(azure) azure_2: new Color(224, 238, 238).hex('#E0EEEE'), azure_3: new Color(193, 205, 205).hex('#C1CDCD'), azure_4: new Color(131, 139, 139).hex('#838B8B'), lightcyan_1: new Color(224, 255, 255).hex('#E0FFFF'), //(lightcyan) lightcyan_2: new Color(209, 238, 238).hex('#D1EEEE'), lightcyan_3: new Color(180, 205, 205).hex('#B4CDCD'), lightcyan_4: new Color(122, 139, 139).hex('#7A8B8B'), paleturquoise_1: new Color(187, 255, 255).hex('#BBFFFF'), paleturquoise_2: new Color(174, 238, 238).hex('#AEEEEE'), //(paleturquoise) paleturquoise_3: new Color(150, 205, 205).hex('#96CDCD'), paleturquoise_4: new Color(102, 139, 139).hex('#668B8B'), darkslategray: new Color(47, 79, 79).hex('#2F4F4F'), darkslategray_1: new Color(151, 255, 255).hex('#97FFFF'), darkslategray_2: new Color(141, 238, 238).hex('#8DEEEE'), darkslategray_3: new Color(121, 205, 205).hex('#79CDCD'), darkslategray_4: new Color(82, 139, 139).hex('#528B8B'), cyan: new Color(0, 255, 255).hex('#00FFFF'), // aqua cyan_2: new Color(0, 238, 238).hex('#00EEEE'), cyan_3: new Color(0, 205, 205).hex('#00CDCD'), cyan_4: new Color(0, 139, 139).hex('#008B8B'), //(darkcyan) teal: new Color(0, 128, 128).hex('#008080'), mediumturquoise: new Color(72, 209, 204).hex('#48D1CC'), lightseagreen: new Color(32, 178, 170).hex('#20B2AA'), manganeseblue: new Color(3, 168, 158).hex('#03A89E'), turquoise: new Color(64, 224, 208).hex('#40E0D0'), coldgrey: new Color(128, 138, 135).hex('#808A87'), turquoiseblue: new Color(0, 199, 140).hex('#00C78C'), aquamarine_1: new Color(127, 255, 212).hex('#7FFFD4'), //(aquamarine) aquamarine_2: new Color(118, 238, 198).hex('#76EEC6'), aquamarine_3: new Color(102, 205, 170).hex('#66CDAA'), //(mediumaquamarine) aquamarine_4: new Color(69, 139, 116).hex('#458B74'), mediumspringgreen: new Color(0, 250, 154).hex('#00FA9A'), mintcream: new Color(245, 255, 250).hex('#F5FFFA'), springgreen: new Color(0, 255, 127).hex('#00FF7F'), springgreen_1: new Color(0, 238, 118).hex('#00EE76'), springgreen_2: new Color(0, 205, 102).hex('#00CD66'), springgreen_3: new Color(0, 139, 69).hex('#008B45'), mediumseagreen: new Color(60, 179, 113).hex('#3CB371'), seagreen_1: new Color(84, 255, 159).hex('#54FF9F'), seagreen_2: new Color(78, 238, 148).hex('#4EEE94'), seagreen_3: new Color(67, 205, 128).hex('#43CD80'), seagreen_4: new Color(46, 139, 87).hex('#2E8B57'), //(seagreen) emeraldgreen: new Color(0, 201, 87).hex('#00C957'), mint: new Color(189, 252, 201).hex('#BDFCC9'), cobaltgreen: new Color(61, 145, 64).hex('#3D9140'), honeydew_1: new Color(240, 255, 240).hex('#F0FFF0'), //(honeydew) honeydew_2: new Color(224, 238, 224).hex('#E0EEE0'), honeydew_3: new Color(193, 205, 193).hex('#C1CDC1'), honeydew_4: new Color(131, 139, 131).hex('#838B83'), darkseagreen: new Color(143, 188, 143).hex('#8FBC8F'), darkseagreen_1: new Color(193, 255, 193).hex('#C1FFC1'), darkseagreen_2: new Color(180, 238, 180).hex('#B4EEB4'), darkseagreen_3: new Color(155, 205, 155).hex('#9BCD9B'), darkseagreen_4: new Color(105, 139, 105).hex('#698B69'), palegreen: new Color(152, 251, 152).hex('#98FB98'), palegreen_1: new Color(154, 255, 154).hex('#9AFF9A'), palegreen_2: new Color(144, 238, 144).hex('#90EE90'), //(lightgreen) palegreen_3: new Color(124, 205, 124).hex('#7CCD7C'), palegreen_4: new Color(84, 139, 84).hex('#548B54'), limegreen: new Color(50, 205, 50).hex('#32CD32'), forestgreen: new Color(34, 139, 34).hex('#228B22'), green_1: new Color(0, 255, 0).hex('#00FF00'), //(lime*) green_2: new Color(0, 238, 0).hex('#00EE00'), green_3: new Color(0, 205, 0).hex('#00CD00'), green_4: new Color(0, 139, 0).hex('#008B00'), green: new Color(0, 128, 0).hex('#008000'), darkgreen: new Color(0, 100, 0).hex('#006400'), sapgreen: new Color(48, 128, 20).hex('#308014'), lawngreen: new Color(124, 252, 0).hex('#7CFC00'), chartreuse_1: new Color(127, 255, 0).hex('#7FFF00'), //(chartreuse) chartreuse_2: new Color(118, 238, 0).hex('#76EE00'), chartreuse_3: new Color(102, 205, 0).hex('#66CD00'), chartreuse_4: new Color(69, 139, 0).hex('#458B00'), greenyellow: new Color(173, 255, 47).hex('#ADFF2F'), darkolivegreen_1: new Color(202, 255, 112).hex('#CAFF70'), darkolivegreen_2: new Color(188, 238, 104).hex('#BCEE68'), darkolivegreen_3: new Color(162, 205, 90).hex('#A2CD5A'), darkolivegreen_4: new Color(110, 139, 61).hex('#6E8B3D'), darkolivegreen: new Color(85, 107, 47).hex('#556B2F'), olivedrab: new Color(107, 142, 35).hex('#6B8E23'), olivedrab_1: new Color(192, 255, 62).hex('#C0FF3E'), olivedrab_2: new Color(179, 238, 58).hex('#B3EE3A'), olivedrab_3: new Color(154, 205, 50).hex('#9ACD32'), //(yellowgreen) olivedrab_4: new Color(105, 139, 34).hex('#698B22'), ivory_1: new Color(255, 255, 240).hex('#FFFFF0'), //(ivory) ivory_2: new Color(238, 238, 224).hex('#EEEEE0'), ivory_3: new Color(205, 205, 193).hex('#CDCDC1'), ivory_4: new Color(139, 139, 131).hex('#8B8B83'), beige: new Color(245, 245, 220).hex('#F5F5DC'), lightyellow_1: new Color(255, 255, 224).hex('#FFFFE0'), //(lightyellow) lightyellow_2: new Color(238, 238, 209).hex('#EEEED1'), lightyellow_3: new Color(205, 205, 180).hex('#CDCDB4'), lightyellow_4: new Color(139, 139, 122).hex('#8B8B7A'), lightgoldenrodyellow: new Color(250, 250, 210).hex('#FAFAD2'), yellow_1: new Color(255, 255, 0).hex('#FFFF00'), //(yellow*) yellow_2: new Color(238, 238, 0).hex('#EEEE00'), yellow_3: new Color(205, 205, 0).hex('#CDCD00'), yellow_4: new Color(139, 139, 0).hex('#8B8B00'), warmgrey: new Color(128, 128, 105).hex('#808069'), olive: new Color(128, 128, 0).hex('#808000'), darkkhaki: new Color(189, 183, 107).hex('#BDB76B'), khaki_1: new Color(255, 246, 143).hex('#FFF68F'), khaki_2: new Color(238, 230, 133).hex('#EEE685'), khaki_3: new Color(205, 198, 115).hex('#CDC673'), khaki_4: new Color(139, 134, 78).hex('#8B864E'), khaki: new Color(240, 230, 140).hex('#F0E68C'), palegoldenrod: new Color(238, 232, 170).hex('#EEE8AA'), lemonchiffon_1: new Color(255, 250, 205).hex('#FFFACD'), //(lemonchiffon) lemonchiffon_2: new Color(238, 233, 191).hex('#EEE9BF'), lemonchiffon_3: new Color(205, 201, 165).hex('#CDC9A5'), lemonchiffon_4: new Color(139, 137, 112).hex('#8B8970'), lightgoldenrod_1: new Color(255, 236, 139).hex('#FFEC8B'), lightgoldenrod_2: new Color(238, 220, 130).hex('#EEDC82'), lightgoldenrod_3: new Color(205, 190, 112).hex('#CDBE70'), lightgoldenrod_4: new Color(139, 129, 76).hex('#8B814C'), banana: new Color(227, 207, 87).hex('#E3CF57'), gold_1: new Color(255, 215, 0).hex('#FFD700'), //(gold) gold_2: new Color(238, 201, 0).hex('#EEC900'), gold_3: new Color(205, 173, 0).hex('#CDAD00'), gold_4: new Color(139, 117, 0).hex('#8B7500'), cornsilk_1: new Color(255, 248, 220).hex('#FFF8DC'), //(cornsilk) cornsilk_2: new Color(238, 232, 205).hex('#EEE8CD'), cornsilk_3: new Color(205, 200, 177).hex('#CDC8B1'), cornsilk_4: new Color(139, 136, 120).hex('#8B8878'), goldenrod: new Color(218, 165, 32).hex('#DAA520'), goldenrod_1: new Color(255, 193, 37).hex('#FFC125'), goldenrod_2: new Color(238, 180, 34).hex('#EEB422'), goldenrod_3: new Color(205, 155, 29).hex('#CD9B1D'), goldenrod_4: new Color(139, 105, 20).hex('#8B6914'), darkgoldenrod: new Color(184, 134, 11).hex('#B8860B'), darkgoldenrod_1: new Color(255, 185, 15).hex('#FFB90F'), darkgoldenrod_2: new Color(238, 173, 14).hex('#EEAD0E'), darkgoldenrod_3: new Color(205, 149, 12).hex('#CD950C'), darkgoldenrod_4: new Color(139, 101, 8).hex('#8B6508'), orange_1: new Color(255, 165, 0).hex('#FFA500'), //(orange) orange_2: new Color(238, 154, 0).hex('#EE9A00'), orange_3: new Color(205, 133, 0).hex('#CD8500'), orange_4: new Color(139, 90, 0).hex('#8B5A00'), floralwhite: new Color(255, 250, 240).hex('#FFFAF0'), oldlace: new Color(253, 245, 230).hex('#FDF5E6'), wheat: new Color(245, 222, 179).hex('#F5DEB3'), wheat_1: new Color(255, 231, 186).hex('#FFE7BA'), wheat_2: new Color(238, 216, 174).hex('#EED8AE'), wheat_3: new Color(205, 186, 150).hex('#CDBA96'), wheat_4: new Color(139, 126, 102).hex('#8B7E66'), moccasin: new Color(255, 228, 181).hex('#FFE4B5'), papayawhip: new Color(255, 239, 213).hex('#FFEFD5'), blanchedalmond: new Color(255, 235, 205).hex('#FFEBCD'), navajowhite_1: new Color(255, 222, 173).hex('#FFDEAD'), //(navajowhite) navajowhite_2: new Color(238, 207, 161).hex('#EECFA1'), navajowhite_3: new Color(205, 179, 139).hex('#CDB38B'), navajowhite_4: new Color(139, 121, 94).hex('#8B795E'), eggshell: new Color(252, 230, 201).hex('#FCE6C9'), tan: new Color(210, 180, 140).hex('#D2B48C'), brick: new Color(156, 102, 31).hex('#9C661F'), cadmiumyellow: new Color(255, 153, 18).hex('#FF9912'), antiquewhite: new Color(250, 235, 215).hex('#FAEBD7'), antiquewhite_1: new Color(255, 239, 219).hex('#FFEFDB'), antiquewhite_2: new Color(238, 223, 204).hex('#EEDFCC'), antiquewhite_3: new Color(205, 192, 176).hex('#CDC0B0'), antiquewhite_4: new Color(139, 131, 120).hex('#8B8378'), burlywood: new Color(222, 184, 135).hex('#DEB887'), burlywood_1: new Color(255, 211, 155).hex('#FFD39B'), burlywood_2: new Color(238, 197, 145).hex('#EEC591'), burlywood_3: new Color(205, 170, 125).hex('#CDAA7D'), burlywood_4: new Color(139, 115, 85).hex('#8B7355'), bisque_1: new Color(255, 228, 196).hex('#FFE4C4'), //(bisque) bisque_2: new Color(238, 213, 183).hex('#EED5B7'), bisque_3: new Color(205, 183, 158).hex('#CDB79E'), bisque_4: new Color(139, 125, 107).hex('#8B7D6B'), melon: new Color(227, 168, 105).hex('#E3A869'), carrot: new Color(237, 145, 33).hex('#ED9121'), darkorange: new Color(255, 140, 0).hex('#FF8C00'), darkorange_1: new Color(255, 127, 0).hex('#FF7F00'), darkorange_2: new Color(238, 118, 0).hex('#EE7600'), darkorange_3: new Color(205, 102, 0).hex('#CD6600'), darkorange_4: new Color(139, 69, 0).hex('#8B4500'), orange: new Color(255, 128, 0).hex('#FF8000'), tan_1: new Color(255, 165, 79).hex('#FFA54F'), tan_2: new Color(238, 154, 73).hex('#EE9A49'), tan_3: new Color(205, 133, 63).hex('#CD853F'), //(peru) tan_4: new Color(139, 90, 43).hex('#8B5A2B'), linen: new Color(250, 240, 230).hex('#FAF0E6'), peachpuff_1: new Color(255, 218, 185).hex('#FFDAB9'), //(peachpuff) peachpuff_2: new Color(238, 203, 173).hex('#EECBAD'), peachpuff_3: new Color(205, 175, 149).hex('#CDAF95'), peachpuff_4: new Color(139, 119, 101).hex('#8B7765'), seashell_1: new Color(255, 245, 238).hex('#FFF5EE'), //(seashell) seashell_2: new Color(238, 229, 222).hex('#EEE5DE'), seashell_3: new Color(205, 197, 191).hex('#CDC5BF'), seashell_4: new Color(139, 134, 130).hex('#8B8682'), sandybrown: new Color(244, 164, 96).hex('#F4A460'), rawsienna: new Color(199, 97, 20).hex('#C76114'), chocolate: new Color(210, 105, 30).hex('#D2691E'), chocolate_1: new Color(255, 127, 36).hex('#FF7F24'), chocolate_2: new Color(238, 118, 33).hex('#EE7621'), chocolate_3: new Color(205, 102, 29).hex('#CD661D'), chocolate_4: new Color(139, 69, 19).hex('#8B4513'), //(saddlebrown) ivoryblack: new Color(41, 36, 33).hex('#292421'), flesh: new Color(255, 125, 64).hex('#FF7D40'), cadmiumorange: new Color(255, 97, 3).hex('#FF6103'), burntsienna: new Color(138, 54, 15).hex('#8A360F'), sienna: new Color(160, 82, 45).hex('#A0522D'), sienna_1: new Color(255, 130, 71).hex('#FF8247'), sienna_2: new Color(238, 121, 66).hex('#EE7942'), sienna_3: new Color(205, 104, 57).hex('#CD6839'), sienna_4: new Color(139, 71, 38).hex('#8B4726'), lightsalmon_1: new Color(255, 160, 122).hex('#FFA07A'), //(lightsalmon) lightsalmon_2: new Color(238, 149, 114).hex('#EE9572'), lightsalmon_3: new Color(205, 129, 98).hex('#CD8162'), lightsalmon_4: new Color(139, 87, 66).hex('#8B5742'), coral: new Color(255, 127, 80).hex('#FF7F50'), orangered_1: new Color(255, 69, 0).hex('#FF4500'), //(orangered) orangered_2: new Color(238, 64, 0).hex('#EE4000'), orangered_3: new Color(205, 55, 0).hex('#CD3700'), orangered_4: new Color(139, 37, 0).hex('#8B2500'), sepia: new Color(94, 38, 18).hex('#5E2612'), darksalmon: new Color(233, 150, 122).hex('#E9967A'), salmon_1: new Color(255, 140, 105).hex('#FF8C69'), salmon_2: new Color(238, 130, 98).hex('#EE8262'), salmon_3: new Color(205, 112, 84).hex('#CD7054'), salmon_4: new Color(139, 76, 57).hex('#8B4C39'), coral_1: new Color(255, 114, 86).hex('#FF7256'), coral_2: new Color(238, 106, 80).hex('#EE6A50'), coral_3: new Color(205, 91, 69).hex('#CD5B45'), coral_4: new Color(139, 62, 47).hex('#8B3E2F'), burntumber: new Color(138, 51, 36).hex('#8A3324'), tomato_1: new Color(255, 99, 71).hex('#FF6347'), //(tomato) tomato_2: new Color(238, 92, 66).hex('#EE5C42'), tomato_3: new Color(205, 79, 57).hex('#CD4F39'), tomato_4: new Color(139, 54, 38).hex('#8B3626'), salmon: new Color(250, 128, 114).hex('#FA8072'), mistyrose_1: new Color(255, 228, 225).hex('#FFE4E1'), //(mistyrose) mistyrose_2: new Color(238, 213, 210).hex('#EED5D2'), mistyrose_3: new Color(205, 183, 181).hex('#CDB7B5'), mistyrose_4: new Color(139, 125, 123).hex('#8B7D7B'), snow_1: new Color(255, 250, 250).hex('#FFFAFA'), //(snow) snow_2: new Color(238, 233, 233).hex('#EEE9E9'), snow_3: new Color(205, 201, 201).hex('#CDC9C9'), snow_4: new Color(139, 137, 137).hex('#8B8989'), rosybrown: new Color(188, 143, 143).hex('#BC8F8F'), rosybrown_1: new Color(255, 193, 193).hex('#FFC1C1'), rosybrown_2: new Color(238, 180, 180).hex('#EEB4B4'), rosybrown_3: new Color(205, 155, 155).hex('#CD9B9B'), rosybrown_4: new Color(139, 105, 105).hex('#8B6969'), lightcoral: new Color(240, 128, 128).hex('#F08080'), indianred: new Color(205, 92, 92).hex('#CD5C5C'), indianred_1: new Color(255, 106, 106).hex('#FF6A6A'), indianred_2: new Color(238, 99, 99).hex('#EE6363'), indianred_4: new Color(139, 58, 58).hex('#8B3A3A'), indianred_3: new Color(205, 85, 85).hex('#CD5555'), brown: new Color(165, 42, 42).hex('#A52A2A'), brown_1: new Color(255, 64, 64).hex('#FF4040'), brown_2: new Color(238, 59, 59).hex('#EE3B3B'), brown_3: new Color(205, 51, 51).hex('#CD3333'), brown_4: new Color(139, 35, 35).hex('#8B2323'), firebrick: new Color(178, 34, 34).hex('#B22222'), firebrick_1: new Color(255, 48, 48).hex('#FF3030'), firebrick_2: new Color(238, 44, 44).hex('#EE2C2C'), firebrick_3: new Color(205, 38, 38).hex('#CD2626'), firebrick_4: new Color(139, 26, 26).hex('#8B1A1A'), red_1: new Color(255, 0, 0).hex('#FF0000'), //(red*) red_2: new Color(238, 0, 0).hex('#EE0000'), red_3: new Color(205, 0, 0).hex('#CD0000'), red_4: new Color(139, 0, 0).hex('#8B0000'), //(darkred) maroon: new Color(128, 0, 0).hex('#800000'), sgi_beet: new Color(142, 56, 142).hex('#8E388E'), sgi_slateblue: new Color(113, 113, 198).hex('#7171C6'), sgi_lightblue: new Color(125, 158, 192).hex('#7D9EC0'), sgi_teal: new Color(56, 142, 142).hex('#388E8E'), sgi_chartreuse: new Color(113, 198, 113).hex('#71C671'), sgi_olivedrab: new Color(142, 142, 56).hex('#8E8E38'), sgi_brightgray: new Color(197, 193, 170).hex('#C5C1AA'), sgi_salmon: new Color(198, 113, 113).hex('#C67171'), sgi_darkgray: new Color(85, 85, 85).hex('#555555'), sgi_gray_12: new Color(30, 30, 30).hex('#1E1E1E'), sgi_gray_16: new Color(40, 40, 40).hex('#282828'), sgi_gray_32: new Color(81, 81, 81).hex('#515151'), sgi_gray_36: new Color(91, 91, 91).hex('#5B5B5B'), sgi_gray_52: new Color(132, 132, 132).hex('#848484'), sgi_gray_56: new Color(142, 142, 142).hex('#8E8E8E'), sgi_lightgray: new Color(170, 170, 170).hex('#AAAAAA'), sgi_gray_72: new Color(183, 183, 183).hex('#B7B7B7'), sgi_gray_76: new Color(193, 193, 193).hex('#C1C1C1'), sgi_gray_92: new Color(234, 234, 234).hex('#EAEAEA'), sgi_gray_96: new Color(244, 244, 244).hex('#F4F4F4'), white: new Color(255, 255, 255).hex('#FFFFFF'), white_smoke: new Color(245, 245, 245).hex('#F5F5F5'), // (gray 96) gainsboro: new Color(220, 220, 220).hex('#DCDCDC'), lightgrey: new Color(211, 211, 211).hex('#D3D3D3'), silver: new Color(192, 192, 192).hex('#C0C0C0'), darkgray: new Color(169, 169, 169).hex('#A9A9A9'), gray: new Color(128, 128, 128).hex('#808080'), dimgray: new Color(105, 105, 105).hex('#696969'), // (gray 42) black: new Color(0, 0, 0).hex('#000000'), gray_99: new Color(252, 252, 252).hex('#FCFCFC'), gray_98: new Color(250, 250, 250).hex('#FAFAFA'), gray_97: new Color(247, 247, 247).hex('#F7F7F7'), white_smoke: new Color(245, 245, 245).hex('#F5F5F5'), // (gray 96) gray_95: new Color(242, 242, 242).hex('#F2F2F2'), gray_94: new Color(240, 240, 240).hex('#F0F0F0'), gray_93: new Color(237, 237, 237).hex('#EDEDED'), gray_92: new Color(235, 235, 235).hex('#EBEBEB'), gray_91: new Color(232, 232, 232).hex('#E8E8E8'), gray_90: new Color(229, 229, 229).hex('#E5E5E5'), gray_89: new Color(227, 227, 227).hex('#E3E3E3'), gray_88: new Color(224, 224, 224).hex('#E0E0E0'), gray_87: new Color(222, 222, 222).hex('#DEDEDE'), gray_86: new Color(219, 219, 219).hex('#DBDBDB'), gray_85: new Color(217, 217, 217).hex('#D9D9D9'), gray_84: new Color(214, 214, 214).hex('#D6D6D6'), gray_83: new Color(212, 212, 212).hex('#D4D4D4'), gray_82: new Color(209, 209, 209).hex('#D1D1D1'), gray_81: new Color(207, 207, 207).hex('#CFCFCF'), gray_80: new Color(204, 204, 204).hex('#CCCCCC'), gray_79: new Color(201, 201, 201).hex('#C9C9C9'), gray_78: new Color(199, 199, 199).hex('#C7C7C7'), gray_77: new Color(196, 196, 196).hex('#C4C4C4'), gray_76: new Color(194, 194, 194).hex('#C2C2C2'), gray_75: new Color(191, 191, 191).hex('#BFBFBF'), gray_74: new Color(189, 189, 189).hex('#BDBDBD'), gray_73: new Color(186, 186, 186).hex('#BABABA'), gray_72: new Color(184, 184, 184).hex('#B8B8B8'), gray_71: new Color(181, 181, 181).hex('#B5B5B5'), gray_70: new Color(179, 179, 179).hex('#B3B3B3'), gray_69: new Color(176, 176, 176).hex('#B0B0B0'), gray_68: new Color(173, 173, 173).hex('#ADADAD'), gray_67: new Color(171, 171, 171).hex('#ABABAB'), gray_66: new Color(168, 168, 168).hex('#A8A8A8'), gray_65: new Color(166, 166, 166).hex('#A6A6A6'), gray_64: new Color(163, 163, 163).hex('#A3A3A3'), gray_63: new Color(161, 161, 161).hex('#A1A1A1'), gray_62: new Color(158, 158, 158).hex('#9E9E9E'), gray_61: new Color(156, 156, 156).hex('#9C9C9C'), gray_60: new Color(153, 153, 153).hex('#999999'), gray_59: new Color(150, 150, 150).hex('#969696'), gray_58: new Color(148, 148, 148).hex('#949494'), gray_57: new Color(145, 145, 145).hex('#919191'), gray_56: new Color(143, 143, 143).hex('#8F8F8F'), gray_55: new Color(140, 140, 140).hex('#8C8C8C'), gray_54: new Color(138, 138, 138).hex('#8A8A8A'), gray_53: new Color(135, 135, 135).hex('#878787'), gray_52: new Color(133, 133, 133).hex('#858585'), gray_51: new Color(130, 130, 130).hex('#828282'), gray_50: new Color(127, 127, 127).hex('#7F7F7F'), gray_49: new Color(125, 125, 125).hex('#7D7D7D'), gray_48: new Color(122, 122, 122).hex('#7A7A7A'), gray_47: new Color(120, 120, 120).hex('#787878'), gray_46: new Color(117, 117, 117).hex('#757575'), gray_45: new Color(115, 115, 115).hex('#737373'), gray_44: new Color(112, 112, 112).hex('#707070'), gray_43: new Color(110, 110, 110).hex('#6E6E6E'), gray_42: new Color(107, 107, 107).hex('#6B6B6B'), dimgray: new Color(105, 105, 105).hex('#696969'), // (gray 42) gray_40: new Color(102, 102, 102).hex('#666666'), gray_39: new Color(99, 99, 99).hex('#636363'), gray_38: new Color(97, 97, 97).hex('#616161'), gray_37: new Color(94, 94, 94).hex('#5E5E5E'), gray_36: new Color(92, 92, 92).hex('#5C5C5C'), gray_35: new Color(89, 89, 89).hex('#595959'), gray_34: new Color(87, 87, 87).hex('#575757'), gray_33: new Color(84, 84, 84).hex('#545454'), gray_32: new Color(82, 82, 82).hex('#525252'), gray_31: new Color(79, 79, 79).hex('#4F4F4F'), gray_30: new Color(77, 77, 77).hex('#4D4D4D'), gray_29: new Color(74, 74, 74).hex('#4A4A4A'), gray_28: new Color(71, 71, 71).hex('#474747'), gray_27: new Color(69, 69, 69).hex('#454545'), gray_26: new Color(66, 66, 66).hex('#424242'), gray_25: new Color(64, 64, 64).hex('#404040'), gray_24: new Color(61, 61, 61).hex('#3D3D3D'), gray_23: new Color(59, 59, 59).hex('#3B3B3B'), gray_22: new Color(56, 56, 56).hex('#383838'), gray_21: new Color(54, 54, 54).hex('#363636'), gray_20: new Color(51, 51, 51).hex('#333333'), gray_19: new Color(48, 48, 48).hex('#303030'), gray_18: new Color(46, 46, 46).hex('#2E2E2E'), gray_17: new Color(43, 43, 43).hex('#2B2B2B'), gray_16: new Color(41, 41, 41).hex('#292929'), gray_15: new Color(38, 38, 38).hex('#262626'), gray_14: new Color(36, 36, 36).hex('#242424'), gray_13: new Color(33, 33, 33).hex('#212121'), gray_12: new Color(31, 31, 31).hex('#1F1F1F'), gray_11: new Color(28, 28, 28).hex('#1C1C1C'), gray_10: new Color(26, 26, 26).hex('#1A1A1A'), gray_9: new Color(23, 23, 23).hex('#171717'), gray_8: new Color(20, 20, 20).hex('#141414'), gray_7: new Color(18, 18, 18).hex('#121212'), gray_6: new Color(15, 15, 15).hex('#0F0F0F'), gray_5: new Color(13, 13, 13).hex('#0D0D0D'), gray_4: new Color(10, 10, 10).hex('#0A0A0A'), gray_3: new Color(8, 8, 8).hex('#080808'), gray_2: new Color(5, 5, 5).hex('#050505'), gray_1: new Color(3, 3, 3).hex('#030303') }); const MaterialFiveHundredlColors = Object.freeze({ SOFT_ORANGE: new Color(244, 67, 54), SOFT_RED: new Color(233, 30, 99), MAGENTA: new Color(156, 39, 176), VIOLET: new Color(103, 58, 183), DARGK_BLUE: new Color(63, 81, 181), BLUE: new Color(33, 150, 243), LIGHT_BLUE: new Color(3, 169, 244), BLUE_GREEN: new Color(0, 188, 212), SEA_GREEN: new Color(0, 150, 136), GREEN: new Color(76, 175, 80), LIGHT_GREEN: new Color(139, 195, 74), YELLOW: new Color(205, 220, 57), GOLD: new Color(255, 235, 59), YELLO_ORANGE: new Color(255, 152, 0), ORANGE: new Color(255, 87, 34), }); /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Enum providing common alignment rules */ const Alignment = Object.freeze({ /* Normal alignment */ NORMAL: "normal", /* Basic positional alignment */ /* align-content does not take left and right values */ START: "start", CENTER: "center", END: "end", FLEX_START: "flex-start", FLEX_END: "flex-end", /* Baseline alignment */ BASELINE: "baseline", FIRST_BASELINE: "first baseline", LAST_BASELINE: "last baseline", /* Distributed alignment */ SPACE_BETWEEN: "space-between", SPACE_AROUND: "space-around", SPACE_EVENLY: "space-evenly", STRETCH: "stretch", /* Overflow alignment */ SAFE_CENTER: "safe center", UNSAFE_CENTER: "unsafe center", /* Global values */ INHERIT: "inherit", INITIAL: "initial", REVERT: "revert", REVERT_LAYER: "revert-layer", UNSET: "unset" }) /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Enum providing common alignment rules */ const Arrangement = Object.freeze({ START: "start", END: "end", CENTER: "center", SPACE_BETWEEN: "space-between", SPACE_EVENLY: "space-evenly", SPACE_AROUND: "space-around", }); /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Enum providing predefined set of Size-Units */ const SizeUnits = Object.freeze({ PIXEL: "px", PERCENT: "%" }) /** * @abstract */ class DirectionUnitDependentAttribute { _unit; _fFirst; _fSecond; _fThird; _fForth; constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) { this._unit = defaultUnit; this._fFirst = defaultValue; this._fSecond = defaultValue; this._fThird = defaultValue; this._fForth = defaultValue; } /** * * @param {Units} unit The unit of the amount or style * @returns {DirectionUnitDependentAttribute} this - Object */ setUnit(unit) { this._unit = unit; return this; } /** * * @returns {array<*>} list of attributes */ getOrderedAttributes() { return [this._fFirst, this._fSecond, this._fThird, this._fForth]; } /** * @returns {Array} */ getOrderedValues() { return this.getOrderedAttributes().map(a => a + this._unit); } /** * Since the basic values are from "first" to "fourth", * they can be also accessed in the ordered way. * * Mainly used by the setup of directions of subclasses. * @param {number} index [1,4] * @param {number} value * @returns {DirectionUnitDependentAttribute} this */ setByIndex(index, value) { switch (index) { case 1: this._fFirst = value; break; case 2: this._fSecond = value; break; case 3: this._fThird = value; break; case 4: this._fForth = value; break; default: this._fFirst = value; break; } return this; } /** * Since the basic values are from "first" to "fourth", * they can be also accessed in the ordered way. * * Mainly used by the setup of directions of subclasses. * @param {number} index [1,4] * @returns {*} this value of index */ getByIndex(index) { switch (index) { case 1: return this._fFirst; case 2: return this._fSecond; case 3: return this._fThird; case 4: return this._fForth; default: return this._fFirst; } } /** * Placeholder for overrides * @returns {Object} */ toModifications() { return this.getOrderedValues() } /** * sets the amount-value for all directions. * @param {number} amount value to set for all directions * @returns {DirectionUnitDependentAttribute} this */ all(amount) { this._fFirst = amount; this._fSecond = amount; this._fThird = amount; this._fForth = amount; return this; } } const SideDirections = Object.freeze({ LEFT: 1, TOP: 2, RIGHT: 3, BOTTOM: 4 }); const SideTransitionDirection = Object.freeze({ HORIZONTAL: 0, VERTICAL: 1 }); const Corners = Object.freeze({ TOP_LEFT: 0, TOP_RIGHT: 1, BOTTOM_LEFT: 2, BOTTOM_RIGHT: 3 }); const CornerTransitionDirection = Object.freeze({ TOP_LEFT_BOTTOM_RIGHT: 0, TOP_RIGHT_BOTTOM_LEFT: 1 }); /** * @inheritdoc * @extends DirectionUnitDependentAttribute * */ class Sides extends DirectionUnitDependentAttribute { /** * * @param {number|string} defaultValue * @param {SizeUnits} defaultUnit */ constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) { super(defaultValue, defaultUnit); } /** * sets the amount-value for the left side. * @param {number} amount siding for left * @returns {Siding} this Siding Object */ left(amount) { return this.setByIndex(1, amount); } /** * sets the amount-value for the right side. * @param {number} amount siding for right * @returns {Siding} this Siding Object */ right(amount) { return this.setByIndex(3, amount); } /** * sets the amount-value for the top side. * @param {number} amount siding for top * @returns {Siding} this Siding Object */ top(amount) { return this.setByIndex(2, amount); } /** * sets the amount-value for the bottom side. * @param {number} amount siding for bottom * @returns {Siding} this Siding Object */ bottom(amount) { return this.setByIndex(4, amount); } /** * sets the amount-value for the horizontal sides (left and right). * @param {number} amount siding for left and right. * @returns {Sides} this Siding Object */ horizontal(amount) { return this.left(amount).right(amount); } /** * sets the amount-value for the vertical sides (left and right). * @param {number} amount siding for top and bottom. * @returns {Sides} this Siding Object */ vertical(amount) { return this.top(amount).bottom(amount); } /** * * @returns {Object} */ getValues() { return { "left": this._fFirst, "top": this._fSecond, "right": this._fThird, "bottom": this._fForth, "horizontal": this._fFirst + this._fThird, "vertical": this._fSecond + this._fForth } } toModifications() { return [ { key: "left", value: this._fFirst + this._unit }, { key: "top", value: this._fSecond + this._unit }, { key: "right", value: this._fThird + this._unit }, { key: "bottom", value: this._fForth + this._unit } ] } } /** * @inheritdoc * @extends Sides */ class PaddingChain extends Sides { _modifier; constructor(modifier) { super(); this._modifier = modifier; } toModifier() { return this._modifier .padding(this); } /** * Returns the corresponding Modifier. * Basically climbs up the chain level. * @returns {Modifier} */ ensureModifier() { return this.toModifier() } /** * Returns the style-modifications of the class. * @returns {Map} */ toModifications() { return [ { key: "padding-left", value: this._fFirst + this._unit }, { key: "padding-top", value: this._fSecond + this._unit }, { key: "padding-right", value: this._fThird + this._unit }, { key: "padding-bottom", value: this._fForth + this._unit } ] } /** * * @returns {Component} the Component that was (supposed to be) modified by this obj. */ toComponent() { return this._modifier .padding(this) .toComponent(); } /** * * @param {Component|Array} innerComponent children of the Component under modification. * @returns {Component} */ childContext(innerComponent) { return this._modifier .padding(this) .toComponent() .childContext(innerComponent); } } /** * */ class TwoDimPoint { /** * * @param {number} x * @param {number} y */ constructor(x, y) { this.x = x; this.y = y; } /** * * @param {number} delta * @returns {TwoDimPoint} */ xMinus(delta) { this.x = this.x - delta; return this; } /** * * @param {number} delta * @returns {TwoDimPoint} */ xPlus(delta) { this.x = this.x + delta; return this; } /** * * @param {number} delta * @returns {TwoDimPoint} */ yMinus(delta) { this.y = this.y - delta; return this; } /** * * @param {number} delta * @returns {TwoDimPoint} */ yPlus(delta) { this.y = this.y + delta; return this; } /** * * @param {TwoDimPoint} delta * @returns {TwoDimPoint} */ minusTDP(delta) { this.x = this.x - delta.x; this.y = this.y - delta.y; return this; } /** * * @param {TwoDimPoint} delta * @returns {TwoDimPoint} */ plusTDP(delta) { this.x = this.x + delta.x; this.y = this.y + delta.y; return this; } } /** * * @param {number} start * @param {number} end * @param {number} value * @param {number} tolerance * @param {boolean} usePercentage * @returns {boolean} */ function isValueInBounds(start, end, value, tolerance = 0, usePercentage = false) { if (tolerance !== 0) { if (usePercentage) { start = start * (1 - tolerance / 100); end = end * (1 + tolerance / 100); } else { start = start - tolerance; end = end + tolerance; } } return value >= start && value <= end; } /** * @param {number} x * @param {number} y * @param {Map} area * @param {number} tolerance * @param {boolean} usePercentage if tolerance is given and this is set to true, * the tolerance will be calculated by percentage, * otherwise it will be subtracted/added * @returns {boolean} */ function areXYInArea(x, y, area, tolerance = 0, usePercentage = false) { return isValueInBounds( area.get(SideDirections.LEFT), area.get(SideDirections.RIGHT), x, tolerance, usePercentage ) && isValueInBounds( area.get(SideDirections.TOP), area.get(SideDirections.BOTTOM), y, tolerance, usePercentage ); } /** * * @param {TwoDimPoint} point * @param {Map} area * @param {number} tolerance * @param {boolean} usePercentage if tolerance is given and this is set to true, * the tolerance will be calculated by percentage, * otherwise it will be subtracted/added */ function isPointInArea(point, area, tolerance = 0, usePercentage = false) { return areXYInArea( point.x, point.y, area, tolerance, usePercentage ); } /** * * @param {HTMLElement} element * @returns {Object} innerComponent children of the Component under modification. * @returns {Component} */ childContext(innerComponent) { return this._modifier .clip(this) .toComponent() .childContext(innerComponent); } } const Shapes = Object.freeze({ Rectangle: new Shape(), RoundedCorner: new Shape(), Circle: new Shape(49, SizeUnits.PERCENT) }) /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * */ const LineStyles = Object.freeze({ dotted: "dotted", dashed: "dashed", solid: "solid", double: "double", groove: "groove", ridge: "ridge", inset: "inset", outset: "outset", none: "none", hidden: "hidden" }) class BorderDefinition { constructor(width = 0, color = Colors.black, style = LineStyles.solid) { this._width = width; this._color = color; this._style = style; } width(width) { this._width = width; return this; } color(color) { this._color = color; return this; } style(style) { this._style = style; return this; } join(def) { Object.keys(def) .forEach(key => this[key] = def[key]); return this; } } const Define = Object.freeze({ width: new BorderDefinition().width, style: new BorderDefinition().style, color: new BorderDefinition().color }) /** * @inheritdoc * @extends Sides */ class Border extends Sides { constructor(width = 0, color = Colors.black, style = LineStyles.solid, defaultUnit = SizeUnits.PIXEL, shape = Shapes.Rectangle) { super(0, defaultUnit); this._fFirst = new BorderDefinition(width, color, style); this._fSecond = new BorderDefinition(width, color, style); this._fThird = new BorderDefinition(width, color, style); this._fForth = new BorderDefinition(width, color, style); this._shape = shape; } setByIndex(index, value) { if (value instanceof BorderDefinition) { this.getByIndex(index).join(value) } else { this.getByIndex(index)._width = value } return this; } /** * * @param {string} key * @param {*} value * @returns {Border} */ setOnDirections(key, value) { let orderedAttributes = this.getOrderedAttributes() for (let i = 0; i < this.getOrderedAttributes.length; i++) { orderedAttributes[i][key] = value; } return this; } /** * * @param {number} width * @returns {Border} */ width(width) { this._fFirst._width = width; this._fSecond._width = width; this._fThird._width = width; this._fForth._width = width; return this; } /** * * @param {*} color * @returns {Border} */ color(color) { this._fFirst._color = color; this._fSecond._color = color; this._fThird._color = color; this._fForth._color = color; return this; } /** * * @param {Shape} shape * @returns {Border} */ shape(shape) { this._shape = shape; return this; } /** * Sets the border-style of all sides to the given. * @param {LineStyles} lineStyle style of the border * @returns {Border} */ setStyleAll(lineStyle) { this._fFirst._style = lineStyle; this._fSecond._style = lineStyle; this._fThird._style = lineStyle; this._fForth._style = lineStyle; return this; } /** * * @param {LineStyles} lineStyle * @param {*} sidingRefSide * @returns {Border} */ setLineStyle(lineStyle, sidingRefSide) { this._sidingStyles.setBySidingRef(sidingRefSide, lineStyle) return this; } /** * * @param {Map { if (bdef.width == 0) return [] return [ { key: `border-${names[i]}-width`, value: bdef._width + this._unit }, { key: `border-${names[i]}-color`, value: bdef._color.cssRGBString() }, { key: `border-${names[i]}-style`, value: bdef._style } ] }) } } /** * @inheritdoc * @extends Border */ class BorderChain extends Border { constructor(modifier){ super(); this._modifier = modifier; } /** * * @returns {Modifier|ChainableModifier} */ toModifier() { return this._modifier .border(this); } /** * * @returns {Modifier|ChainableModifier} */ ensureModifier() { return this.toModifier() } /** * Applies the border modification on the modifier * and returns (through the modifier) to the corresponding component. * @returns {Component} */ toComponent() { return this._modifier .border(this) .toComponent(); } /** * * @param {Component} innerComponent will be set to the corresponding component * @returns {Component} the corr. Component after the childContext was applied. */ childContext(innerComponent) { return this._modifier .border(this) .toComponent() .childContext(innerComponent); } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Simple Dimensions container for the height and width in pixels. * * @inheritdoc * @extends DirectionUnitDependentAttribute */ class Dimensions extends DirectionUnitDependentAttribute { /** * * @param {number} defaultValue * @param {SizeUnits} defaultUnit */ constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) { super(); this._unit = defaultUnit; this._fFirst = defaultValue; this._fSecond = defaultValue; } /** * Sets width (x) value of amount * @param {number} amount * @returns {Dimensions} this Dimensions Modifier */ width(amount) { this._fFirst = amount; return this; } /** * Sets height (y) value of amount * @param {number} amount * @returns {Dimensions} this Dimensions Modifier */ height(amount) { this._fSecond = amount; return this; } /** * * @param {number} size * @returns {Dimensions} */ all(size) { return this.width(size).height(size); } getOrderedValues() { return this.getOrderedValues().slice(2) } /** * * @returns {Object} */ toModifications() { let w = { key: "width", value: this._fFirst + this._unit } let h = { key: "height", value: this._fSecond + this._unit } let is_w = this._fFirst > 0; let is_h = this._fSecond > 0; if (is_h && is_w) { return [w, h] } else if (is_w) { return [w] } else if (is_h) { return [h] } else { return [] } } /** * * @returns {TwoDimPoint} */ toTwoDimPoint() { return new TwoDimPoint( this._fFirst, this._fSecond ); } } /** * @inheritdoc * @extends Dimensions */ class DimensionsChain extends Dimensions { /** * @type {Modifier|ChainableModifier} */ _modifier; /** * * @param {Modifier|ChainableModifier} modifier */ constructor(modifier) { super(); this._modifier = modifier; } /** * * @returns {Modifier|ChainableModifier} */ toModifier() { return this._modifier .dimensions(this); } /** * * @returns {Modifier|ChainableModifier} */ ensureModifier() { return this.toModifier() } /** * * @returns {Component} the Component that was (supposed to be) modified by this obj. */ toComponent() { return this._modifier .dimensions(this) .toComponent(); } /** * * @param {Component|Array} innerComponent children of the Component under modification. * @returns {Component} */ childContext(innerComponent) { return this._modifier .dimensions(this) .toComponent() .childContext(innerComponent); } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Enum to access common events */ const CommonEvents = Object.freeze({ ONCLICK: "onclick", ONCHANGE: "onchange", SCROLL: "scroll", DRAG_START: "dragstart", DRAG_END: "dragend", DRAG_ENTER: "dragenter", DRAG_LEAVE: "dragleave", DRAG_OVER: "dragover", DROP: "drop", }); /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Default implementation of a contextmenu-behaviour */ const DefaultContextMenu = { openContextMenuAction: function (identifier, refPos) { if (!refPos) { refPos = function (event) { return new Sides() .left(event.pageX) .top(event.pageY); } } return function (event) { event.preventDefault(); if (refPos instanceof Function) { refPos = refPos(event); } let menu = document.querySelector(`[data-autocompel="${identifier}"`); menu.style.left = `${refPos.getByIndex(SideDirections.LEFT)}px`; menu.style.top = `${refPos.getByIndex(SideDirections.TOP)}px`; toggleElementVisibility(menu, true); toggleElementVisibility(menu); document.addEventListener( "click", DefaultContextMenu.hideOnClickOutsideBoundsAction(identifier) ); document.addEventListener( "keyup", DefaultContextMenu.hideOnEscapeAction(identifier) ); } } , hideOnEscapeAction: function (identifier) { return function (event) { if (event.key === "Escape") { let menu = document.querySelector(`[data-autocompel="${identifier}"`); toggleElementVisibility(menu, true); document.removeEventListener("keyup"); } } } , hideOnClickOutsideBoundsAction: function (identifier) { return function (event) { let menu = document.querySelector(`[data-autocompel="${identifier}"`); let area = getEnclosingBounds(menu); if (!areXYInArea(area, event.clientX, event.clientY)) { toggleElementVisibility(menu, true); document.removeEventListener("click") } } } };/** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ const EventDrag = Object.freeze({ OVER: "over", START: "start", END: "end", ENTER: "enter", LEAVE: "leave" }); /** * Defines default functions to setup a drag/drop behaviour */ class DragAndDropImplementation { _isMngmtElNeeded; ensureManagementElementExists() { } getManagementElement() { } /** * * @param {string} datakey * @param {string} selector * @returns {Function} */ dragStartAction(datakey, selector) { return function (event) { console.log("DragEvent", event, "on", selector); event.dataTransfer .setData( datakey, JSON.stringify({ ["selector"]: selector, ["relDims"]: new TwoDimPoint( event.clientX - event.target.getBoundingClientRect().left, event.clientY - event.target.getBoundingClientRect().top ) }) ); } } dragEventAction(selector) { return function (event) { } } /** * * @param {string} datakey * @param {string} selector * @returns {Function} */ dropEventAction(datakey, selector) { return function (event) { event.preventDefault(); let draggedData = JSON.parse(event.dataTransfer.getData(datakey)); let draggedElement = document.querySelector(draggedData.selector); let target = event.target .closest(selector); console.log("Default drop event"); //if (![...target.childNodes].includes(draggedElement)) { target.appendChild(draggedElement); //} } } }; const DADInPlace = new DragAndDropImplementation(); DADInPlace.dropEventAction = function (datakey, selector) { /** * @param {Event} event */ return function (event) { event.preventDefault(); let draggedData = JSON.parse(event.dataTransfer.getData(datakey)); let draggedElement = document.querySelector(draggedData.selector); let target = event.target .closest(selector); let dimsDraggedEl = new Dimensions() .width(draggedElement.offsetWidth) .height(draggedElement.offsetHeight); let pagePoint = new TwoDimPoint( event.clientX, event.clientY ); let parentPagePoint = new TwoDimPoint( target.getBoundingClientRect().left, target.getBoundingClientRect().top ); draggedElement.style["width"] = dimsDraggedEl._fFirst + "px"; draggedElement.style["height"] = dimsDraggedEl._fSecond + "px"; // x page to relative is (parent-page), then -rel let dropPosition = pagePoint .minusTDP(parentPagePoint) .minusTDP(draggedData.relDims); draggedElement.style.position = "relative"; draggedElement.style.left = dropPosition.x + "px"; draggedElement.style.top = dropPosition.y + "px"; //if (![...target.childNodes].includes(draggedElement)) { target.appendChild(draggedElement); //} } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Wenity := Web Trinity */ class WebTrinity { /** * * @param {HTMLElement|Component} html * @param {HTMLStyleElement|Map} js * @param {HTMLScriptElement|Array} css */ constructor(html = null, js = null, css = null) { this.html = html; this.js = js; this.css = css; } /** * * @returns {boolean} */ isSSEmpty() { return (this.js === null || this.js.length === 0) && (this.css === null || this.css.size === 0); } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * ESAggregation := Extensions Storage Aggregation (method) */ const ESAggregation = Object.freeze({ INTERNALIZED: "intern", INDIVIDUALLY: "individual", COLLECTED: "collected", CENTRALIZED: "centralized" }); /** * ExtStoragePos := Extensions Storage Position * * Determines where the extensions are positioned. * Only relevant if ExtStorage is not 'internalized'. * Determines where the tag (if individually) or the extensions are positioned. */ const ExtStorePosition = Object.freeze({ WITHIN: "WITHIN", BEFORE: "BEFORE", SEGMENT_BEGIN: "SEGMENT_BEGIN", DOC_HEAD: "DOC_HEAD", DOC_FOOTER: "DOC_FOOTER" }); /** * Defines how an identified dupplication should be "resolved"/dealt with. * REPLACE: * RENAME: * RENAME_OLD: * DROP_NEW: * MOVE_ELEMENT_SPECIFIC: @ATTENTION implementation pending */ const OverwriteBehaviour = Object.freeze({ REPLACE: "REPLACE", RENAME: "RENAME", RENAME_OLD: "RENAME_OLD", DROP_NEW: "DROP_NEW", MOVE_ELEMENT_SPECIFIC: "MOVE_ELEMENT_SPECIFIC" }); /** * Is supposed to shrink all empty strings to length 1 * @param {Function} func * @returns {string} */ function clearFunctionDeclarationText(func) { function shrinkEmptyStrings(text) { for (let i = 1; i < 10; i++) { text = text.replaceAll(" ".slice(i), ' '); } return text; } return shrinkEmptyStrings( func.toString() .replaceAll('\n', ' ') .replaceAll('\r\n', ' ') .replaceAll('\n\r', ' ') ); } /** * * @param {Function} func * @param {string} registrationName * @returns {string} */ function getScriptTagInjectionText(func, registrationName) { let funcHasName = func.name && func.name.trim() !== ''; if (func.startWith('function')) { let label = `function ${registrationName}`; let isNameInFuncText = func.startWith(label); if (funcHasName && isNameInFuncText) { return func; } else { return [label, '(', func.split('(', 1)[1]].join('') } } else { return `const ${registrationName} = ${func}; `; } } /** * Stores a function until generate is called. * Then the additional informations of the store wil be applied * and the funcitons added to the page. */ class FunctionStoreBuffer { /** * Stores a function until generate is called. * Then the additional informations of the store wil be applied * and the funcitons added to the page. * @param {Function} func the function that will be stored * @param {Array} args additional arguments that will be given to the function * @param {boolean} repeats weither the funciton is supposed to execute repeatedly * @param {number} interval the time in milliseconds between executions * @param {boolean} execAfterStart weither the function is supposed to be executed after pageload * @param {number} delay the time in milliseconds the execution will be delayed */ constructor( func, args = [], repeats = false, interval = -1, execAfterStart = false, delay = -1 ) { this.func = func; this.args = args; this.execAfterStart = execAfterStart; this.delay = delay; this.repeats = repeats; this.interval = interval; } } /** * Extracted this super class to differentiate between * internal and external store. */ class ExtStorage { constructor( aggregation = ESAggregation.INTERNALIZED, position = ExtStorePosition.WITHIN, behaviour = OverwriteBehaviour.DROP_NEW ) { /** * @type {ESAggregation} */ this._aggregation = aggregation; /** * @type {ExtStorePosition} */ this._position = position; /** * @type {OverwriteBehaviour} */ this._overwriteBehaviour = behaviour; } /** * * @param {ESAggregation} position */ setExtStoreAggregation(aggregation) { this._aggregation = aggregation; return this; } /** * * @param {ExtStoreType} position */ setExtStorePosition(position) { this._position = position; return this; } /** * * @param {OverwriteBehaviour} behave * @returns {EXPosConfer} */ setOverwriteBehaviour(behave) { this._overwriteBehaviour = behave; return this; } /** * * @param {ExtStorage} extStore * @returns {boolean} */ equals(extStore = null) { if (!extStore) return false; return extStore._type === this._type && extStore._overwriteBehaviour === this._overwriteBehaviour; } /** * * @returns {boolean} */ isMissing() { return this._type === null || this._overwriteBehaviour === null; } /** * * @returns {boolean} */ isNotInternalOrIndividual() { return !( this._aggregation === ESAggregation.INTERNALIZED || this._aggregation === ESAggregation.INDIVIDUALLY ); } /** * * @param {ExtStorage} otherExtStore * @returns {ExtStorage} */ fillBy(otherExtStore) { if (this._type === null) { this._type = otherExtStore._type; } if (this._overwriteBehaviour === null) { this._overwriteBehaviour = otherExtStore._overwriteBehaviour; } return this; } /** * @todo check if still implemented correctly * Takes the singleValue and an ExtStore object to copy all values from. * Then the singleValue will be compared to the three enums for the type of value. * After the type is identified the corresponding (copied) value will be updated. * @param {ExtStoreType|ExtStorePosition|OverwriteBehaviour} singleValue * @param {ExtStorage} extStoreToClone * @returns {ExtStorage} */ setSingleValueToClone(singleValue, extStoreToClone) { this._type = extStoreToClone._type; this._position = extStoreToClone._position; this._overwriteBehaviour = extStoreToClone._overwriteBehaviour; let target = [ ...Object.values(ExtStoreType).map(text => Object({ "value": text, "ref": "type" })), ...Object.values(ExtStorePosition).map(text => Object({ "value": text, "ref": "pos" })), ...Object.values(OverwriteBehaviour).map(text => Object({ "value": text, "ref": "over" })) ] .find(compareObj => compareObj["value"] === singleValue); if (target) { switch (target["ref"]) { case "type": this._type = singleValue; break; case "pos": this._position = singleValue; break; case "over": this._overwriteBehaviour = singleValue; break; } } return this; } /** * * @returns {ExtStorage} this extStore (updated if rules were used, that don't work for functions) */ setupForFunctions() { if (this._type === ExtStoreType.INTERNALIZED_WITHIN) { console.log("Updated Functions extstore from INTERNALIZED_WITHIN to INDIVIDUALLY_BEFORE") this._type = ExtStoreType.INDIVIDUALLY_BEFORE; } return this; } /** * * @returns {ExtStorage} */ setupForGeneralStyling() { if (this === ExtStoreType.INTERNALIZED_WITHIN) { this._position = ExtStorePosition.WITHIN; this._aggregation = ESAggregation.INTERNALIZED; return this; } this._position = ExtStorePosition.DOC_HEAD; switch (this) { case ExtStoreType.INDIVIDUALLY_WITHIN: case ExtStoreType.INDIVIDUALLY_BEFORE: case ExtStoreType.INDIVIDUALLY_SEGMENT_BEGIN: case ExtStoreType.INDIVIDUALLY_DOC_FOOTER: case ExtStoreType.INDIVIDUALLY_DOC_HEAD: this._aggregation = ESAggregation.INDIVIDUALLY; break; case ExtStoreType.COLLECTED_BEFORE: case ExtStoreType.COLLECTED_SEGMENT_BEGIN: case ExtStoreType.COLLECTED_DOC_FOOTER: case ExtStoreType.COLLECTED_DOC_HEAD: this._aggregation = ESAggregation.COLLECTED; this._aggregation = ESAggregation.COLLECTED; break; case ExtStoreType.CENTRALIZED_DOC_HEAD: case ExtStoreType.CENTRALIED_SEGMENT_BEGIN: case ExtStoreType.CENTRALIZED_DOC_FOOTER: default: this._aggregation = ESAggregation.CENTRALIZED; break } return this; } /** * Currently it works the same as the "updateForFunctions()" since the same rules won't work. * @returns {ExtStorage} this extStore (updated if rules were used, that won't work for StyleClasses) */ setupForStyleClass() { /* const positionedAfter = [ COLLECTED_DOC_FOOTER, INDIVIDUALLY_DOC_FOOTER, CENTRALIZED_DOC_FOOTER ]; if (positionedAfter.includes(this._type)) { this._type = ExtStoreType.INTERNALIZED_WITHIN; } */ return this.setupForGeneralStyling(); } /** * * @returns {InsertPosition} */ getRelativePositioning() { switch (this._position) { case ExtStorePosition.BEFORE: return "beforebegin" case ExtStorePosition.SEGMENT_BEGIN: return "afterbegin"; case ExtStorePosition.DOC_HEAD: case ExtStorePosition.DOC_FOOTER: return "beforeend" case ExtStorePosition.WITHIN: default: return "afterbegin"; } } /** * Expects a reference element for the positions before and segment_begin. * Otherwise will return head, footer or element accordingly. * @param {HTMLLIElement|Component} element * @returns {HTMLElement} */ getRefElement(element = null) { let ensuredElement = element; if (!element) { console.log("ExtStorePosition defines a relative position, but no reference Element is given - using head!") return document.querySelector('head'); } if (element instanceof Component) { ensuredElement = element.generate().html; } switch (this._position) { case ExtStorePosition.BEFORE: case ExtStorePosition.SEGMENT_BEGIN: return ensuredElement.closest('[data-compel-isHCompel="true"]'); case ExtStorePosition.DOC_HEAD: return document.querySelector('head'); case ExtStorePosition.DOC_FOOTER: return document.querySelector('footer'); case ExtStorePosition.WITHIN: default: return ensuredElement; } } insertElementAccordingly(element) { this.getRefElement(element) .insertAdjacentElement( this.getRelativePositioning(), this.getRefElement(element) ) } /** * * @returns {function(SStoreDefinition,HTMLElement,number): boolean} */ getStylingDistribution() { switch (this._aggregation) { case ESAggregation.INDIVIDUALLY: return function (ssd, orgElement, counter) { let container = generateAndFillStyleTag([ssd]); container.setAttribute("data-compel-individually-nr", counter++); Page.addElementToPage(container, this); return false; } case ESAggregation.COLLECTED: return function (ssd, orgElement) { return true; } case ESAggregation.CENTRALIZED: return function (ssd, orgElement) { Page.registerStyling(ssd._identifier, ssd._definition); return false; } case ESAggregation.INTERNALIZED: default: return function (ssd, orgElement) { fillAttrsInContainerByCb( ssd._definition, orgElement, (key, val, el) => { el.style[key] = val; } ); return false; } } } /** * * @returns {function(SStoreDefinition, Map, number): boolean} */ getFunctionDistribution() { switch (this._aggregation) { case ESAggregation.INTERNALIZED: case ESAggregation.INDIVIDUALLY: return function (ssd, counter) { let container = document.createElement("script"); container.setAttribute("data-compel-individually-nr", counter++); container.innerText += getScriptTagInjectionText( clearFunctionDeclarationText(ssd._definition), ssd._identifier ); Page.addElementToPage(container, refESType); return false; } case ESAggregation.COLLECTED: return function () { return true; } case ESAggregation.CENTRALIZED: default: return function (ssd) { Page.registerPageFunction(ssd._identifier, ssd._definition); return false; } } } } /** * ExtStorage := Extensions storage (type) * Extensions in this context are stylings and scripts (currently only javascript). * internalized: the extensions are part of the element code/attributes - works obviously only with styling * individually: an individual tag is created/used * collected: the extension can/will be collected with others in a higher position of the element hierarchy * (but not document - root) * centralized: the extensions are send to the Page to be joined in a centralized tag/position of the document * (either head or footer tag) */ const ExtStoreType = Object.freeze({ INTERNALIZED_WITHIN: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.WITHIN), INDIVIDUALLY_WITHIN: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.WITHIN), INDIVIDUALLY_BEFORE: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.BEFORE), INDIVIDUALLY_SEGMENT_BEGIN: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.SEGMENT_BEGIN), INDIVIDUALLY_DOC_HEAD: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.DOC_HEAD), INDIVIDUALLY_DOC_FOOTER: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.DOC_FOOTER), COLLECTED_BEFORE: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.BEFORE), COLLECTED_SEGMENT_BEGIN: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.SEGMENT_BEGIN), COLLECTED_DOC_HEAD: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.DOC_HEAD), COLLECTED_DOC_FOOTER: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.DOC_FOOTER), CENTRALIZED_DOC_HEAD: new ExtStorage(ESAggregation.CENTRALIZED, ExtStorePosition.DOC_HEAD), CENTRALIZED_SEGMENT_BEGIN: new ExtStorage(ESAggregation.CENTRALIZED, ExtStorePosition.SEGMENT_BEGIN), CENTRALIZED_DOC_FOOTER: new ExtStorage(ESAggregation.CENTRALIZED, ExtStorePosition.DOC_FOOTER) }); /** * Style or Script Store Definition * @property {string} _identifier; * @property {any} _definition; * @property {any} _additionaly; * @property {ExtStorage} _extStore; */ class SStoreDefinition { constructor(identifier, definition, extStore = null, additions = null) { /** * Usually the name or the selector * @type {string} _identifier; */ this._identifier = identifier; /** * the values * @type {any} _definition; */ this._definition = definition; /** * additional values, if needed. E.g. funciton args * @type {any} _additionaly; */ this._additions = additions; /** * The corresponding extStore * @type {ExtStorage} _extStore; */ this._extStore = extStore; } } /** * Resolves an overwrite case for a map/object. * @param {string} key * @param {Object} container * @param {OverwriteBehaviour} overwriteBehaviour * @returns {string} the key to be used */ function resolveOverwrite(key, container, overwriteBehaviour) { let occurances = Object.keys(container) .filter(e => e.includes(key)) .length; switch (overwriteBehaviour) { case OverwriteBehaviour.REPLACE: break; case OverwriteBehaviour.RENAME_OLD: nameForOld = `${key}${occurances}`; container[nameForOld] = container[key]; delete container[key]; break; case OverwriteBehaviour.RENAME: default: key = `${key}${occurances}`; break; } return key; } /** * Will resolve the compareKey according to the overwriteBehaviour * and add the newValue to the targetContainer with it. * @param {Object} targetContainer * @param {string} compareKey * @param {Object} newValue * @param {OverwriteBehaviour} overwriteBehaviour * @returns {string} the "resolved" compareKey */ function identifyAndResolveOverwrite(targetContainer, compareKey, newValue, overwriteBehaviour) { let keys = Object.keys(targetContainer); if (keys.includes(compareKey)) { if (overwriteBehaviour === OverwriteBehaviour.DROP_NEW) { console.log("Not Adding, because overwrite is set to DROP_NEW"); return compareKey; } compareKey = resolveOverwrite(compareKey, targetContainer, overwriteBehaviour); } targetContainer[compareKey] = newValue; return compareKey; } /** * * @param {Array} ssdArray * @returns {HTMLScriptElement} */ function generateAndFillScriptTag(ssdArray) { let tag = document.createElement("script"); tag.setAttribute("data-compel-gen", "true"); for (let i = 0; i < ssdArray.length; i++) { const ssd = ssdArray[i]; tag.innerText += getScriptTagInjectionText( clearFunctionDeclarationText(ssd._definition), ssd._identifier ); } return tag; } /** * * @param {string} selector * @param {Map} stylingMap * @returns {string} */ function getStylingInjectionText(selector, stylingMap) { function keyValueToString(key) { return `${key}: ${stylingMap[key]}; `; } return `${selector } { ${Object.keys(stylingMap) .map(keyValueToString) .join(" ") } }; `; } /** * * @param {Array} ssdArray * @returns {HTMLStyleElement} */ function generateAndFillStyleTag(ssdArray) { let tag = document.createElement("style"); tag.setAttribute("data-compel-gen", "true"); for (let i = 0; i < ssdArray.length; i++) { const ssd = ssdArray[i]; tag.innerText += getStylingInjectionText(ssd._identifier, ssd._definition); } return tag; } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Iterates over the keys of attrs, * extracts the corresponding value * and applies the callback (cb) on it in the order (key, value, targetContainer) * @extends StyleAndScriptStoringComponent * @param {map} attrs * @param {Object} intoContainer * @param {Function} cb * @returns {Object} the filled container */ function fillAttrsInContainerByCb(attrs, intoContainer, cb) { let keys = Object.keys(attrs); for (let i = 0; i < keys.length; i++) { cb(keys[i], attrs[keys[i]], intoContainer); } return intoContainer; } /** * */ class ObjectAccessObject { /** * * @param {Object} object */ constructor(object = null) { /** * @type {Array} */ this.keys = (object ? Object.keys(object) : []); /** * @type {Object} */ this.objects = (object ? object : {}); } /** * * @param {Array} keyArr * @param {Object} refObject * @returns */ fillByArrayReference(keyArr, refObject) { this.keys = keyArr; refObject = keyArr.reduce((a, c) => Object.assign(a, { [c]: refObject[c] }), {}); return this; } /** * * @param {string} key * @param {*} value * @returns {ObjectAccessObject} */ add(key, value) { this.objects[key, value]; this.keys.push(key); return this; } /** * * @param {string} key * @returns {*} */ remove(key) { let tmp = this.objects[key]; delete this.objects[key]; return tmp; } } function toggleElementVisibility(element, ensureHidden = false) { element.classList.toggle("compel-mech-hidden"); let isNowHidden = false; if (element.hasAttribute("hidden")) { element.removeAttribute("hidden"); element.style["display"] = "flex"; isNowHidden = false; } else { element.setAttribute("hidden", "hidden"); element.style.removeProperty("display"); isNowHidden = true; } if (ensureHidden && !isNowHidden) { return toggleSelectorElementVisibility(selector) } else { return isNowHidden; } } /** * * @param {string} selector * @returns {boolean} for true element is now hidden and false it is not hidden. */ function toggleSelectorElementVisibility(selector, ensureHidden = false) { /** * @type {HTMLElement} */ let el = document.querySelector(selector); let name = el.getAttribute("data-autocompel"); console.log("De-/hiding", name, selector); return toggleElementVisibility(el, ensureHidden); } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * A chained class that sets most of the stylings of an element * Attributes: * _modifications: {Object} */ class Modifier { /** * @type {Map} _modifications */ _modifications; /** * @type {Array} */ _removeMods; /** * @type {Shape} */ _shape; /** * @type {Sides} paddingValues */ _paddingValues; constructor() { this._modifications = new Object(); this._removeMods = []; } /** * Sets the modifications for widht and height to 100%. * @returns {Modifier} this modifier object */ fillMaxSize(widthFraction = 1, heightFraction = 1) { return this.fillMaxWidth(widthFraction) .fillMaxHeight(heightFraction); } /** * Sets the modification for width to the given fraction of 1 (default 1 := 100%). * @param {number} fraction * @returns {Modifier} this modifier object */ fillMaxWidth(fraction = 1) { this._modifications["width"] = (100 * fraction) + "%"; this._modifications["max-width"] = (100 * fraction) + "%"; return this; } /** * Sets the modification for height to the given fraction of 1 (default 1 := 100%). * @param {number} fraction * @returns {Modifier} this modifier object */ fillMaxHeight(fraction = 1) { this._modifications["height"] = (100 * fraction) + "%"; this._modifications["max-height"] = (100 * fraction) + "%"; return this; } /** * * @param {string} keyWord weither 'height' or 'width' that will be adjusted and looked for * @param {Sides} parentalPadding */ _updateDimensionsBy(parentalPadding) { function updateDirection(keyWord, modifications, parentalAdjustment) { let refKeys = Object.keys(modifications) .filter(k => k.includes(keyWord)); if (refKeys.length > 0) { for (let i = 0; i < refKeys.length; i++) { let key = refKeys[i]; let value = modifications[key]; if (key.includes("calc")) { console.log( `Modifier._updateByParent... ${keyWord } - unexpected value '${value }' for '${key }', skipping...` ); } else { let newValue = `calc(${value} - ${parentalAdjustment});`; modifications[key] = newValue.trim(); } } } return modifications; } if (parentalPadding) { let pval = parentalPadding.getValues(); if (pval["horizontal"] > 0) { this._modifications = updateDirection("width", this._modifications, pval["horizontal"] + parentalPadding._unit); } if (pval["vertical"] > 0) { this._modifications = updateDirection("height", this._modifications, pval["vertical"] + parentalPadding._unit); } } return this; } /** * Sets modifications according to the dimensions object. * @param {Dimensions} dimensions * @returns {Modifier} this modifier object */ dimensions(dimensions) { dimensions.toModifications() .forEach(kvpair => { this._modifications[kvpair.key] = kvpair.value; }) return this; } /** * Sets the padding on all sides according to the given padding object. * Currently the padding will always be set * to the most recent padding/padding. * @param {Sides} siding * @returns {Modifier} this modifier object */ padding(siding) { this._paddingValues = siding; let keyToAdd = ""; if (siding instanceof PaddingChain) { /* TODO what is this supposed to do */ } else if (siding instanceof Sides) { keyToAdd = "padding-" } siding.toModifications() .forEach(kvpair => { this._modifications[keyToAdd + kvpair.key] = kvpair.value; }) return this; } /** * Sets the margin on all sides according to the given siding object. * Currently the margin will always be set * to the most recent margin/siding. * @param {Sides} siding * @returns {Modifier} this modifier object */ margin(siding) { let keyToAdd = ""; if (siding instanceof Sides) { keyToAdd = "margin-" } siding.toModifications() .forEach(kvpair => { this._modifications[keyToAdd + kvpair.key] = kvpair.value; }); return this; } /** * Sets the background-color as a rgb color. * If no color is given/specified the styling will be set to "inherit" * and use the color setting from (one of) the parent. * @param {Color} color * @returns {Modifier} this modifier object */ background(color) { this._modifications["background-color"] = ( color ? color.cssRGBString() : "inherit" ); return this; } /** * Sets the color as a rgb color. * If no color is given/specified the styling will be set to "inherit" * and use the color setting from (one of) the parent. * @param {Color} color * @returns {Modifier} this modifier object */ color(color) { this._modifications["color"] = ( color ? color.cssRGBString() : "inherit" ); return this; } /** * Adds the modifications of the given Modifier to current Modifier. * This is especailly used in the cases of extending existing/pre defined * Components. * * CAUTION matching existing modifications will be overwritten. * * @todo finish second parameter "modifications" - logic * * @param modifier The "new" Modifier * @returns {Modifier} The "old/current" Modifier, * extended with the modifications of the given Modifier. */ join(modifier, modifications = {}) { let keys = Object.keys(modifier.ensureModifier()._modifications); for (let i = 0; i < keys.length; i++) { /* if (!this._modifications.hasOwnProperty(keys[i])) */ this._modifications[keys[i]] = modifier.ensureModifier()._modifications[keys[i]]; } let removeMods = modifier.ensureModifier()._removeMods; if (removeMods.length > 0) { for (let i = 0; i < removeMods.length; i++) { delete this._modifications[removeMods[i]]; } } if (modifier._paddingValues) { this._paddingValues = modifier._paddingValues; } return this; } /** * * @param {string} key a css style rule * @param {string} value the corresponding value to the css style rule * @returns {Modifier} this modifier object */ setStyleRule(key, value) { this._modifications[key] = value; return this; } /** * * @param {StylePropertyMap} rulemap * @returns {Modifier} */ addStyleRuleMap(rulemap) { for (const ruleKey of Object.keys(rulemap)) { this._modifications[ruleKey] = rulemap[ruleKey]; } return this; } /** * * @param {string} key * @returns {Modifier} this modifier object */ removeStyleRule(key) { this._removeMods.push(key); if (Object.keys(this._modifications).includes(key)) { delete this._modifications[key]; } return this; } /** * Sets a border line (with given linestyle) to all sides. * If lineStyle is an array, the containing LineStyles, * are applied in the order: [top, right, bottom, left]. * If the border has a shape defined, * this shape will override earlier shape definitions. * Otherwise existing shape definitions will be applied. * @param {Border} border the style of the border line * @returns {Modifier} this modifier object */ border(border) { if (border._shape) { this.clip(border._shape); } else if (this._shape) { border._shape = this._shape; } border.toModifications() .forEach(e => this._modifications[e.key] = e.value); return this; } /** * * @param {Shape} shape * @returns {Modifier} */ clip(shape) { this._shape = shape; this._modifications["border-radius"] = shape.getOrderedValues().join(' '); return this; } /** * * @param {number} size of width and height in pixels * @returns {DimensionsChain} */ linkDimensions(size = -1) { if (size === -1) { return new DimensionsChain(this); } else { return new DimensionsChain(this).all(size).ensureModifier() } } /** * * @param {number} amount the padding for all four sides * @returns {PaddingChain} */ linkPadding(amount = -1) { if (amount === -1) { return new PaddingChain(this); } else { return new PaddingChain(this).all(amount); } } /** * * @param {number} cornerRadius will create a rounded rectangle with the given cornerRadius * @returns {ShapeChain} */ linkClip(cornerRadius = -1) { if (cornerRadius === -1) { return new ShapeChain(this); } else { return new ShapeChain(this).all(cornerRadius); } } /** * * @param {number} borderWidth sets the width of all four border sides * @returns {BorderChain} */ linkBorder(borderWidth = -1) { if (borderWidth === -1) { return new BorderChain(this); } else { return new BorderChain(this).width(borderWidth); } } /** * * @returns {Modifier} */ ensureModifier() { return this; } } /** * @extends Modifier * @inheritdoc */ class ChainableModifier extends Modifier { /** * @type {Component} */ _component; /** * * @param {Component} component */ constructor(component) { super(); this._component = component; } /** * * @returns {Component} */ toComponent() { return this._component.modifier(this); } /** * * @param {Component|Array} innerComponent * @returns {Component} the parent Component */ childContext(innerComponent) { return this._component .modifier(this) .childContext(innerComponent); } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Represents the most basic and simple form of a Component. * It is mainly a collection of wrapper methods * around the HTMLElement methods to make them chainable. * It serves as base for further functionallity extensions. * @abstract */ class ElementWrapper { /** * The basic HTMLElement the Component is wrapped around. * It will be modified in several ways and in the end returned. * @type {HTMLElement} */ _element; /** * The auto-generated name of the component. * @type {string} */ _compName; /** * The auto-generated name of the component. * @type {string} */ _givenName; /** * Initializes the component * @param {HTMLElement} element the base element * @param {map} attr Specific already known attributes */ constructor(element, attr = {}) { fillAttrsInContainerByCb( attr, element, function cb(k, v, con) { con.setAttribute(k, v); } ); this._compName = Page.autoRegisterComponent(); element.setAttribute('data-autocompel', this._compName); this._element = element; this.addStyleClass(this._compName); } setComponentName(name) { this._givenName = name; this.setAttribute('data-compel', name); Page.registerComponent(name); return this; } /** * (Wrapper) Sets the innerText of the element * @param {string} text * @returns {Component} this component object */ text(text) { if (this._element instanceof HTMLInputElement && this._element.type === "text") { this._element.value = text; } else { this._element.innerText = text; } return this; } /** * Wrapper to set the HTMLElement.title attribute * @param {string} text * @returns {Component} */ title(text) { this._element.title = text; return this; } /** * Wrapper for HTMLElement.classList.add() * @param {string} styleClass * @returns {Component} this component object */ addStyleClass(styleClass) { this._element.classList.add(styleClass); return this; } /** * Wrapper for the HTMLElement.setAttribute() method. * @param {string} key * @param {string} value * @returns {Component} this component object */ setAttribute(key, value) { this._element.setAttribute(key, value); return this; } /** * Wrapper for the HTMLElement.addEventListener() * @param {keyof WindowEventMap|CommonEvents} theEvent * @param {Function} theListener * @param {boolean|AddEventListenerOptions} options * @returns {Component} this component object */ addEventListener(theEvent, theListener, options = null) { this._element.addEventListener(theEvent, theListener, options) return this; } } /** * @inheritdoc * @extends ElementWrapper * @abstract */ class ChildbearerComponent extends ElementWrapper { /** * @type {Array} children */ _children; /** * @type {Alignment} alignment */ _alignment; /** * @type {Arrangement} arrangement */ _arrangement; constructor(element, attr = {}) { super(element, attr); this._children = []; } /** * @todo: Unify logic extract modifications into responsible construct * @todo: Make it work as expected, fix docu * @todo: Differentiate between directions (horizontal, vertiacl) * * Sets the alignment (modifications) for this element or more specific for its children. * @param {Alignment} alignment * @returns {Component} this component object */ alignment(alignment) { /* this._modifier._modifications["display"] = "flex"; this._modifier._modifications["align-content"] = alignment; this._modifier._modifications["align-items"] = alignment; this._modifier._modifications["text-align"] = alignment; */ this._alignment = alignment; return this; } /** * @todo: Unify logic extract modifications into responsible construct * @todo: Differentiate between directions (horizontal, vertical) * @todo: Make it work as expected, fix docu * * Sets the arrangement (modifications) for this element or more specific for its children. * @param {Arrangement} arrangement * @returns {Component} this component object */ arrangement(arrangement) { /* this._modifier._modifications["justify-content"] = arrangement; */ this._arrangement = arrangement; return this; } /** * Opens a context to create children elements. * Either as one component or a list/array of components. * @param {Component|Array} component * @returns {Component} this component object */ childContext(component) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.childContext(arguments[i]); } } else if (component instanceof Array) { for (let i = 0; i < component.length; i++) { this.childContext(component[i]); } } else { this._children.push(component); } return this; } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * @inheritdoc * @extends ChildbearerComponent * @abstract */ class ModifiableComponent extends ChildbearerComponent { /** * @type {Modifier} modifier */ _modifier; constructor(element, attr = {}) { super(element, attr); this._modifier = new Modifier(); } /** * Sets, updates or overwrites the Modifier-Object for this component * @param {Modifier} modifier * @param {boolean|ExtStorage|ExtStoreType|OverwriteBehaviour} [extStore=null] * @returns {Component} this component object */ modifier(modifier, underTheName = "", extStore = false) { if (underTheName === "") { underTheName = `.${this._compName}-style-${this._styles.length}`; } if (!extStore) { this._modifier = this._modifier .join(modifier.ensureModifier()); } else { this.addStyleClass(underTheName); this._styles.push( new SStoreDefinition( underTheName, modifier.ensureModifier() ) ); } return this; } /** * Returns a Modifier to chain modifications * instead of setting them within an sepperate context. * @returns {ChainableModifier} */ chainModifier() { return new ChainableModifier(this); } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * @inheritdoc * @abstract * @extends ModifiableComponent */ class StyleAndScriptStoringComponent extends ModifiableComponent { /** * @type {ExtStorage} */ _styleClassesExtStore /** * @type {ExtStorage} */ _stylesExtStore; /** * @type {Array} */ _styles; /** * @type {ExtStorage} */ _functionsExtStore; /** * @type {Array} */ _functions; constructor(element, attr = {}) { super(element, attr); this._styleClassesExtStore = ExtStoreType.CENTRALIZED_DOC_HEAD .setOverwriteBehaviour(OverwriteBehaviour.REPLACE); this._stylesExtStore = ExtStoreType.INTERNALIZED_WITHIN .setOverwriteBehaviour(OverwriteBehaviour.REPLACE); this._styles = []; this._functionsExtStore = ExtStoreType.CENTRALIZED_DOC_HEAD .setOverwriteBehaviour(OverwriteBehaviour.REPLACE); this._functions = []; } /** * @todo: Unify logic extract modifications into responsible construct * @todo: Make it work as expected, fix docu * @todo: Differentiate between directions (horizontal, vertiacl) * @override * @inheritdoc * Sets the alignment (modifications) for this element or more specific for its children. * @param {Alignment} alignment * @returns {Component} this component object */ alignment(alignment) { /* this._modifier._modifications["display"] = "flex"; this._modifier._modifications["align-content"] = alignment; this._modifier._modifications["align-items"] = alignment; this._modifier._modifications["text-align"] = alignment; */ this._alignment = alignment; this._modifier._modifications["align-content"] = this._alignment; this._modifier._modifications["align-items"] = this._alignment; this._modifier._modifications["text-align"] = this._alignment; return this; } /** * @todo: Unify logic extract modifications into responsible construct * @todo: Differentiate between directions (horizontal, vertical) * @todo: Make it work as expected, fix docu * @override * @inheritdoc * Sets the arrangement (modifications) for this element or more specific for its children. * @param {Arrangement} arrangement * @returns {Component} this component object */ arrangement(arrangement) { /* this._modifier._modifications["justify-content"] = arrangement; */ this._arrangement = arrangement; this._modifier._modifications["justify-content"] = this._arrangement; return this; } /** * Defines/Sets the general "storage-behaviour" for styling of this component. * Further for potential css definitions. * If a styling/modifier/class is passed via the corresponding methods/way, * an alternative behaviour can be passed as a parameter - which will only be applied on/for that single one. * * @param {ExtStorage|ExtStoreType|OverwriteBehaviour} extStore * @returns {Component} */ setStylingsStorage(extStore) { if (extStore) { if (extStore instanceof ExtStorage) { this._stylesExtStore = extStore; } else if (extStore instanceof ExtStoreType) { this._stylesExtStore.setExtStoreType(extStore); } else { this._stylesExtStore.OverwriteBehaviour(extStore); } } else { console.log("(Style)ExtStore was empty, did nothing"); } return this; } /** * Defines/Sets the general "storage-behaviour" for functions of this component. * If a function is passed via "registerFunction", * an alternative behaviour can be passed as a parameter - which will only be applied on/for that single one. * * @param {ExtStorage|ExtStoreType|OverwriteBehaviour} extStore * @returns {Component} */ setFunctionsStorage(extStore) { if (extStore) { if (extStore instanceof ExtStorage) { this._stylesExtStore = extStore; } else if (extStore instanceof ExtStoreType) { this._stylesExtStore.setExtStoreType(extStore); } else { this._stylesExtStore.OverwriteBehaviour(extStore); } } else { console.log("(Function)ExtStore was empty, did nothing"); } return this; } /** * * @param {Function} func * @param {string} underTheName * @param {ExtStorage|ExtStoreType|ExtStorePosition|OverwriteBehaviour|EXPosConfer|ESOverwriteConfer} extStore * if a unique definition is desired, all constants or configurator objects are allowed - they will be processed accordingly * @returns */ registerFunction(func, underTheName = "", extStore = null) { let registrationName = [ underTheName.trim(), func.name.trim(), `func${this._compName}${Object.keys(this._functions).length}` ] .find(e => e !== ''); /* if (!extStore) { extStore = this._functionsExtStore; } else if (extStore instanceof ExtStoreConfer) { extStore = extStore.ensureStore(); } else { extStore = new ExtStorage().setSingleValueToClone(extStore, this._functionsExtStore); } */ let entry = new SStoreDefinition(); entry._identifier = registrationName; entry._definition = func; entry._extStore = extStore; this._functions.push(entry); //identifyAndResolveOverwrite(this._functions, registrationName, entry, extStore._overwriteBehaviour); return this; } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Represents the most basic and simple form of a Component. * It is mainly a collection of wrapper methods * around the HTMLElement methods to make them chainable. * It serves as base for further functionallity extensions. * @extends StyleAndScriptStoringComponent * @inheritdoc * */ class Component extends StyleAndScriptStoringComponent { /** * @type {boolean} */ #isCompel; /** * @type {WebTrinity} */ _wenity; /** * @type {Array} */ _toRegister; /** * @type {boolean} */ _isContextMenu; /** * Initializes the component * @param {HTMLElement} element the base element * @param {Map} attr Specific already known attributes */ constructor(element, attr = {}) { super(element, attr); this.#isCompel = false; this._isContextMenu = false; this._modifier = new Modifier() .margin(new Sides().all(0)); this._modifier._modifications['display'] = "flex"; this._modifier._modifications["box-sizing"] = "border-box"; this._toRegister = []; } /** * Adds a class to classList via HTMLElement.classList.add() method. * Further collects rules in a property until generate is called. * * @CAUGHTION implementation is not safe to use, ignoring extStore is recommended; * * @todo difference between stylings and classes, extStore logic in combination with the Page.register... logic * * @override * * @param {string} styleClass (without the '.' in the front) * @param {string|Modifier|map} styling * @param {ExtStorage|ExtStoreType|ExtStorePosition|OverwriteBehaviour|EXPosConfer|ESOverwriteConfer} extStore * if a unique definition is desired, all constants or configurator objects are allowed - they will be processed accordingly * @returns {Component} this component object */ addStyleClass(styleClass, styling = null, extStore = null) { if (!extStore) { extStore = this._styleClassesExtStore; } else if (extStore.isMissing()) { extStore = extStore.fillBy(this._styleClassesExtStore); } if (styling) { if (styling instanceof Modifier) { styling = styling._modifications; } Page.registerStyling('.' + styleClass, styling); } this._element.classList.add(styleClass); return this; } /** * * @param {boolean} vertical Defines if the Component should overflow vertically (default: true) * @param {boolean} horizontal Defines if the Component should overflow horizontally (default: false) * @returns {Component} */ overflow(vertical = true, horizontal = false) { if (vertical) { this.subscribeOnGenerate(CommonCompelGroups.OVERFLOWING); this._modifier._modifications["overflow-y"] = "auto"; } if (horizontal) { this.subscribeOnGenerate(CommonCompelGroups.OVERFLOWING); this._modifier._modifications["overflow-x"] = "auto"; } return this; } /** * * @param {boolean} untilFound * @returns {Component} */ hidden(untilFound = false) { Page.registerStyling(".compel-mech-hidden", { "hidden": "hidden" }); this._modifier.removeStyleRule("display"); this.setAttribute( "hidden", (untilFound ? "until-found" : "hidden") ); this.subscribeOnGenerate(CommonCompelGroups.HIDDEN_ON_START); return this; } /** * * @returns {Component} */ isHigherComponent() { this.subscribeOnGenerate(CommonCompelGroups.HIGHER_COMPEL); this.#isCompel = true; return this.setAttribute("data-compel-isHCompel", "true") } /** * Collects the given List in the _toRegister attribute. * When generate() is called, * the created Element will be registered (added) in every list * within the list. * @param {*|string|Array<*>} listName */ subscribeOnGenerate(listName) { this._toRegister.push(listName); return this; } /** * * @returns {Component} */ registerAsContextMenu() { this.subscribeOnGenerate(CommonCompelGroups.IS_CONTEXT_MENU); this._isContextMenu = true; return this.addStyleClass('contextmenu') .hidden(); } /** * @todo Positioning of the contextmenu element * @todo extract into an extra function(allity) provider * * @param {Component} component * @param {Sides|Function => Sides} getRefPos * @param {ExtStorage} [extStore=null] * @returns {Component} */ contextMenu(component, getRefPos = null, extStore = null) { if (!component._isContextMenu) { component.registerAsContextMenu(); } if (!getRefPos) { getRefPos = function (cmEvent) { return new Sides() .left(cmEvent.pageX) .top(cmEvent.pageY); } } this.subscribeOnGenerate(CommonCompelGroups.HAS_CONTEXT_MENU); let identifier = component._compName; this.addEventListener( "contextmenu", DefaultContextMenu.openContextMenuAction(identifier, getRefPos) ); return this.childContext(component); } /** * * @param {DragAndDropImplementation} dadImpl * @returns {Component} */ draggable(dadImpl = new DragAndDropImplementation()) { this.subscribeOnGenerate(CommonCompelGroups.DRAGGABLE); this.subscribeOnGenerate(CommonCompelGroups.HAS_DRAG_EVENT); let addedClass = "comp-el-mech-draggable"; let selector = this._element.getAttribute("data-autocompel"); selector = `.${addedClass}[data-autocompel="${selector}"]`; return this.addStyleClass(addedClass) .setAttribute("draggable", "true") .setAttribute("dropEffect", "none") .addEventListener( CommonEvents.DRAG_START, dadImpl.dragStartAction("text/plain", selector) ); } /** * * @param {EventDrag} dragEvent * @param {Function} action */ onDrag(dragEvent, action = (e) => { e.preventDefault(); }) { this.subscribeOnGenerate(CommonCompelGroups.HAS_DRAG_EVENT); let selector = `comp-el-mech-drag${dragEvent}`; return this.addEventListener( 'drag' + dragEvent, action ); } /** * * @param {DragAndDropImplementation} dadImpl * @returns {Component} */ dropTarget(dadImpl = new DragAndDropImplementation()) { this.subscribeOnGenerate(CommonCompelGroups.DROP_TARGET); let specialClass = "comp-el-mech-droptarget"; this.addStyleClass(specialClass) .onDrag(EventDrag.OVER); let selector = `.${specialClass}[data-autocompel="${this._compName}"]` this._element.addEventListener( "drop", dadImpl.dropEventAction("text/plain", selector) ); return this; } /** * An echo of Scope-Functions from kotlin for convenience * * Executes a given function injects this component into the function. * @param {Function} func * @returns {Component} */ apply(func) { func(this); return this; } /** * An echo of Scope-Functions from kotlin for convenience * * Executes a given function injects the htmlelement of this component into the function. * @param {Function} func * @returns {Component} */ applyToEl(func) { func(this._element) return this; } /** * Defines how a child Component is to be appended. * @param {Component} component the child component to add it. * @returns {HTMLElement} */ _appendChildComponent(component) { let child = new WebTrinity(); if (component instanceof Component) { child = component.generate(); } if (component instanceof WebTrinity) { child = component; } if (component instanceof HTMLElement) { console.log("No wenity set - htmlEl was given"); child.html = component; } this._element.append(child.html); return child; } /** * * @param {ExtStorage} extStore * @returns {Array} */ _processStyles(extStore = null) { extStore = (extStore ? extStore : this._stylesExtStore ) .setupForGeneralStyling(); let forCollection = []; let counter = 0; for (const ssd of this._styles) { /* Make sure that the type is unified for later processing */ if (ssd._definition instanceof Modifier) { ssd._definition = ssd._definition._modifications; } /* Check/Ensure proper ExtStorageType for following comparison */ /** * @type {ExtStorage} */ let curExtStore = extStore; if (Object.hasOwn(ssd, "_extStore") && ssd._extStore) { curExtStore = ssd._extStore.setupForGeneralStyling(); } if (curExtStore.getStylingDistribution()(ssd, this._element, counter)) { forCollection.push(ssd); } } return forCollection; } /** * * @param {ExtStorage} extStore * @returns {Array} */ _processFunctions(extStore = null) { extStore = (extStore ? extStore : this._functionsExtStore ) .setupForFunctions(); const forCollection = new Map(); const collectForBefore = []; let counter = 0; for (const ssd of this._functions) { /* Make sure that the type is unified for later processing */ let curExtStore = extStore; if (Object.hasOwn(ssd, "_extStore") && ssd._extStore) { curExtStore = ssd._extStore.setupForFunctions(); } if (curExtStore.getFunctionDistribution()(ssd, counter)) { if (curExtStore._position.BEFORE) { collectForBefore.push(ssd); } else { if (!forCollection.has(curExtStore)) { forCollection.set(curExtStore, []); } forCollection.get(curExtStore).push(ssd); } } } return forCollection; } /** * Ends chain. * Applies all modifications on the element. * Processes alls stored additions. * Returns the constructed HTMLElement of this Component. * * * * @param {ExtStorage} * @returns {WebTrinity} the constructed HTMLElement of this Component. */ generate(styleStore = null, functionStore = null) { this._wenity = new WebTrinity(); if (!styleStore) { styleStore = this._stylesExtStore; } /* DEAL WITH COMPONENT MODIFICATION FIRST */ // @todo pay attention to the "overwrite" behaviour - the local modifier styles are the "closest" // it might be appropriate to use this._styles.unshift(...) instead. this._styles.push(new SStoreDefinition( (styleStore._aggregation !== ESAggregation.INTERNALIZED ? "." : "") + this._compName, this._modifier, this._stylesExtStore )); /* DEAL WITH CHILDREN */ let collectedWenities = []; for (let i = 0; i < this._children.length; i++) { /** * @type {Component} */ let child = this._children[i]; if (child instanceof ChainableModifier) { child = child.toComponent(); } if (Page._useCssCalc) { child._modifier._updateDimensionsBy(this._modifier._paddingValues); } child = child.generate(); let wenity = this._appendChildComponent(child); if (!wenity.isSSEmpty()) { collectedWenities.push(wenity); } } /* DEAL WITH STYLING AND PROCESSING */ /** * @type {Array} */ let styleCollection = this._processStyles(styleStore); /** * @type {Map>} */ const funcCollections = this._processFunctions(functionStore); /** * * @param {Map>} source * @param {Map>} target * @param {ExtStoreType} extStoreType * @returns */ function transferCollectedFunctions(source, target, extStoreType) { if (source) { if (source.has(extStoreType)) { if (funcCollections.has(extStoreType)) { target.get(extStoreType) .push(source.get(extStoreType)) } else { target.set( extStoreType, source.get(extStoreType) ); } } } return target; } function executeOnExtStoreTypeCollectedTriple(func) { return new Map([ { [ExtStoreType.COLLECTED_SEGMENT_BEGIN]: func(ExtStoreType.COLLECTED_SEGMENT_BEGIN) }, { [ExtStoreType.COLLECTED_DOC_HEAD]: func(ExtStoreType.COLLECTED_DOC_HEAD) }, { [ExtStoreType.COLLECTED_DOC_FOOTER]: func(ExtStoreType.COLLECTED_DOC_FOOTER) } ]); } for (let i = 0; i < collectedWenities.length; i++) { const child = collectedWenities[i]; if (child.js) { executeOnExtStoreTypeCollectedTriple( (extstoretype) => transferCollectedFunctions(child.js, funcCollections, extstoretype) ); } } if (this.#isCompel) { function dealCollected(map, extStoreType) { if (map.has(extStoreType)) { let collectionScriptTag = generateAndFillScriptTag(map.get(extStoreType)); if (extStoreType === ExtStoreType.COLLECTED_SEGMENT_BEGIN) { this._element.insertAdjacentElement( "afterbegin", generateAndFillScriptTag(segment) ); } else { Page.addElementToPage( collectionScriptTag, extStoreType ); } } } executeOnExtStoreTypeCollectedTriple((est) => dealCollected(funcCollections, est)); } else { this._wenity.js = funcCollections; this._wenity.css = styleCollection; } this._wenity.html = this._element; for (let i = 0; i < this._toRegister.length; i++) { const group = this._toRegister[i]; Page.subscribeComponentToGroup(group, this._compName) } return this._wenity; } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Represents a Component (of an HTMLElement) that is capable of receiving input. * @extends Component * @inheritdoc */ class InputComponent extends Component { /** * * @param {string} element * @param {Attr} attr * @param {Modifier} modifier */ constructor(element, attr = {}, modifier = null) { super(element, attr); this.addStyleClass("el-input-comp"); if (modifier) { this.modifier(modifier); } } /** * The parameter makes it optional to trigger the state by a variable * @param {boolean} readonly * @returns {Component} */ readonly(readonly = true) { if (readonly) { this._element.setAttribute("readonly", readonly); } return this; } } /** * Represents container Components. * Some predefined modifications are applied on the child components. * @extends Component * @inheritdoc */ class FlexContainerComponent extends Component { /** * * @param {Attr} attr * @param {Modifier} modifier */ constructor(attr = {}, modifier = null) { super(document.createElement("div"), attr); this.addStyleClass("flex-container-component"); if (modifier) { this.modifier(modifier); } } /** * * @param {Component|Array} innerComponent * @returns {FlexContainerComponent} this component object */ childContext(innerComponent) { if (innerComponent instanceof Array) { innerComponent .map(cl => { if (cl instanceof Component) { return cl } else { return cl.ensureModifier().toComponent() } }) .forEach(icomp => { icomp._modifier = new Modifier() .setStyleRule("flex", "none") .join(icomp._modifier) }) } return super.childContext(innerComponent); } /** * * @returns {FlexContainerComponent} */ distibuteSpacingEvenly() { console.log("Doing nothing Flexcontainer doesn't know how to distribute spacing evenly - ask the children (Row, Column)"); return this; } } /** * A FlexContainerComponent, which organizes the children in a column like manner. * @extends FlexContainerComponent * @inheritdoc */ class Column extends FlexContainerComponent { /** * * @param {Attr} attr * @param {Modifier} modifier */ constructor(attr = {}, modifier = null) { super(attr, modifier); this.addStyleClass("column-component"); this.modifier( new Modifier() .setStyleRule("flex-direction", "column") ); } /** * @todo - adapt to extStore logic * @override * @returns {Column} */ distibuteSpacingEvenly() { this._element.children.forEach(child => { child.style["height"] = ((100 - this._element.childElementCount) / innerComponent.length); }) return this; } } /** * A FlexContainerComponent, which organizes the children in a row like manner. * * @extends FlexContainerComponent * @inheritdoc */ class Row extends FlexContainerComponent { /** * * @param {Attr} attr * @param {Modifier} modifier */ constructor(attr = {}, modifier = null) { super(attr); this.addStyleClass("row-component") if (modifier) { this.modifier(modifier); } this.modifier( new Modifier() .fillMaxWidth() .setStyleRule("flex-direction", "row") ) } /** * * @param {Component|Array} innerComponent * @returns {Row} */ childContext(innerComponent) { if (innerComponent instanceof Array) { innerComponent .map(cl => (cl instanceof Component ? cl : cl.ensureModifier().toComponent())) .forEach((icomp, i) => { /* sets the width for all elements, to avoid overlapping or line break because of lacking width, a percent is subtracted for every child element */ /* To enable "override" a new Modifier is generated and joined with the modifier of the component */ icomp._modifier = new Modifier() .setStyleRule("float", (i === 0 ? "left" : "right")) .join(icomp._modifier) }) } return super.childContext(innerComponent) } /** * @todo - adapt to extStore logic * @override * @returns {Row} */ distibuteSpacingEvenly() { this._element.children.forEach(child => { child.style["width"] = ((100 - this._element.childElementCount) / innerComponent.length); }) return this; } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Method Collection with predefined HTMLElements */ const builder = { components: { parent: {}, current: {}, previous: {}, next: {}, openedChain: {} }, /** * @type {Object} */ extensions: {}, /** * * @param {string} htmlText * @param {Modifier} modifier * @returns {Component} */ componentFromHTML: function (htmlText, modifier = null) { /** * @type {Component} */ let compel = new Component(new DOMParser().parseFromString(htmlText, "text/html")); if (modifier) { return compel.modifier(modifier); } return compel; }, /** * * @param {HTMLElement} element * @param {Modifier} modifier * @returns {Component} */ componentFromHTMLElement: function (element, modifier = null) { let newCompel = new Component(element); if (modifier) { return newCompel.modifier(modifier); } return newCompel; }, /** * * @param {string} tag * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ genTag: function (tag, attr = {}, modifier = null) { let compel = new Component(document.createElement(tag), attr).addStyleClass(`el-${tag}`); if (modifier) { return compel.modifier(modifier); } return compel; }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ anchor: function (attr = {}, modifier = null) { return builder.genTag("a", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ label: function (attr = {}, modifier = null) { return builder.genTag("label", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ button: function (attr = {}, modifier = null) { return builder.genTag("button", attr, modifier); }, /** * * @param {InputTypes|string} type * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ input: function (type, attr = {}, modifier = null) { return new InputComponent( document.createElement("input"), Object.assign({ "type": type }, attr), modifier ) .addStyleClass(`el-input`); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Function} */ inputTags: Object.freeze({ button: function (attr = {}, modifier = null) { return builder.input("button", attr, modifier) }, checkbox: function (attr = {}, modifier = null) { return builder.input("checkbox", attr, modifier) }, color: function (attr = {}, modifier = null) { return builder.input("color", attr, modifier) }, date: function (attr = {}, modifier = null) { return builder.input("date", attr, modifier) }, datetime: function (attr = {}, modifier = null) { return builder.input("datetime", attr, modifier) }, datetime_local: function (attr = {}, modifier = null) { return builder.input("datetime-local", attr, modifier) }, email: function (attr = {}, modifier = null) { return builder.input("email", attr, modifier) }, file: function (attr = {}, modifier = null) { return builder.input("file", attr, modifier) }, image: function (attr = {}, modifier = null) { return builder.input("image", attr, modifier) }, month: function (attr = {}, modifier = null) { return builder.input("month", attr, modifier) }, number: function (attr = {}, modifier = null) { return builder.input("number", attr, modifier) }, password: function (attr = {}, modifier = null) { return builder.input("password", attr, modifier) }, radio: function (attr = {}, modifier = null) { return builder.input("radio", attr, modifier) }, range: function (attr = {}, modifier = null) { return builder.input("range", attr, modifier) }, reset: function (attr = {}, modifier = null) { return builder.input("reset", attr, modifier) }, search: function (attr = {}, modifier = null) { return builder.input("search", attr, modifier) }, submit: function (attr = {}, modifier = null) { return builder.input("submit", attr, modifier) }, tel: function (attr = {}, modifier = null) { return builder.input("tel", attr, modifier) }, text: function (attr = {}, modifier = null) { return builder.input("text", attr, modifier) }, time: function (attr = {}, modifier = null) { return builder.input("time", attr, modifier) }, url: function (attr = {}, modifier = null) { return builder.input("url", attr, modifier) }, week: function (attr = {}, modifier = null) { return builder.input("week", attr, modifier) } }), /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ div: function (attr = {}, modifier = null) { return builder.genTag("div", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ span: function (attr = {}, modifier = null) { return builder.genTag("span", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ paragraph: function (attr = {}, modifier = null) { return builder.genTag("p", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ header: function (sizeGroup, attr = {}, modifier = null) { return builder.genTag(`h${sizeGroup}`, attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ checkbox: function (attr = {}, modifier = null) { return builder.input({ "type": "checkbox" }, modifier) }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ select: function (attr = {}, modifier = null) { return builder.genTag("select", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ option: function (attr = {}, modifier = null) { return builder.genTag("option", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ radioBtn: function (attr = {}, modifier = null) { return builder.genTag("radioBtn", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ icon: function (attr = {}, modifier = null) { return builder.genTag("icon", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ img: function (attr = {}, modifier = null) { return builder.genTag("img", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ textarea: function (attr = {}, modifier = null) { return new InputComponent( document.createElement("textarea"), attr, modifier ) .addStyleClass(`el-textarea`); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ table: function (attr = {}, modifier = null) { return builder.genTag("table", attr, modifier) .chainModifier() .removeStyleRule("display") .toComponent(); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableRow: function (attr = {}, modifier = null) { return builder.genTag("tr", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableCell: function (attr = {}, modifier = null) { return builder.genTag("td", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableCaption: function (attr = {}, modifier = null) { return builder.genTag("caption", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableHeadCell: function (attr = {}, modifier = null) { return builder.genTag("th", attr, modifier); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableBody: function (attr = {}, modifier = null) { return builder.genTag("tbody", attr, modifier) .chainModifier() .removeStyleRule("display") .toComponent(); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableHead: function (attr = {}, modifier = null) { return builder.genTag("thead", attr, modifier) .chainModifier() .removeStyleRule("display") .toComponent(); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ tableFooter: function (attr = {}, modifier = null) { return builder.genTag("tfoot", attr, modifier) .chainModifier() .removeStyleRule("display") .toComponent(); }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ iframe: function (attr = {}, modifier = null) { return builder.genTag("iframe", attr, modifier) }, /** * * @param {Map} attr * @param {Modifier} modifier * @returns {Component} */ form: function (attr = {}, modifier = null) { return builder.genTag("form", attr, modifier) .addStyleClass("flex-container-component") .chainModifier() .setStyleRule("flex-direction", "column") .ensureModifier() .toComponent() }, /** * * * @param {Map} attr * @param {Modifier} modifier * @returns {Row} */ row: function (attr = {}, modifier = null) { return new Row(attr, modifier) }, /** * * * @param {Map} attr * @param {Modifier} modifier * @returns {Column} */ column: function (attr = {}, modifier = null) { return new Column(attr, modifier) }, /** * * @todo upwards bubbling of js or css is not dealt with yet. * * * @param {Component} innerComponents */ page: function (innerComponents) { let main = document.querySelector('main'); main.parentElement.insertAdjacentElement( "afterbegin", builder.genTag("main") .isHigherComponent() .alignment(Alignment.CENTER) .arrangement(Arrangement.CENTER) .childContext(innerComponents) .generate() .html ); Page.generate(); main.remove(); } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * Defines an extension that can be added to the jpclw-framework */ class CompelExtension { /** * @type {string} */ name; /** * @type {string} */ diplayTitle; /** * @type {Array} */ stylings; /** * @type {Array} */ functions; /** * Predefined components. Usually of a higher (constructed) kind. * @type {Object} */ components; /** * Extensions for/to the Page or the framework in general. * @type {Object} */ frameworkControls; /** * Additional elements for the builder (likely referencing components) * @type {Object} */ builderElements; /** * defines how jpc-like-websites is to be extended and executes that extension */ install() { builder.extensions = Object.assign(builder.extensions, this.builderElements); for (const key of Object.keys(this.stylings)) { Page.registerStyling(key, this.stylings[key]); } } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * @abstract * Class adds function and style storing properties to the context (PageBuilder). */ class ScriptAndStyleContext { /** * @property {map>} functions */ #css; /** * @property {map} functions */ #functions; constructor() { this.#functions = {}; this.#css = {}; } /** * * @param {string} nameAddition text that will be added to the end of the generated name * @returns a name for a function (e.g. for storing) */ getFunctionName(nameAddition = "") { return `func${this.#functions.length}${nameAddition}`; } /** * Registers a function to be added later in a script tag in the head of the document. * @ATTENTION Be careful with intended empty strings (e.g. in variable values), * empty strings within the function code will be shrunk. * * @param {Function} fun The function that will be registered * @param {string} underTheName (alternative) name for the registration of the function, * if none the name of the function will be used, if that is missing a name will be generated. * @param {OverwriteBehaviour} overwriteBehaviour defines what to do, * if the registration name already exists (default: OverwriteBehaviour.RENAME - adds a nr to the name) * @returns {string} the name under witch the function is registered (and therefore can be called from) */ registerPageFunction(fun, underTheName = '', overwriteBehaviour = OverwriteBehaviour.RENAME) { /* Find name-root */ let registrationName = [ underTheName.trim(), fun.name.trim(), this.getFunctionName() ].find(e => e !== ''); /* deal with name already present */ let functionNames = Object.keys(this.#functions); if (functionNames.includes(registrationName)) { registrationName = resolveOverwrite(registrationName, this.#functions, overwriteBehaviour); } /* clear function text */ let clearedFuncText = clearFunctionDeclarationText(fun); this.#functions[registrationName] = new FunctionStoreBuffer(clearedFuncText); return registrationName; } /** * * @experimental Attention is adviced, registration mechanism doesn't work yet * @param {Function} fun The function that is supposed to be executed repeatedly * @param {number} interval the time in ms between executions * @param {string} underTheName the name the interval will be tied to * @param {OverwriteBehaviour} overwriteBehaviour defines what to do, * if the registration name already exists (default: OverwriteBehaviour.RENAME - adds a nr to the name) */ registerRepeatingFunction(fun, interval, underTheName = '', overwriteBehaviour = OverwriteBehaviour.RENAME, args = []) { let registrationName = this.registerPageFunction(fun, underTheName, overwriteBehaviour); let fsb = this.#functions[registrationName]; fsb.repeats = true; fsb.interval = interval; fsb.args = args; return registrationName; } /** * Registeres a function to be executed after page-load * @param {Function} func the function that will be executed * @param {number} delay the time in ms the execution is delayed after load * @param {string} name if provided the function will be registered as well * @param {Array} args arguments for the function * @param {boolean} repeat defines if the function is supposed to be repeated as well * @param {number} interval if the function is supposed to repeat, this defines the interval of repetition */ executeAfterLoad(fun, delay = 1000, underTheName = '', overwriteBehaviour = OverwriteBehaviour.RENAME, interval = -1, args = []) { let registrationName = this.registerPageFunction(fun, underTheName, overwriteBehaviour); let fsb = this.#functions[registrationName]; fsb.execAfterStart = true; fsb.delay = delay; fsb.args = args; if (interval > 0) { fsb.repeats = true; fsb.interval = interval; } return registrationName; } /** * * @param {string} nameAddition text that will be added to the end of the generated name * @returns a name for a styling (e.g. for storing) */ getStyleName(nameAddition = "") { return `styling${this.#css.length}${nameAddition}`; } /** * Adds the styling rules to the element identifiers into the style tag. * An element definition can only be used once, repeated use will be ignored. * * @todo implement extStore logic * * @param {string} elementIdentifier The element identifier * @param {map|Modifier} styleRuleMap The Styling rules/values */ registerStyling(elementIdentifier, styleRuleMap) { if(styleRuleMap instanceof Modifier){ styleRuleMap = styleRuleMap._modifications; } if (!Object.keys(this.#css).includes(elementIdentifier)) { this.#css[elementIdentifier] = styleRuleMap } return elementIdentifier; } /** * Adds into the (head) document. * - script tag * - function tag(s) * - sets and registers repeatedly executed functions * - sets (timeout and) functions that are supposed to be executed after load * @class ScriptAndStyleContext */ generate() { let head = document.querySelector('head'); /* generate style tag and fill it with stored stylings */ let styleTag = document.createElement('style'); Object.entries(this.#css) .forEach((tuple) => { styleTag.innerText += `${tuple[0]} {${Object.entries(tuple[1]) .map(style => style[0] + ": " + style[1] + "; ") .join(" ") }} `; }); head.appendChild(styleTag); /* generate script tag(s) and fill it with stored functions for now there will be 3 script tags, so interval, execAfterStart and normal functions are sepperated. */ let containersTag = document.createElement('script'); containersTag.setAttribute("data-compel-mech-script", "main"); containersTag.innerText = 'const delayed = {}; '; containersTag.innerText += 'const repeated = {}; '; head.appendChild(containersTag); if (this.#functions.length > 0) { let funcTag = document.createElement('script'); Object.entries(this.#functions) .forEach(tuple => { let regName = tuple[0]; let fsb = tuple[1]; funcTag.innerText += getScriptTagInjectionText(fsb.func, regName); if (fsb.repeats && !fsb.execAfterStart) { repeated[regName] = setInterval(regName, fsb.interval, fsb.args); } if (!fsb.repeats && fsb.execAfterStart) { delayed[regName] = setTimeout(regName, fsb.interval, fsb.args); } if (fsb.repeats && fsb.execAfterStart) { repeated[regName] = setInterval(regName, fsb.interval, fsb.args); delayed[regName] = setTimeout(repeated[regName], fsb.delay, fsb.args); } }); head.appendChild(funcTag); } } } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ function frameworkControlPanel( extensions = [] ) { return builder.row() .alignment(Alignment.CENTER) .arrangement(Arrangement.CENTER) .isHigherComponent() .setStylingsStorage(ExtStoreType.INTERNALIZED_WITHIN) .modifier( new Modifier() .fillMaxWidth() .background(MaterialFiveHundredlColors.ORANGE) .dimensions( new Dimensions() .height(200) ) .border( new Border(3) .color(Colors.goldenrod_3) ) .linkPadding(4) ) .childContext([ builder.column() .modifier(new Modifier().fillMaxHeight()) .childContext([ builder.label().text("Installed Extensions:") , builder.column() .overflow() .modifier( new Modifier() .linkPadding(4).ensureModifier() .linkBorder(1) ) .childContext( extensions.map( /** * * @param {CompelExtension} ext * @returns {Component} */ ext => builder.span().text(ext.diplayTitle) ) ) ]) , builder.div() .alignment(Alignment.CENTER) .arrangement(Arrangement.CENTER) .childContext([ builder.label() .text("to generate and download page displayed below click on 'generate'") , builder.button() .text("generate") ]) ]); } /** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * @todo Potentially class implementation could be removed entirely. * Since it (at the time being) is only working as a singleton anyway. * Further maybe rename to 'AppBuilder' instead of 'PageBuilder', * page might later become part of a navigation setup. * * * The class provides overreaching options for building the website. * @extends ScriptAndStyleContext */ class PageBuilder extends ScriptAndStyleContext { /** * @type {Array} */ #autoRegisteredComponents; /** * @type {Array} */ #registeredComponents; /** * @type {boolean} */ #showFrameworkConsole; /** * @type {Array} */ _extensions; constructor() { super(); this.#showFrameworkConsole = false; this.#autoRegisteredComponents = []; this.#registeredComponents = []; this._extensions = []; this._groups = new Map(); } /** * * @param {*|Array<*>} groups * @param {Component} component */ subscribeComponentToGroup(groups, component) { if (groups instanceof Array && !(groups instanceof String)) { for (let i = 0; i < groups.length; i++) { this.subscribeComponentToGroup(groups[i], component); } } else { if (!this._groups.has(groups)) { this._groups.set(groups, []); } this._groups.get(groups).push(component); } } autoRegisterComponent() { let compName = 'comp-el-' + this.#autoRegisteredComponents.length; this.#autoRegisteredComponents.push(compName); return compName; } registerComponent(compName) { this.#registeredComponents.push(compName); return compName; } /** * Inserts the given element according to the extStore into the page/document. * The refElement is a reference element for the case * that extStore._position defines "before" or "segment_begin", * which will then look for the refElement as the corresponding insert reference. * * @param {HTMLElement|Component} element * @param {ExtStorage} extStore * @param {HTMLElement|Component} refElement */ addElementToPage(element, extStore = ExtStoreType.CENTRALIZED_DOC_HEAD) { let { insertCallEl, relativePositioning } = {}; relativePositioning = extStore.getRelativePositioning(); insertCallEl = extStore.getRefElement(element); insertCallEl.insertAdjacentElement( relativePositioning, element ); } /** * Determines that the jpc-like-websites libs shouldn't be part of the resulting page. * Therefore the generate() methods will package/generate finalized js, css and html elements * into the final html page in the end. * Especially tricky are reusable elements and functions that use such. * * @todo This method/feature will have to have a logic implemented for state altering components * that then can be "packaged" into a single page. * @todo This method/feature will work only, if an automatic reuse logic for elements/components is implemented within the jpc lib. * @ATTENTION DO NOT USE */ enableFrameworkConsole() { this.#showFrameworkConsole = true; return this; } /** * Little helper function. * If a single page application is in development. * This method sets an autoreload interval for the page. * Default is 20 (sec). * @param {number} relaunchSeconds timeinterval for page to reload (changes) */ inDev(relaunchSeconds = 20) { let head = document.querySelector("head"); let meta = document.createElement("meta"); meta.setAttribute("http-equiv", "refresh"); meta.setAttribute("content", `${relaunchSeconds}`); let devScript = document.createElement('script'); devScript.setAttribute("data-label", "devScript"); devScript.innerText = ` let ts = new Date(); console.log("Page is in Dev-Mode (through 'inDev()' call of PageBuilder."); console.log("Refreshed at: ", ts.getHours()+':'+ts.getMinutes()+':'+ts.getSeconds(), "Intervall ${relaunchSeconds}s"); `; head.appendChild(devScript); head.insertAdjacentElement("beforeend", meta); } /** * * @param {CompelExtension} extension */ addExtension(extension) { if (extension instanceof CompelExtension) { this._extensions.push(extension); extension.install(); } } setPageTitle(title) { this._apptitle = title; document.querySelector("title") .innerText = title; } generate() { super.generate(); if (this.#showFrameworkConsole) { let pageContextControlPanel = frameworkControlPanel(this._extensions); pageContextControlPanel = pageContextControlPanel.generate(); document.querySelector('body') .insertAdjacentElement( "afterbegin", pageContextControlPanel.html ); } compelgroups = this._groups; } } const CommonCompelGroups = Object.freeze({ AUTO_REGISTRATED: "auto_registrated", REUSABLE_COMPEL: "reusable", HIGHER_COMPEL: "higher_compel", OVERFLOWING: "overflowing", HIDDEN_ON_START: "hidden_on_start", IS_CONTEXT_MENU: "is_contextmenu", HAS_CONTEXT_MENU: "has_contextmenu", DRAGGABLE: "draggable", HAS_DRAG_EVENT: "has_drag", DROP_TARGET: "droptarget", }); const Page = new PageBuilder();