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.