text: /**
   * Navigation block JS.
   *
   * This file contains the Navigation block JS..
   *
   * @module moodle-block_navigation-navigation
   */
  
  /**
   * This namespace will contain all of the contents of the navigation blocks
   * global navigation and settings.
   * @namespace M
   * @class block_navigation
   * @static
   */
  M.block_navigation = M.block_navigation || {};
  /**
   * The number of expandable branches in existence.
   *
   * @property expandablebranchcount
   * @protected
   * @static
   */
  M.block_navigation.expandablebranchcount = 1;
  /**
   * The maximum number of courses to show as part of a branch.
   *
   * @property courselimit
   * @protected
   * @static
   */
  M.block_navigation.courselimit = 20;
  /**
   * Add new instance of navigation tree to tree collection
   *
   * @method init_add_tree
   * @static
   * @param {Object} properties
   */
  M.block_navigation.init_add_tree = function(properties) {
      if (properties.courselimit) {
          this.courselimit = properties.courselimit;
      }
      new TREE(properties);
  };
  
  /**
   * A 'actionkey' Event to help with Y.delegate().
   * The event consists of the left arrow, right arrow, enter and space keys.
   * More keys can be mapped to action meanings.
   * actions: collapse , expand, toggle, enter.
   *
   * This event is delegated to branches in the navigation tree.
   * The on() method to subscribe allows specifying the desired trigger actions as JSON.
   *
   * @namespace M.block_navigation
   * @class ActionKey
   */
  Y.Event.define("actionkey", {
      // Webkit and IE repeat keydown when you hold down arrow keys.
      // Opera links keypress to page scroll; others keydown.
      // Firefox prevents page scroll via preventDefault() on either
      // keydown or keypress.
      _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
  
      /**
       * The keys to trigger on.
       * @method _keys
       */
      _keys: {
          //arrows
          '37': 'collapse',
          '39': 'expand',
          '32': 'toggle',
          '13': 'enter'
      },
  
      /**
       * Handles key events
       * @method _keyHandler
       * @param {EventFacade} e
       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
       * @param {Object} args
       */
      _keyHandler: function (e, notifier, args) {
          var actObj;
          if (!args.actions) {
              actObj = {collapse:true, expand:true, toggle:true, enter:true};
          } else {
              actObj = args.actions;
          }
          if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
              e.action = this._keys[e.keyCode];
              notifier.fire(e);
          }
      },
  
      /**
       * Subscribes to events.
       * @method on
       * @param {Node} node The node this subscription was applied to.
       * @param {Subscription} sub The object tracking this subscription.
       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
       */
      on: function (node, sub, notifier) {
          // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
          if (sub.args === null) {
              //no actions given
              sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
          } else {
              sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
          }
      },
  
      /**
       * Detaches an event listener
       * @method detach
       */
      detach: function (node, sub) {
          //detach our _detacher handle of the subscription made in on()
          sub._detacher.detach();
      },
  
      /**
       * Creates a delegated event listener.
       * @method delegate
       * @param {Node} node The node this subscription was applied to.
       * @param {Subscription} sub The object tracking this subscription.
       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
       * @param {String|function} filter Selector string or function that accpets an event object and returns null.
       */
      delegate: function (node, sub, notifier, filter) {
          // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
          if (sub.args === null) {
              //no actions given
              sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
          } else {
              sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
          }
      },
  
      /**
       * Detaches a delegated event listener.
       * @method detachDelegate
       * @param {Node} node The node this subscription was applied to.
       * @param {Subscription} sub The object tracking this subscription.
       * @param {SyntheticEvent.Notifier} notifier The notifier used to trigger the execution of subscribers
       * @param {String|function} filter Selector string or function that accpets an event object and returns null.
       */
      detachDelegate: function (node, sub) {
          sub._delegateDetacher.detach();
      }
  });
  
  var EXPANSIONLIMIT_EVERYTHING = 0,
      EXPANSIONLIMIT_COURSE     = 20,
      EXPANSIONLIMIT_SECTION    = 30,
      EXPANSIONLIMIT_ACTIVITY   = 40;
  
  // Mappings for the different types of nodes coming from the navigation.
  // Copied from lib/navigationlib.php navigation_node constants.
  var NODETYPE = {
      // @type int Root node = 0
      ROOTNODE : 0,
      // @type int System context = 1
      SYSTEM : 1,
      // @type int Course category = 10
      CATEGORY : 10,
      // @type int MYCATEGORY = 11
      MYCATEGORY : 11,
      // @type int Course = 20
      COURSE : 20,
      // @type int Course section = 30
      SECTION : 30,
      // @type int Activity (course module) = 40
      ACTIVITY : 40,
      // @type int Resource (course module = 50
      RESOURCE : 50,
      // @type int Custom node (could be anything) = 60
      CUSTOM : 60,
      // @type int Setting = 70
      SETTING : 70,
      // @type int site administration = 71
      SITEADMIN : 71,
      // @type int User context = 80
      USER : 80,
      // @type int Container = 90
      CONTAINER : 90
  };
  
  /**
   * Navigation tree class.
   *
   * This class establishes the tree initially, creating expandable branches as
   * required, and delegating the expand/collapse event.
   *
   * @namespace M.block_navigation
   * @class Tree
   * @constructor
   * @extends Y.Base
   */
  var TREE = function() {
      TREE.superclass.constructor.apply(this, arguments);
  };
  TREE.prototype = {
      /**
       * The tree's ID, normally its block instance id.
       * @property id
       * @type Int
       * @protected
       */
      id : null,
      /**
       * An array of initialised branches.
       * @property branches
       * @type Array
       * @protected
       */
      branches : [],
      /**
       * Initialise the tree object when its first created.
       * @method initializer
       * @param {Object} config
       */
      initializer : function(config) {
          Y.log('Initialising navigation block tree', 'note', 'moodle-block_navigation');
  
          this.id = parseInt(config.id, 10);
  
          var node = Y.one('#inst'+config.id);
  
          // Can't find the block instance within the page
          if (node === null) {
              return;
          }
  
          // Delegate event to toggle expansion
          Y.delegate('click', this.toggleExpansion, node.one('.block_tree'), '.tree_item.branch', this);
          Y.delegate('actionkey', this.toggleExpansion, node.one('.block_tree'), '.tree_item.branch', this);
  
          // Gather the expandable branches ready for initialisation.
          var expansions = [];
          if (config.expansions) {
              expansions = config.expansions;
          } else if (window['navtreeexpansions'+config.id]) {
              expansions = window['navtreeexpansions'+config.id];
          }
          // Establish each expandable branch as a tree branch.
          for (var i in expansions) {
              var branch = new BRANCH({
                  tree:this,
                  branchobj:expansions[i],
                  overrides : {
                      expandable : true,
                      children : [],
                      haschildren : true
                  }
              }).wire();
              M.block_navigation.expandablebranchcount++;
              this.branches[branch.get('id')] = branch;
          }
          // Create siteadmin branch.
          if (window.siteadminexpansion) {
              var siteadminbranch = new BRANCH({
                  tree: this,
                  branchobj: window.siteadminexpansion,
                  overrides : {
                      expandable : true,
                      children : [],
                      haschildren : true
                  }
              }).wire();
              M.block_navigation.expandablebranchcount++;
              this.branches[siteadminbranch.get('id')] = siteadminbranch;
              // Remove link on site admin with JS to keep old UI.
              if (siteadminbranch.node) {
                  var siteadminlinknode = siteadminbranch.node.get('childNodes').item(0);
                  if (siteadminlinknode) {
                      var siteadminnode = Y.Node.create('<span tabindex="0">'+siteadminlinknode.get('innerHTML')+'</span>');
                      siteadminbranch.node.replaceChild(siteadminnode, siteadminlinknode);
                  }
              }
          }
          if (M.block_navigation.expandablebranchcount > 0) {
              // Delegate some events to handle AJAX loading.
              Y.delegate('click', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
              Y.delegate('actionkey', this.fire_branch_action, node.one('.block_tree'), '.tree_item.branch[data-expandable]', this);
          }
      },
      /**
       * Fire actions for a branch when an event occurs.
       * @method fire_branch_action
       * @param {EventFacade} event
       */
      fire_branch_action : function(event) {
          var id = event.currentTarget.getAttribute('id');
          var branch = this.branches[id];
          branch.ajaxLoad(event);
      },
      /**
       * This is a callback function responsible for expanding and collapsing the
       * branches of the tree. It is delegated to rather than multiple event handles.
       * @method toggleExpansion
       * @param {EventFacade} e
       * @return Boolean
       */
      toggleExpansion : function(e) {
          // First check if they managed to click on the li iteslf, then find the closest
          // LI ancestor and use that
  
          if (e.target.test('a') && (e.keyCode === 0 || e.keyCode === 13)) {
              // A link has been clicked (or keypress is 'enter') don't fire any more events just do the default.
              e.stopPropagation();
              return;
          }
  
          // Makes sure we can get to the LI containing the branch.
          var target = e.target;
          if (!target.test('li')) {
              target = target.ancestor('li');
          }
          if (!target) {
              return;
          }
  
          // Toggle expand/collapse providing its not a root level branch.
          if (!target.hasClass('depth_1')) {
              if (e.type === 'actionkey') {
                  switch (e.action) {
                      case 'expand' :
                          target.removeClass('collapsed');
                          target.set('aria-expanded', true);
                          break;
                      case 'collapse' :
                          target.addClass('collapsed');
                          target.set('aria-expanded', false);
                          break;
                      default :
                          target.toggleClass('collapsed');
                          target.set('aria-expanded', !target.hasClass('collapsed'));
                  }
                  e.halt();
              } else {
                  target.toggleClass('collapsed');
                  target.set('aria-expanded', !target.hasClass('collapsed'));
              }
          }
  
          // If the accordian feature has been enabled collapse all siblings.
          if (this.get('accordian')) {
              target.siblings('li').each(function(){
                  if (this.get('id') !== target.get('id') && !this.hasClass('collapsed')) {
                      this.addClass('collapsed');
                      this.set('aria-expanded', false);
                  }
              });
          }
  
          // If this block can dock tell the dock to resize if required and check
          // the width on the dock panel in case it is presently in use.
          if (this.get('candock') && M.core.dock.notifyBlockChange) {
              M.core.dock.notifyBlockChange(this.id);
          }
          return true;
  
      }
  };
  // The tree extends the YUI base foundation.
  Y.extend(TREE, Y.Base, TREE.prototype, {
      NAME : 'navigation-tree',
      ATTRS : {
          /**
           * True if the block can dock.
           * @attribute candock
           * @type Boolean
           */
          candock : {
              validator : Y.Lang.isBool,
              value : false
          },
          /**
           * If set to true nodes will be opened/closed in an accordian fashion.
           * @attribute accordian
           * @type Boolean
           */
          accordian : {
              validator : Y.Lang.isBool,
              value : false
          },
          /**
           * The nodes that get shown.
           * @attribute expansionlimit
           * @type Integer
           */
          expansionlimit : {
              value : 0,
              setter : function(val) {
                  val = parseInt(val, 10);
                  if (val !== EXPANSIONLIMIT_EVERYTHING &&
                      val !== EXPANSIONLIMIT_COURSE &&
                      val !== EXPANSIONLIMIT_SECTION &&
                      val !== EXPANSIONLIMIT_ACTIVITY) {
                      val = EXPANSIONLIMIT_EVERYTHING;
                  }
                  return val;
              }
          },
          /**
           * The navigation tree block instance.
           */
          instance : {
              value : false,
              setter : function(val) {
                  return parseInt(val, 10);
              }
          }
      }
  });
  
  /**
   * The Branch class.
   *
   * This class is used to manage a tree branch, in particular its ability to load
   * its contents by AJAX.
   *
   * @namespace M.block_navigation
   * @class Branch
   * @constructor
   * @extends Y.Base
   */
  BRANCH = function() {
      BRANCH.superclass.constructor.apply(this, arguments);
  };
  BRANCH.prototype = {
      /**
       * The node for this branch (p)
       * @property node
       * @type Node
       * @protected
       */
      node : null,
      /**
       * Initialises the branch when it is first created.
       * @method initializer
       * @param {Object} config
       */
      initializer : function(config) {
          var i,
              children;
          if (config.branchobj !== null) {
              // Construct from the provided xml
              for (i in config.branchobj) {
                  this.set(i, config.branchobj[i]);
              }
              children = this.get('children');
              this.set('haschildren', (children.length > 0));
          }
          if (config.overrides !== null) {
              // Construct from the provided xml
              for (i in config.overrides) {
                  this.set(i, config.overrides[i]);
              }
          }
          // Get the node for this branch
          this.node = Y.one('#'+this.get('id'));
          var expansionlimit = this.get('tree').get('expansionlimit');
          var type = this.get('type');
          if (expansionlimit !== EXPANSIONLIMIT_EVERYTHING &&  type >= expansionlimit && type <= EXPANSIONLIMIT_ACTIVITY) {
              this.set('expandable', false);
              this.set('haschildren', false);
          }
      },
      /**
       * Draws the branch within the tree.
       *
       * This function creates a DOM structure for the branch and then injects
       * it into the navigation tree at the correct point.
       *
       * @method draw
       * @chainable
       * @param {Node} element
       * @return Branch
       */
      draw : function(element) {
  
          var isbranch = (this.get('expandable') || this.get('haschildren'));
          var branchli = Y.Node.create('<li></li>');
          var link = this.get('link');
          var branchp = Y.Node.create('<p class="tree_item"></p>').setAttribute('id', this.get('id'));
          if (!link) {
              //add tab focus if not link (so still one focus per menu node).
              // it was suggested to have 2 foci. one for the node and one for the link in MDL-27428.
              branchp.setAttribute('tabindex', '0');
          }
          if (isbranch) {
              branchli.addClass('collapsed').addClass('contains_branch');
              branchli.set('aria-expanded', false);
              branchp.addClass('branch');
          }
  
          // Prepare the icon, should be an object representing a pix_icon
          var branchicon = false;
          var icon = this.get('icon');
          if (icon && (!isbranch || this.get('type') === NODETYPE.ACTIVITY)) {
              branchicon = Y.Node.create('<img alt="" />');
              branchicon.setAttribute('src', M.util.image_url(icon.pix, icon.component));
              branchli.addClass('item_with_icon');
              if (icon.alt) {
                  branchicon.setAttribute('alt', icon.alt);
              }
              if (icon.title) {
                  branchicon.setAttribute('title', icon.title);
              }
              if (icon.classes) {
                  for (var i in icon.classes) {
                      branchicon.addClass(icon.classes[i]);
                  }
              }
          }
  
          if (!link) {
              var branchspan = Y.Node.create('<span></span>');
              if (branchicon) {
                  branchspan.appendChild(branchicon);
              }
              branchspan.append(this.get('name'));
              if (this.get('hidden')) {
                  branchspan.addClass('dimmed_text');
              }
              branchp.appendChild(branchspan);
          } else {
              var branchlink = Y.Node.create('<a title="'+this.get('title')+'" href="'+link+'"></a>');
              if (branchicon) {
                  branchlink.appendChild(branchicon);
              }
              branchlink.append(this.get('name'));
              if (this.get('hidden')) {
                  branchlink.addClass('dimmed');
              }
              branchp.appendChild(branchlink);
          }
  
          branchli.appendChild(branchp);
          element.appendChild(branchli);
          this.node = branchp;
          return this;
      },
      /**
       * Attaches required events to the branch structure.
       *
       * @chainable
       * @method wire
       * @return {BRANCH} This function is chainable, it always returns itself.
       */
      wire : function() {
          this.node = this.node || Y.one('#'+this.get('id'));
          if (!this.node) {
              return this;
          }
          if (this.get('expandable')) {
              this.node.setAttribute('data-expandable', '1');
              this.node.setAttribute('data-loaded', '0');
          }
          return this;
      },
      /**
       * Gets the UL element that children for this branch should be inserted into.
       * @method getChildrenUL
       * @return Node
       */
      getChildrenUL : function() {
          var ul = this.node.next('ul');
          if (!ul) {
              ul = Y.Node.create('<ul></ul>');
              this.node.ancestor().append(ul);
          }
          return ul;
      },
      /**
       * Load the content of the branch via AJAX.
       *
       * This function calls ajaxProcessResponse with the result of the AJAX
       * request made here.
       *
       * @method ajaxLoad
       * @param {EventFacade} e
       * @return Bool
       */
      ajaxLoad : function(e) {
          if (e.type === 'actionkey' && e.action !== 'enter') {
              e.halt();
          } else {
              e.stopPropagation();
          }
          if ((e.type === 'actionkey' && e.action === 'enter') || e.target.test('a')) {
              // No ajaxLoad for enter.
              this.node.setAttribute('data-expandable', '0');
              this.node.setAttribute('data-loaded', '1');
              return true;
          }
  
          if (this.node.hasClass('loadingbranch')) {
              // Already loading. Just skip.
              return true;
          }
  
          if (this.node.getAttribute('data-loaded') === '1') {
              // We've already loaded this stuff.
              return true;
          }
          Y.log('Loading navigation branch via AJAX: '+this.get('key'), 'note', 'moodle-block_navigation');
          this.node.addClass('loadingbranch');
  
          var params = {
              elementid : this.get('id'),
              id : this.get('key'),
              type : this.get('type'),
              sesskey : M.cfg.sesskey,
              instance : this.get('tree').get('instance')
          };
  
          var ajaxfile = '/lib/ajax/getnavbranch.php';
          // For siteadmin navigation get tree from getsiteadminbranch.php.
          if (this.get('type') === NODETYPE.SITEADMIN) {
              ajaxfile = '/lib/ajax/getsiteadminbranch.php';
          }
  
          Y.io(M.cfg.wwwroot + ajaxfile, {
              method:'POST',
              data:  build_querystring(params),
              on: {
                  complete: this.ajaxProcessResponse
              },
              context:this
          });
          return true;
      },
      /**
       * Processes an AJAX request to load the content of this branch through
       * AJAX.
       *
       * @method ajaxProcessResponse
       * @param {Int} tid The transaction id.
       * @param {Object} outcome
       * @return Boolean
       */
      ajaxProcessResponse : function(tid, outcome) {
          this.node.removeClass('loadingbranch');
          this.node.setAttribute('data-loaded', '1');
          try {
              var object = Y.JSON.parse(outcome.responseText);
              if (object.error) {
                  Y.use('moodle-core-notification-ajaxexception', function () {
                      return new M.core.ajaxException(object).show();
                  });
                  return false;
              }
              if (object.children && object.children.length > 0) {
                  var coursecount = 0;
                  for (var i in object.children) {
                      if (typeof(object.children[i])==='object') {
                          if (object.children[i].type === NODETYPE.COURSE) {
                              coursecount++;
                          }
                          this.addChild(object.children[i]);
                      }
                  }
                  if ((this.get('type') === NODETYPE.CATEGORY || this.get('type') === NODETYPE.ROOTNODE || this.get('type') === NODETYPE.MYCATEGORY)
                      && coursecount >= M.block_navigation.courselimit) {
                      this.addViewAllCoursesChild(this);
                  }
                  Y.log('AJAX loading complete.', 'note', 'moodle-block_navigation');
                  // If this block can dock tell the dock to resize if required and check
                  // the width on the dock panel in case it is presently in use.
                  if (this.get('tree').get('candock') && M.core.dock.notifyBlockChange) {
                      M.core.dock.notifyBlockChange(this.get('tree').id);
                  }
                  return true;
              }
              Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
          } catch (error) {
              if (outcome && outcome.status && outcome.status > 0) {
                  // If we got here then there was an error parsing the result.
                  Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
                  Y.use('moodle-core-notification-exception', function () {
                      return new M.core.exception(error).show();
                  });
              }
  
              return false;
          }
          // The branch is empty so class it accordingly
          this.node.replaceClass('branch', 'emptybranch');
          return true;
      },
      /**
       * Turns the branch object passed to the method into a proper branch object
       * and then adds it as a child of this branch.
       *
       * @method addChild
       * @param {Object} branchobj
       * @return Boolean
       */
      addChild : function(branchobj) {
          // Make the new branch into an object
          var branch = new BRANCH({tree:this.get('tree'), branchobj:branchobj});
          if (branch.draw(this.getChildrenUL())) {
              this.get('tree').branches[branch.get('id')] = branch;
              branch.wire();
              var count = 0, i, children = branch.get('children');
              for (i in children) {
                  // Add each branch to the tree
                  if (children[i].type === NODETYPE.COURSE) {
                      count++;
                  }
                  if (typeof(children[i]) === 'object') {
                      branch.addChild(children[i]);
                  }
              }
              if ((branch.get('type') === NODETYPE.CATEGORY || branch.get('type') === NODETYPE.MYCATEGORY)
                  && count >= M.block_navigation.courselimit) {
                  this.addViewAllCoursesChild(branch);
              }
          }
          return true;
      },
  
      /**
       * Add a link to view all courses in a category
       *
       * @method addViewAllCoursesChild
       * @param {BRANCH} branch
       */
      addViewAllCoursesChild: function(branch) {
          var url = null;
          if (branch.get('type') === NODETYPE.ROOTNODE) {
              if (branch.get('key') === 'mycourses') {
                  url = M.cfg.wwwroot + '/my';
              } else {
                  url = M.cfg.wwwroot + '/course/index.php';
              }
          } else {
              url = M.cfg.wwwroot+'/course/index.php?categoryid=' + branch.get('key');
          }
          branch.addChild({
              name : M.str.moodle.viewallcourses,
              title : M.str.moodle.viewallcourses,
              link : url,
              haschildren : false,
              icon : {'pix':"i/navigationitem",'component':'moodle'}
          });
      }
  };
  Y.extend(BRANCH, Y.Base, BRANCH.prototype, {
      NAME : 'navigation-branch',
      ATTRS : {
          /**
           * The Tree this branch belongs to.
           * @attribute tree
           * @type TREE
           * @required
           * @writeOnce
           */
          tree : {
              writeOnce : 'initOnly',
              validator : Y.Lang.isObject
          },
          /**
           * The name of this branch.
           * @attribute name
           * @type String
           */
          name : {
              value : '',
              validator : Y.Lang.isString,
              setter : function(val) {
                  return val.replace(/\n/g, '<br />');
              }
          },
          /**
           * The title to use for this branch.
           * @attribute title
           * @type String
           */
          title : {
              value : '',
              validator : Y.Lang.isString
          },
          /**
           * The ID of this branch.
           * The ID and Type should always form a unique pair.
           * @attribute id
           * @type String
           */
          id : {
              value : '',
              validator : Y.Lang.isString,
              getter : function(val) {
                  if (val === '') {
                      val = 'expandable_branch_'+M.block_navigation.expandablebranchcount;
                      M.block_navigation.expandablebranchcount++;
                  }
                  return val;
              }
          },
          /**
           * The key used to identify this branch easily if there is one.
           * @attribute key
           * @type String
           */
          key : {
              value : null
          },
          /**
           * The type of this branch.
           * @attribute type
           * @type Int
           */
          type : {
              value : null,
              setter : function(value) {
                  return parseInt(value, 10);
              }
          },
          /**
           * The link to use for this branch.
           * @attribute link
           * @type String
           */
          link : {
              value : false
          },
          /**
           * The Icon to add when displaying this branch.
           * @attribute icon
           * @type Object
           */
          icon : {
              value : false,
              validator : Y.Lang.isObject
          },
          /**
           * True if this branch is expandable.
           * @attribute expandable
           * @type Boolean
           */
          expandable : {
              value : false,
              validator : Y.Lang.isBool
          },
          /**
           * True if this branch is hidden and should be displayed greyed out.
           * @attribute hidden
           * @type Boolean
           */
          hidden : {
              value : false,
              validator : Y.Lang.isBool
          },
          /**
           * True if this branch has any children.
           * @attribute haschildren
           * @type Boolean
           */
          haschildren : {
              value : false,
              validator : Y.Lang.isBool
          },
          /**
           * An array of other branches that appear as children of this branch.
           * @attribute children
           * @type Array
           */
          children : {
              value : [],
              validator : Y.Lang.isArray
          }
      }
  });
  