title: How to create one plugin in drupal 7 custom module?
body: <p>Im trying to create a custom module plugin in drupal 7, but im having any issues that i cant resolve.</p>
  
  <p>I've created other plugins previously, but this is provoke to me more problems that i supposed.</p>
  
  <p>Well, im creating any charts with Progressbar.js library. The idea is generate one chart per plugin, filling the form fields from page_manager, for example, if i want to create 3 charts in any page of my site, i should call 3 plugins.</p>
  
  <p>The problem is that when i go to the page where the charts must to show, only generate one chart (last plugin that i call from pane).</p>
  
  <p>The logic of operation, is that I enter the values of the fields through the form of the plugin. These values are sent to the script to generate the graphics with the library ProgressBar.js and thus render the graphic to html.</p>
  
  <p>I noticed that the plugin is not sending the data to the javascript script. Just send the data to the script the last plugin that I created. The previous two do not. When i use console.log in the script, only get values from last plugin, when it should show 3 logs with their respectives values.</p>
  
  <p>This is directory of my custom module.</p>
  
  <pre><code>├── obs_home_page.features.inc
  ├── obs_home_page.info
  ├── obs_home_page.module
  ├── obs_home_page.pages_default.inc
  ├── plugins
  │   └── content_types
  │       ├── obs_home_page_show.inc
  └── scripts
      └── ProgressBar.js
  </code></pre>
  
  <p><strong>obs_home_page.module:</strong></p>
  
  <pre><code>&lt;?php
  /**
   * @file
   * Code for the Home page feature.
   */
  
  include_once 'obs_home_page.features.inc';
  
  function obs_home_page_ctools_plugin_directory($owner, $plugin) {
      return 'plugins/' . $plugin;
  }
  </code></pre>
  
  <p><strong>obs_home_page_show.inc:</strong></p>
  
  <pre><code>&lt;?php
  
  $plugin = array(
    'single' =&gt; TRUE,  // Just do this one, it is needed.
    'title' =&gt; t('Relojes'),  // Title to show up on the pane screen.
    'description' =&gt; t('Reloj de derecho ipara la portada home'), // Description to show up on the pane screen.
    'category' =&gt; t('Custom'), // A category to put this under.
    'edit form' =&gt; 'obs_home_page_show_edit_form', // A function that will return the settings form for the pane.
    'render callback' =&gt; 'obs_home_page_show_render', // A function that will return the renderable content.
    'admin info' =&gt; 'obs_home_page_show_admin_info', // A function that will return the information displayed on the admin screen (optional).
    'defaults' =&gt; array( // Array of defaults for the settings form.
      'right' =&gt; '',
      'maxValue' =&gt; '33',
      'value' =&gt; '',
      'description' =&gt; ''
    ),
    'all contexts' =&gt; TRUE, // This is NEEDED to be able to use substitution strings in your pane.
  );
  
  /**
   * An edit form for the pane's settings.
   */
  function obs_home_page_show_edit_form($form, &amp;$form_state) {
    $conf = $form_state['conf'];
  
    $form['right'] = array(
      '#title' =&gt; t('Right'),
      '#description' =&gt; t('Right ID'),
      '#type' =&gt; 'textfield',
      '#required' =&gt; TRUE,
      '#default_value' =&gt; !empty($conf['right']) ? $conf['right'] : '',
    );
  
    $form['description'] = array(
      '#title' =&gt; t('Description'),
      '#description' =&gt; t('Right description'),
      '#type' =&gt; 'textfield',
      '#default_value' =&gt; !empty($conf['description']) ? $conf['description'] : '',
    );
  
    $form['maxValue'] = array(
      '#title' =&gt; t('Max value'),
      '#description' =&gt; t('Max value'),
      '#type' =&gt; 'textfield',
      '#size' =&gt; 11,
      '#default_value' =&gt; !empty($conf['maxValue']) ? $conf['maxValue'] : '',
    );
  
    $form['value'] = array(
      '#title' =&gt; t('Value'),
      '#description' =&gt; t('Value'),
      '#type' =&gt; 'textfield',
      '#size' =&gt; 11,
      '#required' =&gt; TRUE,
      '#default_value' =&gt; !empty($conf['value']) ? $conf['value'] : '',
    );
  
    return $form;
  }
  
  /**
   * Submit function, note anything in the formstate[conf] automatically gets saved
   * Notice, the magic that automatically does that for you.
   */
  function obs_home_page_show_edit_form_submit(&amp;$form, &amp;$form_state) {
    foreach (array_keys($form_state['plugin']['defaults']) as $key) {
      if (isset($form_state['values'][$key])) {
        $form_state['conf'][$key] = $form_state['values'][$key];
      }
    }
  }
  
  /**
   * Run-time rendering of the body of the block (content type)
   * See ctools_plugin_examples for more advanced info
   */
  function obs_home_page_show_render($subtype, $conf, $panel_args, $context) {
    //Check context and validates input for each config field.
  
    if (!empty($context) &amp;&amp; !empty($conf['right']) &amp;&amp; check_plain($conf['right'])) {
      $right = ctools_context_keyword_substitute($conf['right'], array(), $context);
    } else {
      $right = arg(1); //fallback, uses the current nid
    }
  
    if (!empty($context) &amp;&amp; !empty($conf['description']) &amp;&amp; check_plain($conf['description'])) {
      $description = ctools_context_keyword_substitute($conf['description'], array(), $context);
    } else {
      $description = null;
    }
  
    if (!empty($context) &amp;&amp; !empty($conf['maxValue']) &amp;&amp; check_plain($conf['maxValue'])) {
      $maxValue = ctools_context_keyword_substitute($conf['maxValue'], array(), $context);
    } else {
      $maxValue = null;
    }
  
    if (!empty($context) &amp;&amp; !empty($conf['value']) &amp;&amp; check_plain($conf['value'])) {
      $value = ctools_context_keyword_substitute($conf['value'], array(), $context);
    } else {
      $value = null;
    }
  
    $right = intval($conf['right']);
    $maxValue = intval($conf['maxValue']);
    $value = intval($conf['value']);
  
    $tid = taxonomy_term_load($right);
    $right_name = $tid-&gt;name;
    $right_description = $tid-&gt;description;
  
    if (empty($conf['description'])) {
      $description = $right_description;
    } else {
      $description = $conf['description'];
    }
  
    $settings = array();
    $settings['clock']['right'] = $right;
    $settings['clock']['maxValue'] = $maxValue;
    $settings['clock']['value'] = $value;
  
    dsm($settings);
    drupal_add_js($settings, 'setting');
  
    drupal_add_js(drupal_get_path('module', 'obs_home_page') . '/scripts/ProgressBar.js');
    dsm(drupal_get_path('module', 'obs_home_page') . '/scripts/ProgressBar.js');
  
    $output = array(
      '#type' =&gt; 'markup',
      '#markup' =&gt; '&lt;div class="home_page_progressbar"&gt;
                    &lt;a href="'. base_path() .'taxonomy/term/'. $right .'"&gt;
                      &lt;div id="progressbar-'. $right .'"&gt;&lt;/div&gt;
                      &lt;h2&gt;'. $right_name .'&lt;/h2&gt;
                    &lt;/a&gt;
                    &lt;div class="descripcion_clock_right_home"&gt;'. $description .'&lt;/div&gt;
                    &lt;/div&gt;',
    );
  
    $block = new stdClass();
    $block-&gt;title = '';
    $block-&gt;content = $output;
    return $block;
  
  }
  
  
  /**
   * 'admin info' callback for panel pane.
   */
  function obs_home_page_show_admin_info($subtype, $conf, $contexts) {
    if (!empty($conf)) {
      $block = new stdClass;
      $block-&gt;title = $conf['override_title'] ? $conf['override_title_text'] : '';
      $block-&gt;content = t('&lt;strong&gt;Max Value:&lt;/strong&gt; @maxValue&lt;br /&gt;
        &lt;strong&gt;Value:&lt;/strong&gt; @value&lt;/p&gt;', 
        array(
          '@maxValue' =&gt; $conf['maxValue'],
          '@value' =&gt; $conf['value'],
         ));    
      return $block;
    }
  }
  </code></pre>
  
  <p><strong>ProgressBar.js:</strong></p>
  
  <pre><code>/**
   * @file Creacion de relojes para la portada home con libreria ProgressBar
   *
   */
  
  (function ($) {
  
    Drupal.behaviors.obs_home_page = {
      attach: function (context, settings) {
        GenerarReloj(settings);
      }
    };
  
    var GenerarReloj = function (settings) {
  
      var right   = parseInt(settings.clock.right);
      var nominator   = parseInt(settings.clock.value);
      var denominator = parseInt(settings.clock.maxValue);
  
      var bar_information = new ProgressBar.Circle('#progressbar-' + right, {
      color: '#aaa',
      // This has to be the same size as the maximum width to
      // prevent clipping
      strokeWidth: 10,
      trailWidth: 10,
      easing: 'easeInOut',
      duration: 2000,
      text: {
        autoStyleContainer: false
      },
      from: { color: '#C5DC70', width: 10 },
      to: { color: '#728530', width: 10 },
      // Set default step function for all animate calls
      step: function(state, circle) {
        circle.path.setAttribute('stroke', state.color);
        circle.path.setAttribute('stroke-width', state.width);
  
        var value = Math.round(circle.value() * denominator);
        if (value === 0) {
          circle.setText('');
        } else {
          circle.setText(value);
        }
  
      }
    });
    // bar.text.style.fontFamily = '"Raleway", Helvetica, sans-serif';
    bar_information.text.style.fontSize = '3rem';
  
    var quotient =  nominator / denominator;  
  
    bar_information.animate(quotient);  // Number from 0.0 to 1.0
  
    }
  
  })(jQuery);
  </code></pre>