SOURCE CODE: UizeSite.Delve
VIEW REFERENCE

/*______________
|       ______  |   U I Z E    J A V A S C R I P T    F R A M E W O R K
|     /      /  |   ---------------------------------------------------
|    /    O /   |    MODULE : UizeSite.Delve
|   /    / /    |
|  /    / /  /| |    ONLINE : http://www.uize.com
| /____/ /__/_| | COPYRIGHT : (c)2010-2012 UIZE
|          /___ |   LICENSE : Available under MIT License or GNU General Public License
|_______________|             http://www.uize.com/license.html
*/

/*?
  Introduction
    document...

    *DEVELOPERS:* `Chris van Rensburg`
*/

Uize.module ({
  name:'UizeSite.Delve',
  required:[
    'UizeSite.ModulesTree',
    'Uize.Widget.Tree.List',
    'Uize.Widget.TextInput',
    'Uize.Widget.Options.Tabbed',
    'Uize.Widget.Log.InstanceEvents',
    'Uize.Widget.TableSort',
    'Uize.Xml',
    'Uize.Util.Oop',
    'Uize.Node',
    'Uize.String',
    'Uize.Data.PathsTree',
    'Uize.Array.Sort',
    'Uize.Json',
    'Uize.Templates.HashTable',
    'Uize.Tooltip'
  ],
  superclass:'Uize.Widget.Page',
  builder:function (_superclass) {
    /*** Variables for Scruncher Optimization ***/
      var _undefined;

    /*** Utility Functions ***/
      function _getNodeOuterHtml (_node) {
        return 'outerHTML' in _node ? _node.outerHTML : new XMLSerializer ().serializeToString (_node);
        // REFERENCE: http://stackoverflow.com/questions/1700870/how-do-i-do-outerhtml-in-firefox
      }

    /*** General Variables ***/
      var
        _sacredEmptyObject = {},
        _notInitialized = {},
        _knownUizeModulesLookup = Uize.lookup (
          Uize.Data.PathsTree.toList (UizeSite.ModulesTree (),'.'),
          1,
          true
        ),
        _javaScriptBuiltInObjectsLookup = Uize.lookup (
          ['Array','Boolean','Function','Number','Object','RegExp','String','Window','XMLHttpRequest'],
          1,
          true
        ),
        _objectNotValidOrNotLoadedMessage = '-- object is undefined, not valid, or is not loaded on page --',
        _reportDivider = '------------------------------------------------------------------------------',
        _treeListDividerQuery = {_title:'- - - - - - - - - - - - - - - - - - - - - - - - - - - -'},
        _treeListQueries = {
          /*** widget instance queries ***/
            widgetsOnPageTree:{
              _title:'All widgets on the page, as a tree',
              _itemsGenerator:function () {
                var
                  _this = this,
                  _items = [],
                  _pageWidget = _this._getPageWidget ()
                ;
                if (_pageWidget) {
                  function _widgetToItem (_widget,_level) {
                    var
                      _item = {
                        title:_this._getWidgetName (_widget),
                        link:_treeItemLink,
                        expanded:!_level,
                        objectPath:_this._getWidgetPath (_widget)
                      },
                      _itemItems = []
                    ;
                    _level++;
                    for (
                      var
                        _childNo = -1,
                        _children = _widget.children,
                        _childNames = Uize.keys (_children).sort (),
                        _childNamesLength = _childNames.length
                      ;
                      ++_childNo < _childNamesLength;
                    )
                      _itemItems.push (_widgetToItem (_children [_childNames [_childNo]],_level))
                    ;
                    if (_itemItems.length)
                      _item.items = _itemItems
                    ;
                    return _item;
                  }
                  _items [0] = _widgetToItem (_pageWidget,0);
                }
                return _items;
              }
            },
            widgetsOnPageList:{
              _title:'All widgets on the page',
              _itemsGenerator:function () {return this._getMatchingWidgetsTreeListItems ()}
            },
            widgetsOnPageGroupedByClass:{
              _title:'All widgets on the page, grouped by class',
              _itemsGenerator:function () {
                var
                  _this = this,
                  _instancesPerWidgetClassMap = _this._getInstancesPerWidgetClassMap (),
                  _widgetClasses = Uize.keys (_instancesPerWidgetClassMap)
                ;

                /*** sort widget classes, from those with most instance to those with least instances ***/
                  Uize.Array.Sort.sortBy (
                    _widgetClasses,
                    function (_widgetClass) {
                      return (
                        ((10000 - _instancesPerWidgetClassMap [_widgetClass].length) + '').slice (1) +
                        _widgetClass
                      );
                    }
                  );

                /*** generate items ***/
                  var _items = [];
                  for (
                    var
                      _widgetClassNo = -1,
                      _widgetClassesLength = _widgetClasses.length,
                      _widgetClass,
                      _widgetClassWidgets,
                      _widgetClassWidgetsLength
                    ;
                    ++_widgetClassNo < _widgetClassesLength;
                  ) {
                    if (
                      _widgetClassWidgetsLength =
                        (
                          _widgetClassWidgets =
                            _instancesPerWidgetClassMap [_widgetClass = _widgetClasses [_widgetClassNo]]
                        ).length
                    )
                      _items.push ({
                        title:_widgetClass + ' (' + _widgetClassWidgetsLength + ')',
                        link:_treeItemLink,
                        expanded:false,
                        objectPath:_widgetClass,
                        items:Uize.map (
                          _widgetClassWidgets,
                          function (_widgetClassWidget) {
                            var _widgetPath = _this._getWidgetPath (_widgetClassWidget);
                            return {
                              title:_widgetPath,
                              link:_treeItemLink,
                              objectPath:_widgetPath
                            };
                          }
                        )
                      })
                    ;
                  }

                return _items;
              }
            },
            widgetsDisabled:{
              _title:'Widgets that are in the disabled state',
              _itemsGenerator:function () {
                return this._getMatchingWidgetsTreeListItems (
                  function (_widget) {return !_widget.get ('enabledInherited')}
                );
              }
            },
            widgetsNotWired:{
              _title:'Widgets that are not wired',
              _itemsGenerator:function () {
                return this._getMatchingWidgetsTreeListItems (function (_widget) {return !_widget.isWired});
              }
            },
            wiredWidgetsMissingSomeNodeDOM:{
              _title:'Wired widgets that are missing some DOM nodes',
              _itemsGenerator:function () {
                var _this = this;
                return _this._getMatchingWidgetsTreeListItems (
                  function (_widget) {
                    var _result = false;
                    if (_widget.isWired) {
                      var _nodeCache = _this._getWidgetNodeCache (_widget);
                      for (var _nodeName in _nodeCache) {
                        if (!_nodeCache [_nodeName]) {
                          _result = true;
                          break;
                        }
                      }
                    }
                    return _result;
                  }
                );
              }
            },
            wiredWidgetsMissingAllNodeDOM:{
              _title:'Wired widgets that appear to missing all DOM nodes',
              _itemsGenerator:function () {
                var _this = this;
                return _this._getMatchingWidgetsTreeListItems (
                  function (_widget) {
                    var _result = false;
                    if (_widget.isWired) {
                      var _nodeCache = _this._getWidgetNodeCache (_widget);
                      for (var _nodeName in _nodeCache) {
                        _result = true;
                        if (_nodeCache [_nodeName]) {
                          _result = false;
                          break;
                        }
                      }
                    }
                    return _result;
                  }
                );
              }
            },
            widgetsWithLocalizedStringsSpecified:{
              _title:'Widgets for which localized strings are specified',
              _itemsGenerator:function () {
                return this._getMatchingWidgetsTreeListItems (
                  function (_widget) {return !Uize.isEmpty (_widget.get ('localized'))}
                );
              }
            },
            widgetsWithRemappedNodes:{
              _title:'Widgets that have some remapped DOM nodes',
              _itemsGenerator:function () {
                return this._getMatchingWidgetsTreeListItems (
                  function (_widget) {return !Uize.isEmpty (_widget.get ('nodeMap'))}
                );
              }
            },
            widgetsWithValueInterface:{
              _title:'Widgets that implement the value interface',
              _itemsGenerator:function () {
                return this._getMatchingWidgetsTreeListItems (
                  function (_widget) {return 'value' in _widget.get ()}
                );
              }
            },

          divider1:_treeListDividerQuery,

          /*** module queries ***/
            builtModulesTree:{
              _title:'All built modules, as a tree',
              _itemsGenerator:function () {
                var _this = this;
                function _itemsFromModulesTree (_namespace,_modulesTree,_level) {
                  if (_modulesTree) {
                    var
                      _items = [],
                      _moduleName
                    ;
                    for (var _property in _modulesTree)
                      _items.push ({
                        title:_moduleName = _namespace + (_namespace && '.') + _property,
                        link:_treeItemLink,
                        expanded:!_level,
                        objectPath:_moduleName,
                        items:_itemsFromModulesTree (_moduleName,_modulesTree [_property],_level + 1)
                      })
                    ;
                    return _items;
                  }
                }
                return _itemsFromModulesTree (
                  '',
                  Uize.Data.PathsTree.fromList (_this._getBuiltModules (),'.'),
                  0
                );
              }
            },
            builtModulesList:{
              _title:'All built modules (listed in build order)',
              _itemsGenerator:function () {return this._getMatchingModulesTreeListItems ()}
            },
            widgetClasses:{
              _title:'All widgets classes',
              _itemsGenerator:function () {
                return this._getWidgetClassesTreeListItems ();
              }
            },
            widgetClassesInstancesCreated:{
              _title:'Widget classes with instances created',
              _itemsGenerator:function () {
                return this._getWidgetClassesTreeListItems (true);
              }
            },
            widgetClassesNoInstancesCreated:{
              _title:'Widget classes with no instances created',
              _itemsGenerator:function () {
                return this._getWidgetClassesTreeListItems (false);
              }
            },
            nonWidgetClasses:{
              _title:'All non-widget built modules',
              _itemsGenerator:function () {
                var _this = this;
                return _this._getMatchingModulesTreeListItems (
                  function (_moduleName) {return !_this._isWidgetClass (_moduleName)}
                );
              }
            },
            uizeModules:{
              _title:'All UIZE built modules',
              _itemsGenerator:function () {return this._getMatchingModulesTreeListItems (_isUizeModule)}
            },
            nonUizeModules:{
              _title:'All non-UIZE built modules',
              _itemsGenerator:function () {
                return this._getMatchingModulesTreeListItems (
                  function (_moduleName) {return !_isUizeModule (_moduleName)}
                );
              }
            },

          divider2:_treeListDividerQuery,

          /*** DOM node queries ***/
            allWidgetDomNodes:{
              _title:'All accessed widget DOM nodes',
              _itemsGenerator:function () {
                return this._getMatchingWidgetDomNodesTreeListItems ();
              }
            },
            allPresentWidgetDomNodes:{
              _title:'All present accessed widget DOM nodes',
              _itemsGenerator:function () {
                return this._getMatchingWidgetDomNodesTreeListItems (function (_node) {return _node});
              }
            },
            allMissingWidgetDomNodes:{
              _title:'All missing accessed widget DOM nodes',
              _itemsGenerator:function () {
                return this._getMatchingWidgetDomNodesTreeListItems (function (_node) {return !_node});
              }
            },
            unaccessedWidgetDomNodes:{
              _title:'All unaccessed widget DOM nodes',
              _itemsGenerator:function () {return this._getUnaccessedDomNodesWithIdsTreeListItems (true)}
            },
            domNodesWithIdsNotBelongingToWidgets:{
              _title:'DOM nodes with IDs not belonging to widgets',
              _itemsGenerator:function () {return this._getUnaccessedDomNodesWithIdsTreeListItems (false)}
            }
        }
      ;

    /*** Utility Functions ***/
      var _childrenDelimiter = '.children.';
      function _getObjectLink (_linkText,_objectPath) {
        return (
          '<a' +
            ' href="javascript://"' +
            ' class="objectLink"' +
            ' title="' + Uize.Xml.toAttributeValue (_objectPath || _linkText) + '"' +
          '>' +
            (
              _linkText.indexOf (_childrenDelimiter) > -1
                ? Uize.String.hugJoin (
                  _linkText.split (_childrenDelimiter),
                  '<b>',
                  '</b>',
                  _childrenDelimiter
                )
                : _linkText
            ) +
          '</a>'
        );
      }

      function _isUizeModule (_moduleName) {
        return Uize.String.startsWith (_moduleName,'Uize');
      }

      function _addTabContentsSection (
        _htmlChunks,_title,_subtitle,_contents,_contentsDefault,_preformattedText
      ) {
        _htmlChunks.push (
          '<div class="objectInspectorTabContentsSectionTitle">' +
            _title +
            (
              _subtitle
                ? (
                  '<span class="objectInspectorTabContentsSectionSubtitle">&nbsp;&nbsp;-&nbsp;&nbsp;' +
                  _subtitle +
                  '</span>'
                )
                : ''
            ) +
          '</div>',
          _preformattedText ? '<pre>' : '',
            ((Uize.isArray (_contents) ? _contents.join ('\n') : _contents) || _contentsDefault) + '\n',
          _preformattedText ? '</pre>' : ''
        );
      }

    /*** Class Constructor ***/
      var
        _class = _superclass.subclass (
          function () {
            var _this = this;

            /*** Private Instance Properties ***/
              _this._summaryLastObject = _this._featuresLastObject = _notInitialized;
          },
          function () {
            var _this = this;

            /*** add tree list widget ***/
              _this.addChild (
                'treeList',
                Uize.Widget.Tree.List,
                {
                  levelClasses:[
                    'tree-list-level1',
                    'tree-list-level2',
                    'tree-list-level3',
                    'tree-list-level4'
                  ],
                  iconTheme:'arrows-black',
                  tooltip:'infoTooltip',
                  tooltipTemplate:function (_item) {
                    return _this._getInfoTooltipHtml (_this._lastTreeItemOverObjectPath = _item.objectPath);
                  },
                  iconBgColor:'',
                  built:false
                }
              ).wire ({
                'After Show Tooltip':
                  function (_event) {_this._highlightObjectInWindow (_event.item.objectPath)},
                'After Hide Tooltip':
                  function () {_this._removeObjectHighlight ()}
              });

            /*** add object entry widget ***/
              var _objectEntry = _this.addChild ('objectEntry',Uize.Widget.TextInput);
              _objectEntry.wire (
                'Changed.value',
                function () {
                  _this.set ({_objectInspectedPath:_objectEntry + ''});
                  _this._updateDocumentationUrl ();
                }
              );

            /*** add object inspector tabs widget ***/
              _this.addChild (
                'objectInspectorTabs',
                Uize.Widget.Options.Tabbed,
                {
                  bodyClassActive:'tabBodyActive',
                  bodyClassInactive:'tabBodyInactive',
                  value:'summary',
                  values:['summary','state','features','documentation','eventsLog']
                }
              ).wire ('Changed.value',function () {_this._updateUiVariousaTabs ()});

            /*** add summary wrapper widget (for wiring/unwiring objectLink tooltips) ***/
              _this.addChild ('objectInspectorSummary',Uize.Widget);

            /*** add state wrapper widget (for wiring/unwiring objectLink tooltips) ***/
              _this.addChild ('objectInspectorState',Uize.Widget);

            /*** add features wrapper widget (and features table sorter widget) ***/
              _this.addChild ('objectInspectorFeatures',Uize.Widget)
                .addChild (
                  'table',
                  Uize.Widget.TableSort,
                  {
                    headingOverClass:'headingOver',
                    headingLitClass:'headingLit',
                    rowOverClass:'rowOver',
                    cellTooltips:true
                  }
                )
              ;

            /*** add events log widget ***/
              _this.addChild ('objectInspectorEventsLog',Uize.Widget.Log.InstanceEvents);

            /*** initialization ***/
              _this._updateTreeListItems ();
              _this._updateObjectEntry ();
              _this._updateEventsLog ();
          }
        ),
        _classPrototype = _class.prototype
      ;

    /*** Private Instance Methods ***/
      _classPrototype._evalInWindow = function (_expression) {
        var _window = this._window;
        if (_window && _expression) {
          try {return _window.eval ('(' + _expression + ')')} catch (_error) {}
        }
      };

      _classPrototype._getInfoTooltipHtml = function (_objectPath) {
        var
          _this = this,
          _object = _this._resolveToObject (_objectPath),
          _whatItIs = _object == _undefined
            ? _whatItIs + ''
            : _this._isWidget (_object)
              ? 'widget'
              : Uize.Util.Oop.isUizeClass (_object)
                ? 'class'
                : Uize.Node.isNode (_object)
                  ? 'DOM node'
                  : _object.constructor == _this._window.Object
                    ? 'simple object'
                    : typeof _object
          ,
          _tooltipData = {'what it is':_whatItIs}
        ;
        if (_object != _undefined)
          _tooltipData ['instance of'] = Uize.Util.Oop.getClassName (_object.constructor)
        ;
        if (_whatItIs == 'widget') {
          var
            _depth = 0,
            _parent = _object
          ;
          while (_parent = _parent.parent) _depth++;
          _tooltipData ['depth in tree'] = _depth || 'this is the root widget';
          _tooltipData.children = Uize.totalKeys (_object.children) || 'no children';
          _tooltipData.siblings =
            (_object.parent && (Uize.totalKeys (_object.parent.children) - 1)) || 'no siblings'
          ;
          _tooltipData ['localized strings'] =
            Uize.totalKeys (_object.get ('localized')) || 'no localized strings'
          ;
          _tooltipData ['DOM nodes'] = _this._getWidgetNodesInfo (_object)._summary;
        } else if (_whatItIs == 'class') {
          _tooltipData.superclass =
            Uize.Util.Oop.getClassName (_object.superclass) || 'this is the root class'
          ;
          _tooltipData ['inheritance depth'] =
            Uize.Util.Oop.getInheritanceChain (_object).length - 1 || 'this is the root class'
          ;
          _tooltipData.subclasses =
            _this._getSubclassesOfClassTreeListItems (_object).length || 'no subclasses on this page'
          ;
          if (_this._isWidgetClass (_object))
            _tooltipData ['widget instances'] =
              _this._getMatchingWidgetsFromTree (
                function (_widget) {return _widget.constructor == _object}
              ).length || 'no instances of this class'
          ;
        } else if (_whatItIs == 'DOM node') {
          _tooltipData.id = _object.id;
          _tooltipData.tag = _object.tagName;

          var _widget = _this._getWidgetFromNodeId (_object.id);
          _tooltipData ['owner widget'] =
            _widget ? _this._getWidgetPath (_widget) : 'not owned by a widget'
          ;
          _tooltipData ['owner widget class'] =
            _widget ? Uize.Util.Oop.getClassName (_widget.constructor) : 'not owned by a widget'
          ;
        }
        if (Uize.Util.Oop.isUizeClassInstance (_object) && 'value' in _object.get ())
          _tooltipData.value = _object.valueOf ()
        ;
        return (
          '<div class="info-tooltip-heading">' + _objectPath + '</div>' +
          Uize.Templates.HashTable.process (_tooltipData)
        );
      };

      _classPrototype._getPageWidget = function () {
        var _window = this._window;
        return _window && (_window.zPage || _window.page);
      };

      _classPrototype._getPageWidgetName = function () {
        var _pageWidget = this._getPageWidget ();
        return _pageWidget && (_pageWidget == this._window.zPage ? 'zPage' : 'page');
      };

      _classPrototype._getWidgetName = function (_widget) {
        return (
          _widget.get ('name') ||
          (_widget == this._getPageWidget () ? this._getPageWidgetName () : '')
        );
      };

      _classPrototype._getBuiltModules = function () {
        var _openerUize = this._window.Uize;
        return (
          (_openerUize.getModulesBuilt && _openerUize.getModulesBuilt ()) ||
          Uize.keys (_openerUize.getModuleByName ('*')) // NOTE: Uize.getModulesBuilt is deprecated */
        );
      };

      _classPrototype._getWidgetPath = function (_widget) {
        var
          _this = this,
          _widgetPath = []
        ;
        while (_widget) {
          _widgetPath.unshift (_this._getWidgetName (_widget));
          _widget = _widget.parent
        }
        return _widgetPath.join ('.children.');
      };

      _classPrototype._getWidgetNodeCache = function (_widget) {
        var _this = this;

        /*** discover node cache object name (if not already known) ***/
          /* NOTE:
            The node cache object is not part of the public interface for the Uize.Widget class, so we have to play some tricks in order to find it.
          */
          if (!_this._widgetNodeCachePropertyName) {
            var
              _crazyNodeName = 'THIS_IS_THE_NAME_FOR_A_NODE_THAT_SHOULD_NEVER_EXIST_IN_PRACTICE',
              _nodeCache
            ;
            _widget.getNode (_crazyNodeName);
            for (var _propertyName in _widget) {
              var _propertyValue = _widget [_propertyName];
              if (
                _propertyValue != _undefined &&
                typeof _propertyValue == 'object' &&
                _crazyNodeName in _propertyValue
              ) {
                _this._widgetNodeCachePropertyName = _propertyName;
                delete _propertyValue [_crazyNodeName];
                break;
              }
            }
          }

        return _widget [_this._widgetNodeCachePropertyName] || {};
      };

      _classPrototype._getWidgetNodesInfo = function (_widget) {
        var
          _this = this,
          _nodeCache = _this._getWidgetNodeCache (_widget),
          _allNodesMap = Uize.copyInto ({},_nodeCache),
          _idPrefix = _widget.get ('idPrefix'),
          _nodeIdPrefix = _idPrefix + '-',
          _nodeIdPrefixLength = _nodeIdPrefix.length,
          _nodeCacheIdLookup = {},
          _totalPresentAccessedNodes = 0,
          _totalMissingAccessedNodes = 0,
          _totalUnaccessedNodes = 0,
          _node
        ;
        for (_nodeName in _nodeCache) {
          if (_node = _nodeCache [_nodeName]) {
            _nodeCacheIdLookup [_node.id] = 1;
            _totalPresentAccessedNodes++;
          } else {
            _totalMissingAccessedNodes++;
          }
        }
        _this._window.Uize.Node.find ({
          id:function (_nodeId) {
            if (
              _nodeId &&
              !(_nodeId in _nodeCacheIdLookup) &&
              (_nodeId == _idPrefix || Uize.String.startsWith (_nodeId,_nodeIdPrefix))
            ) {
              _totalUnaccessedNodes++;
              _allNodesMap [_nodeId.slice (_nodeIdPrefixLength)] = this;
            }
          }
        });
        var
          _totalAccessedNodes = _totalPresentAccessedNodes + _totalMissingAccessedNodes,
          _totalNodes = _totalAccessedNodes + _totalUnaccessedNodes,
          _allAccessed = _totalAccessedNodes == _totalNodes
        ;
        return {
          _nodeCache:_nodeCache,
          _allNodesMap:_allNodesMap,
          _totalNodes:_totalNodes,
          _totalAccessedNodes:_totalAccessedNodes,
          _totalUnaccessedNodes:_totalUnaccessedNodes,
          _totalPresentAccessedNodes:_totalPresentAccessedNodes,
          _totalMissingAccessedNodes:_totalMissingAccessedNodes,
          _summary:
            _totalNodes
              ?
                _totalNodes + ' (' +
                  (
                    _allAccessed
                      ? 'all accessed'
                      : _totalUnaccessedNodes == _totalNodes
                        ? 'none accessed'
                        : _totalAccessedNodes + ' accessed, ' + _totalUnaccessedNodes + ' unaccessed'
                  ) +
                  (
                    _totalAccessedNodes
                      ? (
                        ', ' +
                        (
                          _allAccessed && (!_totalPresentAccessedNodes || !_totalMissingAccessedNodes)
                            ? (_totalPresentAccessedNodes ? 'all present' : 'all missing')
                            : _totalMissingAccessedNodes + ' known missing'
                        )
                      )
                      : ''
                  ) +
                ')'
              : 'no DOM nodes'
        };
      };

      _classPrototype._getWidgetFromNodeId = function (_nodeId) {
        var _this = this;
        if (_nodeId) {
          function _findWidgetWithIdPrefix (_parent) {
            if (_parent.get ('idPrefix') == _idPrefix) {
              return _parent;
            } else {
              var
                _children = _parent.children,
                _widget
              ;
              for (var _childName in _children) {
                if (_widget = _findWidgetWithIdPrefix (_children [_childName]))
                  return _widget
                ;
              }
            }
          }
          var
            _nodeNamePos = _nodeId.indexOf ('-'),
            _idPrefix = _nodeNamePos > -1 ? _nodeId.slice (0,_nodeNamePos) : _nodeId
          ;
          return _findWidgetWithIdPrefix (_this._getPageWidget ());
        }
      };

      _classPrototype._resolveToObject = function (_object) {
        return typeof _object == 'string' ? this._evalInWindow (_object) : _object;
      };

      _classPrototype._getMatchingModulesTreeListItems = function (_moduleMatcher) {
        var
          _this = this,
          _items = [],
          _builtModules = _this._getBuiltModules ()
        ;
        Uize.forEach (
          _moduleMatcher ? _builtModules.sort () : _builtModules,
          function (_builtModule) {
            _builtModule && (!_moduleMatcher || _moduleMatcher (_builtModule)) &&
              _items.push ({
                title:_builtModule,
                link:_treeItemLink,
                objectPath:_builtModule
              })
            ;
          }
        );
        return _items;
      };

      _classPrototype._getSubclassesOfClassTreeListItems = function (_class) {
        var _this = this;
        return _this._getMatchingModulesTreeListItems (
          function (_moduleName) {
            var _module = _this._resolveToObject (_moduleName);
            return _module != _class && Uize.Util.Oop.inheritsFrom (_module,_class);
          }
        );
      };

      _classPrototype._getInstancesPerWidgetClassMap = function () {
        var
          _this = this,
          _instancesPerWidgetClassMap = {}
        ;
        _this._getMatchingWidgetsFromTree (
          function (_widget) {
            var _widgetClass = _widget.constructor.moduleName;
            (_instancesPerWidgetClassMap [_widgetClass] || (_instancesPerWidgetClassMap [_widgetClass] = []))
              .push (_widget)
            ;
          }
        );
        return _instancesPerWidgetClassMap;
      };

      _classPrototype._getWidgetClassesTreeListItems = function (_instancesCreated) {
        var
          _this = this,
          _instancesPerWidgetClassMap = _this._getInstancesPerWidgetClassMap ()
        ;
        return _this._getMatchingModulesTreeListItems (
          function (_moduleName) {
            return (
              (
                _instancesCreated == _undefined ||
                !!(_instancesPerWidgetClassMap [_moduleName] || _sacredEmptyObject).length == _instancesCreated
              ) &&
              _this._isWidgetClass (_moduleName)
            );
          }
        );
      };

      _classPrototype._getMatchingWidgetsFromTree = function (_widgetMatcher) {
        var
          _this = this,
          _widgets = []
        ;
        function _processWidget (_widget,_widgetPath) {
          _widgetPath += _this._getWidgetName (_widget);
          (!_widgetMatcher || _widgetMatcher (_widget)) && _widgets.push (_widgetPath);
          _widgetPath += '.children.';
          var _children = _widget.children;
          for (var _childName in _children)
            _processWidget (_children [_childName],_widgetPath)
          ;
        }
        _processWidget (_this._getPageWidget (),'');
        return _widgets;
      };

      _classPrototype._getMatchingWidgetsTreeListItems = function (_widgetMatcher) {
        var _this = this;
        return Uize.map (
          _this._getMatchingWidgetsFromTree (_widgetMatcher).sort (),
          function (_widget) {
            return {
              title:_objectPath,
              link:_treeItemLink,
              objectPath:_objectPath
            };
          }
        );
      };

      _classPrototype._getMatchingWidgetDomNodesTreeListItems = function (_nodeMatcher) {
        var
          _this = this,
          _items = []
        ;
        function _processWidget (_widget) {
          if (_widget) {
            /*** iterate through widget's accessed DOM nodes ***/
              var
                _nodeCache = _this._getWidgetNodeCache (_widget),
                _objectPathPrefix
              ;
              for (var _nodeName in _nodeCache) {
                var _node = _nodeCache [_nodeName];
                (!_nodeMatcher || _nodeMatcher (_node)) && _items.push ({
                  title:
                    '#' +
                    (
                      _node
                        ? _node.id || ' [DOM NODE WITH NO ID]'
                        : _widget.get ('idPrefix') + (_nodeName && ('-' + _nodeName)) + ' [MISSING]'
                    ),
                  link:_treeItemLink,
                  objectPath:
                    (
                      _objectPathPrefix ||
                      (_objectPathPrefix = _this._getWidgetPath (_widget) + '.getNode (\'')
                    ) +
                      _nodeName +
                    '\')'
                });
              }

            /*** iterate through child widgets ***/
              var _children = _widget.children;
              for (var _childName in _children)
                _processWidget (_children [_childName])
              ;
          }
        }
        _processWidget (_this._getPageWidget ());
        return _items;
      };

      _classPrototype._getUnaccessedDomNodesWithIdsTreeListItems = function (_widgetOrNotWidget) {
        var
          _this = this,
          _accessedNodesLookup = {},
          _idPrefixMap = {}
        ;
        /*** iterate through widget tree, creating lookup of all accessed, non-null DOM nodes, and idPrefix map ***/
          function _processWidget (_widget) {
            if (_widget) {
              _idPrefixMap [_widget.get ('idPrefix')] = 1;

              /*** iterate through widget's accessed DOM nodes ***/
                var
                  _nodeCache = _this._getWidgetNodeCache (_widget),
                  _node,
                  _nodeId
                ;
                for (var _nodeName in _nodeCache) {
                  if ((_node = _nodeCache [_nodeName]) && (_nodeId = _node.id))
                    _accessedNodesLookup [_nodeId] = 1
                  ;
                }

              /*** iterate through child widgets ***/
                var _children = _widget.children;
                for (var _childName in _children)
                  _processWidget (_children [_childName])
                ;
            }
          }
          _processWidget (_this._getPageWidget ());

        return Uize.Array.Sort.sortBy (
          Uize.map (
            _this._window.Uize.Node.find ({
              id:function (_nodeId) {
                if (!_nodeId || _accessedNodesLookup [_nodeId]) return false;
                var _nodeBelongsToWidget = _idPrefixMap [_nodeId];
                if (!_nodeBelongsToWidget) {
                  var _nodeNamePos = _nodeId.indexOf ('-');
                  _nodeBelongsToWidget = _nodeNamePos > -1 && _idPrefixMap [_nodeId.slice (0,_nodeNamePos)];
                }
                return _nodeBelongsToWidget == _widgetOrNotWidget;
              }
            }),
            function (_node) {
              var _nodeId = _node.id;
              return {
                title:'#' + _nodeId,
                link:_treeItemLink,
                objectPath:'document.getElementById (\'' + _nodeId + '\')'
              };
            }
          ),
          'value.title'
        );
      };

      _classPrototype._isWidgetClass = function (_class) {
        return Uize.Util.Oop.inheritsFrom (this._resolveToObject (_class),this._window.Uize.Widget);
      };

      _classPrototype._isWidget = function (_object) {
        return Uize.isInstance (_object) && this._isWidgetClass (_object);
      };

      _classPrototype._showReport = function (_title,_report) {
        var
          _window = this.launchPopup ({
            name:'delveReport',
            width:980,
            height:650
          }),
          _document = _window.document
        ;
        _document.open ('text/html');
        _document.writeln (
          [
            '<html>',
              '<head>',
                '<title>REPORT: ' + _title + '</title>',
              '</head>',
              '<body>',
                '<pre>' + Uize.Xml.toAttributeValue (_report) + '</pre>',
              '</body>',
            '</html>'
          ].join ('\n')
        );
        _document.close ();
        _window.focus ();
      };

      _classPrototype._updateUiSummary = function () {
        var
          _this = this,
          _object = _this._objectInspected
        ;
        if (
          _this.isWired &&
          _this.children.objectInspectorTabs + '' == 'summary' &&
          _object !== _this._summaryLastObject
        ) {
          var
            _objectInspectedPath = _this._objectInspectedPath,
            _htmlChunks = []
          ;
          if (_object != _undefined) {
            var
              _objectIsWidget = _this._isWidget (_object),
              _objectIsWidgetClass = !_objectIsWidget && _this._isWidgetClass (_object)
            ;
            _addTabContentsSection (
              _htmlChunks,
              'SUMMARY FOR',
              '',
              _getObjectLink (_objectInspectedPath),
              '',
              true
            );
            _addTabContentsSection (
              _htmlChunks,
              'INSTANCE OF',
              '',
              _getObjectLink (Uize.Util.Oop.getClassName (_object.constructor)),
              '',
              true
            );

            function _addWidgetsSummarySection (_title,_widgets,_contentsDefault) {
              _addTabContentsSection (
                _htmlChunks,
                _title,
                _widgets.length + ' widgets',
                _widgets.length
                  ? (
                    '<table class="objectInspectorTabContentsInfoTable">' +
                      '<tr class="heading">' +
                        '<td>WIDGET PATH</td>' +
                        '<td>CLASS</td>' +
                      '</tr>' +
                      Uize.map (
                        _widgets,
                        function (_widget) {
                          _widget = _this._resolveToObject (_widget);
                          return (
                            '<tr>' +
                              '<td>' +
                                _getObjectLink (_this._getWidgetPath (_widget)) +
                              '</td>' +
                              '<td>' +
                                _getObjectLink (Uize.Util.Oop.getClassName (_widget.constructor)) +
                              '</td>' +
                            '</tr>'
                          );
                        }
                      ).join ('') +
                    '</table>'
                  )
                  : ('<pre>' + _contentsDefault + '</pre>')
              );
            }

            if (_objectIsWidget) {
              /*** determine parentage ***/
                var
                  _parent = _object.parent,
                  _parents = []
                ;
                while (_parent) {
                  _parents.push (_parent);
                  _parent = _parent.parent;
                }
                _addWidgetsSummarySection ('PARENTAGE',_parents,'no parents');

              /*** determine children ***/
                var
                  _childWidgets = [],
                  _children = _object.children
                ;
                for (var _childName in _children)
                  _childWidgets.push (_children [_childName])
                ;
                _addWidgetsSummarySection ('CHILDREN',_childWidgets,'no children');

              /*** determine siblings ***/
                var
                  _siblings = [],
                  _parentChildren = _object.parent && _object.parent.children,
                  _sibling
                ;
                for (var _siblingName in _parentChildren)
                  (_sibling = _parentChildren [_siblingName]) != _object &&
                    _siblings.push (_sibling)
                ;
                _addWidgetsSummarySection ('SIBLINGS',_siblings,'no siblings');

              /*** determine DOM nodes ***/
                var
                  _nodesInfo = _this._getWidgetNodesInfo (_object),
                  _nodeCache = _nodesInfo._nodeCache,
                  _allNodesMap = _nodesInfo._allNodesMap,
                  _nodeNames = Uize.keys (_allNodesMap).sort (),
                  _getNodeMethodCallPrefix = _this._getWidgetPath (_object) + '.getNode (\''
                ;
                _addTabContentsSection (
                  _htmlChunks,
                  'DOM NODES',
                  _nodesInfo._summary,
                  _nodeNames.length
                    ? (
                      '<table class="objectInspectorTabContentsInfoTable">' +
                        '<tr class="heading">' +
                          '<td>NODE NAME</td>' +
                          '<td>ACCESSED</td>' +
                          '<td>EXISTS</td>' +
                          '<td>ID</td>' +
                          '<td>TAG</td>' +
                        '</tr>' +
                        Uize.map (
                          _nodeNames,
                          function (_nodeName) {
                            var
                              _node = _allNodesMap [_nodeName],
                              _accessed = _nodeName in _nodeCache
                            ;
                            return (
                              '<tr>' +
                                '<td>' +
                                  _getObjectLink (
                                    _nodeName || '[ROOT NODE]',
                                    _accessed
                                      ? _getNodeMethodCallPrefix + _nodeName + '\')'
                                      : 'document.getElementById (\'' + _node.id + '\')'
                                  ) +
                                '</td>' +
                                '<td style="text-align:center;">' +
                                  (_accessed ? 'yes' : '<b>NO</b>') +
                                '</td>' +
                                '<td style="text-align:center;">' +
                                  (_node ? 'present' : '<b>MISSING</b>') +
                                '</td>' +
                                '<td>' + (_node ? _node.id : '-----') + '</td>' +
                                '<td style="text-align:center;">' +
                                  (_node ? _node.tagName : '-----') +
                                '</td>' +
                              '</tr>'
                            );
                          }
                        ).join ('') +
                      '</table>'
                    )
                    : '<pre>no DOM nodes</pre>'
                );

              /*** determine localized strings ***/
                var
                  _localized = _object.get ('localized'),
                  _stringNames = Uize.keys (_localized).sort (),
                  _localizeMethodCallPrefix = _objectInspectedPath + '.localize ('
                ;
                function _getLocalizeMethodCall (_stringName) {
                  var
                    _stringValue = _localized [_stringName],
                    _totalTokens = 0,
                    _tokensHash = {},
                    _tokenMatcher = /\{([^\}]+)\}/g,
                    _tokenMatch
                  ;
                  while (_tokenMatch = _tokenMatcher.exec (_stringValue)) {
                    _tokensHash [_tokenMatch [1]] = '';
                    _totalTokens++;
                  }
                  return (
                    _localizeMethodCallPrefix + Uize.Json.to (_stringName) +
                    (_totalTokens ? ',' + Uize.Json.to (_tokensHash,'mini') : '') +
                    ')'
                  );
                }
                _addTabContentsSection (
                  _htmlChunks,
                  'LOCALIZED STRINGS',
                  _stringNames.length + ' strings',
                  _stringNames.length
                    ? (
                      '<table class="objectInspectorTabContentsInfoTable">' +
                        '<tr class="heading">' +
                          '<td>NAME</td>' +
                          '<td>VALUE</td>' +
                        '</tr>' +
                        Uize.map (
                          _stringNames,
                          function (_stringName) {
                            return (
                              '<tr>' +
                                '<td>' +
                                  _getObjectLink (_stringName,_getLocalizeMethodCall (_stringName)) +
                                '</td>' +
                                '<td>' + _localized [_stringName] + '</td>' +
                              '</tr>'
                            );
                          }
                        ).join ('') +
                      '</table>'
                    )
                    : '<pre>no localized strings specified for this widget</pre>'
                );

              /*** determine HTML for the widget ***/
                var _widgetNode = _object.get ('container') || _object.getNode ('shell') || _object.getNode ();
                _addTabContentsSection (
                  _htmlChunks,
                  'HTML',
                  '',
                  Uize.Xml.toAttributeValue (
                    _widgetNode
                      ? _getNodeOuterHtml (_widgetNode)
                      : 'this widget has no container node, shell node, or root node'
                  ),
                  '',
                  true
                );

            } else if (Uize.Util.Oop.isUizeClass (_object)) {
              /*** determine inheritance ***/
                var _inheritanceChain = Uize.Util.Oop.getInheritanceChain (_object);
                _addTabContentsSection (
                  _htmlChunks,
                  'INHERITANCE CHAIN',
                  'inheritance depth: ' + (_inheritanceChain.length - 1),
                  Uize.map (
                    _inheritanceChain,
                    function (_class) {return _getObjectLink (_class.moduleName)}
                  ).join (' -> '),
                  'this is the root class',
                  true
                );

              /*** determine subclasses ***/
                var _subclasses = _this._getSubclassesOfClassTreeListItems (_object);
                _addTabContentsSection (
                  _htmlChunks,
                  'SUBCLASSES (ON THIS PAGE)',
                  _subclasses.length + ' subclasses',
                  Uize.map (
                    _subclasses,
                    function (_item) {
                      return (
                        _getObjectLink (_item.title) +
                        (
                          _this._resolveToObject (_item.objectPath).superclass == _object
                            ? ' - <b>DIRECT SUBCLASS</b>'
                            : ''
                        )
                      );
                    }
                  ),
                  'no subclasses on this page',
                  true
                );

              /*** determine widgets of this class ***/
                _objectIsWidgetClass &&
                  _addWidgetsSummarySection (
                    'INSTANCES OF THIS WIDGET CLASS',
                    _this._getMatchingWidgetsFromTree (
                      function (_widget) {return _widget.constructor == _object}
                    ),
                    'no widgets of this class instantiated'
                  )
                ;
            } else if (Uize.Node.isNode (_object)) {
              var _widget = _this._getWidgetFromNodeId (_object.id);
              _addTabContentsSection (
                _htmlChunks,
                'OWNER WIDGET',
                '',
                _widget ? _getObjectLink (_this._getWidgetPath (_widget)) : '',
                'this node does not appear to belong to a widget',
                true
              );
              _addTabContentsSection (_htmlChunks,'ID','',_object.id,'no id specified for this node',true);
              _addTabContentsSection (_htmlChunks,'TAG','',_object.tagName,'',true);
              _addTabContentsSection (
                _htmlChunks,
                'HTML',
                '',
                Uize.Xml.toAttributeValue (_getNodeOuterHtml (_object)),
                '',
                true
              );
            } else if (Uize.Util.Oop.isUizeClass (_object) || Uize.Util.Oop.isUizeClassInstance (_object)) {
              // do nothing, since state is shown in the state tab
            } else if (Uize.isFunction (_object)) {
              _addTabContentsSection (
                _htmlChunks,'CODE','',Uize.Xml.toAttributeValue (_object.toString ()),'',true
              );
            } else {
              _addTabContentsSection (
                _htmlChunks,'TO STRING','',Uize.Xml.toAttributeValue (_object.toString ()),'',true
              );
              _addTabContentsSection (
                _htmlChunks,'JSON SERIALIZATION','',Uize.Xml.toAttributeValue (Uize.Json.to (_object)),'',true
              );
            }
          } else {
            _htmlChunks.push ('<br/>' + _objectNotValidOrNotLoadedMessage);
          }

          _this._rebuildTabContetsHtmlAndWireUi (_this.children.objectInspectorSummary,_htmlChunks);
           _this._summaryLastObject = _object;
        }
      };

      _classPrototype._updateUiState = function () {
        var
          _this = this,
          _object = _this._objectInspected
        ;
        if (
          _this.isWired &&
          _this.children.objectInspectorTabs + '' == 'state' &&
          _object !== _this._stateLastObject
        ) {
          var _htmlChunks = [];
          if (Uize.Util.Oop.isUizeClass (_object) || Uize.Util.Oop.isUizeClassInstance (_object)) {
            var
              _objectInspectedPath = _this._objectInspectedPath,
              _propertiesHtmlChunks = [
                '<table class="objectInspectorTabContentsInfoTable">' +
                  '<tr class="heading">',
                    '<td>NAME</td>',
                    '<td>ACTIONS</td>',
                    '<td>VALUE</td>',
                  '</tr>'
              ]
            ;
            for (
              var
                _propertyNo = -1,
                _propertyValues = _object.get (),
                _properties = Uize.keys (_propertyValues).sort (),
                _propertiesLength = _properties.length,
                _propertyGetMethodCallPrefix = _objectInspectedPath + '.get (\'',
                _propertySetMethodCallPrefix = _objectInspectedPath + '.set (\'',
                _property
              ;
              ++_propertyNo < _propertiesLength;
            )
              (_property = _properties [_propertyNo]).indexOf ('_') < 0 &&
                _propertiesHtmlChunks.push (
                  '<tr>',
                    '<td>',
                      _getObjectLink (_property,_propertyGetMethodCallPrefix + _property + '\')'),
                    '</td>',
                    '<td style="white-space:nowrap; text-align:center;">',
                      _getObjectLink ('get',_propertyGetMethodCallPrefix + _property + '\')'),
                      ' | ',
                      _getObjectLink ('set',_propertySetMethodCallPrefix + _property + '\',#)'),
                    '</td>',
                    '<td>' + Uize.Xml.toAttributeValue (_propertyValues [_property]) + '</td>',
                  '</tr>'
                )
            ;
            _propertiesHtmlChunks.push ('</table>');
            _addTabContentsSection (
              _htmlChunks,
              'STATE PROPERTIES',
              _propertiesLength + ' properties',
              _propertiesLength
                ? _propertiesHtmlChunks.join ('')
                : '<pre>this class has no state properties</pre>'
            );
          } else {
            _htmlChunks.push ('<br/>' + '-- object does not support a state properties state interface --');
          }

          _this._rebuildTabContetsHtmlAndWireUi (_this.children.objectInspectorState,_htmlChunks);
          _this._stateLastObject = _object;
        }
      };

      _classPrototype._updateUiFeatures = function () {
        var
          _this = this,
          _object = _this._objectInspected
        ;
        if (
          _this.isWired &&
          _this.children.objectInspectorTabs + '' == 'features' &&
          _object !== _this._featuresLastObject
        ) {
          var _htmlChunks = [];
          if (_object != _undefined) {
            var
              _features = Uize.Util.Oop.getFeatures (_object),
              _featuresLength = _features.length
            ;
            if (_featuresLength) {
              var _objectPath = Uize.Util.Oop.getClassName (_object = Uize.Util.Oop.resolveToClass (_object));
              function _getLinkedModuleCell (_object) {
                return (
                  '<td class="moduleName">' +
                    _getObjectLink (Uize.Util.Oop.getClassName (_object),'',true) +
                  '</td>'
                );
              }
              _htmlChunks.push (
                '<table id="' + _this.children.objectInspectorFeatures.children.table.get ('idPrefix') + '" class="data">',
                  '<tr class="title">',
                    '<td colspan="6">',
                      'Features detected for ' + _objectPath,
                    '</td>',
                  '</tr>',
                  '<tr class="heading">',
                    '<td>NAME</td>',
                    '<td>ACCESS</td>',
                    '<td>CONTEXT</td>',
                    '<td>TYPE</td>',
                    '<td>INTRODUCED IN</td>',
                    '<td>LAST OVERRIDDEN IN</td>',
                  '</tr>'
              );
              for (
                var
                  _featureNo = -1,
                  _objectPrototypePath = _objectPath + '.prototype.'
                ;
                ++_featureNo < _featuresLength;
              ) {
                var _feature = _features [_featureNo];
                _htmlChunks.push (
                  '<tr>',
                    '<td class="featureName">' +
                      (
                        _feature.context == 'State'
                          ? _feature.name
                          : _getObjectLink (
                            _feature.name,
                            _feature.context == 'Instance' ? _objectPrototypePath + _feature.name : '',
                            true
                          )
                      ) +
                    '</td>',
                    '<td>' + _feature.access + '</td>',
                    '<td>' + _feature.context + '</td>',
                    '<td>' + _feature.type + '</td>',
                    _getLinkedModuleCell (_feature.introducedIn),
                    _getLinkedModuleCell (_feature.overriddenIn),
                  '</tr>'
                );
              }
              _htmlChunks.push ('</table>');
            } else {
              _htmlChunks.push ('<br/>-- no features could be automatically detected for this object');
            }
          } else {
            _htmlChunks.push ('<br/>' + _objectNotValidOrNotLoadedMessage);
          }

          _this._rebuildTabContetsHtmlAndWireUi (_this.children.objectInspectorFeatures,_htmlChunks);
          _this._featuresLastObject =  _object;
        }
      };

      _classPrototype._updateUiVariousaTabs = function () {
        var _this = this;
        _this._updateUiSummary ();
        _this._updateUiState ();
        _this._updateUiFeatures ();
        _this._updateUiDocumentation ();
      };

      _classPrototype._updateEventsLog = function () {
        var _objectInspectorEventsLog = this.children.objectInspectorEventsLog;
        _objectInspectorEventsLog &&
          _objectInspectorEventsLog.set ({instance:this._objectInspected})
        ;
      };

      for (var _treeItemClickedMethodName in {_treeItemClicked:1});
      var _treeItemLink = 'javascript:page.' + _treeItemClickedMethodName + ' ()';
      _classPrototype._treeItemClicked = function () {
        this.set ({_objectInspectedPath:this._lastTreeItemOverObjectPath});
      };

      _classPrototype._updateTreeListItems = function () {
        var
          _this = this,
          _treeList = _this.children.treeList
        ;
        if (_treeList) {
          var _itemsGenerator = _treeListQueries [_this._treeListQuery]._itemsGenerator;
          _treeList.set ({items:_itemsGenerator ? _itemsGenerator.call (_this) : []})
        }
      };

      _classPrototype._updateUiTreeListDropdown = function () {
        var _this = this;
        if (_this.isWired) {
          _this.setNodeValue ('treeListDropdown',_this._treeListQuery);
        }
      };

      _classPrototype._updateObjectEntry = function () {
        var _objectEntry = this.children.objectEntry;
        _objectEntry && _objectEntry.set ({value:this._objectInspectedPath});
      };

      _classPrototype._updateDocumentationUrl = function () {
        var
          _this = this,
          _objectInspected = _this._objectInspected,
          _objectInspectedPath = _this._objectInspectedPath,
          _className = _knownUizeModulesLookup [_objectInspectedPath] && _objectInspectedPath
        ;
        if (!_className && _objectInspected != _undefined) {
          _className = Uize.Util.Oop.getClassName (Uize.Util.Oop.resolveToClass (_objectInspected));
          if (!_isUizeModule (_className)) {
            var _class = _this._resolveToObject (_className);
            if (Uize.Util.Oop.isUizeClass (_class))
              while (
                (_className = (_class = _class.superclass).moduleName) &&
                !_isUizeModule (_className)
              )
            ;
          }
        }
        _this.set ({
          _documentationUrl:
            _this._baseUrl + '/' +
            (
              (
                _className &&
                (
                  _isUizeModule (_className)
                    ? 'reference/' + _className + '.html'
                    : _javaScriptBuiltInObjectsLookup [_className]
                      ? 'javascript-reference/' + _className + '.html'
                      : null
                )
              ) || 'explainers/using-the-delve-tool.html'
            )
        });
      };

      _classPrototype._updateUiDocumentation = function () {
        var _this = this;
        if (_this.isWired && _this.children.objectInspectorTabs + '' == 'documentation') {
          var _documentationUrl = _this._documentationUrl;
          if (_documentationUrl != _this._lastDisplayedDocumentationUrl) {
            var _contentWindow = _this.getNode ('documentation').contentWindow;
            if (_contentWindow)
              _contentWindow.location.href = _documentationUrl
            ;
            _this._lastDisplayedDocumentationUrl = _documentationUrl;
          }
        }
      };

      _classPrototype._updateUiWindowInspected = function () {
        var _this = this;
        if (_this.isWired) {
          var _window = _this._window;
          _this.setNodeValue (
            'windowInspected',
            _window
              ? Uize.String.limitLength (_window.location.href,120)
              : 'no window being inspected'
          );
          _this.setNodeProperties (
            'windowInspected',
            {title:_window ? _window.document.title : ''}
          );
        }
      };

      _classPrototype._refresh = function () {
        var
          _this = this,
          _objectInspectedPath = _this._objectInspectedPath
        ;
        _this._updateTreeListItems ();
        _this._updateUiWindowInspected ();
        _this.set ({_objectInspectedPath:''}); // first set empty string, so resetting triggers reevaluation
        _this.set ({_objectInspectedPath:_objectInspectedPath});
      };

      _classPrototype._highlightObjectInWindow = function (_objectPath) {
        var
          _this = this,
          _object = _this._resolveToObject (_objectPath),
          _objectIsWidget = _this._isWidget (_object),
          _objectIsNode = !_objectIsWidget && Uize.Node.isNode (_object)
        ;
        if (_objectIsWidget || _objectIsNode) {
          var
            _window = _this._window,
            _getCoords = _window.Uize.Node.getCoords,
            _coords
          ;
          if (_objectIsNode) {
            _coords = _getCoords (_object);
          } else {
            var
              _left = Infinity,
              _right = -Infinity,
              _top = Infinity,
              _bottom = -Infinity
            ;
            function _expandCoords (_nodeCoords) {
              if (_nodeCoords.area && _nodeCoords.seen) {
                if (_nodeCoords.left < _left) _left = _nodeCoords.left;
                if (_nodeCoords.top < _top) _top = _nodeCoords.top;
                if (_nodeCoords.right > _right) _right = _nodeCoords.right;
                if (_nodeCoords.bottom > _bottom) _bottom = _nodeCoords.bottom;
              }
            }
            function _processWidget (_widget) {
              /*** iterate through accessed DOM nodes ***/
                var
                  _nodeCache = _this._getWidgetNodeCache (_widget),
                  _node
                ;
                for (var _nodeName in _nodeCache)
                  if (_node = _nodeCache [_nodeName]) _expandCoords (_getCoords (_node))
                ;

              /*** iterate through child widgets ***/
                var _children = _widget.children;
                for (var _childName in _children)
                  _processWidget (_children [_childName])
                ;
            }
            _processWidget (_object);

            if (_left != Infinity) {
              var
                _width = _right - _left + 1,
                _height = _bottom - _top + 1
              ;
              _coords = {
                left:_left,
                right:_right,
                top:_top,
                bottom:_bottom,
                width:_width,
                height:_height,
                area:_width * _height,
                seen:true
              };
            }
          }
          if (_coords && _coords.area && _coords.seen) {
            _document = _window.document;
            _document.body.appendChild (_this._highlightNode = _document.createElement ('DIV'));
            Uize.Node.setStyle (
              _this._highlightNode,
              {
                position:'absolute',
                zIndex:1000000,
                left:_coords.left,
                top:_coords.top,
                width:_coords.width,
                height:_coords.height,
                background:'#ffa200'
              }
            );
            Uize.Node.setOpacity (_this._highlightNode,.5);
          }
        }
      };

      _classPrototype._removeObjectHighlight = function () {
        Uize.Node.remove (this._highlightNode);
        this._highlightNode = null;
      };

      _classPrototype._rebuildTabContetsHtmlAndWireUi = function (_wrapperWidget,_htmlChunks) {
        var _this = this;
        function _cleanupAfterObjectLinkPreview (_link) {
          if (!_link.title) {
            _link.title = _link.UizeSite_Delve_title;
            // we'd like to do a cleanup, but we can't do a delete because of stupid IE throwing an error
            // delete _link.UizeSite_Delve_title;
            Uize.Tooltip.showTooltip ('infoTooltip',false);
            _this._removeObjectHighlight ();
          }
        }
        _wrapperWidget.unwireUi ();
        _wrapperWidget.setNodeInnerHtml ('',_htmlChunks.join (''));
        _wrapperWidget.setNodeProperties ('',{scrollTop:0});
        _wrapperWidget.wireUi ();
        _wrapperWidget.wireNode (
          Uize.Node.find ({
            root:_wrapperWidget.getNode (),
            tagName:'a',
            className:'objectLink'
          }),
          {
            mouseover:function () {
              var _title = this.UizeSite_Delve_title = this.title;
              this.title = '';
              Uize.Node.setInnerHtml ('infoTooltip',_this._getInfoTooltipHtml (_title));
              Uize.Tooltip.showTooltip ('infoTooltip',true);
              _this._highlightObjectInWindow (_title);
            },
            mouseout:function () {_cleanupAfterObjectLinkPreview (this)},
            click:function () {
              _cleanupAfterObjectLinkPreview (this);
              _this.children.objectInspectorTabs.set ({value:'summary'});
              _this.set ({_objectInspectedPath:this.title || this.title});
            }
          }
        );
      };

    /*** Public Instance Methods ***/
      _classPrototype.updateUi = function () {
        var _this = this;
        if (_this.isWired) {
          _this._updateUiWindowInspected ();
          _this._updateUiTreeListDropdown ();
          _this._updateUiVariousaTabs ();
          _superclass.prototype.updateUi.call (_this);
        }
      };

      _classPrototype.wireUi = function () {
        var _this = this;
        if (!_this.isWired) {
          /*** make sure to clean up when reloading ***/
            _this.wireNode (
              window,
              'unload',
              function () {
                _this._removeObjectHighlight ();
                _this.set ({objectInspectedPath:''});
                /* NOTE:
                  Setting objectInspectedPath to an empty string on unload ensures that the events log widget is set to no longer watch anything, which results in it unwiring any currently wired event handlers. If the event handlers were allowed to remain wired in the page being inspected across reload of DELVE, then execution of the handlers later would cause JavaScript errors as a result of the handler function references becoming invalid from the reload.
                */
              }
            );

          /*** wire link for refreshing tree list and object inspector ***/
            _this.wireNode ('refresh','click',function () {_this._refresh ()});

          /*** wire link for getting widget from DOM node ID ***/
            _this.wireNode (
              'getWidgetFromNodeId',
              'click',
              function () {
                var _nodeId = prompt ('Enter a DOM node id...','');
                if (_nodeId) {
                  var _widget = _this._getWidgetFromNodeId (_nodeId);
                  _widget
                    ? _this.set ({_objectInspectedPath:_this._getWidgetPath (_widget)})
                    : alert ('The DOM node with the ID "' + _nodeId + '" does not appear belong to a widget.')
                  ;
                }
              }
            );

          /*** wire help link ***/
            _this.wireNode (
              'help',
              'click',
              function () {
                _this.children.objectInspectorTabs.set ({value:'documentation'});
                _this.set ({_objectInspectedPath:''});
              }
            );

          /*** wire close link ***/
            _this.wireNode ('close','click',function () {top.close ()});

          /*** populate and wire up tree list dropdown ***/
            var _treeListDropdown = _this.getNode ('treeListDropdown');
            if (_treeListDropdown) {
              var _treeListDropdownOptions = _treeListDropdown.options;
              for (var _treeListQueryName in _treeListQueries)
                _treeListDropdownOptions [_treeListDropdownOptions.length] = new Option (
                  _treeListQueries [_treeListQueryName]._title,
                  _treeListQueryName
                )
              ;
            }
            _this.wireNode (
              _treeListDropdown,
              'onchange',
              function () {_this.set ({treeListQuery:_this.getNodeValue (_treeListDropdown)})}
            );

          /*** wire up links for expanding tree list ***/
            _this.wireNode (
              'expandTreeListOneLevel',
              'click',
              function () {_this.children.treeList.setExpandedDepth (1)}
            );
            _this.wireNode (
              'expandTreeListTwoLevels',
              'click',
              function () {_this.children.treeList.setExpandedDepth (2)}
            );
            _this.wireNode (
              'expandTreeListThreeLevels',
              'click',
              function () {_this.children.treeList.setExpandedDepth (3)}
            );
            _this.wireNode (
              'expandTreeListAll',
              'click',
              function () {_this.children.treeList.setExpandedDepth (Infinity)}
            );

          /*** wire up link for getting items in the tree list as a report ***/
            _this.wireNode (
              'getTreeListItemsAsReport',
              'click',
              function () {
                var
                  _reportTitle = _treeListQueries [_this._treeListQuery]._title,
                  _reportLines = []
                ;
                _this.children.treeList.traverseTree ({
                  itemHandler:function (_item,_itemSpecifier,_depth) {
                    _reportLines.push (Uize.String.repeat ('\t',_depth) + _item.title);
                  }
                });
                _this._showReport (
                  _reportTitle,
                  'REPORT FOR: ' + _this._window.location.href + '\n' +
                  _reportDivider + '\n' +
                  'REPORT TYPE: ' + _reportTitle + ' (' + _reportLines.length + ' items)\n' +
                  _reportDivider + '\n' +
                  _reportLines.join ('\n')
                );
              }
            );

          /*** wire up link for getting a summary of all available queries as a report ***/
            _this.wireNode (
              'getAllQueriesSummary',
              'click',
              function () {
                var _querySummaries = [];
                for (var _treeListQueryName in _treeListQueries) {
                  var
                    _treeListQuery = _treeListQueries [_treeListQueryName],
                    _itemsGenerator = _treeListQuery._itemsGenerator
                  ;
                  function _countItems (_items) {
                    if (!_items) return 0;
                    var _totalItems = _items.length;
                    for (var _itemNo = _totalItems; --_itemNo > -1;)
                      _totalItems += _countItems (_items [_itemNo].items)
                    ;
                    return _totalItems;
                  }
                  _querySummaries.push (
                    _treeListQuery._title +
                    (
                      _itemsGenerator
                        ? (' -- ' + _countItems (_itemsGenerator.call (_this)) + ' items')
                        : ''
                    )
                  );
                }
                _this._showReport (
                  'Summary of all available queries',
                  'SUMMARY OF ALL AVAILABLE QUERIES FOR: ' + _this._window.location.href + '\n' +
                  _reportDivider + '\n' +
                  _querySummaries.join ('\n')
                );
              }
            );

          _superclass.prototype.wireUi.call (_this);

          _this.set ({_objectInspectedPath:_this._getPageWidgetName ()});
        }
      };

    /*** Register Properties ***/
      _class.registerProperties ({
        _baseUrl:{
          name:'baseUrl',
          value:''
          /* NOTE:
            This state property is needed for IE, because even though the base tag is in the DELVE page and IE resolves relative stylesheet URLs just fine, the base tag is apparently not good enough for IE when it comes to changing the documentation IFRAME's URL by setting its src.
          */
        },
        _documentationUrl:{
          onChange:_classPrototype._updateUiDocumentation
        },
        _objectInspected:{
          name:'objectInspected',
          onChange:[
            _classPrototype._updateUiVariousaTabs,
            _classPrototype._updateEventsLog
          ]
        },
        _objectInspectedPath:{
          name:'objectInspectedPath',
          conformer:function (_value) {
            return (
              !this._resolveToObject (_value) &&
              this._window && this._window.document.getElementById (_value)
                ? 'document.getElementById (\'' + _value + '\')'
                : _value
            );
          },
          onChange:[
            function () {this.set ({_objectInspected:this._resolveToObject (this._objectInspectedPath)})},
            _classPrototype._updateObjectEntry
          ],
          value:''
        },
        _treeListQuery:{
          name:'treeListQuery',
          onChange:[
            _classPrototype._updateTreeListItems,
            _classPrototype._updateUiTreeListDropdown
          ],
          value:'widgetsOnPageTree'
        },
        _window:{
          name:'window',
          onChange:[
            function () {this._widgetNodeCachePropertyName = _undefined},
            _classPrototype._updateTreeListItems,
            _classPrototype._updateUiWindowInspected
          ]
        }
      });

    return _class;
  }
});