text: 
  /*
   * aciTree jQuery Plugin v4.5.0-rc.6
   * http://acoderinsights.ro
   *
   * Copyright (c) 2014 Dragos Ursu
   * Dual licensed under the MIT or GPL Version 2 licenses.
   *
   * Require jQuery Library >= v1.9.0 http://jquery.com
   * + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
   */
  
  /*
   * This extension adds item selection/keyboard navigation to aciTree and need to
   * be always included if you care about accessibility.
   *
   * There is an extra property for the item data:
   *
   * {
   *   ...
   *   selected: false,                    // TRUE means the item will be selected
   *   ...
   * }
   *
   */
  
  (function($, window, undefined) {
  
      // extra default options
  
      var options = {
          selectable: true,               // if TRUE then one item can be selected (and the tree navigation with the keyboard will be enabled)
          multiSelectable: false,         // if TRUE then multiple items can be selected at a time
          // the 'tabIndex' attribute need to be >= 0 set on the tree container (by default will be set to 0)
          fullRow: false,                 // if TRUE then the selection will be made on the entire row (the CSS should reflect this)
          textSelection: false            // if FALSE then the item text can't be selected
      };
  
      // aciTree selectable extension
      // adds item selection & keyboard navigation (left/right, up/down, pageup/pagedown, home/end, space, enter, escape)
      // dblclick also toggles the item
  
      var aciTree_selectable = {
          __extend: function() {
              // add extra data
              $.extend(this._instance, {
                  focus: false
              });
              $.extend(this._private, {
                  blurTimeout: null,
                  spinPoint: null // the selected item to operate against when using the shift key with selection
              });
              // call the parent
              this._super();
          },
          // test if has focus
          hasFocus: function() {
              return this._instance.focus;
          },
          // init selectable
          _selectableInit: function() {
              if (this._instance.jQuery.attr('tabindex') === undefined) {
                  // ensure the tree can get focus
                  this._instance.jQuery.attr('tabindex', 0);
              }
              if (!this._instance.options.textSelection) {
                  // disable text selection
                  this._selectable(false);
              }
              this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
                  switch (eventName) {
                      case 'closed':
                          var focused = api.focused();
                          if (api.isChildren(item, focused)) {
                              // move focus to parent on close
                              api._focusOne(item);
                          }
                          // deselect children on parent close
                          api.children(item, true).each(api.proxy(function(element) {
                              var item = $(element);
                              if (this.isSelected(item)) {
                                  this.deselect(item);
                              }
                          }, true));
                          break;
                  }
              }).bind('focusin' + this._private.nameSpace, this.proxy(function() {
                  // handle tree focus
                  window.clearTimeout(this._private.blurTimeout);
                  if (!this.hasFocus()) {
                      this._instance.focus = true;
                      domApi.addClass(this._instance.jQuery[0], 'aciTreeFocus');
                      this._trigger(null, 'focused');
                  }
              })).bind('focusout' + this._private.nameSpace, this.proxy(function() {
                  // handle tree focus
                  window.clearTimeout(this._private.blurTimeout);
                  this._private.blurTimeout = window.setTimeout(this.proxy(function() {
                      if (this.hasFocus()) {
                          this._instance.focus = false;
                          domApi.removeClass(this._instance.jQuery[0], 'aciTreeFocus');
                          this._trigger(null, 'blurred');
                      }
                  }), 10);
              })).bind('keydown' + this._private.nameSpace, this.proxy(function(e) {
                  if (!this.hasFocus()) {
                      // do not handle if we do not have focus
                      return;
                  }
                  var focused = this.focused();
                  if (focused.length && this.isBusy(focused)) {
                      // skip when busy
                      return false;
                  }
                  var item = $([]);
                  switch (e.which) {
                      case 65: // aA
                          if (this._instance.options.multiSelectable && e.ctrlKey) {
                              // select all visible items
                              var select = this.visible(this.enabled(this.children(null, true))).not(this.selected());
                              select.each(this.proxy(function(element) {
                                  this.select($(element), {
                                      focus: false
                                  });
                              }, true));
                              if (!this.focused().length) {
                                  // ensure one item has focus
                                  this._focusOne(this.visible(select, true).first());
                              }
                              // prevent default action
                              e.preventDefault();
                          }
                          break;
                      case 38: // up
                          item = focused.length ? this._prev(focused) : this.first();
                          break;
                      case 40: // down
                          item = focused.length ? this._next(focused) : this.first();
                          break;
                      case 37: // left
                          if (focused.length) {
                              if (this.isOpen(focused)) {
                                  item = focused;
                                  // close the item
                                  this.close(focused, {
                                      collapse: this._instance.options.collapse,
                                      expand: this._instance.options.expand,
                                      unique: this._instance.options.unique
                                  });
                              } else {
                                  item = this.parent(focused);
                              }
                          } else {
                              item = this._first();
                          }
                          break;
                      case 39: // right
                          if (focused.length) {
                              if (this.isInode(focused) && this.isClosed(focused)) {
                                  item = focused;
                                  // open the item
                                  this.open(focused, {
                                      collapse: this._instance.options.collapse,
                                      expand: this._instance.options.expand,
                                      unique: this._instance.options.unique
                                  });
                              } else {
                                  item = this.first(focused);
                              }
                          } else {
                              item = this._first();
                          }
                          break;
                      case 33: // pgup
                          item = focused.length ? this._prevPage(focused) : this._first();
                          break;
                      case 34: // pgdown
                          item = focused.length ? this._nextPage(focused) : this._first();
                          break;
                      case 36: // home
                          item = this._first();
                          break;
                      case 35: // end
                          item = this._last();
                          break;
                      case 13: // enter
                      case 107: // numpad [+]
                          item = focused;
                          if (this.isInode(focused) && this.isClosed(focused)) {
                              // open the item
                              this.open(focused, {
                                  collapse: this._instance.options.collapse,
                                  expand: this._instance.options.expand,
                                  unique: this._instance.options.unique
                              });
                          }
                          break;
                      case 27: // escape
                      case 109: // numpad [-]
                          item = focused;
                          if (this.isOpen(focused)) {
                              // close the item
                              this.close(focused, {
                                  collapse: this._instance.options.collapse,
                                  expand: this._instance.options.expand,
                                  unique: this._instance.options.unique
                              });
                          }
                          if (e.which == 27) {
                              // prevent default action on ESC
                              e.preventDefault();
                          }
                          break;
                      case 32: // space
                          item = focused;
                          if (this.isInode(focused) && !e.ctrlKey) {
                              // toggle the item
                              this.toggle(focused, {
                                  collapse: this._instance.options.collapse,
                                  expand: this._instance.options.expand,
                                  unique: this._instance.options.unique
                              });
                          }
                          // prevent page scroll
                          e.preventDefault();
                          break;
                      case 106: // numpad [*]
                          item = focused;
                          if (this.isInode(focused)) {
                              // open all children
                              this.open(focused, {
                                  collapse: this._instance.options.collapse,
                                  expand: true,
                                  unique: this._instance.options.unique
                              });
                          }
                          break;
                  }
                  if (item.length) {
                      if (this._instance.options.multiSelectable && !e.ctrlKey && !e.shiftKey) {
                          // unselect others
                          this._unselect(this.selected().not(item));
                      }
                      if (!this.isVisible(item)) {
                          // bring it into view
                          this.setVisible(item);
                      }
                      if (e.ctrlKey) {
                          if ((e.which == 32) && this.isEnabled(item)) { // space
                              if (this.isSelected(item)) {
                                  this.deselect(item);
                              } else {
                                  this.select(item);
                              }
                              // remember for later
                              this._private.spinPoint = item;
                          } else {
                              this._focusOne(item);
                          }
                      } else if (e.shiftKey) {
                          this._shiftSelect(item);
                      } else {
                          if (!this.isSelected(item) && this.isEnabled(item)) {
                              this.select(item);
                          } else {
                              this._focusOne(item);
                          }
                          // remember for later
                          this._private.spinPoint = item;
                      }
                      return false;
                  }
              }));
              this._fullRow(this._instance.options.fullRow);
              this._multiSelectable(this._instance.options.multiSelectable);
          },
          // change full row mode
          _fullRow: function(state) {
              this._instance.jQuery.off(this._private.nameSpace, '.aciTreeLine,.aciTreeItem').off(this._private.nameSpace, '.aciTreeItem');
              this._instance.jQuery.on('mousedown' + this._private.nameSpace + ' click' + this._private.nameSpace, state ? '.aciTreeLine,.aciTreeItem' : '.aciTreeItem', this.proxy(function(e) {
                  var item = this.itemFrom(e.target);
                  if (!this.isVisible(item)) {
                      this.setVisible(item);
                  }
                  if (e.ctrlKey) {
                      if (e.type == 'click') {
                          if (this.isEnabled(item)) {
                              // (de)select item
                              if (this.isSelected(item)) {
                                  this.deselect(item);
                                  this._focusOne(item);
                              } else {
                                  this.select(item);
                              }
                          } else {
                              this._focusOne(item);
                          }
                      }
                  } else if (this._instance.options.multiSelectable && e.shiftKey) {
                      this._shiftSelect(item);
                  } else {
                      if (this._instance.options.multiSelectable && (!this.isSelected(item) || (e.type == 'click'))) {
                          // deselect all other (keep the old focus)
                          this._unselect(this.selected().not(item));
                      }
                      this._selectOne(item);
                  }
                  if (!e.shiftKey) {
                      this._private.spinPoint = item;
                  }
              })).on('dblclick' + this._private.nameSpace, state ? '.aciTreeLine,.aciTreeItem' : '.aciTreeItem', this.proxy(function(e) {
                  var item = this.itemFrom(e.target);
                  if (this.isInode(item)) {
                      // toggle the item
                      this.toggle(item, {
                          collapse: this._instance.options.collapse,
                          expand: this._instance.options.expand,
                          unique: this._instance.options.unique
                      });
                      return false;
                  }
              }));
              if (state) {
                  domApi.addClass(this._instance.jQuery[0], 'aciTreeFullRow');
              } else {
                  domApi.removeClass(this._instance.jQuery[0], 'aciTreeFullRow');
              }
          },
          // change selection mode
          _multiSelectable: function(state) {
              if (state) {
                  this._instance.jQuery.attr('aria-multiselectable', true);
              } else {
                  var focused = this.focused();
                  this._unselect(this.selected().not(focused));
                  this._instance.jQuery.removeAttr('aria-multiselectable');
              }
          },
          // process `shift` key selection
          _shiftSelect: function(item) {
              var spinPoint = this._private.spinPoint;
              if (!spinPoint || !$.contains(this._instance.jQuery[0], spinPoint[0]) || !this.isOpenPath(spinPoint)) {
                  spinPoint = this.focused();
              }
              if (spinPoint.length) {
                  // select a range of items
                  var select = [item[0]], start = spinPoint[0], found = false, stop = item[0];
                  var visible = this.visible(this.children(null, true));
                  visible.each(this.proxy(function(element) {
                      // find what items to select
                      if (found) {
                          if (this.isEnabled($(element))) {
                              select.push(element);
                          }
                          if ((element == start) || (element == stop)) {
                              return false;
                          }
                      } else if ((element == start) || (element == stop)) {
                          if (this.isEnabled($(element))) {
                              select.push(element);
                          }
                          if ((element == start) && (element == stop)) {
                              return false;
                          }
                          found = true;
                      }
                  }, true));
                  this._unselect(this.selected().not(select));
                  // select the items
                  $(select).not(item).each(this.proxy(function(element) {
                      var item = $(element);
                      if (!this.isSelected(item)) {
                          // select item (keep the old focus)
                          this.select(item, {
                              focus: false
                          });
                      }
                  }, true));
              }
              this._selectOne(item);
          },
          // override `_initHook`
          _initHook: function() {
              if (this.extSelectable()) {
                  this._selectableInit();
              }
              // call the parent
              this._super();
          },
          // override `_itemHook`
          _itemHook: function(parent, item, itemData, level) {
              if (this.extSelectable() && itemData.selected) {
                  this._selectableDOM.select(item, true);
              }
              // call the parent
              this._super(parent, item, itemData, level);
          },
          // low level DOM functions
          _selectableDOM: {
              // (de)select one or more items
              select: function(items, state) {
                  if (state) {
                      domApi.addListClass(items.toArray(), 'aciTreeSelected', function(node) {
                          node.firstChild.setAttribute('aria-selected', true);
                      });
                  } else {
                      domApi.removeListClass(items.toArray(), 'aciTreeSelected', function(node) {
                          node.firstChild.setAttribute('aria-selected', false);
                      });
                  }
              },
              // focus one item, unfocus one or more items
              focus: function(items, state) {
                  if (state) {
                      domApi.addClass(items[0], 'aciTreeFocus');
                      items[0].firstChild.focus();
                  } else {
                      domApi.removeListClass(items.toArray(), 'aciTreeFocus');
                  }
              }
          },
          // make element (un)selectable
          _selectable: function(state) {
              if (state) {
                  this._instance.jQuery.css({
                      '-webkit-user-select': 'text',
                      '-moz-user-select': 'text',
                      '-ms-user-select': 'text',
                      '-o-user-select': 'text',
                      'user-select': 'text'
                  }).attr({
                      'unselectable': null,
                      'onselectstart': null
                  }).unbind('selectstart' + this._private.nameSpace);
              } else {
                  this._instance.jQuery.css({
                      '-webkit-user-select': 'none',
                      '-moz-user-select': '-moz-none',
                      '-ms-user-select': 'none',
                      '-o-user-select': 'none',
                      'user-select': 'none'
                  }).attr({
                      'unselectable': 'on',
                      'onselectstart': 'return false'
                  }).bind('selectstart' + this._private.nameSpace, function(e) {
                      if (!$(e.target).is('input,textarea')) {
                          return false;
                      }
                  });
              }
          },
          // get first visible item
          _first: function() {
              return $(domApi.first(this._instance.jQuery[0], function(node) {
                  return this.hasClass(node, 'aciTreeVisible') ? true : null;
              }));
          },
          // get last visible item
          _last: function() {
              return $(domApi.last(this._instance.jQuery[0], function(node) {
                  return this.hasClass(node, 'aciTreeVisible') ? true : null;
              }));
          },
          // get previous visible starting with item
          _prev: function(item) {
              return $(domApi.prevAll(item[0], function(node) {
                  return this.hasClass(node, 'aciTreeVisible') ? true : null;
              }));
          },
          // get next visible starting with item
          _next: function(item) {
              return $(domApi.nextAll(item[0], function(node) {
                  return this.hasClass(node, 'aciTreeVisible') ? true : null;
              }));
          },
          // get previous page starting with item
          _prevPage: function(item) {
              var space = this._instance.jQuery.height();
              var now = item[0].firstChild.offsetHeight;
              var prev = item, last = $();
              while (now < space) {
                  prev = this._prev(prev);
                  if (prev[0]) {
                      now += prev[0].firstChild.offsetHeight;
                      last = prev;
                  } else {
                      break;
                  }
              }
              return last;
          },
          // get next page starting with item
          _nextPage: function(item) {
              var space = this._instance.jQuery.height();
              var now = item[0].firstChild.offsetHeight;
              var next = item, last = $();
              while (now < space) {
                  next = this._next(next);
                  if (next[0]) {
                      now += next[0].firstChild.offsetHeight;
                      last = next;
                  } else {
                      break;
                  }
              }
              return last;
          },
          // select one item
          _selectOne: function(item) {
              if (this.isSelected(item)) {
                  this._focusOne(item);
              } else {
                  if (this.isEnabled(item)) {
                      // select the item
                      this.select(item);
                  } else {
                      this._focusOne(item);
                  }
              }
          },
          // unselect the items
          _unselect: function(items) {
              items.each(this.proxy(function(element) {
                  this.deselect($(element));
              }, true));
          },
          // focus one item
          _focusOne: function(item) {
              if (!this._instance.options.multiSelectable) {
                  this._unselect(this.selected().not(item));
              }
              if (!this.isFocused(item)) {
                  this.focus(item);
              }
          },
          // select item
          // `options.focus` when set to FALSE will not set the focus
          // `options.oldSelected` will keep the old selected items
          select: function(item, options) {
              options = this._options(options, 'selected', 'selectfail', 'wasselected', item);
              if (this.extSelectable() && this.isItem(item)) {
                  // a way to cancel the operation
                  if (!this._trigger(item, 'beforeselect', options)) {
                      this._fail(item, options);
                      return;
                  }
                  // keep the old ones
                  options.oldSelected = this.selected();
                  if (!this._instance.options.multiSelectable) {
                      // deselect all other
                      var unselect = options.oldSelected.not(item);
                      this._selectableDOM.select(unselect, false);
                      unselect.each(this.proxy(function(element) {
                          this._trigger($(element), 'deselected', options);
                      }, true));
                  }
                  if (this.isSelected(item)) {
                      this._notify(item, options);
                  } else {
                      this._selectableDOM.select(item, true);
                      this._success(item, options);
                  }
                  // process focus
                  if ((options.focus === undefined) || options.focus) {
                      if (!this.isFocused(item) || options.focus) {
                          this.focus(item, this._inner(options));
                      }
                  }
              } else {
                  this._fail(item, options);
              }
          },
          // deselect item
          deselect: function(item, options) {
              options = this._options(options, 'deselected', 'deselectfail', 'notselected', item);
              if (this.extSelectable() && this.isItem(item)) {
                  // a way to cancel the operation
                  if (!this._trigger(item, 'beforedeselect', options)) {
                      this._fail(item, options);
                      return;
                  }
                  if (this.isSelected(item)) {
                      this._selectableDOM.select(item, false);
                      this._success(item, options);
                  } else {
                      this._notify(item, options);
                  }
              } else {
                  this._fail(item, options);
              }
          },
          // set `virtual` focus
          // `options.oldFocused` will keep the old focused item
          focus: function(item, options) {
              options = this._options(options, 'focus', 'focusfail', 'wasfocused', item);
              if (this.extSelectable() && this.isItem(item)) {
                  // a way to cancel the operation
                  if (!this._trigger(item, 'beforefocus', options)) {
                      this._fail(item, options);
                      return;
                  }
                  // keep the old ones
                  options.oldFocused = this.focused();
                  // blur all other
                  var unfocus = options.oldFocused.not(item);
                  this._selectableDOM.focus(unfocus, false);
                  // unfocus all others
                  unfocus.each(this.proxy(function(element) {
                      this._trigger($(element), 'blur', options);
                  }, true));
                  if (this.isFocused(item)) {
                      this._notify(item, options);
                  } else {
                      this._selectableDOM.focus(item, true);
                      this._success(item, options);
                  }
              } else {
                  this._fail(item, options);
              }
          },
          // remove `virtual` focus
          blur: function(item, options) {
              options = this._options(options, 'blur', 'blurfail', 'notfocused', item);
              if (this.extSelectable() && this.isItem(item)) {
                  // a way to cancel the operation
                  if (!this._trigger(item, 'beforeblur', options)) {
                      this._fail(item, options);
                      return;
                  }
                  if (this.isFocused(item)) {
                      this._selectableDOM.focus(item, false);
                      this._success(item, options);
                  } else {
                      this._notify(item, options);
                  }
              } else {
                  this._fail(item, options);
              }
          },
          // get selected items
          selected: function() {
              return this._instance.jQuery.find('.aciTreeSelected');
          },
          // override `_serialize`
          _serialize: function(item, callback) {
              // call the parent
              var data = this._super(item, callback);
              if (data && this.extSelectable()) {
                  if (data.hasOwnProperty('selected')) {
                      data.selected = this.isSelected(item);
                  } else if (this.isSelected(item)) {
                      data.selected = true;
                  }
              }
              return data;
          },
          // test if item is selected
          isSelected: function(item) {
              return item && domApi.hasClass(item[0], 'aciTreeSelected');
          },
          // return the focused item
          focused: function() {
              return this._instance.jQuery.find('.aciTreeFocus');
          },
          // test if item is focused
          isFocused: function(item) {
              return item && domApi.hasClass(item[0], 'aciTreeFocus');
          },
          // test if selectable is enabled
          extSelectable: function() {
              return this._instance.options.selectable;
          },
          // override set `option`
          option: function(option, value) {
              if (this.wasInit() && !this.isLocked()) {
                  if ((option == 'selectable') && (value != this.extSelectable())) {
                      if (value) {
                          this._selectableInit();
                      } else {
                          this._selectableDone();
                      }
                  }
                  if ((option == 'multiSelectable') && (value != this._instance.options.multiSelectable)) {
                      this._multiSelectable(value);
                  }
                  if ((option == 'fullRow') && (value != this._instance.options.fullRow)) {
                      this._fullRow(value);
                  }
                  if ((option == 'textSelection') && (value != this._instance.options.textSelection)) {
                      this._selectable(value);
                  }
              }
              // call the parent
              this._super(option, value);
          },
          // done selectable
          _selectableDone: function(destroy) {
              if (this._instance.jQuery.attr('tabindex') == 0) {
                  this._instance.jQuery.removeAttr('tabindex');
              }
              if (!this._instance.options.textSelection) {
                  this._selectable(true);
              }
              this._instance.jQuery.unbind(this._private.nameSpace);
              this._instance.jQuery.off(this._private.nameSpace, '.aciTreeLine,.aciTreeItem').off(this._private.nameSpace, '.aciTreeItem');
              domApi.removeClass(this._instance.jQuery[0], ['aciTreeFocus', 'aciTreeFullRow']);
              this._instance.jQuery.removeAttr('aria-multiselectable');
              this._instance.focus = false;
              this._private.spinPoint = null;
              if (!destroy) {
                  // remove selection
                  this._unselect(this.selected());
                  var focused = this.focused();
                  if (focused.length) {
                      this.blur(focused);
                  }
              }
          },
          // override `_destroyHook`
          _destroyHook: function(unloaded) {
              if (unloaded) {
                  this._selectableDone(true);
              }
              // call the parent
              this._super(unloaded);
          }
  
      };
  
      // extend the base aciTree class and add the selectable stuff
      aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_selectable, 'aciTreeSelectable');
  
      // add extra default options
      aciPluginClass.defaults('aciTree', options);
  
      // for internal access
      var domApi = aciPluginClass.plugins.aciTree_dom;
  
  })(jQuery, this);
  