/**************
 * Global Namespace for all spiceworks stuff
 */
var SPICEWORKS = {};

if (document.location.toString().match(/logEvents/)) {
  SPICEWORKS.logEvents = true;  
}

// JSLINT - The following statement will allow this file to pass the jslint tests by adding a couple of global variables
/*global Element, $, $$, Event, GMap2, Ajax */


// Create a closure, passing in the global namespace (SPICEWORKS) to a local variable (spiceworks)
(function (spiceworks) {
  
  // Spiceworks Events
  // Register for events that might be occuring
  // Passes through to the browser event code (prototype) for now, but by change in the future
  spiceworks.observe = function (event, func) {
    Event.observe(document, 'spiceworks:' + event, func);
  };
  
  // catch the loaded event so we'll know that we're ready to go.
  spiceworks.isReady = false;
  
  // Fire an event within the spiceworks infrastructure.
  // the event name is currently attacted to the DOM.  
  // "spiceworks:" will be prepended to all events
  // immediate will force the event to fire even if the page is not "ready" (dom fully loaded).
  // By default, events will be queued up until the document is fully loaded
  // there is (currently) no guarentee on the order that events will be fired once added (FF and IE are different)
  spiceworks.fire = function (event, memo) {
    memo = memo || {};
        
    if (event == 'ready') {
      spiceworks.isReady = true;
    }
    
    // if the page is not ready, then try again once the page is ready.
    if (spiceworks.isReady || memo.immediate) {
      
      if (spiceworks.logEvents){
        console.log(' [Fire] ' + event + '  ' + Object.toJSON(memo));
      }
      document.fire('spiceworks:' + event, memo);
    } else {
      if (spiceworks.logEvents){
        console.log('[Delay] ' + event + '  ' + Object.toJSON(memo));
      }
      spiceworks.ready(function () { spiceworks.fire(event, memo); });
    }
  };

  // This is called once all plugins are given the chance to load.  Use this if you depend on something in another plugin.
  // If this app is already loaded, then the function will be called immediately (unless onLoadOnly is passed as true).
  spiceworks.ready = function (func, loadOnly) {
    if (spiceworks.isReady && !loadOnly) {
      func(); // app already initialized, so let's just call the method.
    } else {
      spiceworks.observe('ready', func);      
    }
  };  

  // Create an object which derives from another object.
  // See: http://javascript.crockford.com/prototypal.html
  spiceworks.clone = function (object) {
    if (!object) {
      return {};
    }
    var Creator = function () { 
      object.constructor = arguments.callee; 
    };
    Creator.prototype = object;
    return new Creator();
  };



  // == Plugins Namespace ==
  // Manages the spiceworks plugins.
  spiceworks.plugin = function () {
    var plugins = [],
        that;
    that = {
      add: function (plugin) {
        /* Verify Attributes */
        if (!plugin.name) { 
          throw "Plugin name is required"; 
        }
        if (!plugin.version) { 
          throw "Plugin version is required"; 
        }
        if (!plugin.initialize) { 
          throw "Plugin initialize function is required"; 
        }

        /* A method for the plugin writer to define user settings*/
        plugin.configure = function (conf) {
          plugin.settingDefinitions = conf.settingDefinitions;
          if (!plugin.settings) {
            plugin.settings = {};
          }
          plugin.settingDefinitions.each( function (settingDefinition) {
            if (!plugin.settings[settingDefinition.name]){
              plugin.settings[settingDefinition.name] = settingDefinition.defaultValue;
            }
          });
        };
        
        /* A helper method for storing data for this plugin */
        plugin.store = function (key, data, callback) {
          spiceworks.persistence.store(plugin.guid + '_' + key, data, callback);
        };
        /* A helper method for loading data for this plugin*/
        plugin.load = function (key, callback, defaultValue) {
          spiceworks.persistence.load(plugin.guid + '_' + key, callback, defaultValue);
        };
        /* A helper method for removing data from the system*/
        plugin.remove = function (key, callback) {
          spiceworks.persistence.remove(plugin.guid + '_' + key, callback);
        };
        /* A method to store the settings for this plugin.  Settings are loaded by default. */
        plugin.storeSettings = function (settings, callback) {
          plugin.store( 'settings', settings, callback );
          plugin.settings = settings;
        };

        /* Capture the plugin and return a reference to it. */
        plugins.push(plugin);
        return plugin;
      },

      initializePlugins: function () {
        plugins.each(function (plugin) {
          try {
            if(!plugin.hasBeenInitialized) {
              plugin.initialize(plugin);
              plugin.hasBeenInitialized = true;
              spiceworks.fire('plugin:initialized', {immediate: true, pluginName:plugin.name});
            }
            // Fire off an event to tell the world that we've initialized.
          } catch (e) {
            // there was an error initializing a plugin...
            // Ignoring this for now.
          }
        });
        spiceworks.fire('plugins:initialized', {immediate: true});
      },

      getPlugins: function () {
        return plugins;
      },
      
      getPlugin: function (guid) {
        var plugin = plugins.find(function (plugin) { 
          return (plugin.guid === guid); 
        });
        return plugin;
      }
    };

    return that;
  }();

  Event.observe(document, 'dom:loaded', function (e) {    
    spiceworks.plugin.initializePlugins();
    spiceworks.fire('ready');
  });



  // == App Namespace ==
  spiceworks.app = function () {
    var that, userMethods;
    
    that = {
      ready: function (func) {
        spiceworks.observe('app:ready', func);
      },
      
      isShowing: function () {
        return $(document.body).hasClassName('spiceworks_application');        
      },
      
      setUser: function (user) {
        that.user = user;
      }      
    };
    return that;
  }();
  
  // == SPICEWORKS.portal ==
  spiceworks.portal = function () {
    var that = {
      ready: function (callback) {
        spiceworks.observe('portal:ready', callback);
      },
      
      isShowing: function () {
        return $(document.body).hasClassName('spiceworks_portal');
      }
    };
    return that;  
  }();
  
  
  // == SPICEWORKS.app.dashboard ==
  // Add Widget Types and tabs here.  A plugin should not add a widget to the user's page.
  spiceworks.app.dashboard = function () {
    var widgetTypes = [],
        tabs = [],
        that; // TODO

    that = {
      // Create a new type of widget
      addWidgetType: function (widgetType) {
        var imgsrc, widgetList, li;
        
        if (!widgetType.name) { 
          throw 'WidgetType name required'; 
        }
        if (!widgetType.label) { 
          throw 'WidgetType label required'; 
        }
        if (!widgetType.update) { 
          throw 'WidgetType update method required'; 
        }
        
        /* Support either settingDefinitions or prefs for the preferences. */
        if (widgetType.prefs && !widgetType.settingDefinitions) {
          widgetType.settingDefinitions = widgetType.prefs;
        }
        
        /* Default the settingDefinitions to be an empty array if none were specified. */
        if( !widgetType.settingDefinitions ){
          widgetType.settingDefinitions = [];
        }
        
        widgetTypes.push(widgetType);

        /* Only do the following if we're on the dashboard */
        if (that.isShowing()) { 
          imgsrc = widgetType.icon || '/images/icons/small/gear.png';
        
          widgetList = $$('#dashboard_quickform ul').first();
          if (widgetList) {
            li = new Element('li', {'id': widgetType.name + '_small', 'class': 'module-small classname-javascript'});
            li.update('<a href="#"><img src="' + imgsrc + '"></img><span class="title">' + widgetType.label + '</span></a>');
            widgetList.insert(li);
          }
        }
      },

      getWidgetTypes: function () {
        return widgetTypes;
      },
      
      getWidgetType: function (name) {
        var widgetType = widgetTypes.find(function (widgetType) { 
          return (widgetType.name === name); 
        });
        
        if (widgetType == null) {
          widgetType = {
            name: name,
            label: 'Missing Definition',
            settingDefinitions:[],
            update: function (element, settings) {
              element.innerHTML = 'The definition of this widget is missing.  This is probably due to a plugin being disabled or removed.';
            }
          };
        }
        
        return widgetType;
      },
      
      isShowing: function () {
        return $(document.body).hasClassName('dashboard');
      },
      
      ready: function (callback) {
        spiceworks.observe('app:dashboard:ready', callback);
      }
    };
    
    return that;
  }();
  
  
  // == SPICEWORKS.app.navigation ==
  // define new tools (i.e. pages) that will be shown when the user clicks on them.
  spiceworks.app.navigation = function () {
    var items = [],
        menuItems = [],
        that;

    that = {
      addItem: function (item) {
        var linkElem = null;

        items.push(item);

        if (!spiceworks.app.isShowing()) { 
          return; 
        } // do nothing unless the app is showing.
        
        item.element = new Element('dd', {});
        linkElem = new Element('a', {'href': '/tools/' + item.name, 'id': item.name});
        linkElem.update(item.label);
        item.element.insert(linkElem);

        $('navigation_my_stuff').insert(item.element);
      },

      // Show the specified tool
      showItem: function (name) {
        var content, item;        
        item = items.find(function (item) { 
          return (item.name === name); 
        });

        $$('dd.current').each(function (e) {
          e.removeClassName('current');
        });
        item.element.addClassName('current');
        content = $('content');
        content.innerHTML = '';

        item.update(content);
        document.title = 'Spiceworks - ' + item.label;
      },

      getItems: function () {
        return items;
      },
      
      addMenuItem: function (spec) {
        menuItems.push(spec);
      },
      
      addMenuItems: function (spec) {
        that.addMenuItem(spec);
      },
      
      // Add a menu item once the page is loaded.
      dynamicAddMenuItem: function (spec) {
        $$(spec.selector).each( function ( itemElemOrig ) {
          var menuItem, itemElem, items;
          
          // Find the DD
          if( itemElemOrig.nodeName != 'DD'){
            itemElem = itemElemOrig.up('DD');              
          }else{
            itemElem = itemElemOrig;
          }

          // Build the menu if needed.
          if( !itemElem.menu ){
            itemElem.pivotable = new Element('div', {style: 'display:none; right:-1px; top: 16px; zoom:1; width:130px', 'class':'pivotable'});
            itemElem.menu = new Element('ul', {'class':'nav_menu'});
            itemElem.pivotable.insert(itemElem.menu);
            itemElem.link = itemElem.down('a.dashboard_page_link');
            itemElem.opener = new Element('a',{'class': 'meta edit', 'style':'cursor:pointer'});
            // itemElem.keepOpen = false;

            // insert the menu helper
            itemElem.insert({top: itemElem.opener});
            itemElem.opener.style.display='none';


            itemElem.insert({bottom:itemElem.pivotable});
            
            var keepOpen = false,
                menuTimeout = null,
                openerTimeout = null,
                showMenuTimeout = null;
            
            
            function showOpener() {
              if (!itemElem.hasClassName('editing') && !itemElem.down('input')){
                itemElem.opener.style.display='block';
              }else{
                itemElem.opener.style.display='none';
              }
            }
            function hideOpener() {
              itemElem.opener.removeClassName('on');
              itemElem.opener.style.display='none';
            }
            
            function hideMenu() {
              itemElem.opener.removeClassName('on');
              itemElem.pivotable.hide();
              itemElem.style.zIndex=null;
              var dl = itemElem.up('dl');
              if(dl){
                itemElem.up('dl').style.zIndex=null;                
              }
            }
            
            function showMenu() {
              itemElem.pivotable.show();
              itemElem.style.zIndex=500;
              itemElem.up('dl').style.zIndex=500;
              itemElem.opener.addClassName('on');
            }
            
            function hideMenuAndOpener() {
              hideMenu();
              hideOpener();
            }
            
            

            function itemOver(event) {
              // console.log('itemOver: '+ itemElem.link.innerHTML);
              Event.stop(event);
              clearTimeout(openerTimeout);
              clearTimeout(showMenuTimeout);
              openerTimeout = setTimeout( showOpener, 100);
            }
            function itemOut(event) {
              // console.log('itemOut'+ itemElem.link.innerHTML);
              Event.stop(event);
              clearTimeout(openerTimeout);
              openerTimeout = setTimeout( hideMenuAndOpener, 100);
            }
            
            function openerOver(event) {
              // console.log('openerOver'+ itemElem.link.innerHTML);
              Event.stop(event);
              clearTimeout(menuTimeout);
              clearTimeout(openerTimeout);
              showMenuTimeout = setTimeout(showMenu, 200);
            }
            function openerOut(event) {
              // console.log('openerOut'+ itemElem.link.innerHTML);
              Event.stop(event);
              clearTimeout(menuTimeout);
              clearTimeout(openerTimeout);
              clearTimeout(showMenuTimeout);
              menuTimeout = setTimeout( hideMenu, 100);
            }
            
            function menuOver(event) {
              // console.log('menuOver'+ itemElem.link.innerHTML);
              Event.stop(event);
              clearTimeout(openerTimeout);              
              clearTimeout(menuTimeout);              
            }
            function menuOut(event) {
              // console.log('menuOut'+ itemElem.link.innerHTML);
              Event.stop(event);
              clearTimeout(menuTimeout);
              menuTimeout = setTimeout(hideMenu, 100);
              openerTimeout = setTimeout(hideOpener, 100);

            }
            


            Event.observe(itemElem, 'mouseover', itemOver);
            Event.observe(itemElem, 'mouseout', itemOut);

            Event.observe(itemElem.opener, 'mouseover', openerOver);
            Event.observe(itemElem.opener, 'mouseout', openerOut);

            Event.observe(itemElem.pivotable, 'mouseover', menuOver);
            Event.observe(itemElem.pivotable, 'mouseout', menuOut);
          }
          
          // Add the new item(s) to the menu.          
          // determine if multiple items have been passed in or not.
          if (spec.items) {
            items = spec.items;
          } else {
            items = [spec];
          }
          
          items.each(function (itemSpec) {
            if (!itemSpec.className) {
              throw 'Menu item specification must include className.';
            }
            
            if (!itemElem.menu.down('.' + itemSpec.className)) {
              
              if (itemSpec.separator) {
                itemElem.menu.insert(new Element('li').insert(new Element('div', {'class':'separator icon ' + itemSpec.className}).update('&nbsp;')));
              } else {
                menuItem = new Element('a', {href:itemSpec.href || '#', 'class':'icon ' + itemSpec.className, 'target': (itemSpec.target || '_top')}).update(itemSpec.label);
                if (itemSpec.onclick) {
                  Event.observe( menuItem, 'click', function (e) {
                    Event.stop(e);
                    itemElem.pivotable.hide();
                    itemSpec.onclick( itemElemOrig );
                  });                  
                }
                itemElem.menu.insert(new Element('li').update(menuItem));                            
              }
            }
          });
        });
      },
      
      updateNavMenus: function () {
        menuItems.each( function (spec) {
          that.dynamicAddMenuItem(spec);
        });        
      }
    };
    
    // Attach the menu items once the app is ready.
    // This is also done in _customizable_sections.html.erb
    // It's fine to call this twice, it figures out what to do.
    spiceworks.app.ready(that.updateNavMenus);

    return that;
  }();
  
  
  // == SPICEWORKS.app.helpdesk ==
  // Currently, we just have stuff in here to help with events
  spiceworks.app.helpdesk = function () {
    var that = {
      ready: ready_func('app:helpdesk:ready'),
      newTicket: {
        ready: ready_func('app:helpdesk:new_ticket:ready')
      },
      ticket: {
        ready: ready_func('app:helpdesk:ticket:ready'),
        closed: ready_func('app:helpdesk:ticket:closed')
      }
    };
    return that;
  }();
  
  
  // == SPICEWORKS.app.inventory ==
  // Currently, we just have stuff in here to help with events
  spiceworks.app.inventory = function () {
    var that = {
      ready: ready_func('app:inventory:ready'),
      
      environment: {
        ready: ready_func('app:inventory:environment_summary:ready')
      },

      group: {
        ready: ready_func('app:inventory:group:ready'),
        device: {
          ready: ready_func('app:inventory:group:device:ready')
        },
        environment: {
          ready: ready_func('app:inventory:group:environment_summary:ready')
        }
      },      
      
      softwareGroup: {
        ready: ready_func('app:inventory:software_group:ready'),
        
        environment: {
          ready: ready_func('app:inventory:software_group:environment_summary:ready')
        },

        software:{
          ready: ready_func('app:inventory:software_group:software:ready')
        },
        service:{
          ready: ready_func('app:inventory:software_group:services:ready')
        },
        hotfix:{
          ready: ready_func('app:inventory:software_group:hotfixes:ready')
        }        
      }
    };
    return that;
  }();
  
  
  
  
  
  // == SPICEWORKS.app.messaging ==
  // Allow users to push and pop messages into the header messaging area
  // This is a fairly thin wrapper around the current messaging infrastructure.
  spiceworks.app.messaging = {
    push: function (messageText, options) {
      var message = Object.extend({
        dismissable: false, // give the message a clickable element to remove it
        selfRemoving: false, // make the message remove itself after timeSeconds have lapsed
        timeoutSeconds: 5, // when selfRemoving is true, this is time duration the message is displayed
        id: 'p_' + (new Date()).getTime()
      }, options || {});
      
      Messaging.push( message.id, messageText, message );
            
      return message.id;
    },
    
    pop: function (messageId) {
      Messaging.pop(messageId);
    }
  };
  
  // == SPICEWORKS.app.settings ==
  spiceworks.app.settings = function () {
    var that = {
      ready: ready_func('app:settings:ready')
    };
    
    // Setup ready functions for each othe settings panes.
    ['help_desk', 'monitors', 'network', 'email', 'accounts', 'spicemeter', 'events', 'categories', 'backup', 'advanced'].each(function (settingsPane) {
      that[settingsPane] = {
        ready: ready_func('app:settings:' + settingsPane + ':ready')
      };
    });    
    
    return that;
  }();
  
  
  // == SPICEWORKS.app.settings.plugin ==
  // Some helpers for extending the plugins page.  Currently, there's only support here for
  // adding example code to the "Insert Code" dropdown.
  spiceworks.app.settings.plugin = function () {
    var codeExamples = [],
        that;
    
    that = {
      
      ready: ready_func( 'app:settings:plugin:ready' ),
      
      viewer: {
        ready: ready_func('app:settings:plugin:viewer:ready')
      },
      
      editor: {
        ready: ready_func( 'app:settings:plugin:editor:ready' ),

        // spec needs {label: '', code: ''}
        addCodeExample: function (spec) {
          codeExamples.push(spec);
        },
        
        getCodeExamples: function () {
          return codeExamples;
        },
        
        insertCode: function (code) {
          // IE doesn't like it when the pre tag doesn't have focus and you try to insert code.
          if (Browser.ie6 || Browser.ie7) {
            plugin_content.editor.body.getElementsByTagName('pre')[0].focus();
            code = code.gsub(/\n/, '\n\n'); // IE doesn't deal with new lines properly for some reason.
          }

          plugin_content.editor.insertCode(code);
          plugin_content.editor.syntaxHighlight();
        },
        
        setCode: function (code) {
          // IE doesn't like it when the pre tag doesn't have focus and you try to insert code.
          if (Browser.ie6 || Browser.ie7) {
            plugin_content.editor.body.getElementsByTagName('pre')[0].focus();
            code = code.gsub(/\n/, '\n\n'); // IE doesn't deal with new lines properly for some reason.
          }

          plugin_content.editor.setCode(code);
          plugin_content.editor.syntaxHighlight();          
        }
        
      }
    };
    
    
    // Now watch for this event and actually plug in the new code.
    that.editor.ready( function () {
      var elem = $('insert_code_list'),
          link, li;

      codeExamples.each( function (example) {
        li = new Element('li');
        link = new Element('a', {href: '#'} );
        li.insert(link);
        link.update( example.label );
        
        Event.observe( link, 'click', function (e) {
          Event.stop(e);
          that.editor.insertCode( example.code );
        });
        
        elem.insert(li);
      });
    });
    
    return that;
  }();
  
  // == SPICEWORKS.field ==
  // Datatypes that will be used to render editors
  // These objects are delegates that will handle rendering different types of values in the configuration sections of widgets
  // Field builders are expected to return an object with a set() and get() method.  They can return more than this, but these two
  // are required for them to work with widgets and the dashboard.
  spiceworks.field = {
    // creation helper
    // fieldDesc => { name:'foo', type:'string', label:'Foo', defaultValue:'Life is good' }
    createField: function (element, fieldDef, value) {
      var field = (spiceworks.field[fieldDef.type] || spiceworks.field.string)(element, fieldDef);
      if (value) { 
        field.set(value); 
      }
      return field;
    },
    
    // Create a set of fields within the element passed using the options passed.
    // options should be like widget options (including an id)
    createFieldSet: function (element, fieldDefs) {
      var fields = {},
          nsField = spiceworks.field,  // temp namespace
          fieldset;
          
      fieldset = {
        // return all the values
        get: function () {
          var options = {};
          Object.keys(fields).each(function (fieldname) {
            options[fieldname] = fields[fieldname].get();
          });
          return options;
        },
        
        set: function (options) {
          Object.keys(fields).each(function (fieldname) {
            fields[fieldname].set(options[fieldname]);
          });
        },
        fields: fields      
      };
      
      // Initialize the fields
      fieldDefs.each(function (field) {
        var fieldElement = new Element('div', {'class': field.name + ' setting', 'id':field.name + "_" + (Math.floor(Math.random()*1000+1))});
        element.insert(fieldElement);
        fields[field.name] = nsField.createField(fieldElement, field);
        //if( options[field.name] ){  fields[field.name].set( options[field.name] ); }
      });
      
      return fieldset;
    },

    // TYPES
    // TODO: add validation...
    string: function (element, fieldDef) {
      var id = element.id + "_value";
      element.insert("<label for='" + id + "'>" + fieldDef.label + "</label>");
      element.insert("<input id='" + id + "' name='" + id + "' type='string' class='text' value='" + (fieldDef.defaultValue ? fieldDef.defaultValue : '') + "'></input>");
      if (fieldDef.example) {
        element.insert(" <span class='example'>" + fieldDef.example + "</span>");
      }
      return {
        get: function () {
          return element.down('input').value;
        },
        set: function (value) {
          element.down('input').value = value;
        }
      };
    },
    
    enumeration: function (element, fieldDef) {
      var select, 
          id = element.id + "_value";
          
      element.insert("<label for='" + id + "'>" + fieldDef.label + "</label>");
      
      select = new Element('select', {'id': id, 'name': id});
      element.insert(select);
      
      if (fieldDef.options) {
        fieldDef.options.each( function (option) {
          if(typeof(option)==='string'){
            select.insert(new Element('option').update(option));            
          }else{
            select.insert(new Element('option', {'value':option[1]}).update(option[0]));
          }
        });
      }
      
      if (fieldDef.example) {
        element.insert(" <span class='example'>" + fieldDef.example + "</span>");
      }
      
      return {
        get: function () {
          return $F(select);
        },
        set: function (value) {
          for(index = 0; index < select.options.length; index++) {
            if ((select.options[index].value || select.options[index].text) === value) {
              select.selectedIndex = index;
            }
          }
        }
      };
    },

    textarea: function (element, fieldDef) {
      var textarea,
          id = element.id + "_value";   
      element.insert("<label for='" + id + "'>" + fieldDef.label + "</label>");  
      if (fieldDef.example) {
        element.insert("<div style='float:right;width: 210px'><span  class='example' style='vertical-align:top'>" + fieldDef.example + "</span></div>");
      }
      textarea = new Element('textarea', {'id': id, 'name': id, 'style':''});
      element.insert(textarea);

      return {
        get: function () {
          return textarea.value;
        },
        set: function (value) {
          textarea.value = value;
        }
      };
    },
    
    checkbox: function (element, fieldDef) {
      var checkbox,
          id = element.id + "_value";
      element.insert("<label for='" + id + "'>" + fieldDef.label + "</label>");  
      checkbox = new Element('input', {'id':id, 'type':'checkbox'});
      element.insert(checkbox);
      
      if (fieldDef.example) {
        element.insert(" <span class='example'>" + fieldDef.example + "</span>");
      }
      
      return {
        get: function () {
          return checkbox.checked;
        },
        set: function(value) {
          checkbox.checked = value;
        }
      };
          
    },
    
    date: function (element, fieldDef) {
      var trigger, input,  id = element.id + "_value";
      element.insert("<label for='" + id + "'>" + fieldDef.label + "</label>");
      
      input = new Element('input', {'id':id, 'name':id, 'type':'string', 'class':'text date_field'});
      element.insert(input);
      
      trigger = new Element('a', {'id': id + '_trigger', 'href':'javascript:void(0)', 'class':'calendar_trigger'} ).update('<img title="Choose a date from the calendar" src="/images/icons/calendar.png" alt="Calendar"/>');
      element.insert(trigger);
      
      CalendarPopup.setup(input, trigger);
      
      if (fieldDef.example) {
        element.insert(" <span class='example'>" + fieldDef.example + "</span>");
      }
      
      return {
        get: function () {
          return input.value;
        },
        set: function (value) {
          input.value = value.gsub('/','-');
        }
      };
    }
    
  };




  // == SPICEWORKS.data ==
  spiceworks.data = function () {
    var tempStore = {},
        that;
    
    that = {
      
      // Models
      // This is very alpha right now.  Check here for status updates as we fix this.
      // See Jester Docs for more detail on how to use these: http://thoughtbot.com/projects/jester
      Ticket:       Jester.Resource.model('Ticket', {prefix: '/api', format: 'json'}),
      Device:       Jester.Resource.model('Device', {prefix: '/api', format: 'json'}),
      Alert:        Jester.Resource.model('Alert', {prefix: '/api', format:'json'}),
      DataMonitor:  Jester.Resource.model('DataMonitor', {prefix: '/api', format: 'json'}),
      Activity:     Jester.Resource.model('Activity', {format: 'json'}),
      Agreement:    Jester.Resource.model('Agreement', {format: 'json'}),

      // Following need more testing
      Group:        Jester.Resource.model('Group', {prefix: '/api', format:'json'}),
      Software:     Jester.Resource.model('Software', {prefix: '/api', format:'json', plural:'software'}),
      Service:      Jester.Resource.model('Service', {prefix: '/api', format:'json', plural:'services'}),
      Hotfix:       Jester.Resource.model('Hotfix', {prefix: '/api', format:'json', plural:'hotfixes'}),
      User:         Jester.Resource.model('User', {prefix: '/api', format:'json' }),
      Report:       Jester.Resource.model('Report', {prefix: '/api', format:'json' }),
      

      // Run a report and pass the resulting JSON to the callback...
      // callback => function(json, transport){}
      // TODO: get report name working as well as id
      runReport: function (report_id, callback) {
        (new Ajax.Request('/reports/show/' + report_id + '.json', {
          method: 'get',
          onSuccess: function (transport) {
            var json = transport.responseText.evalJSON();
            callback(json, transport);
          },
          onFailure: function (transport) {
            callback({ items:[], columns: [], error: transport.responseText });
          }
        }));
      }
    };

    return that;
  }();

  // == SPICEWORKS.persistence ==
  // Store documents (hashes) in a specified location for later retrieval.
  // Currently, just a key/value store.  Document can be a hash, an array, or just a string.
  spiceworks.persistence = function () {
    // helper method to process results from calls
    function wrap(callback) {
      if (callback) {
        return function (transport) {
          var result = transport.responseText;
          if (result.isJSON()) {
            result = result.evalJSON();
          }
          callback(result, transport);
        };        
      } else {
        return function () {};
      }
    }
    
    var that = {      
      // callback is optional
      store: function (key, doc, callback) {
        (new Ajax.Request('/documents/' + key + '.json', {
          method: 'put',
          parameters: { 'doc': Object.toJSON(doc) },
          onSuccess: wrap(callback)
        }));
      },
            
      load: function (key, callback, defaultValue) {
        (new Ajax.Request('/documents/' + key + '.json', {
          method: 'get',
          onSuccess: wrap(callback),
          onFailure: function (transport) {
            callback(defaultValue,transport);
          }
        }));
      },
      
      loadAll: function (prefix, callback, defaultValue) {
        if (prefix && prefix !== '') {
          (new Ajax.Request('/documents.json?prefix=' + prefix, {
            method: 'get',
            onSuccess: wrap(callback),
            onFailure: function (transport) {
              callback(defaultValue,transport);
            }
          }));          
        }
      },
      
      remove: function (key, callback) {
        (new Ajax.Request('/documents/' + key + '.json', {
          method: 'delete',
          onSuccess: wrap(callback)
        }));
      }
    };
    
    return that;
  }();

  // == SPICEWORKS.config ==
  // placeholder for configuration options
  // see _script_includes.html.erb for more info on how this gets setup.
  spiceworks.config = {};


  // == SPICEWORKS.utils ==
  // A namespace for things that don't fit anywhere else currently.  These are mostly related to integration
  // with external services and the like.
  spiceworks.utils = {};
  
  // Include an external script
  // EX:
  //   SPICEWORKS.utils.include('http://myserver/my.js');
  spiceworks.utils.include = function (script_filename, callback) {
    var head = document.getElementsByTagName('head')[0],
        script = document.createElement('script');
        
    script.type = 'text/javascript';
    script.onreadystatechange = function () {
      if (this.readyState === 'complete' || this.readyState === 'loaded') {
        callback(); 
      }
    };
    script.onload = callback;
    script.src = script_filename;
    head.appendChild(script);
  };
  
  // Quickly add some style to the document.  Any valid Stylesheet style string works here.
  // EX:  SPICEWORKS.utils.addStyle("body { background-color: red }");
  spiceworks.utils.addStyle = function (styleText) {
    CSSLoader.load(styleText);
  };
  
  
  // == SPICEWORKS.utils.google ==
  // Namespace for all sorts of nice google goodies
  // Users will need to set their own "key" by setting 
  //    SPICEWORKS.utils.google.key = 'mykey';
  spiceworks.utils.google = function () {
    var googleLoaded = false,
        google = null,
        that;
    
    that = {
      key: 'ABQIAAAAXU7eNwZ9M4Sc9cn16StyDBRFChKT55K5FMfS9iKz1mkixBESPBSb4vJEgLxDzIfGJkiGB3x3myIrcQ',
      
      withGoogle: function (callback) {
        if (!googleLoaded) {
          spiceworks.utils.include("http://www.google.com/jsapi?key=" + that.key, function () {
            google = window.google; // google is global, just grab for now.
            googleLoaded = true;
            
            callback(google);
          });
        } else {
          callback(google);
        }
      },
      
      withMaps: function (callback) {
        that.withGoogle(function (google) {
          google.load('maps', '2.x', {callback: function () {
            callback(GMap2);
          }});
        });
      },
      
      withVisualizations: function (packages, callback) {
        that.withGoogle(function (google) {
          google.load("visualization", "1", {packages: packages, callback: function () {
            callback(google.visualization);
          }});
        });        
      }
    };
    
    return that;
  }();
  
  
  // ------- HELPER FUNCTIONS ------
  // Helper function to define spiceworks observer functions
  function ready_func(event) {
    return function( callback ) {
      spiceworks.observe(event, callback);
    };
  }
  
})(SPICEWORKS);


