text: /* Copyright 2013 Twitter, Inc. Licensed under The MIT License. http://opensource.org/licenses/MIT */
  
  define(
  
    ['./registry'],
  
    function(registry) {
      'use strict';
  
      // ==========================================
      // Search object model
      // ==========================================
  
      function traverse(util, searchTerm, options) {
        options = options || {};
        var obj = options.obj || window;
        var path = options.path || ((obj == window) ? 'window' : '');
        var props = Object.keys(obj);
        props.forEach(function(prop) {
          if ((tests[util] || util)(searchTerm, obj, prop)) {
            console.log([path, '.', prop].join(''), '->', ['(', typeof obj[prop], ')'].join(''), obj[prop]);
          }
          if (Object.prototype.toString.call(obj[prop]) == '[object Object]' && (obj[prop] != obj) && path.split('.').indexOf(prop) == -1) {
            traverse(util, searchTerm, {obj: obj[prop], path: [path,prop].join('.')});
          }
        });
      }
  
      function search(util, expected, searchTerm, options) {
        if (!expected || typeof searchTerm == expected) {
          traverse(util, searchTerm, options);
        } else {
          console.error([searchTerm, 'must be', expected].join(' '));
        }
      }
  
      var tests = {
        'name': function(searchTerm, obj, prop) {return searchTerm == prop;},
        'nameContains': function(searchTerm, obj, prop) {return prop.indexOf(searchTerm) > -1;},
        'type': function(searchTerm, obj, prop) {return obj[prop] instanceof searchTerm;},
        'value': function(searchTerm, obj, prop) {return obj[prop] === searchTerm;},
        'valueCoerced': function(searchTerm, obj, prop) {return obj[prop] == searchTerm;}
      };
  
      function byName(searchTerm, options) {search('name', 'string', searchTerm, options);}
      function byNameContains(searchTerm, options) {search('nameContains', 'string', searchTerm, options);}
      function byType(searchTerm, options) {search('type', 'function', searchTerm, options);}
      function byValue(searchTerm, options) {search('value', null, searchTerm, options);}
      function byValueCoerced(searchTerm, options) {search('valueCoerced', null, searchTerm, options);}
      function custom(fn, options) {traverse(fn, null, options);}
  
      // ==========================================
      // Event logging
      // ==========================================
  
      var ALL = 'all'; //no filter
  
      //log nothing by default
      var logFilter = {
        eventNames: [],
        actions: []
      }
  
      function filterEventLogsByAction(/*actions*/) {
        var actions = [].slice.call(arguments);
  
        logFilter.eventNames.length || (logFilter.eventNames = ALL);
        logFilter.actions = actions.length ? actions : ALL;
        saveLogFilter();
      }
  
      function filterEventLogsByName(/*eventNames*/) {
        var eventNames = [].slice.call(arguments);
  
        logFilter.actions.length || (logFilter.actions = ALL);
        logFilter.eventNames = eventNames.length ? eventNames : ALL;
        saveLogFilter();
      }
  
      function hideAllEventLogs() {
        logFilter.actions = [];
        logFilter.eventNames = [];
        saveLogFilter();
      }
  
      function showAllEventLogs() {
        logFilter.actions = ALL;
        logFilter.eventNames = ALL;
        saveLogFilter();
      }
  
      function saveLogFilter() {
        try {
          if (window.localStorage) {
            localStorage.setItem('logFilter_eventNames', logFilter.eventNames);
            localStorage.setItem('logFilter_actions', logFilter.actions);
          }
        } catch (ignored) {};
      }
  
      function retrieveLogFilter() {
        var eventNames, actions;
        try {
          eventNames = (window.localStorage && localStorage.getItem('logFilter_eventNames'));
          actions = (window.localStorage && localStorage.getItem('logFilter_actions'));
        } catch (ignored) {
          return;
        }
        eventNames && (logFilter.eventNames = eventNames);
        actions && (logFilter.actions = actions);
  
        // reconstitute arrays in place
        Object.keys(logFilter).forEach(function(k) {
          var thisProp = logFilter[k];
          if (typeof thisProp == 'string' && thisProp !== ALL) {
            logFilter[k] = thisProp ? thisProp.split(',') : [];
          }
        });
      }
  
      return {
  
        enable: function(enable) {
          this.enabled = !!enable;
  
          if (enable && window.console) {
            console.info('Booting in DEBUG mode');
            console.info('You can configure event logging with DEBUG.events.logAll()/logNone()/logByName()/logByAction()');
          }
  
          retrieveLogFilter();
  
          window.DEBUG = this;
        },
  
        registry: registry,
  
        find: {
          byName: byName,
          byNameContains: byNameContains,
          byType: byType,
          byValue: byValue,
          byValueCoerced: byValueCoerced,
          custom: custom
        },
  
        events: {
          logFilter: logFilter,
  
          // Accepts any number of action args
          // e.g. DEBUG.events.logByAction("on", "off")
          logByAction: filterEventLogsByAction,
  
          // Accepts any number of event name args (inc. regex or wildcards)
          // e.g. DEBUG.events.logByName(/ui.*/, "*Thread*");
          logByName: filterEventLogsByName,
  
          logAll: showAllEventLogs,
          logNone: hideAllEventLogs
        }
      };
    }
  );
  