1

Drupal Version: 6.x

Currently, I have a custom module created. In my template, I keep html code that I would like to embed within a container, such as a div, with a particular id. For this question, let's say <div id = "embeddableContainer"></div>. Right now, I am able to perform the task of embedding the html code from the template like this:

function first_order_decay_init() {
  $path = drupal_get_path('module', 'first_order_decay');
  $path .= '/';

  $code = file_get_contents($path . 'templates/first_order_decay-static.tpl.php', false);
  $code = preg_replace("/[\r\n]*/","",$code);
  drupal_add_js('var code = \'' . $code . '\'', 'inline');
  drupal_add_js($path . 'scripts/jquery.flot.js');
  drupal_add_js($path .'scripts/jquery.flot.dashes.js');
  drupal_add_js($path . 'scripts/jquery.flot.selection.js');
  drupal_add_js($path . 'scripts/flotScript.js');
}

The flotScript.js file appends the code within the "embeddableContainer". However, I am not particularly certain that adding these scripts on module_init is the best practice. Is there a way to load these scripts only on a page that contains an "embeddableContainer"? (Later in the question, you will read about my issue with jmol. I was told jmol uses a certain hook to load scripts correctly) The issue is that the php is executed before the page load, so I do not see a way this is possible.

The second issue I have is that the module does not correctly work with the jmol module we use to display interactive images. When including an instance of jmol in the same page, my scripts are not executed. To fix this, I tried adjusting my module's weight to load the scripts before jmol scripts, but nothing changed. We also use jsmol. Here's a link to some drupal documentation about jmol: https://www.drupal.org/project/jmol. I am not particularly knowledgeable about the module, and I was wondering if anyone else had experience with it or a similar issue as mine. I know that jmol/jsmol works by identifying a container in a similar manner. I believe it searches for text like this, [JMOL](13C60), that matches one of our jmol/jsmol instances. I am not particularly certain about the implementation of the scripts it uses because they are minified, and I do not know the source of them or even where I would begin looking within them.

To sum everything up, how can I load scripts on only pages with "embeddableContainers," and how can I avoid the conflict with jmol/jsmol?

EDIT: MORE INFORMATION ON JMOL

I was given this information about jmol.

"The Jmol module uses hook_nodeapi (specifically during the "alter" operation) to replace the placeholders on page load. Hook_nodeapi allows for modules to modify the content of nodes defined by other modules ("alter" occurs after the node content has been filtered and rendered by drupal and is used for simple text operations)."

In my script, flotScript.js, I wait for $(document).ready. Could this have any effect when replacing the text? I am thinking about making a separate js file that replaces only the text within the container. I am thinking that I should not be using $(document).ready for replacing the text.

Relevant jmol code:

function jmol_nodeapi(&$node, $op, $a3, $a4) {
  if($op == 'alter') {
    $placeholders = _jmol_get_placeholders($node->body);

    // If the node doesn't have any placeholders, do nothing
    if(count($placeholders) != 0) {
      _jmol_replace_placeholders($node, $placeholders);
    }
  }
}

function _jmol_get_placeholders($text) {
  $placeholders = array();

  // Match placeholders of the form [JMOL]('placeholder')
  preg_match_all('/\\[JMOL\\]\(([[:graph:]]+?)\)/', $text, $placeholders);

  return $placeholders;
}

function _jmol_replace_placeholders(&$node, $placeholders) {
  foreach($placeholders[0] as $i=>$placeholder) {
    // Load the instance associated with this placeholder from the db
    $instance = _jmol_load_instance_by_placeholder($placeholders[1][$i]);

    $applet_container_html = jmol_get_applet_container_html($instance);

    // Replace the placeholder with the applet container html
    $node->body = str_replace($placeholder, $applet_container_html, $node->body);
  }
}

function _jmol_load_instance_by_placeholder($placeholder) {
  $instance = db_fetch_object(db_query('SELECT * FROM {jmol_instances} WHERE placeholder=\'%s\'', $placeholder));

  if($instance === FALSE) return FALSE;

  // Get the models associated with the instance
  $instance->models = _jmol_get_models($instance->jid);

  return $instance;
}