// =============================================================
// Using SPICEWORKS to configure areas of the product
// =============================================================

// Exmaple Plugin Editor Code
SPICEWORKS.app.settings.plugin.editor.addCodeExample({
  label: 'Widget Type',
  code: "\nSPICEWORKS.app.dashboard.addWidgetType({\n  name: 'my_widget',\n  label: 'My Widget',\n  settingDefinitions: [\n    {name: 'text', label: 'Widget Text Option', type: 'string', defaultValue: 'Hello World'}\n  ],\n\n  update: function (element, settings) {\n    element.innerHTML = settings.text;\n  }\n});\n"
});

SPICEWORKS.app.settings.plugin.editor.addCodeExample({
  label: 'Navigation Item',
  code: "\nSPICEWORKS.app.navigation.addItem({\n  name: 'my_page',\n  label: 'My Page',\n  update: function (element) {\n    /*update the element with information*/\n    element.innerHTML = 'Hello World';\n  }\n});\n"
});

SPICEWORKS.app.settings.plugin.editor.addCodeExample({
  label: 'Run Report',
  code: "\nSPICEWORKS.data.runReport( 'Report Name or ID', function (response) {\n  if (response.error) { /* Error occured, deal with it here */ }\n  \n  response.items.each(function (item) {\n    response.columns.each(function (column) {\n      /* process each cell of the table */\n      item[column];\n    });\n  });\n});\n"
});

