PageSourceSearch

https://zan.kz/lib/aciTree/js/jquery.aciTree.radio.js

js zan.kz collected 2026-09-24 15:34:20 UTC 19,475 bytes, 471 lines download raw bytes

1
2/*
3 * aciTree jQuery Plugin v4.5.0-rc.10
4 * http://acoderinsights.ro
5 *
6 * Copyright (c) 2015 Dragos Ursu
7 * Dual licensed under the MIT or GPL Version 2 licenses.
8 *
9 * Require jQuery Library >= v1.9.0 http://jquery.com
10 * + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
11 */
12
13/*
14 * This extension adds radio-button support to aciTree,
15 * should be used with the selectable extension.
16 *
17 * The are a few extra properties for the item data:
18 *
19 * {
20 *   ...
21 *   radio: true,                       // TRUE (default) means the item will have a radio button (can be omitted if the `checkbox` extension is not used)
22 *   checked: false,                    // if should be checked or not
23 *   ...
24 * }
25 *
26 */
27
28(function($, window, undefined) {
29
30    // extra default options
31
32    var options = {
33        radio: false,                   // if TRUE then each item will have a radio button
34        radioChain: true,               // if TRUE the selection will propagate to the parents/children
35        radioBreak: true,               // if TRUE then a missing radio button will break the chaining
36        radioClick: false               // if TRUE then a click will trigger a state change only when made over the radio-button itself
37    };
38
39    // aciTree radio extension
40
41    var aciTree_radio = {
42        // init radio
43        _radioInit: function() {
44            this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
45                switch (eventName) {
46                    case 'loaded':
47                        if (item) {
48                            // check/update on item load
49                            api._radioLoad(item);
50                        }
51                        break;
52                }
53            }).bind('keydown' + this._private.nameSpace, this.proxy(function(e) {
54                switch (e.which) {
55                    case 32: // space
56                        // support `selectable` extension
57                        if (this.extSelectable && this.extSelectable() && !e.ctrlKey) {
58                            var item = this.focused();
59                            if (this.hasRadio(item) && this.isEnabled(item)) {
60                                if (!this.isChecked(item)) {
61                                    this.check(item);
62                                }
63                                e.stopImmediatePropagation();
64                                // prevent page scroll
65                                e.preventDefault();
66                            }
67                        }
68                        break;
69                }
70            })).on('click' + this._private.nameSpace, '.aciTreeItem', this.proxy(function(e) {
71                if (!this._instance.options.radioClick || $(e.target).is('.aciTreeCheck')) {
72                    var item = this.itemFrom(e.target);
73                    if (this.hasRadio(item) && this.isEnabled(item) && (!this.extSelectable || !this.extSelectable() || (!e.ctrlKey && !e.shiftKey))) {
74                        // change state on click
75                        if (!this.isChecked(item)) {
76                            this.check(item);
77                        }
78                        e.preventDefault();
79                    }
80                }
81            }));
82        },
83        // override `_initHook`
84        _initHook: function() {
85            if (this.extRadio()) {
86                this._radioInit();
87            }
88            // call the parent
89            this._super();
90        },
91        // override `_itemHook`
92        _itemHook: function(parent, item, itemData, level) {
93            if (this.extRadio()) {
94                // support `checkbox` extension
95                var checkbox = this.extCheckbox && this.hasCheckbox(item);
96                if (!checkbox && (itemData.radio || ((itemData.radio === undefined) && (!this.extCheckbox || !this.extCheckbox())))) {
97                    this._radioDOM.add(item, itemData);
98                }
99            }
100            // call the parent
101            this._super(parent, item, itemData, level);
102        },
103        // low level DOM functions
104        _radioDOM: {
105            // add item radio
106            add: function(item, itemData) {
107                domApi.addClass(item[0], itemData.checked ? ['aciTreeRadio', 'aciTreeChecked'] : 'aciTreeRadio');
108                var text = domApi.childrenByClass(item[0].firstChild, 'aciTreeText');
109                var parent = text.parentNode;
110                var label = window.document.createElement('LABEL');
111                var check = window.document.createElement('SPAN');
112                check.className = 'aciTreeCheck';
113                label.appendChild(check);
114                label.appendChild(text);
115                parent.appendChild(label);
116                item[0].firstChild.setAttribute('aria-checked', !!itemData.checked);
117            },
118            // remove item radio
119            remove: function(item) {
120                domApi.removeClass(item[0], ['aciTreeRadio', 'aciTreeChecked']);
121                var text = domApi.childrenByClass(item[0].firstChild, 'aciTreeText');
122                var label = text.parentNode;
123                var parent = label.parentNode;
124                parent.replaceChild(text, label)
125                item[0].firstChild.removeAttribute('aria-checked');
126            },
127            // (un)check items
128            check: function(items, state) {
129                domApi.toggleListClass(items.toArray(), 'aciTreeChecked', state, function(node) {
130                    node.firstChild.setAttribute('aria-checked', state);
131                });
132            }
133        },
134        // update item on load
135        _radioLoad: function(item) {
136            if (!this._instance.options.radioChain) {
137                // do not update on load
138                return;
139            }
140            if (this.hasRadio(item)) {
141                if (this.isChecked(item)) {
142                    if (!this.radios(this.children(item, false, true), true).length) {
143                        // the item is checked but no children are, check the children
144                        this._radioUpdate(item, true);
145                    }
146                } else {
147                    // the item is not checked, uncheck children
148                    this._radioUpdate(item);
149                }
150            }
151        },
152        // get children list
153        _radioChildren: function(item) {
154            if (this._instance.options.radioBreak) {
155                var list = [];
156                var process = this.proxy(function(item) {
157                    var children = this.children(item, false, true);
158                    children.each(this.proxy(function(element) {
159                        var item = $(element);
160                        // break on missing radio
161                        if (this.hasRadio(item)) {
162                            list.push(element);
163                            process(item);
164                        }
165                    }, true));
166                });
167                process(item);
168                return $(list);
169            } else {
170                var children = this.children(item, true, true);
171                return this.radios(children);
172            }
173        },
174        // get children across items
175        _radioLevel: function(items) {
176            var list = [];
177            items.each(this.proxy(function(element) {
178                var item = $(element);
179                var children = this.children(item, false, true);
180                children.each(this.proxy(function(element) {
181                    var item = $(element);
182                    if (!this._instance.options.radioBreak || this.hasRadio(item)) {
183                        list.push(element);
184                    }
185                }, true));
186            }, true));
187            return $(list);
188        },
189        // update radio state
190        _radioUpdate: function(item, state) {
191            // update siblings
192            var siblings = this.proxy(function(item) {
193                var siblings = this.siblings(item, true);
194                this._radioDOM.check(this.radios(siblings), false);
195                siblings.each(this.proxy(function(element) {
196                    var item = $(element);
197                    if (!this._instance.options.radioBreak || this.hasRadio(item)) {
198                        this._radioDOM.check(this._radioChildren(item), false);
199                    }
200                }, true));
201            });
202            if (state) {
203                siblings(item);
204            }
205            // update children
206            var checkDown = this.proxy(function(item) {
207                var children = this._radioLevel(item);
208                var radios = this.radios(children);
209                if (radios.length) {
210                    var checked = this.radios(children, true);
211                    if (checked.length) {
212                        checked = checked.first();
213                        this._radioDOM.check(checked, true);
214                        siblings(checked);
215                        checkDown(checked);
216                    } else {
217                        checked = radios.first();
218                        this._radioDOM.check(checked, true);
219                        siblings(checked);
220                        checkDown(checked);
221                    }
222                } else if (children.length) {
223                    checkDown(children);
224                }
225            });
226            if (state) {
227                checkDown(item);
228            } else {
229                this._radioDOM.check(this._radioChildren(item), false);
230            }
231            // update parents
232            var checkUp = this.proxy(function(item) {
233                var parent = this.parent(item);
234                if (parent.length) {
235                    if (this.hasRadio(parent)) {
236                        if (state) {
237                            siblings(parent);
238                        }
239                        this._radioDOM.check(parent, state);
240                        checkUp(parent);
241                    } else {
242                        if (!this._instance.options.radioBreak) {
243                            if (state) {
244                                siblings(parent);
245                            }
246                            checkUp(parent);
247                        }
248                    }
249                }
250            });
251            if (state !== undefined) {
252                checkUp(item);
253            }
254        },
255        // test if item have a radio
256        hasRadio: function(item) {
257            return item && domApi.hasClass(item[0], 'aciTreeRadio');
258        },
259        // add radio button
260        addRadio: function(item, options) {
261            options = this._options(options, 'radioadded', 'addradiofail', 'w
261asradio', item);
262            if (this.isItem(item)) {
263                // a way to cancel the operation
264                if (!this._trigger(item, 'beforeaddradio', options)) {
265                    this._fail(item, options);
266                    return;
267                }
268                if (this.hasRadio(item)) {
269                    this._notify(item, options);
270                } else {
271                    var process = function() {
272                        this._radioDOM.add(item, {
273                        });
274                        this._success(item, options);
275                    };
276                    // support `checkbox` extension
277                    if (this.extCheckbox && this.hasCheckbox(item)) {
278                        // remove checkbox first
279                        this.removeCheckbox(item, this._inner(options, {
280                            success: process,
281                            fail: options.fail
282                        }));
283                    } else {
284                        process.apply(this);
285                    }
286                }
287            } else {
288                this._fail(item, options);
289            }
290        },
291        // remove radio button
292        removeRadio: function(item, options) {
293            options = this._options(options, 'radioremoved', 'removeradiofail', 'notradio', item);
294            if (this.isItem(item)) {
295                // a way to cancel the operation
296                if (!this._trigger(item, 'beforeremoveradio', options)) {
297                    this._fail(item, options);
298                    return;
299                }
300                if (this.hasRadio(item)) {
301                    this._radioDOM.remove(item);
302                    this._success(item, options);
303                } else {
304                    this._notify(item, options);
305                }
306            } else {
307                this._fail(item, options);
308            }
309        },
310        // test if it's checked
311        isChecked: function(item) {
312            if (this.hasRadio(item)) {
313                return domApi.hasClass(item[0], 'aciTreeChecked');
314            }
315            // support `checkbox` extension
316            if (this._super) {
317                // call the parent
318                return this._super(item);
319            }
320            return false;
321        },
322        // check radio button
323        check: function(item, options) {
324            if (this.extRadio && this.hasRadio(item)) {
325                options = this._options(options, 'checked', 'checkfail', 'waschecked', item);
326                // a way to cancel the operation
327                if (!this._trigger(item, 'beforecheck', options)) {
328                    this._fail(item, options);
329                    return;
330                }
331                if (this.isChecked(item)) {
332                    this._notify(item, options);
333                } else {
334                    this._radioDOM.check(item, true);
335                    if (this._instance.options.radioChain) {
336                        // chain them
337                        this._radioUpdate(item, true);
338                    }
339                    this._success(item, options);
340                }
341            } else {
342                // support `checkbox` extension
343                if (this._super) {
344                    // call the parent
345                    this._super(item, options);
346                } else {
347                    this._trigger(item, 'checkfail', options);
348                    this._fail(item, options);
349                }
350            }
351        },
352        // uncheck radio button
353        uncheck: function(item, options) {
354            if (this.extRadio && this.hasRadio(item)) {
355                options = this._options(options, 'unchecked', 'uncheckfail', 'notchecked', item);
356                // a way to cancel the operation
357                if (!this._trigger(item, 'beforeuncheck', options)) {
358                    this._fail(item, options);
359                    return;
360                }
361                if (this.isChecked(item)) {
362                    this._radioDOM.check(item, false);
363                    if (this._instance.options.radioChain) {
364                        // chain them
365                        this._radioUpdate(item, false);
366                    }
367                    this._success(item, options);
368                } else {
369                    this._notify(item, options);
370                }
371            } else {
372                // support `checkbox` extension
373                if (this._super) {
374                    // call the parent
375                    this._super(item, options);
376                } else {
377                    this._trigger(item, 'uncheckfail', options);
378                    this._fail(item, options);
379                }
380            }
381        },
382        // filter items with radio by state (if set)
383        radios: function(items, state) {
384            if (state !== undefined) {
385                return $(domApi.withClass(items.toArray(), state ? ['aciTreeRadio', 'aciTreeChecked'] : 'aciTreeRadio', state ? null : 'aciTreeChecked'));
386            }
387            return $(domApi.withClass(items.toArray(), 'aciTreeRadio'));
388        },
389        // override `_serialize`
390        _serialize: function(item, callback) {
391            var data = this._super(item, callback);
392            if (data && this.extRadio()) {
393                if (data.hasOwnProperty('radio')) {
394                    data.radio = this.hasRadio(item);
395                    data.checked = this.isChecked(item);
396                } else if (this.hasRadio(item)) {
397                    if (this.extCheckbox && this.extCheckbox()) {
398                        data.radio = true;
399                    }
400                    data.checked = this.isChecked(item);
401                }
402            }
403            return data;
404        },
405        // override `serialize`
406        serialize: function(item, what, callback) {
407            if (what == 'radio') {
408                var serialized = '';
409                var children = this.children(item, true, true);
410                this.radios(children, true).each(this.proxy(function(element) {
411                    var item = $(element);
412                    if (callback) {
413                        serialized += callback.call(this, item, what, this.getId(item));
414                    } else {
415                        serialized += this._instance.options.serialize.call(this, item, what, this.getId(item));
416                    }
417                }, true));
418                return serialized;
419            }
420            return this._super(item, what, callback);
421        },
422        // test if radio is enabled
423        extRadio: function() {
424            return this._instance.options.radio;
425        },
426        // override set `option`
427        option: function(option, value) {
428            if (this.wasInit() && !this.isLocked()) {
429                if ((option == 'radio') && (value != this.extRadio())) {
430                    if (value) {
431                        this._radioInit();
432                    } else {
433                        this._radioDone();
434                    }
435                }
436            }
437            // call the parent
438            this._super(option, value);
439        },
440        // done radio
441        _radioDone: function(destroy) {
442            this._instance.jQuery.unbind(this._private.nameSpace);
443            this._instance.jQuery.off(this._private.nameSpace, '.aciTreeItem');
444            if (!destroy) {
445                // remove radios
446                this.radios(this.children(null, true, true)).each(this.proxy(function(element) {
447                    this.removeRadio($(element));
448                }, true));
449            }
450        },
451        // override `_destroyHook`
452        _destroyHook: function(unloaded) {
453            if (unloaded) {
454                this._radioDone(true);
455            }
456            // call the parent
457            this._super(unloaded);
458        }
459
460    };
461
462    // extend the base aciTree class and add the radio stuff
463    aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_radio, 'aciTreeRadio');
464
465    // add extra default options
466    aciPluginClass.defaults('aciTree', options);
467
468    // for internal access
469    var domApi = aciPluginClass.plugins.aciTree_dom;
470
471})(jQuery, this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.