1/* DokuWiki MoaiEditor Button.js file 2 Version : 0.5 (May 5, 2026) 3 Author : MoaiTools <info@moaitools.org> 4 License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ 5 6/* Class to create the buttons you see at the top. 7 It can handle buttons with multiple states. 8*/ 9/* 10 DEF 11 12 outer: 13 name: for #id and local storage variable names 14 type: submit 15 icon: 16 onClick: 17 tooltip: 18 tooltip_width: 19 default_mode: 20 classes: 21 cls_remove: 22 states[mode] 23 icon: 24 onClick: 25 tooltip: 26 next_mode: 27 classes_add: 28 classes_remove: 29*/ 30MoaiEditor.Button = class { 31 32 constructor(def) { 33 34 // Parameters 35 this.id = 'moaied__btn_'+def.name; 36 this.name = def.name; 37 this.outer = def.outer; 38 this.type = def?.type ?? ''; 39 this.icon = def?.icon; 40 this.text = def?.text; 41 this.onClick = def.onClick; 42 this.classes = def?.classes; 43 this.states = def?.states; 44 this.tooltip = def?.tooltip; 45 this.tooltip_width = def?.tooltip_width; 46 this.tooltips = def?.tooltips; 47 this.cls_remove = def?.cls_remove; 48 this.default_mode = def?.default_mode; 49 50 // Objects 51 if (this.states !== undefined) { 52 const mode_names = Object.keys(this.states); 53 this.storage = new MoaiEditor.LocalStorage('btn_'+def.name, this.default_mode, mode_names); 54 } 55 // Init 56 this.__init(); 57 } 58 // ──────────────────────────────────── 59 __init() { 60 const id = 'id="'+this.id+'"'; 61 const icon = '<i></i>'; 62 const text = '<span class="moaied-button-text"></span>'; 63 const type = 'type="'+this.type+'"'; 64 const classes = 'class="moaied-button moaied-tooltip '+(this.classes ?? '')+'"'; 65 const tooltip = '<span class="moaied-tooltip-text" '+this.__getTooltipWidthStyle()+'></span>'; 66 const onclick = 'onclick="moaiEditor.buttons.'+this.name+'.__onClick(event)"'; 67 const html = '<button form="" '+id+' '+type+' '+classes+' '+onclick+'>'+text+icon+tooltip+'</button>'; 68 this.handle = moaiEditor.createHTML(html); 69 //document.body.appendChild(this.handle); 70 this.update(); 71 } 72 // ──────────────────────────────────── 73 __getTooltipWidthStyle() { 74 if (!this.tooltip_width) 75 return ''; 76 const width = "width:"+this.tooltip_width+"px !important;"; 77 const mleft = "margin-left: -"+Math.round(this.tooltip_width/2)+"px !important;"; 78 return 'style="'+width+mleft+'"'; 79 } 80 // ──────────────────────────────────── 81 __onClick(event) { 82 83 // Toggle state and call state function 84 if (this.mode !== null && this.states) { 85 86 // Change the mode if applicable 87 const next_mode = this.states[this.mode]?.next_mode; 88 if (next_mode) 89 this.mode = next_mode; 90 91 // Get the current state 92 const state = this.states[this.mode]; 93 94 // Call a state function if defined 95 if (state.onClick) 96 state.onClick(event); 97 } 98 // Call a common function if defined 99 if (this.onClick) 100 this.onClick(event); 101 102 // Update the button appearance 103 this.update(); 104 } 105 // ──────────────────────────────────── 106 update() { 107 // Return if the browser local storage failed 108 if (this.state === null) 109 return; 110 // Get the handle and the state 111 var state = {}; 112 if (this.states) 113 state = this.states[this.mode]; 114 // Icon 115 var icon = ''; 116 if (state?.icon ?? this.icon) { 117 var icon = state?.icon ?? this.icon; 118 if (!icon.startsWith('<img')) 119 icon = moaiEditor.icons?.[state?.icon ?? this.icon]; 120 } 121 this.handle.querySelector(':scope > i').innerHTML = icon; 122 // Text 123 var text = ''; 124 if (state?.text ?? this.text) 125 text = state?.text ?? this.text; 126 this.handle.querySelector(':scope > span.moaied-button-text').innerHTML = text; 127 // State tooltip 128 var tooltip = this.handle.querySelector(':scope > span.moaied-tooltip-text'); 129 if (state?.tooltip ?? this?.tooltip) 130 tooltip.style.display = 'block'; 131 // Button tooltip 132 else 133 tooltip.style.display = 'none'; 134 tooltip.innerHTML = state?.tooltip ?? this?.tooltip; 135 // Composite tooltip (tooltip is composed of messages added by several external processes) 136 if (this.tooltips) { 137 let html = ''; 138 for (let key in this.tooltips) 139 html += this.tooltips[key]; 140 tooltip.innerHTML = html; 141 } 142 // Classes 143 var classList = new MoaiEditor.ElementClassList(this.handle); 144 classList.removeByString(this?.cls_remove); 145 classList.removeByString(state?.cls_remove); 146 classList.addByString(this?.classes); 147 classList.addByString(state?.cls_add); 148 // Mirror mode into data-attribute for convinience 149 this.handle.dataset.moaiedMode = this.mode; 150 } 151 // ──────────────────────────────────── 152 set mode(mode) { 153 if (this.storage !== undefined) { 154 this.storage.value = mode; 155 this.update(); 156 } 157 } 158 // ──────────────────────────────────── 159 get mode() { 160 if (this.storage !== undefined) { 161 return this.storage.value; 162 } else 163 return null; 164 } 165}; // End Class 166 167// ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ 168 169MoaiEditor.ElementClassList = class { 170 171 constructor(node) { 172 this.node = node; 173 } 174 // ──────────────────────────────────── 175 addByString(string) { 176 for (let name of this.splitString(string)) 177 this.node.classList.add(name) 178 } 179 // ──────────────────────────────────── 180 removeByString(string) { 181 for (let name of this.splitString(string)) 182 this.node.classList.remove(name) 183 } 184 // ──────────────────────────────────── 185 splitString(string) { 186 if (!string) 187 return []; 188 var array = []; 189 for (let name of string.split(" ")) { 190 name = name.trim(); 191 if (name.length > 0) 192 array.push(name); 193 } 194 return array; 195 } 196}; // End Class 197 198