SPICEWORKS.app.settings.plugin.editor.addCodeExample({
  label: 'Add Message',
  code: "\nvar mID = SPICEWORKS.app.messaging.push('Message Text');\n/* SPICEWORKS.app.messaging.pop(mID); */\n"
});

SPICEWORKS.app.settings.plugin.editor.addCodeExample({
  label: 'Plugin Configuration',
  code: "plugin.configure({\n  settingDefinitions:[\n    { name:'company', label:'Company', type:'string', defaultValue:'Spiceworks'}\n  ]\n});\n"
});

// == NAVIGATION MENUS ==


// DASHBOARD PAGE MENUS
SPICEWORKS.app.navigation.addMenuItem({
  selector: 'div#navigation dl a.dashboard_page_link',
  label: "Rename...", 
  className: 'rename',
  onclick: function (linkElem) {
    var tabId = linkElem.getAttribute('dashboard_id'),
        input = new Element('input', {type:'text', value: linkElem.innerHTML, 'class':'text'});
    input.setStyle("position:relative;z-index:500;margin-bottom:2px;margin-left:20px;margin-top:2px;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;width:120px;");
    linkElem.up().insert({top:input});
    linkElem.hide();
    Event.observe( input, 'keypress', function (event) {
      if (Event.KEY_RETURN == event.keyCode) {
        new Ajax.Request("/dashboard/rename_tab", {parameters: {'tab_name': input.value, 'tab_id': tabId}});
        linkElem.update(input.value);
      }
      if (Event.KEY_RETURN == event.keyCode || Event.KEY_ESC == event.keyCode){
        linkElem.show();
        input.remove();
      }
    });
    Event.observe( input, 'blur', function (event) {
      linkElem.show();
      input.remove();        
    });
    input.focus();
  }
});
SPICEWORKS.app.navigation.addMenuItem({
  selector: 'div#navigation dl a.dashboard_page_link.deletable',
  label: "Delete...", 
  className: 'delete',
  onclick: function (linkElem) {
    var tabId = linkElem.getAttribute('dashboard_id');
    var dd;
    if (confirm('Are you sure you want to delete "' + linkElem.innerHTML + '"?')) {
      dd = linkElem.up('dd');
      if(dd.hasClassName('current')){
        document.location.href = '/dashboard/delete_tab?tab=' + tabId;
      }else{
        new Ajax.Request('/dashboard/delete_tab', {parameters: {tab: tabId}});
      }
      new Effect.Fade(dd, {duration:0.5});
      setTimeout( function () { dd.remove(); }, 600 );
    }
  }
});


