array('name' => t('Documentation'), 'module' => 'jsdoc_doc', 'description' => t('A JavaScript documentation file')), 'jsdoc_object' => array('name' => t('JavaScript Object'), 'module' => 'jsdoc_object', 'description' => t('An implemented JavaScript object')) ); } /** * Implementation of hook_perm(). */ function jsdoc_perm() { return array('edit jsdoc'); } /** * Implementation of hook_menu(). */ function jsdoc_menu($may_cache) { $items = array(); $version = isset($_SESSION['jsdoc_version']) ? $_SESSION['jsdoc_version'] : null; if ($may_cache) { $items[] = array( 'path' => 'admin/settings/jsdoc', 'title' => t('JavaScript Documentation'), 'callback' => 'drupal_get_form', 'callback arguments' => array('jsdoc_admin'), 'access' => user_access('access administration pages'), 'type' => MENU_NORMAL_ITEM ); $items[] = array( 'path' => 'jsdoc/jsonp', 'type' => MENU_CALLBACK, 'callback' => 'jsdoc_jsonp', 'access' => true ); $items[] = array( 'path' => 'jsdoc/404', 'title' => t('Page not found'), 'type' => MEN_CALLBACK, 'callback' => 'jsdoc_404', 'access' => true ); } else { if (arg(0) == 'jsdoc' && arg(1) != 'jsonp') { $version = false; $resource = false; $name = ''; $path = ''; $switch = false; $args = array(); for ($i = 0; $arg = arg($i); $i++){ if ($arg == '.switch') { $switch = arg($i + 1); break; } $args[] = $arg; } if ($args[2]) { // If we have 2 arguments, assume that we don't have a resource or project // So basically, we assume that version is more important than project $version = $args[1]; $name = $args[2]; $path = "jsdoc/$version/$name"; } elseif ($args[1]) { // Allow the user to only pass the name $name = $args[1]; $path = "jsdoc/$name"; } $node = jsdoc_get_closest($name, $version); if ($node && $node->nid) { jsdoc_current_node($node); $item = array( 'path' => $path, 'title' => t('View'), 'access' => node_access('view', $node), 'type' => MENU_CALLBACK ); $version = jsdoc_get_version($node); $path = array('jsdoc', $version->title, $name); if ($switch) { $path[1] = $switch; drupal_goto(implode('/', $path)); return; } $versions = jsdoc_get_versions($node); if (count($versions) > 1) { $path = explode('/', $node->jsdoc_url); foreach (array_reverse($versions) as $release) { $path[2] = $release->title; $items[] = array( 'path' => $_GET['q'] . '/.switch/' . $release->title, 'title' => $project->title . ' ' . $release->title, 'type' => ($release->nid == $version->nid) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK ); } } $item['callback'] = ($name == $node->title) ? 'jsdoc_object_node_view' : 'jsdoc_child_node_view'; $item['callback arguments'] = ($name == $node->title) ? array($node) : array($node, $name); } $items[] = $item; } } return $items; } /** * Impelementation of hook_block */ function jsdoc_block($op='list', $delta=0) { if ($op == 'list') { if (module_exists('search')) { $block[0]['info'] = t('JavaScript Documentation Search'); } $block[1]['info'] = t('JavaScript Namespace List'); return $block; } elseif ($op == 'view') { if ($delta == 0 && module_exists('search')) { $block['subject'] = 'Search'; $block['content'] = preg_replace('%ignore.*endignore%s', theme('jsdoc_search', 'search'), drupal_get_form('_jsdoc_block_search')); return $block; } elseif ($delta == 1) { $namespaces = array(); $current = jsdoc_current_node(); list($current_namespace,) = explode('.', $current->title, 2); if (!($version = jsdoc_get_version($current))) { foreach (jsdoc_get_versions() as $version) { if ($version->title != 'HEAD') { break; } } } if ($content = cache_get("jsdoc_namespaces_{$version->title}:$current_namespace")) { $content = $content->data; } else { $query = db_query("SELECT n.nid, n.title, version.title AS version FROM {node} AS n JOIN {jsdoc_objects} j ON (j.nid = n.nid) JOIN {node} AS version ON (version.nid = j.did) WHERE j.did = %d", $version->nid); while ($node = db_fetch_object($query)) { if (strpos($node->title, '._') !== false) { continue; } $parts = explode('.', $node->title); if (count($parts)) { for ($i = 1; $i <= count($parts); $i++) { $parent = implode('.', array_slice($parts, 0, $i)); if (!$namespaces[$parent]) { $namespaces[$parent] = (object)array( 'title' => $parent, 'children' => array() ); } } array_pop($parts); $parent = implode('.', $parts); if ($parent) { $children = &$namespaces[$parent]->children; } else { $children = &$namespaces; } $children[$node->title]->jsdoc_url = 'jsdoc/' . $node->version . '/' . $node->title; $children[$node->title]->url = url('jsdoc/' . $node->version . '/' . $node->title); $children[$node->title]->a = l($node->title, 'jsdoc/' . $node->version . '/' . $node->title); } } uksort($namespaces, 'strnatcasecmp'); foreach ($namespaces as $namespace) { if ($namespace->children) { uksort($namespace->children, 'strnatcasecmp'); } } $content = theme('jsdoc_namespaces', $namespaces, $version->title, $current->title); cache_set("jsdoc_namespaces_{$version->title}:$current_namespace", 'cache', $content, time() + 86400); } //$namespaces = unserialize(cache_get('jsdoc_namespaces', 'cache')->data); $block['subject'] = 'Objects'; $block['content'] = $content; return $block; } } } function _jsdoc_block_search() { if (module_exists('search')) { return array( 'ignore' => array( '#value' => 'ignore' ), 'search' => array( '#type' => 'textfield' ), 'go' => array( '#type' => 'submit', '#value' => 'Go' ), 'endignore' => array( '#value' => 'endignore' ) ); } } function _jsdoc_block_search_submit($form_id, $form) { // TODO $search = $form['search']; if (strpos($search, ' ') === false) { $query = db_query("SELECT title FROM {node} WHERE type = 'jsdoc_object' AND title = '%s' GROUP BY BINARY title", $search); if (db_num_rows($query) == 1) { $object = jsdoc_object_node_load(db_result($query)); watchdog('search', t('%keys (@type).', array('%keys' => $search, '@type' => 'JavaScript Documentation')), WATCHDOG_NOTICE, l(t('result'), $object->jsdoc_url)); drupal_goto($object->jsdoc_url); return; } else { $query = db_query("SELECT title FROM {node} WHERE type = 'jsdoc_object' AND title LIKE '%%%s%%' GROUP BY BINARY title", $search); if (db_num_rows($query) == 1) { $object = jsdoc_object_node_load(db_result($query)); watchdog('search', t('%keys (@type).', array('%keys' => $search, '@type' => 'JavaScript Documentation')), WATCHDOG_NOTICE, l(t('result'), $object->jsdoc_url)); drupal_goto($object->jsdoc_url); return; } } } drupal_goto('search/jsdoc/' . $search); } /** * shutdown function to make sure we always mark the last node processed. */ function jsdoc_update_shutdown() { global $jsdoc_last_change, $jsdoc_last_nid; if ($jsdoc_last_change && $jsdoc_last_nid) { variable_set('jsdoc_cron_last', $jsdoc_last_change); variable_set('jsdoc_cron_last_nid', $jsdoc_last_nid); } } /** * Implementation of hook_update_index(). * * Handle node status the way that the node module does... through remembering nids, last change time, * and last comment change time. * * We need to save things by version/node pair though. That way, we can keep things so that the latest * update is always the latest version of the node. */ function jsdoc_update_index() { global $jsdoc_last_change, $jsdoc_last_nid; register_shutdown_function('jsdoc_update_shutdown'); $jsdoc_last_change = variable_get('jsdoc_cron_last', 0); $jsdoc_last_nid = variable_get('jsdoc_cron_last_nid', 0); $limit = (int)variable_get('search_cron_limit', 500); // $result = db_query_range("SELECT GREATEST(IF(c.last_comment_timestamp IS NULL, 0, c.last_comment_timestamp), n.changed) as last_change, n.nid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.type = 'jsdoc_variable' AND n.status = 1 AND ((GREATEST(n.changed, c.last_comment_timestamp) = %d AND n.nid > %d) OR (n.changed > %d OR c.last_comment_timestamp > %d)) ORDER BY GREATEST(n.changed, c.last_comment_timestamp) ASC, n.nid ASC", $last, $last_nid, $last, $last, $last, 0, $limit); $result = db_query_range("SELECT n.changed as last_change, n.nid FROM {node} n WHERE n.type = 'jsdoc_object' AND ((n.changed = %d AND n.nid > %d) OR n.changed > %d) ORDER BY n.changed ASC, n.nid ASC", $jsdoc_last_change, $jsdoc_last_nid, $jsdoc_last_change, 0, $limit); while ($node = db_fetch_object($result)) { $node = node_load($node->nid); $type = 'jsdoc_' . jsdoc_get_version($node)->title; $items = array($node); foreach (jsdoc_get_methods($node) as $method) { $method->title = "{$node->title}.{$method->name}"; $items[] = $method; } foreach (jsdoc_get_properties($node) as $property) { $property->title = "{$node->title}.{$property->name}"; $items[] = $property; } foreach ($items as $item) { $text = '
' . t('View and edit JavaScript Documentation') . '
'; case 'admin/modules#jsdoc': return t('View and edit JavaScript Documentation'); case 'admin/settings/jsdoc/ignore': return t("There will be several vocabularies created by this resource. Two of the more important are \"JavaScript Environments\" and \"JavaScript Conditions\". You should read their explanations below and make sure that these values exist in your vocabularies.
In order for this to work, you need to declare both a file and two functions within that file. When the cron task runs, it will call a function that gets a list of files within your project. The files will be run one by one, each calling the second function. Our task expects an array to be returned in the following format:
array( 'variable' => array( '#requires' => array( array('environment/condition', 'resource') ) 'type' => '', 'source' => '', 'summary' => '', 'description' => '', 'aliases' => '', 'instance' => '', 'initialized' => boolean, 'prototype' => '', 'returns' => '', 'return_summary' => '', 'chains' => array( array('chain-type', 'function') ), 'parameters' => array( 'parameter' => array( 'optional' => boolean, 'repeating' => boolean, 'type' => '', 'summary' => '' ) ) ) )"); } return ''; } // Views // ===== function jsdoc_doc_form(&$node) { $form = array( '#attributes' => array( 'enctype' => 'multipart/form-data' ) ); $form['title'] = array( '#type' => 'textfield', '#title' => t('Version Number'), '#default_value' => $node->title ); $form['counts'] = array( '#type' => 'item', '#title' => t('Objects'), '#value' => ($node->nid ? db_result(db_query("SELECT COUNT(*) FROM {jsdoc_objects} WHERE did = %d", $node->nid)) : 0) ); $form['upload'] = array( '#title' => t('Documentation File'), '#type' => 'file' ); return $form; } function jsdoc_doc_view($node, $teaser = FALSE, $page = FALSE) { $node->content['counts'] = array( '#type' => 'item', '#title' => t('Objects'), '#value' => db_result(db_query("SELECT COUNT(*) FROM {jsdoc_objects} WHERE did = %d", $node->nid)) ); return $node; } function jsdoc_doc_update($node) { jsdoc_doc_insert($node); } function jsdoc_doc_insert($node) { if ($file = file_check_upload()) { $start = time(); $doc = fopen($file->filepath, 'r'); $buffer = array(); while (!feof($doc)) { set_time_limit(30); $line = stream_get_line($doc, 9999, "\n"); if (preg_match('%^\t"([^"]+)": \{%', $line, $match)) { $id = $match[1]; $in = true; } elseif (preg_match('%^\t\}%', $line, $match)) { $in = false; $json = json_decode('{' . implode("\n", $buffer) . '}'); if ($nid = db_result(db_query("SELECT n.nid FROM {node} AS n INNER JOIN {jsdoc_objects} j ON (j.nid = n.nid) WHERE BINARY n.title = '%s' AND j.did = %d", $id, $node->nid))) { $object = node_load($nid); $object->json = $json; } else { $object = (object)array( 'title' => $id, 'type' => 'jsdoc_object', 'version' => $node, 'json' => $json ); } node_save($object); node_load($object->nid, NULL, TRUE); $buffer = array(); } elseif($in) { $buffer[] = $line; } } // cache_set("jsdoc_namespaces_{$version->title} $query = db_query("DELETE FROM {cache} WHERE cid LIKE 'jsdoc_namespaces_%s%%'", $node->title); $query = db_query("SELECT n.nid FROM {node} AS n INNER JOIN {jsdoc_objects} AS j ON (j.nid = n.nid) WHERE type = 'jsdoc_object' AND j.did = %d AND changed < %d", $node->nid, $start); while ($nid = db_fetch_object($query)) { set_time_limit(30); node_delete($nid->nid); } } } // Implemented node-type hook functions // ==================================== /** * Implementation of hook_access(). */ function jsdoc_object_access($op, $node) { if ($op == 'update') { return user_access('edit jsdoc'); } elseif ($op == 'view') { return user_access('access content'); } return false; } /** * Implementation of hook_insert(). */ function jsdoc_object_insert($node) { db_query("INSERT INTO {jsdoc_objects} (nid, did, json) VALUES (%d, %d, '%s')", $node->nid, $node->version->nid, serialize($node->json)); } /** * Implementation of hook_update(). */ function jsdoc_object_update($node) { db_query("UPDATE {jsdoc_objects} SET json = '%s' WHERE nid = %d", serialize($node->json), $node->nid); } /** * Implementation of hook_delete */ function jsdoc_object_delete(&$node, $vid=null) { db_query("DELETE FROM {jsdoc_objects} WHERE nid = %d", $node->nid); } function _jsdoc_convert_object(&$node, $object, $parent = FALSE, $method = FALSE) { $node->teaser = $object->summary; $node->body = $object->description; if (!$node->title && $object->name) { $node->title = $object->name; } if (!$node->jsdoc_version) { $node->jsdoc_version = $parent->jsdoc_version; } if ($method) { $node->jsdoc_raw_type = 'Function'; } else { $node->jsdoc_raw_type = $object->type ? $object->type : 'Object'; } $node->jsdoc_classlike = $object->classlike; $node->jsdoc_formatted = _jsdoc_format_type($node->jsdoc_raw_type, $node->jsdoc_classlike); $returndescription = 'return-description'; $node->jsdoc_return_summary = $object->$returndescription; $returntypes = 'return-types'; $node->jsdoc_return_types = $object->$returntypes; $node->jsdoc_examples = $object->examples; $node->jsdoc_resources = $object->resources; $node->jsdoc_provides = $object->provides; $node->jsdoc_superclass = $object->superclass; $node->jsdoc_mixins = $object->mixins; if (!empty($object->tags)) { $node->jsdoc_tags = explode(' ', $object->tags); } $node->jsdoc_private = $object->private; $node->jsdoc_private_parent = $object->privateparent; $node->jsdoc_scope = $object->scope ? $object->scope : 'normal'; if ($node->jsdoc_raw_type == 'Function') { if (!$node->jsdoc_classlike) { $parameters = $object->parameters; } else { if (is_array($object->methods)) { $constructors = array(); foreach ($object->methods as $method) { if ($method->constructor) { $constructors[$method->constructor] = $method; } } foreach (array('constructor', 'preamble', 'postscript') as $constructor) { if ($method = $constructors[$constructor]) { $node->jsdoc_constructor = $method; if (!empty($method->parameters)) { $parameters = $method->parameters; break; } } } } } $node->jsdoc_parameters = $parameters; } if (!empty($object->methods)) { foreach ($object->methods as $method) { if (!$method->constructor) { $method->jsdoc_url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $node->title . '.' . $method->name; $node->jsdoc_methods[] = $method; } } } if (!empty($object->properties)) { foreach ($object->properties as $property) { $property->jsdoc_url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $node->title . '.' . $property->name; $node->jsdoc_properties[] = $property; } } if ($object->jsdoc_url) { $node->jsdoc_url = $object->jsdoc_url; } else { $node->jsdoc_url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $node->title; } unset($node->json); return $node; } /** * Implementation of hook_load(). */ function jsdoc_object_load($node, $method = FALSE) { // TODO: Deal with alias _jsdoc_init(); $additions = db_fetch_object(db_query("SELECT json, did FROM {jsdoc_objects} WHERE nid = %d", $node->nid)); $additions->title = $node->title; $additions->json = unserialize($additions->json); $node->jsdoc_version = $additions->jsdoc_version = node_load($additions->did); _jsdoc_convert_object($additions, $additions->json, $node, $method); return $additions; } /** * Implementation of hook_view(). */ function jsdoc_object_view($node, $teaser = false, $page = false) { $node->teaser = ''; $node->body = ''; if (!$teaser) { $node->body = l($node->title, $node->jsdoc_url); } $node->title = t('Object'); $node->taxonomy = array(); $node = node_prepare($node, $teaser); return $node; } /** * Path: admin/settings/jsdoc */ function jsdoc_admin() { $form = array(); $formats = filter_formats(); $options = array(); foreach ($formats as $format) { $options[$format->format] = $format->name; } $form['jsdoc_input_format'] = array( '#title' => t('Default Input Format'), '#type' => 'radios', '#options' => $options, '#required' => true, '#default_value' => variable_get('jsdoc_input_format', 1) ); return system_settings_form($form); } function jsdoc_get_version(&$node = FALSE) { if (!$node) { foreach (jsdoc_get_versions() as $version) { if ($version->title != 'HEAD') { return $version; } } } return $node->jsdoc_version; } function jsdoc_get_versions($node = FALSE) { $versions = array(); if ($node) { $query = db_query("SELECT j.did AS nid FROM {jsdoc_objects} j INNER JOIN {node} n ON (n.nid = j.nid) WHERE n.title = '%s' GROUP BY j.did", $node->title); } else { $query = db_query("SELECT nid FROM {node} WHERE type = 'jsdoc_doc' ORDER BY nid DESC"); } while ($version = db_fetch_object($query)) { $versions[] = node_load($version->nid); } return $versions; } function jsdoc_get_return_summary(&$node) { if (isset($node->jsdoc_return_summary_formatted)) { return $node->jsdoc_return_summary_formatted; } return ($node->jsdoc_return_summary_formatted = trim(preg_replace('%(^
|
$)%', '', check_markup(str_replace("\n", ' ', $object->jsdoc_return_summary), variable_get('jsdoc_input_format', 1), FALSE)))); } function jsdoc_get_return_types(&$node) { if (empty($node->jsdoc_get_return_types)) { return array(); } return $node->jsdoc_return_types; } function jsdoc_get_parameters(&$node) { if (isset($node->jsdoc_formatted_parameters)) { return $node->jsdoc_formatted_parameters; } if (empty($node->jsdoc_parameters)) { $node->jsdoc_parameters = array(); } foreach ($node->jsdoc_parameters as $i => $parameter) { $node->jsdoc_parameters[$i]->jsdoc_formatted = _jsdoc_format_type($parameter->type, false, $parameter->usage == 'optional', $parameter->usage == 'repeating' || $parameter->usage == 'one-or-more'); } return ($node->jsdoc_formatted_parameters = $node->jsdoc_parameters); } function jsdoc_get_examples(&$node, $markedup=false) { if (empty($node->jsdoc_examples)) { return array(); } if (isset($node->jsdoc_formatted_examples)) { return $node->jsdoc_formatted_examples; } $examples = array(); foreach ($node->jsdoc_examples as $example) { $examples[] = _jsdoc_markup_text($example, jsdoc_get_version($node)); } return ($node->jsdoc_formatted_examples = $examples); } function jsdoc_get_parent_prototype(&$node) { if ($node->jsdoc_superclass) { return jsdoc_object_node_load($node->jsdoc_superclass, jsdoc_get_version($node)); } } function jsdoc_is_initialized(&$node) { return false; } function jsdoc_get_tags(&$node) { if (empty($node->jsdoc_tags)) { return array(); } return $node->jsdoc_tags; } function jsdoc_is_classlike(&$node) { return $node->jsdoc_classlike; } function jsdoc_get_teaser(&$node) { if (isset($node->formatted_teaser)) { return $node->formatted_teaser; } return ($node->formatted_teaser = $node->teaser); } function jsdoc_get_body(&$node) { return $node->body; } function jsdoc_is_function(&$node) { return (jsdoc_get_type($node) == 'Function' || jsdoc_get_type($node) == 'Constructor'); } function jsdoc_is_private(&$node) { if ($node->jsdoc_private || (is_array($node->jsdoc_tags) && (in_array('private', $node->jsdoc_tags) || in_array('protected', $node->jsdoc_tags)))) { return true; } return false; } function jsdoc_has_private_parent(&$node) { foreach (jsdoc_get_namespace_parents($node) as $parent) { if ($parent->jsdoc_private || (is_array($node->jsdoc_tags) && (in_array('private', $node->jsdoc_tags) || in_array('protected', $node->jsdoc_tags)))) { return true; } } return $node->jsdoc_private_parent; } function jsdoc_get_type(&$node) { if (isset($node->jsdoc_type)) { return $node->jsdoc_type; } $type = $node->jsdoc_raw_type; unset($node->jsdoc_raw_type); if ($type == 'Function') { if (jsdoc_is_classlike($node)) { $type = 'Constructor'; } elseif (count(jsdoc_get_child_instances($node, true)) || count(jsdoc_get_child_prototypes($node, true))) { $type = 'Constructor'; } } return ($node->jsdoc_type = $type); } function jsdoc_get_resource(&$node) { if (count($node->jsdoc_resources) == 1) { return $node->jsdoc_resources[0]; } } function jsdoc_get_source($node, $resolve=false, $depth=0) { return ''; } function jsdoc_get_provide(&$node) { if (count($node->jsdoc_provides) == 1) { return $node->jsdoc_provides[0]; } } function jsdoc_get_mixins(&$node, $type = NULL) { if (empty($node->jsdoc_mixins)) { return array(); } if (isset($node->jsdoc_resolved_mixins)) { return $type ? $node->jsdoc_resolved_mixins[$type] : $node->jsdoc_resolved_mixins; } $version = jsdoc_get_version($node); $resolved_mixins = array('prototype' => array(), 'instance' => array(), 'normal' => array()); foreach ($node->jsdoc_mixins as $from => $mixins) { foreach ($mixins as $mixin) { $mixin_node = jsdoc_object_node_load($mixin->location, $version); if ($mixin_node->nid) { $resolved_mixins[$from][] = (object)array( 'scope' => $mixin->scope, 'node' => $mixin_node ); } } } $node->jsdoc_resolved_mixins = $resolved_mixins; return jsdoc_get_mixins($node, $type); } function _jsdoc_get_attributes($attribute, &$node, $is_method, $scope) { $attribute = 'jsdoc_' . $attribute; $items = array(); if (!empty($node->$attribute)) { foreach ($node->$attribute as $item){ $converted = (object)array( 'title' => "{$node->title}.{$item->name}", 'name' => $item->name ); if ($scope) { $scopes = explode('-', $item->scope); if (in_array($scope, $scopes)) { $items[] = _jsdoc_convert_object($converted, $item, $node, $is_method); } } else { $items[] = _jsdoc_convert_object($converted, $item, $node, $is_method); } } } if (!$scope || $scope == 'normal') { $query = db_query("SELECT n.nid, n.title FROM {node} AS n INNER JOIN {jsdoc_objects} AS j ON (j.nid = n.nid) WHERE n.title LIKE '%s.%%' AND j.did = %d", $node->title, jsdoc_get_version($node)->nid); while ($result = db_fetch_object($query)) { $parts = explode('.', $result->title); $name = array_pop($parts); if (implode('.', $parts) == $node->title) { $item = node_load($result->nid); $item->name = $name; if ($is_method) { if ($item->jdsoc_raw_type == 'Function') { $items[] = $item; } } else { $items[] = $item; } } } } return $items; } function jsdoc_get_methods(&$node, $scope = NULL) { return _jsdoc_get_attributes('methods', $node, TRUE, $scope); } function jsdoc_get_properties(&$node, $scope = NULL) { return _jsdoc_get_attributes('properties', $node, FALSE, $scope); } function jsdoc_get_child_instances() { return array(); } function jsdoc_get_child_prototypes() { return array(); } function jsdoc_is_namespace(&$node) { return !!$node->nid && $node->jsdoc_raw_type != 'Function'; } // Node Load Functions // =================== /** * Custom version of node_load for versions */ function jsdoc_version_node_load($version_name) { return node_load(array( 'type' => 'jsdoc_doc', 'title' => $version_name )); } function jsdoc_object_node_load($name, $version=false, $exact=true, $broad=false) { $nodes = jsdoc_object_nodes_load($name, $version, $exact, $broad); if (count($nodes) == 1) { return $nodes[0]; } elseif (!empty($nodes)) { $version = jsdoc_get_version(); foreach($nodes as $match) { if ($match->version->nid == $version->nid) { return $match; } } } return (object)array(); } /** * Custom version of node_load for objects */ function jsdoc_object_nodes_load($name, $version=false, $exact=true, $broad=false) { $lower_name = strtolower($name); $override_name = false; $name_override = $name; if ($lower_name == 'bool') { $name = 'Boolean'; } elseif ($lower_name == 'int' || $lower_name == 'integer') { $override_name = true; $name_override = 'Integer'; $name = 'Number'; } elseif ($lower_name == 'float' || $lower_name == 'decimal') { $override_name = true; $name_override = ucfirst($lower_name); $name = 'Number'; } $global_vars = array('Array', 'Boolean', 'Date', 'Error', 'Function', 'Number', 'Object', 'RegExp', 'String'); $lower_global_vars = array('array', 'boolean', 'date', 'error', 'function', 'number', 'object', 'regexp', 'string'); if (($pos = array_search($lower_name, $lower_global_vars)) !== false) { $name = $global_vars[$pos]; } if (in_array($name, $global_vars)) { return array((object)array( 'title' => ($override_name) ? $name_override : $name, 'jsdoc_url' => 'http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:' . $name )); } elseif ($lower_name == 'window') { return array((object)array( 'title' => $name, 'jsdoc_url' => 'http://developer.mozilla.org/en/docs/DOM:window' )); } elseif ($lower_name == 'documentelement' || $lower_name == 'document') { return array((object)array( 'title' => $name, 'jsdoc_url' => 'http://developer.mozilla.org/en/docs/DOM:document' )); } elseif (in_array($lower_name, array('node', 'htmlelment', 'domnode'))) { return array((object)array( 'title' => $name, 'jsdoc_url' => 'http://developer.mozilla.org/en/docs/DOM:element' )); } elseif ($name == 'Constructor') { return array((object)array( 'title' => $name, 'jsdoc_url' => 'http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Class-Based_vs._Prototype-Based_Languages#Defining_a_Class' )); } // Resolve the version the best that we can if (is_object($version) && $version->nid) { $version_nid = $version->nid; } elseif (is_numeric($version)) { $version_nid = $version; } elseif (is_string($version)) { $version_nid = jsdoc_version_node_load($version)->nid; } if ($version_nid && (!is_object($version) || !$version->nid)) { $version = node_load($version_nid); } // We have a few situations: // name // project, name // project, version, name // project, version, resource, name // If we're missing the version, assume the latest release // If we're missing the project, look up stuff by name $binary = ($exact) ? 'BINARY' : ''; $title_check = ($exact) ? "= '%s'" : "LIKE '%%%s%%'"; $nodes = array(); $results = array(); if ($version_nid) { $query = db_query("SELECT n.nid FROM {node} n INNER JOIN {jsdoc_objects} j ON (j.nid = n.nid) WHERE n.title $title_check AND $binary n.title $title_check AND j.did = %d", $name, $name, $version_nid); } else { $version_nids = array(-1); foreach (jsdoc_get_versions() as $version) { $version_nids[] = $version->nid; } $query = db_query("SELECT n.nid, j.did, version.title AS version FROM {node} n INNER JOIN {jsdoc_objects} j ON (j.nid = n.nid) INNER JOIN {node} version ON (version.nid = j.did) WHERE n.title $title_check AND $binary n.title $title_check AND j.did IN (%s) ORDER BY version.created DESC", $name, $name, implode(',', $version_nids)); } while ($result = db_fetch_object($query)) { if ($result->version) { if ($result->version != 'HEAD') { $nodes[] = node_load($result->nid); break; } } else { $nodes[] = node_load($result->nid); } } if (empty($nodes)) { if ($broad) { return array((object)array( 'title' => $name )); } else { return array(); } } return $nodes; } function jsdoc_parameter_weight_sort($a, $b){ return ($a['weight'] == $b['weight']) ? 0 : ($a['weight'] < $b['weight']) ? -1 : 1; } /** * Path: jsdoc/404 */ function jsdoc_404() { $args = explode('/', $_REQUEST['destination']); if (count($args) == 1 && $args[0] != 'jsdoc') { global $_menu; unset($_menu['items']); menu_set_active_item('jsdoc/' . $args[0]); print theme('page', menu_execute_active_handler()); exit(); } return '' . t('The page you requested was not found.') . '
'; } /** * Path: jsdoc/jsonp */ function jsdoc_jsonp($batch=false, $json=false) { $recursion = false; if ($json) { $recursion = true; $_JSON = $json; } else { $_JSON = array(); foreach (explode('&', getenv('QUERY_STRING')) as $query_string) { list($key, $value) = explode('=', $query_string, 2); $value = urldecode($value); if (isset($_JSON[$key])) { if (!is_array($_JSON[$key])) { $_JSON[$key] = array($_JSON[$key]); } $_JSON[$key][] = $value; } else { $_JSON[$key] = $value; } } } $names = array(); if ($batch) { $names = (is_array($_JSON['names'])) ? $_JSON['names'] : array($_JSON['names']); } else { $names = array($_JSON['name']); } $attributes = ($_JSON['attributes']) ? $_JSON['attributes'] : array('summary', 'type', 'returns', 'parameters'); if (!is_array($attributes)) { $attributes = array($attributes); } foreach ($names as $name) { if (empty($name)) { continue; } if ($_JSON['exact']) { if ($nodes = jsdoc_object_node_load($name, $_JSON['version'], true)){ $nodes = array($nodes); } } else { $nodes = jsdoc_object_nodes_load($name, $_JSON['version'], false); } foreach ($nodes as $node) { $formatted = (object)array('name' => $name); foreach ($attributes as $attribute) { switch ($attribute) { case 'summary': $formatted->summary = jsdoc_get_teaser($node); break; case 'type': $formatted->type = jsdoc_get_type($node); break; case 'returns': $formatted->returns = array( 'types' => array(), 'summary' => jsdoc_get_return_summary($node) ); foreach (jsdoc_get_return_types($node) as $type) { $themed = _jsdoc_get_type_themed($type, $node, true); if ($themed) { $formatted->returns['types'][] = $themed; } else { $formatted->returns['types'][] = array( 'title' => $type ); } } case 'parameters': $formatted->parameters = _jsdoc_get_parameters_themed($node, true); if (!count($formatted->parameters)) { unset($formatted->parameters); } break; } } if ($_JSON['recursive']) { $children = array_keys(jsdoc_get_child_variables($node)); if (!empty($children)) $formatted->children = jsdoc_jsonp(true, array( 'attributes' => $_JSON['attributes'], 'names' => $children, 'recursive' => $_JSON['recursive'] )); } $output[] = $formatted; } } $found = array(); foreach ($output as $object) { $found[] = $object->name; } foreach (array_diff($names, $found) as $name) { $output[] = (object)array( 'name' => $name ); } if ($recursion) { return $output; } print $_GET['callback'] . '(' . str_replace('"__className":"stdClass",', '', json_encode($output)) . ');'; } function jsdoc_get_closest($name, $version) { $node = jsdoc_object_node_load($name, $version); if (!$node->nid) { $parts = explode('.', $name); while (!$node->nid) { array_pop($parts); $node = jsdoc_object_node_load(implode('.', $parts), $version); } } return $node; } function _jsdoc_resolve_child($node, $name) { list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($node); $actual = (object)array( 'title' => $name, 'jsdoc_version' => jsdoc_get_version($node) ); foreach ($children as $possible) { if ($possible->title == $name || $node->title . '.' . $possible->title == $name) { $actual = $possible->node; } } $actual->format = $node->format; $actual->title = $name; if (empty($actual->teaser) && empty($actual->body)) { $actual->teaser = ' '; $actual->body = ' '; } return $actual; } /** * A menu callback * Will only ever be a function or property */ function jsdoc_child_node_view($node, $name) { $actual = _jsdoc_resolve_child($node, $name); drupal_set_title(theme('jsdoc_object_title', _jsdoc_get_title_themed($actual))); $form = _jsdoc_node_prepare($actual); if ($provide = jsdoc_get_provide($actual)) { $form['require'] = array( '#value' => theme('jsdoc_object_require', $provide), '#weight' => -20 ); } if ($resource = jsdoc_get_resource($actual)) { $form['resource'] = array( '#value' => theme('jsdoc_object_resource', jsdoc_get_resource($node)) ); } $examples = array(); foreach (jsdoc_get_examples($actual, true) as $weight => $example) { $examples[] = theme('jsdoc_object_example', $example, $weight + 1); } if (!empty($examples)) { $form['examples'] = array( '#value' => theme('jsdoc_object_examples', $examples), '#weight' => 35 ); } if (jsdoc_is_function($actual)) { $parameters = _jsdoc_get_parameters_themed($actual); if (count($parameters)) { $form['parameters'] = array( '#value' => theme('jsdoc_function_parameters', $parameters, _jsdoc_get_object_themed($actual, $actual->title)), '#weight' => 20 ); } $function = (object)array( 'source' => jsdoc_get_source($actual, true), 'markedup' => _jsdoc_markup_code(jsdoc_get_source($actual, true), jsdoc_get_version($node)->nid, FALSE), 'signature' => _jsdoc_build_function_signature($actual, $parameters) ); if (count($parameters)) { $function->parameters = $parameters; } $form['function'] = array( '#value' => theme('jsdoc_function_information', $function, $children, _jsdoc_get_object_themed($actual, $actual->title)), '#weight' => 30 ); } return drupal_render_form('jsdoc_child_form', $form); } /** * Climbs up the prototype chain through mixins * * Move down the prototype chain * The prototype chains of the parents have already been calculated * For each one, inject its resolved mixins above the current class */ function _jsdoc_resolve_mixins($node, $from) { $resolved_mixins = array(); if ($from == 'prototype') { $all_mixins = array(); $prototype_chain = jsdoc_get_prototype_chain($node); foreach ($prototype_chain as $parent) { if (!$parent->nid) { continue; } $all_mixins[] = $parent; } } else { $all_mixins = array($node); } foreach ($all_mixins as $parent) { foreach (jsdoc_get_mixins($parent, $from) as $mixin) { foreach (_jsdoc_resolve_mixins($mixin->node, $from) as $other_mixin) { if (!$other_mixin->node->nid) { continue; } $resolved_mixins[] = $other_mixin; } $resolved_mixins[] = $mixin; } $resolved_mixins[] = (object)array( 'scope' => 'prototype', 'node' => $parent ); } for ($i = 0; $i < count($resolved_mixins); $i++) { for ($j = $i + 1; $j < count($resolved_mixins); $j++) { if ($resolved_mixins[$i]->node->title == $resolved_mixins[$j]->node->title) { array_splice($resolved_mixins, $i, 1); continue 2; } } } return $resolved_mixins; } function jsdoc_get_oo_info(&$node) { if (isset($node->jsdoc_oo_info)) { return $node->jsdoc_oo_info; } $returned_mixin_chain = array(); $mixin_chain = array(); if (jsdoc_is_classlike($node)) { $prototype_chain = jsdoc_get_prototype_chain($node); // Start out with simply the prototype chain $prototype_chain_values = array(); foreach ($prototype_chain as $prototype) { if ($prototype->nid) { $prototype_chain_values[] = $prototype->title; } } // Resolve prototype-level items foreach (_jsdoc_resolve_mixins($node, 'prototype') as $mixin) { $mixin_chain[] = (object)array( 'scope' => 'prototype', 'from' => $mixin->scope, 'methods' => jsdoc_get_methods($mixin->node, $mixin->scope), 'properties' => jsdoc_get_properties($mixin->node, $mixin->scope), 'in_prototype_chain' => in_array($mixin->node->title, $prototype_chain_values), 'node' => $mixin->node ); } $returned_mixin_chain = $mixin_chain; $mixin_chain[] = (object)array( 'scope' => 'prototype', 'from' => 'prototype', 'methods' => jsdoc_get_methods($mixin, 'prototype'), 'properties' => jsdoc_get_properties($mixin, 'prototype'), 'in_prototype_chain' => true, 'node' => $mixin ); // Now with instance-level items foreach (_jsdoc_resolve_mixins($node, 'instance') as $mixin) { $mixin_chain[] = (object)array( 'scope' => 'instance', 'from' => $mixin->scope, 'methods' => jsdoc_get_methods($mixin->node, $mixin->scope), 'properties' => jsdoc_get_properties($mixin->node, $mixin->scope), 'in_prototype_chain' => in_array($mixin->node->title, $prototype_chain_values), 'node' => $mixin->node ); } $mixin_chain[] = (object)array( 'scope' => 'instance', 'from' => 'instance', 'methods' => jsdoc_get_methods($mixin, 'instance'), 'properties' => jsdoc_get_properties($mixin, 'instance'), 'in_prototype_chain' => false, 'node' => $mixin ); } $mixin_chain[] = (object)array( 'scope' => 'normal', 'from' => 'instance', 'methods' => jsdoc_get_methods($node, 'normal'), 'properties' => jsdoc_get_properties($node, 'normal'), 'in_prototype_chain' => false, 'node' => $node ); $children = array(); foreach ($mixin_chain as $mixin) { foreach (array('methods', 'properties') as $child_type) { foreach ($mixin->$child_type as $item) { if ($mixin->scope == 'normal') { $title = $item->title; } else { list($title) = array_slice(explode('.', $item->title), -1); } if (jsdoc_get_type($item) == 'Constructor' && ($resolved = jsdoc_resolve_constructor($item))) { $item->jsdoc_parameters = $resolved->jsdoc_parameters; $item->jsdoc_return_summary = $resolved->jsdoc_return_summary; $item->jsdoc_return_types = $resolved->jsdoc_return_types; $item->teaser = $resolved->teaser; } if ($children[$title]) { $found = false; if ($children[$title]->inheritance) { foreach ($children[$title]->inheritance as $parent) { if ($parent->title == $mixin->node->title) { $found = true; break; } } } if (!$found) { if ($mixin->node->title == $node->title) { $children[$title]->override = true; } else { $children[$title]->inheritance[] = _jsdoc_get_object_themed($mixin->node); } $inherited = _jsdoc_get_object_themed($item, $title); foreach ($inherited as $key => $value) { if ($key == 'summary' && trim($value) == '') { continue; } $children[$title]->$key = $value; } } } else { $children[$title] = _jsdoc_get_object_themed($item, $title); $children[$title]->node = $item; if ($mixin->node->title != $node->title && ($mixin->scope != 'normal' || implode('.', array_slice(explode('.', $title), 0, -1)) != $node->title)) { $children[$title]->inheritance[] = _jsdoc_get_object_themed($mixin->node); } } if ($title == $item->title) { $children[$title]->actual_title = $title; } else { $children[$title]->actual_title = $mixin->node->title . '.' . $title; } $type = $item->jsdoc_scope; $children[$title]->$type = true; if (trim(jsdoc_get_teaser($item)) != '') { $children[$title]->summary = jsdoc_get_teaser($item); } } } } uksort($children, "strnatcasecmp"); return ($node->jsdoc_oo_info = array($prototype_chain, $returned_mixin_chain, $children)); } /** * A menu callback * Will only ever be a constructor or namespace */ function jsdoc_object_node_view($node) { ini_set('memory_limit', '128M'); // Test for the basics if (!$node) { return drupal_not_found(); } drupal_set_title(theme('jsdoc_object_title', _jsdoc_get_title_themed($node))); if (empty($node->teaser) && empty($node->body)) { $node->teaser = ' '; $node->body = ' '; } $form = _jsdoc_node_prepare($node); if ($provide = jsdoc_get_provide($node)) { $form['require'] = array( '#value' => theme('jsdoc_object_require', $provide), '#weight' => -20 ); } if ($resource = jsdoc_get_resource($node)) { $form['resource'] = array( '#value' => theme('jsdoc_object_resource', jsdoc_get_resource($node)) ); } $examples = array(); foreach (jsdoc_get_examples($node, true) as $weight => $example) { $examples[] = theme('jsdoc_object_example', $example, $weight + 1); } if (!empty($examples)) { $form['examples'] = array( '#value' => theme('jsdoc_object_examples', $examples), '#weight' => 35 ); } list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($node); if (jsdoc_is_classlike($node)) { $form['prototype_chain'] = array( '#value' => theme('jsdoc_object_prototype_chain', $prototype_chain), '#weight' => 10 ); } if (!empty($mixin_chain)) { $form['parent_mixins'] = array( '#value' => theme('jsdoc_object_mixins', $mixin_chain), '#weight' => 40 ); } if (jsdoc_is_function($node)) { $actual = $node; if (jsdoc_is_classlike($node)) { $actual = jsdoc_resolve_constructor($node); } $parameters = _jsdoc_get_parameters_themed($actual); if (count($parameters)) { $form['parameters'] = array( '#value' => theme('jsdoc_function_parameters', $parameters, _jsdoc_get_object_themed($node, $node->title)), '#weight' => 20 ); } $base = false; if (jsdoc_get_type($node) == 'Constructor') { $base = $node->title; } $function = (object)array( 'source' => jsdoc_get_source($actual, true), 'markedup' => _jsdoc_markup_code(jsdoc_get_source($actual, true), jsdoc_get_version($node)->nid, $base), 'signature' => _jsdoc_build_function_signature($actual, $parameters) ); if (count($parameters)) { $function->parameters = $parameters; } $form['function'] = array( '#value' => theme('jsdoc_function_information', $function, $children, _jsdoc_get_object_themed($node, $node->title)), '#weight' => 30 ); } if (count($children)) { $form['children'] = array( '#value' => theme('jsdoc_object_children', $children), '#weight' => 40 ); } if (function_exists('comment_render') && $node->comment) { $form['comments'] = array( '#value' => comment_render($node), '#weight' => 50 ); } return drupal_render_form('jsdoc_object_form', $form); } function _jsdoc_get_title_themed($node) { $themed = _jsdoc_get_object_themed($node); $themed->crumbs = array(); if ($type = jsdoc_get_type($node)) { $type = jsdoc_object_node_load($type, jsdoc_get_version($node)); $themed->type = (object)array( 'title' => $type->title, 'url' => $type->jsdoc_url ); if ($type->jsdoc_url) { $themed->type->a = l($type->title, $type->jsdoc_url); } else { $themed->type->a = $type->title; } } $themed->return = (object)array( 'types' => array(), 'summary' => jsdoc_get_return_summary($node) ); foreach (jsdoc_get_return_types($node) as $type) { $themed->return->types[] = _jsdoc_get_type_themed($type, $node, false); } $parts = explode('.', $node->title); $title_text = ""; $end = array_pop($parts); foreach ($parts as $part) { if (!empty($title_text)) { $title_text .= '.'; } $title_text .= $part; $obj = jsdoc_object_node_load($title_text, jsdoc_get_version($node)); if ($obj->nid) { $themed->crumbs[] = l($part, $obj->jsdoc_url); } else { $themed->crumbs[] = $part; } } $themed->crumbs[] = $end; drupal_set_breadcrumb($themed->crumbs); return $themed; } function _jsdoc_get_object_themed($node, $title=null, $depth=0) { if (is_null($title)) { $title = $node->title; } $object = (object)array( 'title' => $title, 'url' => $node->jsdoc_url, 'tags' => jsdoc_get_tags($node), 'summary' => jsdoc_get_teaser($node), 'singleton' => jsdoc_is_initialized($node), 'namespace' => jsdoc_is_namespace($node) && !jsdoc_is_classlike($node), 'private' => jsdoc_is_private($node), 'private_parent' => jsdoc_has_private_parent($node) ); if ($object->url) { $object->a = l($node->title, $node->jsdoc_url); } else { $object->a = $object->title; } if ($type = jsdoc_get_type($node)) { $optional = false; if (strpos($type, '?') !== false){ $optional = true; $type = str_replace('?', '', $type); } $repeating = false; if (strpos($type, '...') !== false){ $repeating = true; $type = str_replace('...', '', $type); } $type = jsdoc_object_node_load($type, jsdoc_get_version($node)); $object->type = (object)array( 'title' => $type->title, 'url' => $type->jsdoc_url, 'optional' => $optional, 'repeating' => $repeating ); if ($type->jsdoc_url) { $object->type->a = l($type->title, $type->jsdoc_url); } else { $object->type->a = $type->title; } if (jsdoc_is_function($node)) { $parameters = _jsdoc_get_parameters_themed($node, false, $depth); if (!empty($parameters)) { $object->parameters = $parameters; } $object->return = (object)array( 'types' => array(), 'summary' => jsdoc_get_return_summary($node) ); foreach (jsdoc_get_return_types($node) as $type) { $object->return->types[] = _jsdoc_get_type_themed($type, $node, false); } $signature = _jsdoc_build_function_signature($node, $parameters); if (!empty($signature)) { $object->signature = $signature; } $source = jsdoc_get_source($node, true, $depth); if (!empty($source)) { $object->source = $source; } } } return $object; } function jsdoc_resolve_constructor(&$node) { if ($node->jsdoc_resolved_constructor) { return $node->jsdoc_resolved_constructor; } $superclass = jsdoc_get_parent_prototype($node); while ($superclass) { $constructor = (object)array(); _jsdoc_convert_object($constructor, $superclass->jsdoc_constructor, $node, TRUE); if ($constructor->jsdoc_parameters) { return ($node->jsdoc_resolved_constructor = $constructor); } $superclass = jsdoc_get_parent_prototype($superclass); } return ($node->jsdoc_resolved_constructor = $node->jsdoc_constructor); } function _jsdoc_get_type_themed($type, $node, $cleaned){ $obj = jsdoc_object_node_load($type, jsdoc_get_version($node), TRUE, TRUE); if ($obj) { $type = (object)array( 'title' => $obj->title, 'summary' => jsdoc_get_teaser($obj), 'fields' => false ); if (!$cleaned && $obj->jsdoc_url) { $type->url = $obj->jsdoc_url; $type->a = l($obj->title, $obj->jsdoc_url); } // I basically want to find the children of this as // if it appeared in the normal API. // It will have an nid if it's local. For example type: Function isn't local if ($obj->nid) { // TODO: This is used for inline types $type->fields = array(); list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($obj); foreach ($children as $child) { if (jsdoc_get_type($child) != 'Function') { $title = $child->title; $field = (object)array( 'title' => $title, 'summary' => $child->summary, 'types' => false ); if (!$cleaned) { $field->url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $obj->title . '.' . $child->title; $field->a = l($title, $field->url); } $field_types = preg_split('%\|+%', jsdoc_get_type($node)); foreach ($field_types as $field_type) { $found = 0; $field_type = str_replace('?', '', $field_type, $found); $optional = ($found > 0); $found = 0; $field_type = str_replace('...', '', $field_type, $found); $repeating = ($found > 0); $field_obj = jsdoc_object_node_load($field_type, jsdoc_get_version($node)); $field_type = (object)array( 'title' => $field_obj->title, 'repeating' => $repeating, 'optional' => $optional, 'summary' => jsdoc_get_teaser($field_obj) ); if (!$cleaned) { $field_type->url = $field_obj->jsdoc_url; $field_type->a = l($field_obj->title, $field_obj->jsdoc_url); } $field->types[] = $field_type; } $type->fields[$field->title] = $field; } } if (empty($type->fields)) { unset($type->fields); }else{ uksort($type->fields, 'strnatcasecmp'); } } return $type; } } function _jsdoc_get_parameters_themed(&$node, $cleaned=false) { if (isset($node->jsdoc_parameters_themed)) { return $node->jsdoc_parameters_themed; } $parameters = array(); $retrieved_parameters = jsdoc_get_parameters($node); foreach ($retrieved_parameters as $item) { $parameter = (object)$item->jsdoc_formatted; $parameter->summary = $item->summary; $parameter->name = $item->name; $parameter->optional = $item->usage == 'optional'; $parameter->repeating = $item->usage == 'repeating' || $item->usage == 'one-or-more'; $parameter->types = false; if ($cleaned) { unset($parameter->html_type_prefix); unset($parameter->html_type_suffix); unset($parameter->separator); } $type = $item->type; $types = array(); if (strpos($type, '|') !== false) { $types = preg_split('%\s*\|+\s*%', $type); } elseif ($type) { $types = array($type); } foreach ($types as $type) { if ($type = _jsdoc_get_type_themed($type, $node, $cleaned)) { $parameter->types[] = $type; } } if (empty($parameter->types)) { unset($parameter->html_type_prefix); unset($parameter->html_type_suffix); unset($parameter->separator); } unset($parameter->type); $parameters[] = $parameter; } return ($node->jsdoc_parameters_themed = $parameters); } function _jsdoc_format_type($type, $classlike = false, $optional = false, $repeating = false) { $output = array(); $name = ''; if ($type) { $output['html_type_prefix'] = '/*'; $output['html_type_suffix'] = '*/'; $output['type'] = $type; if ($type == 'Function' && $classlike) { $output['type'] = 'Constructor'; } if ($optional) { $output['type'] .= '?'; } if ($repeating) { $output['type'] .= '...'; } } if ($type) { $output['separator'] .= ' '; } return $output; } function _jsdoc_flatten(&$item, $key) { if (is_object($item)) { $item = $item->title; } } function jsdoc_get_namespace_parents(&$node) { if (isset($node->jsdoc_namespace_parents)) { return $node->jsdoc_namespace_parents; } $parents = array(); $parts = explode('.', $node->title); array_pop($parts); while (count($parts)) { $parents[] = jsdoc_object_node_load(implode('.', $parts), jsdoc_get_version($node)); array_pop($parts); } return ($node->jsdoc_namespace_parents = $parents); } function jsdoc_get_prototype_chain(&$node) { if (isset($node->jsdoc_prototype_chain)) { return $node->jsdoc_prototype_chain; } $chain = array($node); $superclass = $node->jsdoc_superclass; while ($superclass) { $superclass = jsdoc_object_node_load($superclass, jsdoc_get_version($node)); $chain[] = $superclass; $superclass = $superclass->jsdoc_superclass; } $chain[] = jsdoc_object_node_load('Object'); return ($node->jsdoc_prototype_chain = array_reverse($chain)); } function jsdoc_current_node($node=null) { static $current; if (is_null($node)) { return $current; } $current = $node; } function _jsdoc_build_function_signature($node, $parameters) { if (isset($node->jsdoc_function_signature)) { return $node->jsdoc_function_signature; } if (jsdoc_get_type($node) != 'Function' && jsdoc_get_type($node) != 'Constructor') { return; } $signature = '('; if ($parameters) { foreach ($parameters as $weight => $parameter) { if ($weight) { $signature .= ', '; } $types = array(); if ($parameter->types) { foreach ($parameter->types as $type) { $types[] = $type->a; } } $signature .= $parameter->html_type_prefix . implode('|', $types); if ($parameter->optional) { $signature .= '?'; } if ($parameter->repeating) { $signature .= '...'; } $signature .= $parameter->html_type_suffix . $parameter->separator . $parameter->name; } } $signature .= ')'; return ($node->jsdoc_function_signature = $signature); } function _jsdoc_resource_expand($tree, $child, $depth, &$resources){ $output = array(); foreach ($tree as $leaf) { if ($leaf->depth == $depth && in_array($child, $leaf->children) && !in_array($leaf->vid, $resources)) { $resources[] = $leaf->vid; $output[] = $leaf; } } return $output; } function _jsdoc_file_location() { static $location; if (!isset($location)) { $location = variable_get('jsdoc_file_location', false); } return $location; } function _jsdoc_dir_location() { static $location; if (!isset($location)) { $location = variable_get('jsdoc_dir_location', false); } return $location; } function _jsdoc_base() { static $base; if (!isset($base)) { $base = variable_get('jsdoc_base', 'jsdoc'); } return $base; } function _jsdoc_vocabularies($name) { static $vocabularies; if (!isset($vocabularies)) { $vocabularies = array(); } if ($vocabularies[$name]) { return $vocabularies[$name]; } foreach (taxonomy_get_vocabularies('jsdoc_object') as $vocabulary) { $vocabularies[$vocabulary->name] = $vocabulary; } return $vocabularies[$name]; } function _jsdoc_environments() { return _jsdoc_vocabularies('Environments'); } function _jsdoc_conditions() { return _jsdoc_vocabularies('Conditions'); } function _jsdoc_flags() { return _jsdoc_vocabularies('Flags'); } /** * Validate some of the expected global settings */ function _jsdoc_cron_validate() { ini_set('memory_limit', '256M'); if (!_jsdoc_file_location() || !_jsdoc_dir_location()) { print t('Check Drupal logs for errors'); watchdog('jsdoc', t('jsdoc settings should be configured'), WATCHDOG_ERROR); return false; } if (!file_exists(_jsdoc_file_location())) { print t('Check Drupal logs for errors'); watchdog('jsdoc', t('File does not exist ') . _jsdoc_file_location(), WATCHDOG_ERROR); return false; } return true; } function _jsdoc_cron_validate_include() { $files_function = _jsdoc_base() . '_get_files'; if (!function_exists($files_function)) { print t('Check Drupal logs for errors'); watchdog('jsdoc', $files_function . t(' does not exist in ') . _jsdoc_file_location() . t('. Check this file to make sure your functions really start with ') . _jsdoc_base(), WATCHDOG_ERROR); return false; } $test_file = _jsdoc_get_base_path() . '/' . drupal_get_path('module', 'jsdoc') . '/cache/test'; if (!@touch($test_file)) { print t('Check Drupal logs for errors'); watchdog('jsdoc', t('Need permission to access the jsdoc cache directory'), WATCHDOG_ERROR); return false; } else { unlink($test_file); } if (function_exists(_jsdoc_base() . '_get_conditions')) { $conditions = call_user_func(_jsdoc_base() . '_get_conditions'); $found = false; foreach ($conditions as $condition) { foreach (taxonomy_get_term_by_name($condition) as $term) { if ($term->vid == _jsdoc_conditions()->vid) { $found = true; break; } } if (!$found) { $term = array('name' => $condition, 'description' => '', 'vid' => _jsdoc_conditions()->vid, 'weight' => 0); taxonomy_save_term($term); } } } if (function_exists(_jsdoc_base() . '_get_environments')) { $environments = call_user_func(_jsdoc_base() . '_get_environments'); _jsdoc_build_taxonomy($environments, _jsdoc_environments()->vid); } if (!function_exists(_jsdoc_base() . '_get_contents')) { print t('Check Drupal logs for errors'); watchdog('jsdoc', _jsdoc_base() . '_get_contents' . t(' does not exist in ') . _jsdoc_file_location(), WATCHDOG_ERROR); return false; } return true; } function _jsdoc_build_terms($title) { $output = array($title, array()); $parts = explode('.', $title); foreach ($parts as $part) { if (preg_match('%^[_.$]*([a-zA-Z][a-z0-9_.$]*(?:[A-Z][a-z0-9_.$]*)+)%', $part, $match)) { if (preg_match_all('%(^[a-zA-Z][a-z0-9_.$]*|[A-Z][a-z0-9_.$]*)%', $match[1], $cased)) { $output[1] = array_merge($output[1], $cased[0]); } } } $output[1] = implode(' ', $output[1]); $output[2] = implode(' ', $parts); return $output; } function _jsdoc_build_taxonomy($names, $vid, $parent = false) { foreach ($names as $name => $content) { foreach (taxonomy_get_term_by_name($name) as $term) { if ($term->vid == $vid) { $found = true; break; } } if (!$found) { $term = array('name' => $name, 'description' => '', 'vid' => $vid, 'weight' => 0); if ($parent) { $term['parent'] = $parent; } taxonomy_save_term($term); } if (is_array($content)) { _jsdoc_build_taxonomy($content, $vid, $term->tid); } } } function _jsdoc_get_base_path() { static $path; if (!isset($path)) { $path = getcwd(); } return $path; } function _jsdoc_cron_chdir($enter=false) { static $location; if ($enter) { $location = _jsdoc_get_base_path(); if (!is_dir(_jsdoc_dir_location())){ print t('Check Drupal logs for errors'); watchdog('jsdoc', _jsdoc_dir_location() . t(' should be a directory'), WATCHDOG_ERROR); return false; } if (!@chdir(_jsdoc_dir_location())) { print t('Check Drupal logs for errors'); watchdog('jsdoc', _jsdoc_dir_location() . t(' could not be accessed'), WATCHDOG_ERROR); return false; } include_once(_jsdoc_file_location()); } else { chdir($location); } } // Private Utility Functions // ========================= function _jsdoc_theme_clone($node) { $clone = drupal_clone($node); foreach ($clone as $key => $value) { if (substr($key, 0, 6) == 'jsdoc_') { unset($clone->$key); } } return $clone; } function _jsdoc_node_prepare($node) { $node->format = variable_get('jsdoc_input_format', 1); $form = node_prepare($node)->content; $form['summary']['#weight'] = $form['body']['#weight'] - 1; $teaser = jsdoc_get_teaser($node); $body = jsdoc_get_body($node); if ($body == t('n/a')) { $body = ''; } $form['summary']['#value'] = theme('jsdoc_object_summary', $teaser); $form['description']['#value'] = theme('jsdoc_object_description', $teaser, $body); unset($form['body']); return $form; } function _jsdoc_markup_code($text, $version, $base=false) { static $variables = array(); $min = 99; $lines = preg_split('%\r?\n%', $text); foreach ($lines as $line) { if (!empty($line)) { if (preg_match('%^\t+%', $line, $match)) { if (strlen($match[0]) < $min) { $min = strlen($match[0]); } } } } if ($min) { foreach ($lines as $i => $line) { $lines[$i] = preg_replace('%^\t{' . $min . '}%', '', $line); } } $text = implode("\n", $lines); _jsdoc_init(); $language = 'javascript'; if (preg_match('%(^\W*<|>\W*$)%', $text)) { $language = 'html4strict'; } $highlighter =& new GeSHi($text, $language); $highlighter->enable_classes(); $highlighter->enable_keyword_links(false); $highlighter->set_overall_style('color: #666;', true); $highlighter->set_tab_width(4); $highlighter->add_keyword_group(4, '', true, array('Math', 'Error', 'Array')); $text = $highlighter->parse_code(); if (preg_match_all('%[\w.$]+(?:\.(?:)?[\w.$]+(?:)?)+%', $text, $matches)) { natcasesort(array_unique($matches[0])); $matches = array_reverse($matches[0]); foreach ($matches as $i => $match) { $variable = preg_replace('%<[^>]+>%', '', $match); if ($variables && array_key_exists($variable, $variables)) { $text = str_replace($match, "%$i%", $text); } } foreach ($matches as $i => $match) { $variable = preg_replace('%<[^>]+>%', '', $match); if ($variables[$variable]) { $version = node_load($variables[$variable]); $text = str_replace("%$i%", l($match, 'jsdoc/' . $version->title . '/' . $variable, array('style' => 'border-bottom: 1px dotted #ccc;'), NULL, NULL, FALSE, TRUE), $text); } } } if ($base && preg_match_all('%this(\.(?:[\w.$]+)+)%', $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $variable = $base . preg_replace('%<[^>]+>%', '', $match[1]); if ($variables && array_key_exists($variable, $variables)) { if ($variables[$variable]) { $version = node_load($variables[$variable]); $text = str_replace($match[0], l($match[0], 'jsdoc/' . $version->title . '/' . $variable, array('style' => 'border-bottom: 1px dotted #ccc;'), NULL, NULL, FALSE, TRUE), $text); } } } } return $text; } function _jsdoc_markup_text($text, $version, $format = false){ if (!$format) { $format = variable_get('jsdoc_input_format', 1); } $text = check_markup($text, $format, FALSE); if (preg_match_all('%(?:\s*(?:|)\s*)+(.*?)(?:\s*(?:
|)\s*)+%s', $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$match[1] = str_replace('<', '<', str_replace('>', '>', $match[1]));
$text = str_replace($match[0], _jsdoc_markup_code($match[1], $version), $text);
}
}
return $text;
}
function _jsdoc_init() {
include_once(_jsdoc_get_base_path() . '/' . drupal_get_path('module', 'jsdoc') . '/lib/geshi/geshi.php');
drupal_add_css(drupal_get_path('module', 'jsdoc') . '/jsdoc.css');
}
// Theme functions
// ===============
/**
* When an object occurs in more than one file, we want
* to show this to the user so that they can choose
* the likely location until we disambiguate it.
*/
function theme_jsdoc_object_resources($node, $resources) {
$output = '