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