// MY TOOLS MENUS
SPICEWORKS.app.navigation.addMenuItems({
  selector: 'div#navigation dl dd.custom',
  items: [
    { // RENAME MENU ITEM
      label: "Rename...",
      className: 'rename',
      onclick: function (navItem) {
        var linkElem = navItem.down('a.portal_link'),
            toolId = linkElem.id.match( /custom_nav_(\d+)/ )[1],
            input = new Element('input', {type:'text', value: linkElem.innerHTML, 'class':'text'});

        input.setStyle("position:relative;z-index:500;margin-bottom:2px;margin-left:20px;margin-top:2px;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;width:120px;");
        linkElem.up().insert({top:input});
        linkElem.hide();
        Event.observe( input, 'keypress', function (event) {
          if (Event.KEY_RETURN == event.keyCode) {
            new Ajax.Request('/web_portal/update/' + toolId, {asynchronous:true, evalScripts:true, parameters:{'navigation[name]': input.value}});
            linkElem.update(input.value); // even though this will get updated automatically!
          }
          if (Event.KEY_RETURN == event.keyCode || Event.KEY_ESC == event.keyCode){
            linkElem.show();
            if (input) {
              input.remove();
              input = null;            
            }
          }
        });
        Event.observe( input, 'blur', function (event) {
          linkElem.show();
          if (input) {
            input.remove();
            input = null;            
          }
        });
        input.focus();
      }
    }, {  // EDIT DETAILS MENU ITEM
      label: "Edit Details...",
      className: 'edit_details',
      onclick: function (navItem) {
        var linkElem = navItem.down('a.portal_link'),
            toolId = linkElem.id.match( /custom_nav_(\d+)/ )[1];
        new Ajax.Request('/web_portal/edit/' + toolId, {asynchronous:true, evalScripts:true});
      }
    }, {  // DELETE MENU ITEM
      label: "Delete...",
      className: 'delete',
      onclick: function (navItem) {
        var linkElem = navItem.down('a.portal_link');
        var toolId = linkElem.id.match( /custom_nav_(\d+)/ )[1];
        if (confirm('Are you sure you want to delete "' + linkElem.innerHTML + '"?')) {        
          new Ajax.Request('/web_portal/destroy/'+toolId, {asynchronous:true, evalScripts:true});
          navItem.remove();
        }
      }
    }
  ] // end items
});

