SPICEWORKS.plugin.add(     { name:"Power Manager sponsored by Intel", version:"1.48218", description:"Track power usage", guid:"p-056e70b0-86b9-012c-3174-0016355a8e86-1255722072", settings:{}, contentAreas: [{"content_type":"image/png","updated_at":"2010/05/06 14:24:45 +0100","id":25,"description":null,"content_name":"add.png","user_id":null},{"content_type":"image/png","updated_at":"2010/05/06 14:24:45 +0100","id":26,"description":null,"content_name":"icon.png","user_id":null},{"content_type":"text/javascript","updated_at":"2010/05/06 14:24:45 +0100","id":27,"description":null,"content_name":"initialize.js","user_id":null},{"content_type":"text/css","updated_at":"2010/05/06 14:24:45 +0100","id":28,"description":null,"content_name":"intel_pwr_man.css","user_id":null},{"content_type":"image/gif","updated_at":"2010/05/06 14:24:45 +0100","id":29,"description":null,"content_name":"loading.gif","user_id":null},{"content_type":"image/png","updated_at":"2010/05/06 14:24:45 +0100","id":30,"description":null,"content_name":"shutdown.png","user_id":null},{"content_type":"image/png","updated_at":"2010/05/06 14:24:45 +0100","id":31,"description":null,"content_name":"sleep.png","user_id":null},{"content_type":"image/png","updated_at":"2010/05/06 14:24:46 +0100","id":32,"description":null,"content_name":"wake.png","user_id":null}], initialize:function(plugin){ // ==SPICEWORKS-PLUGIN==
// @name          Power Manager sponsored by Intel
// @description   Track power usage
// @version       0.4
// ==/SPICEWORKS-PLUGIN==  

plugin.configure({
  settingDefinitions:[
    {name:'on_usage', label:'On', type:'string', defaultValue:73, example:'usage in watts'},
    {name:'off_usage', label:'Off', type:'string', defaultValue:3, example:'usage in watts'},
    {name:'price_per_kwh', label:'Price/kWh', type:'string', defaultValue:0.10, example:'in ' + Application.currency.code + ' (' + Application.currency.symbol + ')'}
  ]
});

// originally tried to use base64 images here, but that doesn't work in IE7 so had to switch to hosted imagery
// base64 images works good in all other browsers, including IE8 surprisingly, even when running in IE7 compatibility mode
var IMAGERY = {
  ICON:    "/settings/plugins/p-056e70b0-86b9-012c-3174-0016355a8e86-1255722072/content/icon.png" 
};

// storedSchedules is a collection of the schedules a user has defined and is stored on the backend
// inventoryGroups is a collection of all the device groups for this installation
// dataSpec houses a few different specs for either presenting or collecting data
var storedSchedules = $A(), inventoryGroups, dataSpec = {
  // scheduleHTML is the template for rendering a schedule to the "schedules" tab of the widget config panel
  scheduleHTML: new Template('<li class="#{schedule_classes}" title="click to edit this power schedule"><input type="checkbox" value="enabled" #{enabled} name="enabled" class="schedule-enabled" />&nbsp;<div class="content"><h4>#{title}</h4><p>#{meta}</p></div>'),
  // scheduleFields is a fieldset-style definition that we use to create new schedules and edit existing ones
  scheduleFields: [
    {name:'title', label:'Title', type:'string', defaultValue:''},
    {name:'action', label:'Action', type:'enumeration', options:['suspend', 'shutdown', 'wake'], defaultValue:'suspend'},
    {name:'when', label:'When', type:'enumeration', options:[['Weekdays', '1,2,3,4,5'], ['Weekends', '0,6'], ['Sunday', '0'], ['Monday', '1'], ['Tuesday', '2'], ['Wednesday', '3'], ['Thursday', '4'], ['Friday', '5'], ['Saturday', '6']], defaultValue:'1,2,3,4,5'},
    {name:'at', label:'At', type:'enumeration', options:[['12am', '0'], ['1am', '1'], ['2am', '2'], ['3am', '3'], ['4am', '4'], ['5am', '5'], ['6am', '6'], ['7am', '7'], ['8am', '8'], ['9am', '9'], ['10am', '10'], ['11am', '11'], ['12pm', '12'], ['1pm', '13'], ['2pm', '14'], ['3pm', '15'], ['4pm', '16'], ['5pm', '17'], ['6pm', '18'], ['7pm', '19'], ['8pm', '20'], ['9pm', '21'], ['10pm', '22'], ['11pm', '23']], defaultValue:'18'},
    {name:'applies_to', label:'Applies to', type:'enumeration', options:[], defaultValue:''},
    {name:'enabled', label:'Enabled', type:'checkbox', defaultValue:true}
  ]
};

// a global function to our widget for parsing out the groups we care about from the entire inventory groups collection and also presenting them to us in a nice way (ordered, grouped for a select)
function selectUserGroups(collection){
  // we only want groups that the user has control over
  return collection.select(function(group){
    return !$w('identified offline wireless wired dhcp static anti_virus_current anti_virus_out anti_virus_none computers anti_virus_status_unknown devices').include(group.name);
  }).collect(function(group){
    // group them into arrays that the select fieldset element understands
    return [group.name, group.id];
  }).sortBy(function(group){
    // and sort them for easy reading
    return group[0];
  });
}

// given an hour (24-hour style), return a string that represents the time in a human readable form
function asTime(hour){
  if (hour == 0) return '12am';
  else if (hour == 12) return '12pm';
  else if (hour > 12) return (hour - 12) + 'pm';
  else return hour + 'am';
};

// given a day schedule in cron syntax, return a string that represents that day in human readable form
function asWhen(cronDay){
  switch (cronDay){
    case '1,2,3,4,5': return 'Weekdays'; break;
    case '0,6': return 'Weekends'; break;
    case '0': return 'Sunday'; break;
    case '1': return 'Monday'; break;
    case '2': return 'Tuesday'; break;
    case '3': return 'Wednesday'; break;
    case '4': return 'Thursday'; break;
    case '5': return 'Friday'; break;
    case '6': return 'Saturday'; break;
  }
}

// given a node, find its index amongst a collection of child nodes relative to a parent node
function nodeIndex(node){
  var index = 0, ps = node;
  // short circuit for the first child
  if (!ps.previousSibling) return index;
  while (ps = ps.previousSibling) index++;
  return index;
};

// save the schedules to the backend and trigger a method to tell the finder that it's time to parse new schedules
function persistSchedules(scheduleData){
  plugin.store('schedules', scheduleData, function(){
    new Ajax.Request('/finder/reload_plugin_schedules', {method:'get'});
  });
};

SPICEWORKS.app.dashboard.addWidgetType({
  name: 'intel-power-management',
  label: 'Intel &reg; Power Manager',
  icon: IMAGERY.ICON,
  widgetOptions:{
    hover_title: 'Intel (r) Power Manager', // overriding the label widget label which is normally used for the tooltip because we have an HTML-encoded symbol in there and the widget code escapes the tooltip
    // this method is called when the user clicks the remove link in the widget's title bar
    beforeRemove: function(widget){
      // if there are any active schedules, prompt the user to see if they would like to have those schedules removed
      // otherwise the schedules will continue running even after they have removed the widget
      // we want to prompt the user so they are aware of the conflict of removing the widget but keeping schedules active
      // we don't want to just silently remove the schedules because in a multi-user environment, just because one user removes a widget doesn't mean all users are removing it
      if (storedSchedules.size() > 0 && confirm("Removing this widget does not remove the schedules you've defined, would you like to remove those schedules now?")) persistSchedules([]);
    }
  },
  update: function (element, options) {
    // add a hook for CSS
    element.addClassName('module-intel-power-management');
    element.up('div.module').addClassName('module-intel');

    plugin.includeStyles();

    if (Prototype.Browser.IE){
      SPICEWORKS.utils.addStyle(
        'div#modules div.module-intel-power-management div.config-panel{ width:100%; }' +
        'div#modules div.module-intel-power-management div.config-panel p.button em.highlight{ zoom:1 }' +
        'div#modules div.module-intel-power-management div.config-panel div.fields{ zoom:1; }'
      );
    }

    // edit a given schedule entry
    function editSchedule(event){
      var container = event.element();
      if (container.tagName != 'LI') container = container.up('li');
      var index = nodeIndex(container), editing = storedSchedules[index];

      switch (event.element().tagName){
        case 'INPUT':
          // if they clicked an input, it was a checkbox to enable/disable the schedule entry
          // should let that event pass through and do not flip to edit mode
          storedSchedules[index].enabled = !storedSchedules[index].enabled;
          persistSchedules(storedSchedules);
          needsRedraw = true;
          return;
          break;
        case 'A':
          // if they clicked a link, we should not interfere
          return
          break;
      }

      event.stop();

      // mark the schedule item as being edited, this is so we can find it later
      container.addClassName('editing');

      // generate a form based on the schedule spec we've already defined
      activeFieldset = SPICEWORKS.field.createFieldSet(manager.down('div.fields').update(''), dataSpec.scheduleFields);
      // set the values corresponding to the schedule we are editing
      activeFieldset.set(editing);
      schedules.hide();
      manager.show();
      // the delete button might be hidden if the user was previously creating a new schedule entry
      manager.down('input.delete').show();
      manager.down('input.text').focus();
    };

    // a schedule entry (in the list, on the schedules tab of the edit panel) can have some CSS classes associated with it that we use to give it a different background image
    function buildScheduleClasses(schedule){
      var classes = $A();
      classes.push(schedule.enabled ? 'on' : 'off');
      classes.push(schedule.action.toLowerCase())
      return classes.join(' ');
    };

    // take a schedule entry and build up the subheading
    function buildScheduleMeta(schedule){
      var group, time;
      group = '<a href="/inventory/groups/' + schedule.applies_to + '">' + schedule.applies_to_name + '</a>';
      time = asWhen(schedule.when) + ' at ' + asTime(parseInt(schedule.at, 10));
      return schedule.action.toUpperCase() + ' ' + group + ', ' + time;
    };

    // for the list of schedules, build out the unordered list
    function buildSchedules(scheduleData){
      var list = schedules.down('ul'), values = {schedule_classes:'', title:'', meta:'', enabled:''};
      scheduleData.each(function(schedule, index){
        values.schedule_classes = buildScheduleClasses(schedule);
        values.title = schedule.title;
        values.meta = buildScheduleMeta(schedule);
        values.enabled = schedule.enabled ? 'checked="checked"' : '';
        list.insert(dataSpec.scheduleHTML.evaluate(values));
      });

      // fetch our newly created list items
      var items = list.select('li');
      if (items.size() > 0) {
        // only make the list visible if we have some schedules
        list.show();
        // and hide the "you have no schedules" message
        schedules.down('h4.none').hide();
      }

      // attach an observer to each list item to edit it
      items.invoke('observe', 'click', editSchedule);
    };

    function parseSchedule(fields){
      if (fields.title == '') fields.title = 'schedule-' + (new Date()).getTime();
      fields.title = fields.title.escapeHTML();
      fields.applies_to_name = dataSpec.scheduleFields[4].options.detect(function(group){
        return group[1] == fields.applies_to;
      })
      if (fields.applies_to_name) fields.applies_to_name = fields.applies_to_name[0];
      fields['cron'] = (new Template('0 #{hour} * * #{day}')).evaluate({hour:fields.at, day:fields.when});
      return fields;
    };

    function generalTabClick(event){
      if (event) event.stop();
      tabs.select('li.active').invoke('removeClassName', 'active');
      tabs.down('.tab-general').addClassName('active');
      schedules.hide();
      accounts.hide();
      manager.hide();
      general.show();
    };

    function scheduleTabClick(event){
      if (event) event.stop();
      tabs.select('li.active').invoke('removeClassName', 'active');
      tabs.down('.tab-schedules').addClassName('active');
      general.hide();
      accounts.hide();
      manager.hide();
      schedules.show();
    };

    var vproAccounts;
    function accountTabClick(event){
      if (event) event.stop();
      tabs.select('li.active').invoke('removeClassName', 'active');
      tabs.down('.tab-accounts').addClassName('active');

      if (!vproAccounts){
        // load the network accounts from the backend
        new Ajax.Request('/settings/network/accounts.json', {method:'get', onComplete:function(request){
          vproAccounts = request.responseJSON.vpro;
          if (vproAccounts.size() == 0){
            // prompt to create a vPro account
            accountConfig.description = 'vpro account';
          } else {
            // show the first account in the list, assume only one account
            accountConfig.description = vproAccounts.first().description;
            accountConfig.login = vproAccounts.first().login;
          }
          accountConfig.type = 'vpro';
          accountFieldset.set(accountConfig);
        }});
      }

      general.hide();
      schedules.hide();
      manager.hide();
      accounts.show();
    }

    function toggleConfig(showTab){
      if (configPanel.visible()){
        accounts.down('em.highlight').update('');
        accountFieldset.fields.password.set('');
        schedules.down('p.message').update('').hide();
        configPanel.hide();
        viewPanel.show();

        // if the redraw flag is set, that means something chart-related has changed
        if (needsRedraw) {
          // recompute the statistics, redraw the chart, recalculate the power savings
          chartSpace.update('<p class="loading"><span>Loading Scheduled Offline Chart</span></p>');
          element.down('div.power-savings').update('');
          compileStatistics(storedSchedules);
          // turn off the flag
          needsRedraw = false;
        }
      } else {
        if (helpPanel.visible()) toggleHelp();
        configPanel.show();
        viewPanel.hide();

        if (showTab == 'schedules') scheduleTabClick();
      }
    };

    function toggleHelp(){
      if (helpPanel.visible()){
        helpPanel.blindUp({duration:0.5});
      } else {
        helpPanel.blindDown({duration:0.5});
      }
    };

    function nothingToGraph(message){
      if (message && typeof message == 'string') schedules.down('p.message').update(message).show();
      element.up('div.module').addClassName('module-intel-loaded');
      toggleConfig('schedules');
      chartSpace.update('<h3 class="none">You have no schedules, nothing to graph.</h3><p class="none">Click edit in the widget title bar to add a schedule.</p>');
      element.down('div.power-savings').update('');
    }

    // groupStats will hold the returns statistics (online, offline, etc) for all groups for which schedules exist
    // parsedSchedule is a collection of milestone numbers that are calculated on the backend from parsing the schedules the user has defined
    var groupStats, parsedSchedule;
    function compileStatistics(schedules){
      // gather stats for all the groups that we have schedules for, but do not include a schedule entry which is disabled
      var groups = schedules.reject(function(s){ return !s.enabled; }).pluck('applies_to').uniq();
      if (groups.size() == 0) {
        nothingToGraph();
        return;
      }

      new Ajax.Request('/api/groups/multiple.json?method=statistics&group[]=' + groups.join('&group[]='), {method:'get', onComplete: function(request){
        if (request.status != 200){
          nothingToGraph('Problem finding inventory groups, please check your schedules');
          return;
        }

        groupStats = request.responseJSON;
        new Ajax.Request('/finder/online_offline_schedule.json', {method:'get', onComplete: function(request){
          if (request.status != 200){
            nothingToGraph('Problem finding one (or more) of your inventory groups, please check your schedules');
            return;
          }
          parsedSchedule = request.responseJSON;
          buildChart(groupStats, parsedSchedule);
        }});
      }});

    };

    function showSavings(devicesOfflinePerHour, hoursOffline){
      function roundCents(number){
        return Math.round(number*Math.pow(10,2))/Math.pow(10,2);
      };
      var power = element.down('div.power-savings');
      // ((devicesOfflinePerHour * (onUsage - offUsage) * hoursOff) / 1000) * pricePerKWH
      var savingsPerDay = (devicesOfflinePerHour * (plugin.settings.on_usage - plugin.settings.off_usage) * hoursOffline) * plugin.settings.price_per_kwh / 1000
      power.update('<h3>Saving: ' + Application.currency.symbol + roundCents(savingsPerDay) + '/day (' + Application.currency.symbol + roundCents(savingsPerDay * 30) + '/mo)</h3>');
    };

    function buildChart(stats, schedule){

      // build a collection that maps a time to a number of devices offline count
      var hourSlices = {}, currentDate, nextSchedule, nextDate, transientDate = new Date();
      schedule.each(function(s, index){
        currentDate = new Date(s.when);
        hourSlices[currentDate.toString()] = s.group_count;

        nextSchedule = schedule[index+1];
        if (nextSchedule){
          nextDate = new Date(nextSchedule.when);
          $R(1, Math.ceil((nextDate - currentDate) / 1000 / 60 / 60)).each(function(hour){
            transientDate.setTime(currentDate.getTime() + (hour * 1000 * 60 * 60));
            hourSlices[transientDate.toString()] = s.group_count;
          });
        }
      });


      SPICEWORKS.utils.google.withVisualizations('linechart', function(viz){
        element.up('div.module').addClassName('module-intel-loaded');
        chartSpace.update('');
        var data = new viz.DataTable();
        data.addColumn('string', 'Time');
        data.addColumn('number', 'Actual Offline');
        data.addColumn('number', 'Scheduled Offline');

        data.addRows(24);

        var now = new Date(), transientDate = new Date(), powerSavings = {deviceCount:0, hours:0}, offlineCount;
        transientDate.setHours(0);
        transientDate.setMinutes(0);
        transientDate.setSeconds(0);
        $R(0, 23).each(function(hour){

          data.setValue(hour, 0, asTime(hour));

          offlineCount = hourSlices[transientDate.toString()] || 0;
          data.setValue(hour, 2, offlineCount);
          if (offlineCount > 0){
            powerSavings.hours++;
            powerSavings.deviceCount += offlineCount;
          }
          transientDate.setTime(transientDate.getTime() + (1000*60*60));
        });

        var currentOffline = stats.inject(0, function(memo, groupStats){
          memo += groupStats.offline_count;
          return memo;
        });
        data.setValue(now.getHours(), 1, currentOffline);

        var chart = new viz.LineChart(chartSpace);
        chart.draw(data, {height: 240, width: chartSpace.getWidth(), colors:['red', 'green'], title:'Scheduled Offline Devices', legend:'bottom', pointSize:3, titleX:'Time of day', titleY:'Scheduled Offline', titleFontSize:13, axisColor:'#000', titleColor:'#444', legendFontSize:12 });

        if (powerSavings.deviceCount == 0) element.down('div.power-savings').update('<h3 class="none">No devices scheduled offline for today</h3>');
        else showSavings(powerSavings.deviceCount / powerSavings.hours, powerSavings.hours);
      });
    }

    element.update(
      '<div id="help-' + options.id + '" class="help-panel" style="display:none">' +
        '<h3>Help with the Intel &reg; Power Manager</h3>' +
        '<p>Have you tested your computers to ensure WOL and/or vPro&trade; is properly configured? When you create a schedule to turn on a computer group, it is your responsibility to make sure those computers can be turned on remotely.</p>' +
        '<p>If your computers have vPro&trade;, then Spiceworks will use it to turn your computers on. If vPro&trade; is not configured, then Spiceworks will use WOL packets. If not properly configured, your routers may drop WOL packets.</p>' +
        '<p>Remember that if you create a schedule to turn off computers, you will need a corresponding schedule to turn them back on.</p>' +
        '<p class="button"><input type="image" src="/images/forms/buttons/small/close.gif" class="close" /></p>' +
      '</div>' +
      '<div id="config-' + options.id + '" class="config-panel" style="display:none"></div>' +
      '<div class="view-panel">' +
        '<div id="chart-' + options.id + '" class="graph ' + options.name + '">' +
          '<p class="loading"><span>Loading Scheduled Offline Chart</span></p>' +
        '</div>' +
        '<div class="power-savings"></div>' +
      '</div>'
    );

    var viewPanel = element.down('div.view-panel'), configPanel = element.down('div.config-panel'), chartSpace = element.down('#chart-' + options.id), helpPanel = element.down('div.help-panel');
    configPanel.insert(
      '<div class="sui-tabbed-box">' +
        '<div class="sui-tabs">' +
          '<ul>' +
            '<li class="active tab-general"><a href="#">General</a></li>' +
            '<li class="tab-schedules"><a href="#">Schedules</a></li>' +
            '<li class="tab-accounts"><a href="#">vPro&trade;</a></li>' +
          '</ul>' +
        '</div>' + 
        '<div class="sui-sheet">' +
          '<div class="general"><form>' +
            '<h4 class="intro">Power Usage for Your Network</h4>' +
            '<p class="intro">This information is used to calculate your savings by using this widget.</p>' +
            '<div class="fields"></div>' +
            '<p class="button"><input type="image" src="/images/forms/buttons/small/save.gif" class="save" />' +
              '&nbsp;<input type="image" src="/images/forms/buttons/small/cancel.gif" class="cancel" />' +
            '</p>' +
          '</form></div>' +
          '<div class="view-schedules" style="display:none">' +
            '<h4 class="none">You have no schedules.</h4>' +
            '<p class="message highlight" style="display:none"></p>' +
            '<ul style="display:none"></ul>' +
            '<p class="new"><a href="#">Add a new schedule</a></p>' +
            '<p class="button"><input type="image" src="/images/forms/buttons/small/close.gif" class="close" /></p>' +
          '</div>' +
          '<div class="manage-schedule" style="display:none"><form>' +
            '<div class="fields"></div>' +
            '<p class="button"><input type="image" src="/images/forms/buttons/small/save.gif" class="save" />' + 
              '&nbsp;<input type="image" src="/images/forms/buttons/small/cancel.gif" class="cancel" />' +
              '&nbsp;<input type="image" src="/images/forms/buttons/small/delete.gif" class="delete" /></p>' +
          '</form></div>' +
          '<div class="manage-accounts" style="display:none"><form>' +
            '<h4 class="intro">Use vPro&trade; to Remotely Control Computers</h4>' +
            '<p class="intro">Supply credentials for your vPro&trade; enabled computers</p>' +
            '<div class="fields"></div>' +
            '<p class="button"><em class="highlight"></em>&nbsp;<input type="image" src="/images/forms/buttons/small/save.gif" class="save" />' +
              '&nbsp;<input type="image" src="/images/forms/buttons/small/cancel.gif" class="cancel" /></p>' +
          '</form></div>' +
        '</div>' +
      '</div>'
    );

    var widgetControls = element.up('div.module-inner').down('h3.heading span.right span.widget-controls');
    widgetControls.insert({top:'<a href="#" class="edit" title="Edit">Edit</a>&nbsp;|&nbsp;'});
    widgetControls.down('a.edit').observe('mousedown', function(event){ event.stop(); });
    widgetControls.down('a.edit').observe('click', function(event){
      event.stop();
      toggleConfig();
    });

    widgetControls.insert({top:'<a href="#" class="help" title="Help">Help</a>&nbsp;|&nbsp;'});
    widgetControls.down('a.help').observe('mousedown', function(event){ event.stop(); });
    $A([widgetControls.down('a.help'), helpPanel.down('p.button input.close')]).invoke('observe', 'click', function(event){
      event.stop();
      toggleHelp();
    });

    var tabs = configPanel.down('ul'), general = configPanel.down('div.general'), schedules = configPanel.down('div.view-schedules'), manager = configPanel.down('div.manage-schedule'), activeFieldset;
    var generalFieldset = SPICEWORKS.field.createFieldSet(general.down('div.fields'), plugin.settingDefinitions), accounts = configPanel.down('div.manage-accounts');
    var accountDefinition = [
      {name:'type', type:'hidden'},
      {name:'description', type:'hidden'},
      {name:'login', label:'Login', type:'string', example:'vPro login'},
      {name:'password', label:'Password', type:'password', example:'vPro password'}
    ], accountConfig = { type: 'vpro', description: 'vpro user', login: '', password:'' }, accountFieldset = SPICEWORKS.field.createFieldSet(accounts.down('div.fields'), accountDefinition);

    inventoryGroups = selectUserGroups(SPICEWORKS.data.Group.find('all')); // fetch all the groups and then strip out the groups we don't want to expose to the user
    dataSpec.scheduleFields[4].options = inventoryGroups; // used to populate the drop-down when creating/editing a power schedule

    generalFieldset.set(plugin.settings);
    accountFieldset.set(accountConfig);

    plugin.load('schedules', function(config){
      storedSchedules = $A(config);
      buildSchedules(storedSchedules);
      compileStatistics(storedSchedules);
    }, []);

    tabs.down('.tab-general a').observe('click', generalTabClick);
    tabs.down('.tab-schedules a').observe('click', scheduleTabClick);
    tabs.down('.tab-accounts a').observe('click', accountTabClick);

    schedules.down('p.new a').observe('click', function(event){
      event.stop();
      schedules.hide();
      manager.show();

      activeFieldset = SPICEWORKS.field.createFieldSet(manager.down('div.fields').update(''), dataSpec.scheduleFields);
      activeFieldset.newSchedule = true;
      manager.down('input.delete').hide();
    });

    var needsRedraw = false;
    general.down('form').observe('submit', function(event){
      event.stop();
      plugin.settings = generalFieldset.get();    // store the new settings in the local store
      plugin.store('settings', plugin.settings);  // and persist it to the backend
      needsRedraw = true;                         // mark things as needing a redraw since the power savings calculation numbers have changed
      toggleConfig();                             // close the config panel
    });

    manager.down('form').observe('submit', function(event){
      event.stop();
      var scheduleEntry = parseSchedule(activeFieldset.get());
      if (activeFieldset.newSchedule){
        // save a new schedule to the list
        storedSchedules.push(scheduleEntry);
        var values = { 
          schedule_classes: buildScheduleClasses(scheduleEntry),
          title:scheduleEntry.title, 
          meta:buildScheduleMeta(scheduleEntry),
          enabled:(scheduleEntry.enabled ? 'checked="checked"' : '')
        };

        var list = schedules.down('ul');
        if (!list.visible()) list.show();
        list.insert(dataSpec.scheduleHTML.evaluate(values));
        schedules.select('ul li').last().observe('click', editSchedule);
        schedules.down('h4.none').hide();
      } else {
        // update an existing schedule
        var editing = schedules.down('li.editing'), index = nodeIndex(editing), values = {
          schedule_classes: buildScheduleClasses(scheduleEntry),
          title: scheduleEntry.title.escapeHTML(), 
          meta: buildScheduleMeta(scheduleEntry),
          enabled:(scheduleEntry.enabled ? 'checked="checked"' : '')
        };
        editing.replace(dataSpec.scheduleHTML.evaluate(values));
        schedules.select('ul li')[index].observe('click', editSchedule);
        storedSchedules[index] = scheduleEntry;
      }

      manager.hide();
      schedules.show();
      persistSchedules(storedSchedules);
      needsRedraw = true;
    });

    accounts.down('form').observe('submit', function(event){
      event.stop();
      var fields = accountFieldset.get(), accountReq;
      var accountParams = {'account[type]':fields.type, 'account[description]':fields.description, 'account[login]':fields.login, 'account[password]':fields.password};
      if (vproAccounts.size() == 0){
        // add new account
        new Ajax.Request('/settings/network/create_account.json', {method:'post', parameters:accountParams, onComplete: function(request){
          if (request.status != 201) { // if we didn't get the 201 "created" status, we have an error
            accounts.down('em.highlight').update('Login and password are required');
          } else {
            vproAccounts = $A([fields]);
            accounts.down('em.highlight').update('Account created');
            setTimeout(function(){ toggleConfig(); }, 500);
          }
        }});
      } else {
        // update existing
        new Ajax.Request('/settings/network/update_account.json', {method:'post', parameters:accountParams, onComplete: function(request){
          if (request.status != 202) { // if we didn't get the 202 "accepted" status, we have an error
            accounts.down('em.highlight').update('Login required, password is optional');
          } else {
            accounts.down('em.highlight').update('Account updated');
            setTimeout(function(){ toggleConfig(); }, 500);
          }
        }});
      }
    });

    $A([general.down('p.button input.cancel'), schedules.down('p.button input.close'), accounts.down('p.button input.cancel')]).invoke('observe', 'click', function(event){
      event.stop();
      generalFieldset.set(plugin.settings); // reset form to original state
      accounts.down('em.highlight').update('');
      accountFieldset.set(accountConfig);
      toggleConfig();
    });

    manager.down('p.button input.cancel').observe('click', function(event){
      event.stop();
      manager.hide();
      schedules.show();
    });

    manager.down('p.button input.delete').observe('click', function(event){
      event.stop();
      var deleting = schedules.down('li.editing');
      if (confirm('Are you sure?') && deleting){
        var index = nodeIndex(deleting);
        deleting.remove();
        delete storedSchedules[index];
        storedSchedules = storedSchedules.compact();
        persistSchedules(storedSchedules);

        if (storedSchedules.size() == 0) {
          schedules.down('h4.none').show();
          schedules.down('ul').hide();
        }
        manager.hide();
        schedules.show();
        needsRedraw = true;
      }
    });

    SPICEWORKS.adserver.buildTechLink(element, 31);
  }
});

SPICEWORKS.app.settings.plugin.ready(function(){
  var pluginReference = SPICEWORKS.plugin.getPlugin(plugin.guid);
  pluginReference.toggleEnabledBeforeFilter = function(event){
    var check = event.element();
    if (!check.checked && confirm("Would you like to remove the power schedules you have defined?")) persistSchedules([]);
  };
});


      }
    }
 );