EDIT: SOLUTION TO CONFLICT WITH JMOL

function first_order_decay_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  if($op == 'alter') {
    $path = drupal_get_path('module', 'first_order_decay');
    $path .= '/';

    $code = file_get_contents($path . 'templates/first_order_decay-static.tpl.php', false);
    $code = preg_replace("/[\r\n]*/","",$code);
    drupal_add_js('var code = \'' . $code . '\'', 'inline');
    drupal_add_js($path . 'scripts/jquery.flot.js');
    drupal_add_js($path .'scripts/jquery.flot.dashes.js');
    drupal_add_js($path . 'scripts/jquery.flot.selection.js');
    drupal_add_js($path . 'scripts/flotScript.js');
  }
}

Will I still be able to check content types for loading scripts?

  • Do you know which module is outputting the embeddableContainer div? That's the first thing to find out. – rooby Jul 31 '14 at 3:51
  • The embeddableContainer is meant to be a placeholder within a page. The div is created in full HTML mode of CKEditor (php mode of CKEditor is not an option). The goal is to have a small piece of code (one line or so) that allows a page editor with no experience coding to insert what needs to be there. If you have any other ideas as to how I could support this feature, I am open to any of them. I'm not sure if my way is the most practical. – user3735633 Jul 31 '14 at 5:43
  • I found a solution. Posting in edit. Thanks for the help! – user3735633 Jul 31 '14 at 18:19
  • I made a modification to how I add in my html. I search for a specific string of text, [FLOT](kinetics 1) (they wanted it to look like JMOL instances), and then I replace it with a div which my javascript later appends to. I count the amount of replacement, and if it's greater than 0, I load my scripts because I know my html should be present. That way the scripts load only when needed regardless of content type. – user3735633 Aug 1 '14 at 1:57
1

If you are dealing with content on node pages you could add something like this in your init function:

if ($node = menu_get_item()) {
  // If we have an article node, add our js.
  if ($node->type == 'article') {
    // Add your js here.
  }
}

You could do other similar things to make it so you are not unnecessarily loading your js as much (although it may still be unnecessarily loading sometimes).

That will probably not help you if you also have this markup on listing pages because it deals with individual node pages.

For all node views you could do something like this:

function MODULENAME_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
    case 'view':
      // Your js gets loaded here.
      // You can also single out specific node types here, or check
      // nodes for a specific field having a non-empty value, or check
      // the view mode, etc. to further limit the adding of your js.
      break;
  }
}

If you are also dealing with non-node entities then there are other similar hooks that could help you out. You might need to have a function that adds all your js and then call it from a few places to catch all your use cases.

  • I tried the second option, using MODULE_nodeapi. My scripts are loading to the page, but the jmol is still taking priority. I received some more information about how jmol works, and I'll put that in an edit. – user3735633 Jul 31 '14 at 18:02
2

It is probably better to create a input filter by using hook_filter and then applying that filter to the 'Full HTML' input format (at /admin/settings/filters).

Input filters are used to massage HTML before output.

  • This is probably the way that will get you the most specific targeting of which pages to add your js to, however it has a drawback: you will likely have to set "no cache" to TRUE for your filter, which means any content that uses a text format (like Filtered HTML) that your input filter is used on will not be cached and will have to run through all input filters every time it is viewed, which is bad for performance. If it is not something you use very often you could create a text format specifically for these nodes so you aren't taking the performance hit as often. – rooby Jul 31 '14 at 7:02
  • It depends on whether it is better overall for performance to not cache this input filtered content or to include the js on all pages. The answer to that will be specific to your site and requirements. – rooby Jul 31 '14 at 7:04
  • For anonymous users, the whole page will be cached anyway. Right? – Rimu Atkinson Jul 31 '14 at 7:04
  • Generally, although it depends on the site configuration. – rooby Jul 31 '14 at 7:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.