// HELP DESK MENUS
SPICEWORKS.app.navigation.addMenuItems({
  selector: 'div#navigation dl dd#nav_help_desk',
  items: [
    {
      label: "New Ticket...",
      className: 'new_ticket',
      onclick: function (navItem) {
        Toolbar.createTicket({stop:function () {}});
      }
    },{
      label: "User Portal",
      className: 'user_portal',
      href: '/helpdesk', 
      target: '_blank'
    },{
      className: 'ticket_separator', separator: true
    },{
      label: "Settings",
      className: 'help_desk_settings',
      href: '/settings/help_desk'
    },{
      label: "Email Settings",
      className: 'email_settings',
      href: '/settings/email'
    }
  ]
});  


// INVENTORY MENUS 
SPICEWORKS.app.navigation.addMenuItem({
  selector: 'div#navigation dl dd#nav_inventory',
  items: [
    {
      label: "New Asset",
      className: 'new_asset',
      onclick: function (navItem){
        Toolbar.options.canAddNewAsset = true;
        Toolbar.createAsset({stop:function() {}});
      }
    },{
      className: 'inventory_separator', separator: true
    },{
      label: "Edit Groups",
      className: 'category_settings',
      href: '/settings/categories'
    },{
      label: "Scan Settings",
      className: 'scan_settings',
      href: '/settings/network'
    },{
      label: "Monitor Settings",
      className: 'monitor_settings',
      href: '/settings/monitors'
    }
  ]
});

// REPORT MENUS
SPICEWORKS.app.navigation.addMenuItem({
  selector: 'div#navigation dl dd#nav_reporting',
  items: [
    {
      label: "New Report",
      className: 'new_report',
      href: '/reports/new'
    },{
      label: "Shared Reports",
      className: 'shared_reports',
      onclick: function (navItem) {
        Community.go("/reports");
        return false;
      }        
    }
  ]
});

// ADD PAGE/TOOL LEFT NAV OPTIONS
// This is not in a nice Plugin-y API form yet.  Still sort of a manual process.
SPICEWORKS.app.ready( function () {
  // ADD PAGE
  $('add_page').title = 'Add a new Dashboard Page';
  Event.observe('add_page', 'click', function () {
    if($('add_page_form')){
      return; // do nothing if it's already there.
    }
    var dd = new Element('dd', {'class':'editing', 'style':'display:none', 'id':'add_page_form'});
    var form = new Element('form', {'class': 'simple', 'style':''});
    var input = new Element( 'input', {name: 'new_tab_name', 'class':'text', value:'Page Name' });
    input.setStyle("margin-bottom:2px;margin-top:2px;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;width:120px;");
    var saveBtn = new Element('input', {'name':'save', 'id':'add_page_save', 'class': 'image_button', 'type': 'image', 'src': '/images/forms/buttons/small/save.gif', 'alt':'Save'});
    var cancelBtn = new Element('input', {'name':'cancel', 'id':'add_page_cancel', 'class': 'image_button', 'type': 'image', 'src': '/images/forms/buttons/small/cancel.gif', 'alt':'Cancel'});
    var p = new Element('p', {'class':'btn','style':''});
    p.insert("<h1>New Dashboard Page</h1>"). insert(input).insert('<br/>').insert(saveBtn).insert(' ').insert(cancelBtn);
    form.insert(p);
    
    dd.insert(form);
    $('my_network_header').insert( {after: dd} );
    new Effect.BlindDown(dd,{duration:0.5});
    
    function hideAndRemove(event){
      var localdd = dd;
      if (event){
        Event.stop(event);
      }
      if (dd) {
        dd = null;
        new Effect.BlindUp(localdd,{duration:0.5}); 
        setTimeout( function () {
          localdd.remove();
          input = null;
        }, 500);
      }       
      return false; 
    }
    function save() {
      document.location = '/dashboard/add_tab?new_tab_name=' + encodeURIComponent( input.value );
    }
    
    Event.observe( input, 'keypress', function (event) {
      if (Event.KEY_RETURN == event.keyCode) {
        save();
      }
      if (Event.KEY_RETURN == event.keyCode || Event.KEY_ESC == event.keyCode){
        hideAndRemove(event);
      }
    });
    Event.observe(cancelBtn, 'click', hideAndRemove);
    Event.observe(saveBtn, 'click', function (event) {
      save();
      hideAndRemove(event);
      return false;
    });
    Event.observe(form, 'submit', function (event) {
      Event.stop(event);
      return false;
    });
    
    setTimeout(function () { input.focus(); }, 600);
  });
  
  // ADD TOOL
  $('add_tool').title = 'Add an External Tool';
  Event.observe( 'add_tool', 'click', function () {
    if (NavigationManager.shouldAllowAddNewClick()) { 
      new Ajax.Request('/web_portal/new', {asynchronous:true, evalScripts:true}); 
    } 
    return false;
  });      
});


/* == PLUGIN CONFIGURATION == */
SPICEWORKS.app.settings.plugin.ready( function () {
  SPICEWORKS.plugin.getPlugins().each( function (plugin) {
    if (plugin.settingDefinitions) {
      var row = $$$('tr#' + plugin.guid ), actions, link;
      var configRow, configCell, configDiv, configFields, saveBtn, cancelBtn, btnDiv;

      actions = row.down('td.actions');
      link = new Element('a', {href:'javascript:void(0)', 'class':'configure_link'}).update('configure');
      // actions.down('.disabled_configure_link').remove();
      actions.insert(link);
      
      function showConfig(){
        configRow = new Element('tr', {'class':'edit-row'});
        configCell = new Element('td', {colSpan: row.cells.length});
        configForm = new Element('form', {style: 'display:none', 'class':'field_set'});
        configForm.insert('<h3>Configure ' + plugin.name + '</h3>');
        configForm.insert('<p>' + plugin.description + '</p>');
        
        saveBtn = new Element('input', {type:'image', src:'/images/forms/buttons/save.gif'});
        cancelBtn = new Element('input', {type:'image', src:'/images/forms/buttons/cancel.gif'});
        btnP = new Element('p', {'class':'btn'}).insert(saveBtn).insert(cancelBtn);
        
        configFields = SPICEWORKS.field.createFieldSet( configForm, plugin.settingDefinitions );
        configFields.set( plugin.settings );
        
        configForm.insert(btnP);
                
        configCell.insert(configForm);
        configRow.insert(configCell);
        row.insert({after: configRow});

        configForm.show();
        row.hide();
        
        Event.observe(cancelBtn, 'click', hideConfig);
        Event.observe(saveBtn, 'click', function () {
          plugin.storeSettings(configFields.get(), hideConfig);
        });
        Event.observe(configForm, 'submit', function (event) {
          Event.stop(event);
          return false;
        });
      }
      function hideConfig() {
        Event.stopObserving(saveBtn);
        Event.stopObserving(cancelBtn);
        row.show();
        configRow.remove();
        configRow = null;
      }
      function toggleConfig() {
        if( !configRow ){
          showConfig();
        } else {
          hideConfig();
        }
      }
      Event.observe(link, 'click', toggleConfig);
    }
  });
  
  // Hide the loading sign and show the table.
  new Effect.Fade('plugins_loading');
});




