array('name' => t('JavaScript Object'), 'module' => 'jsdoc_object', 'description' => t('An implemented JavaScript object')), 'jsdoc_variable' => array('name' => t('JavaScript Variable'), 'module' => 'jsdoc_variable', 'description' => t('A disambiguation in case an object occurs in more than one file.')), 'jsdoc_resource' => array('name' => t('JavaScript Resource'), 'module' => 'jsdoc_resource', 'description' => t('A "dot-notation" pointer to a file or include')), 'jsdoc_project' => array('name' => t('JavaScript Project'), 'module' => 'jsdoc_project'), 'jsdoc_version' => array('name' => t('JavaScript Project Version'), 'module' => 'jsdoc_version', 'description' => t('A version ID for a project')) ); } /** * 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' => 'node/add/jsdoc', 'access' => false ); $items[] = array( 'path' => 'node/add/jsdoc_object', 'access' => false ); $items[] = array( 'path' => 'node/add/jsdoc_resource', 'access' => false ); $items[] = array( 'path' => 'jsdoc/jsonp', 'type' => MENU_CALLBACK, 'callback' => 'jsdoc_jsonp', 'access' => true ); $items[] = array( 'path' => 'jsdoc/jsonp', 'type' => MENU_CALLBACK, 'callback' => 'jsdoc_jsonp', 'access' => true ); $items[] = array( 'path' => 'jsdoc/ide', 'type' => MENU_CALLBACK, 'callback' => 'jsdoc_ide', 'access' => true ); $items[] = array( 'path' => 'jsdoc/404', 'title' => t('Page not found'), 'type' => MEN_CALLBACK, 'callback' => 'jsdoc_404', 'access' => true ); } else { if (db_result(db_query("SELECT 1 FROM {jsdoc_objects} WHERE used = 0 OR new = 1"))) { $items[] = array('path' => 'jsdoc/manage', 'title' => t('Manage Documentation Changes'), 'callback' => 'drupal_get_form', 'callback arguments' => array('jsdoc_manage'), 'access' => user_access('edit jsdoc'), 'type' => MENU_NORMAL_ITEM ); } if (arg(0) == 'jsdoc' && arg(1) != 'jsonp' && arg(1) != 'ide') { if (arg(1) == 'manage') { return $items; } $project = false; $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[4]) { // If we have 4 arguments, assume that we've been passed a resource $project = $args[1]; $version = $args[2]; $resource = str_replace('__', '/', $args[3]); $name = $args[4]; $path = "jsdoc/$project/$version/" . $args[3] . "/$name"; // TODO: Remove this once search engines have stopped indexing it. if ($resource == 'object') { drupal_goto("jsdoc/$project/$version/$name"); } } elseif ($args[3]) { // If we have 3 arguments, assume that we don't have a resource $project = $args[1]; $version = $args[2]; $name = $args[3]; $path = "jsdoc/$project/$version/$name"; } elseif ($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"; } if (($node = jsdoc_object_node_load($name, $project, $version, $resource)) && $node->nid) { /* // Restore this when jsdoc_object_nodes_load uses namespaces if (count($node) == 1) { $node = $node[0]; */ jsdoc_current_node($node); /* } */ $item = array( 'path' => $path, 'title' => t('View'), 'access' => node_access('view', $node), 'type' => MENU_CALLBACK ); if (is_array($node)) { $item['callback'] = 'jsdoc_variables_node_view'; $item['callback arguments'] = array($node); } else { $path = explode('/', $node->jsdoc_url); if ($switch) { $path[2] = $switch; drupal_goto(implode('/', $path)); return; } $project = jsdoc_get_project($node); $version = jsdoc_get_version($node); $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'] = $node->type . '_node_view'; $item['callback arguments'] = array($node); } $items[] = $item; } else { $_REQUEST['destination'] = $path; drupal_not_found(); exit(); } } } 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(); if ($_GET['jsdoc_project'] || $current) { if ($_GET['jsdoc_project']) { $project_title = $_GET['jsdoc_project']; } else { $project_title = jsdoc_get_project($current)->title; } foreach (jsdoc_projects() as $project) { if ($project->title != $project_title) { $namespaces[] = (object)array( 'title' => $project->title, 'url' => url($_GET['q'], 'jsdoc_project=' . $project->title), 'a' => l($project->title, $_GET['q'], array(), 'jsdoc_project=' . $project->title) ); } else { if ($_GET['jsdoc_project']) { $version = jsdoc_get_version('HEAD', $_GET['jsdoc_project']); $project = jsdoc_get_project($version); } else { $version = jsdoc_get_version($current); $project = jsdoc_get_project($current); } if (!($all_namespaces = cache_get('jsdoc_namespaces_' . $project->title, 'cache'))) { $all_namespaces = array(); } else { $all_namespaces = unserialize($all_namespaces->data); } if (empty($all_namespaces[$version->nid])) { $query = db_query("SELECT nr.title, nr.nid, nr.vid, jo.type AS jsdoc_type, jo.initialized AS jsdoc_initialized FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (nr.vid = jo.vid) WHERE jo.private = 0 AND jo.private_parent = 0 AND jo.version = %d AND (nr.title = '%s' OR nr.title LIKE '%s.%%') AND (jo.type = 'Object' OR (jo.type = 'Function' AND jo.initialized = 1)) GROUP BY BINARY nr.title ORDER BY nr.title", $version->nid, $project->title, $project->title); while ($result = db_fetch_object($query)) { if (jsdoc_is_namespace($result)) { $all_namespaces[$version->nid][] = array($result->nid, $result->vid); } } cache_set('jsdoc_namespaces_' . $project->title, 'cache', serialize($all_namespaces)); } foreach ($all_namespaces[$version->nid] as $namespace) { $namespace = _jsdoc_node_load($namespace[0], $namespace[1]); $children = false; if ($_GET['jsdoc_object'] == $namespace->title) { $variables = jsdoc_get_child_variables($namespace); if (!empty($variables)) { foreach ($variables as $child) { $children[$child->title] = _jsdoc_get_object_themed($child); } uksort($children, "strnatcasecmp"); } } $jsdoc_url = $namespace->jsdoc_url; $namespace = _jsdoc_get_object_themed($namespace); $namespace->children = $children; $namespace->jsdoc_url = $jsdoc_url; $namespace->url = url($namespace->title, $_GET['q'], 'jsdoc_project=' . $_GET['jsdoc_project'] . '&jsdoc_object=' . $namespace->title); $namespace->a = l($namespace->title, $_GET['q'], array(), 'jsdoc_project=' . $_GET['jsdoc_project'] . '&jsdoc_object=' . $namespace->title); $namespaces[] = $namespace; } } } } else { foreach (jsdoc_projects() as $project) { $namespaces[] = (object)array( 'title' => $project->title, 'url' => url($_GET['q'], 'jsdoc_project=' . $project->title), 'a' => l($project->title, $_GET['q'], array(), 'jsdoc_project=' . $project->title) ); } } //$namespaces = unserialize(cache_get('jsdoc_namespaces', 'cache')->data); $block['subject'] = 'Objects'; $block['content'] = theme('jsdoc_namespaces', $namespaces, $version->title); 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) { $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); } /** * 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 $last_change, $last_nid; register_shutdown_function('jsdoc_update_shutdown'); $last = variable_get('jsdoc_cron_last', 0); $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); while ($node = db_fetch_object($result)) { $last_change = $node->last_change; $last_nid = $node->nid; $node = _jsdoc_node_load($node->nid); $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_manage($edit = array()) { // If the first parse has run, all items in the DB have new = 1; $query = db_query("SELECT 1 FROM {jsdoc_objects} WHERE new != 1"); if (!db_num_rows($query)) { db_query("UPDATE {jsdoc_objects} SET new = 0"); drupal_goto(''); } $header = array('Variable', 'Project', 'Resource', 'New', 'Deleted or Renamed'); //_drupal_add_js('../dojo/dojo.js', 'module', 'header', false, true); _drupal_add_js(drupal_get_path('module', 'jsdoc') . '/jsdoc.js', 'module', 'header', false, true); // Objects $query = db_query("SELECT j.nid, j.vid, j.used, j.new, j.version FROM {jsdoc_objects} j INNER JOIN {node_revisions} nr ON (nr.vid = j.vid) INNER JOIN {node_revisions} nr2 ON (nr2.vid = j.resource_vid) WHERE j.used = 0 OR j.new = 1 ORDER BY j.version, nr2.title, nr.title"); while ($object = db_fetch_object($query)) { set_time_limit(10); if (!$version) { $version = $object->version; } if($version != $object->version){ break; } $node = _jsdoc_node_load($object->nid, $object->vid); $project = jsdoc_get_project($node); drupal_set_title(t('Manage Documentation Changes for ') . $project->title); $resource = jsdoc_get_resource($node); $new = ''; $renamed = ''; if ($object->new) { $new = "title}][{$node->nid}][{$node->vid}]\" value=\"{$project->title}|{$resource->title}|{$node->title}|{$node->nid}|{$node->vid}\" checked=\"checked\" />"; } else { $renamed = ""; } $rows[] = array($node->title, jsdoc_get_project($node)->title, theme('jsdoc_resource_source_link', jsdoc_get_project($resource)->title, $resource->title), $new, $renamed); } $form['table'] = array( '#value' => theme('table', $header, $rows) ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Done') ); return $form; } function jsdoc_manage_submit($form_id, $form) { if ($form_id == 'jsdoc_manage') { if ($_POST['modified']) { foreach ($_POST['modified'] as $projects) { foreach ($projects as $nid => $vids) { foreach ($vids as $vid => $props) { set_time_limit(10); if (!$props) { // Deleted $node = _jsdoc_node_load($nid, $vid); jsdoc_object_delete($node, $vid); } else { // Moved: Change the nid of the new node, delete the revision of the old list($move_nid, $move_vid) = explode('_', $props); $node = _jsdoc_node_load($nid, $vid); $move_node = _jsdoc_node_load($move_nid, $move_vid); foreach (array('jsdoc_objects', 'jsdoc_variables', 'jsdoc_examples', 'jsdoc_parameters', 'jsdoc_variable_hierarchy') as $i => $table) { $extra = ''; if (!$i) { $extra = ", new = 0"; } db_query("UPDATE {" . $table . "} SET nid = %d$extra WHERE vid = %d", $node->nid, $move_node->vid); } jsdoc_object_delete($node, $vid); } } } } } if ($_POST['new']) { foreach ($_POST['new'] as $projects) { foreach ($projects as $nid => $vids) { foreach ($vids as $vid => $isnew) { if ($isnew) { db_query("UPDATE {jsdoc_objects} SET new = 0 WHERE vid = %d", $vid); } } } } } } if (db_result(db_query("SELECT 1 FROM {jsdoc_objects} WHERE used = 0 OR new = 1"))) { drupal_goto('jsdoc/manage'); } else { drupal_goto(''); } } // 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_form(). */ function jsdoc_object_form(&$node) { $form = array( '#redirect' => $node->jsdoc_url ); if ($node->teaser) { $form['summary'] = array( '#type' => 'item', '#title' => t('Summary'), '#value' => $node->teaser, '#weight' => -10 ); } if ($node->jsdoc_type != 'Function' && !$node->jsdoc_source) { // Force them to specify summary in source where possible $form['teaser'] = array( '#type' => 'textfield', '#title' => t('Summary'), '#default-value' => $node->teaser, '#weight' => -10 ); } else { $form['teaser'] = array( '#type' => 'hidden', '#value' => $node->teaser ); $form['jsdoc_classlike'] = array( '#type' => 'checkbox', '#title' => t('Is intended to be instantiated (like a class)'), '#default_value' => $node->jsdoc_classlike, '#weight' => -2 ); } $form['jsdoc_updating'] = array( '#type' => 'checkbox', '#title' => t('Update on next cron'), '#default_value' => 0, '#weight' => 0 ); $parameters = jsdoc_get_parameters($parameters); if ($parameters) { foreach ($parameters as $parameter) { $form['jsdoc_parameters'][$parameter['name']] = array( '#type' => 'fieldset', '#title' => $parameter['jsdoc_formatted']['type'] . $parameter['jsdoc_formatted']['separator'] . $parameter['name'], '#weight' => $parameter['weight'] ); $form['jsdoc_parameters'][$parameter['name']]['summary'] = array( '#title' => t('Summary'), '#type' => 'textfield', '#default_value' => $parameter['summary'] ); $form['jsdoc_parameters']['#type'] = 'fieldset'; $form['jsdoc_parameters']['#title'] = t('Parameters'); $form['jsdoc_parameters']['#tree'] = true; $form['jsdoc_parameters']['#collapsible'] = true; $form['jsdoc_parameters']['#weight'] = -8; } } $form['body_filter']['#weight'] = -6; $form['body_filter']['body'] = array( '#type' => 'textarea', '#title' => t('Description'), '#default_value' => $node->body, '#rows' => 10, '#required' => FALSE, ); $form['body_filter']['format'] = filter_form($node->format); return $form; } /** * Implementation of hook_insert(). */ function jsdoc_object_insert($node) { $project = jsdoc_get_project($node) ? jsdoc_get_project($node) : _jsdoc_project_get_or_create(jsdoc_get_project($node)->title); $version = jsdoc_get_version($node) ? jsdoc_get_version($node) : _jsdoc_version_get_or_create('HEAD', $project); if (!isset($node->jsdoc_new)) { $node->jsdoc_new = true; } db_query("INSERT INTO {jsdoc_objects} (vid, nid, resource_vid, resource_nid, provide_vid, provide_nid, initialized, classlike, type, returns, return_summary, source, private, private_parent, version, new) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s', '%s', '%s', '%s', %d, %d, %d, %d)", $node->vid, $node->nid, jsdoc_get_resource($node)->vid, jsdoc_get_resource($node)->nid, jsdoc_get_provide($node)->vid, jsdoc_get_provide($node)->nid, jsdoc_is_initialized($node), jsdoc_get_classlike($node), jsdoc_get_type($node), implode('|', jsdoc_get_return_types($node)), jsdoc_get_return_summary($node), jsdoc_get_source($node), jsdoc_is_private($node), jsdoc_has_private_parent($node), $version->nid, $node->jsdoc_new); if ($node->revision) { return; } _jsdoc_detail_update_joins($node); if ($parameters = jsdoc_get_parameters($node)) { $i = 0; foreach ($parameters as $parameter_name => $parameter) { db_query("INSERT INTO {jsdoc_parameters} (vid, nid, weight, name, type, summary, optional, repeating) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d)", $node->vid, $node->nid, $i++, $parameter_name, $parameter['type'], $parameter['summary'], $parameter['optional'], $parameter['repeating']); } } if (is_array(jsdoc_get_examples($node))) { foreach (jsdoc_get_examples($node) as $weight => $example) { $example = preg_replace('%^([\s]*)\n%', '', $example); db_query("INSERT INTO {jsdoc_examples} (vid, nid, weight, example, markedup) VALUES (%d, %d, %d, '%s', '%s')", $node->vid, $node->nid, $weight, $example, _jsdoc_markup_text($example, jsdoc_get_version($node)->nid)); } } } /** * Implementation of hook_update(). */ function jsdoc_object_update($node) { if ($node->revision) { jsdoc_object_insert($node); db_query("INSERT INTO {jsdoc_parameters} (vid, nid, weight, name, type, summary, optional, repeating) SELECT %d, nid, weight, name, type, summary, optional, repeating FROM {jsdoc_parameters} WHERE vid = %d", $node->vid, $node->old_vid); db_query("INSERT INTO {jsdoc_examples} (vid, nid, weight, example, markedup) SELECT %d, nid, weight, example, markedup FROM {jsdoc_examples} WHERE vid = %d", $node->vid, $node->old_vid); return; } db_query("UPDATE {jsdoc_objects} SET updating = %d, resource_vid = %d, resource_nid = %d, provide_vid = %d, provide_nid = %d, initialized = %d, classlike = %d, type = '%s', returns = '%s', return_summary = '%s', source = '%s', private = %d, private_parent = %d, version = %d WHERE vid = %d", $node->jsdoc_updating, $node->jsdoc_resource_vid, $node->jsdoc_resource, $node->jsdoc_provide_vid, $node->jsdoc_provide, $node->jsdoc_initialized, $node->jsdoc_classlike, $node->jsdoc_type, $node->jsdoc_returns, $node->jsdoc_return_summary, $node->jsdoc_source, $node->jsdoc_private, $node->jsdoc_private_parent, jsdoc_get_version($node)->nid, $node->vid); if ($node->status){ _jsdoc_detail_update_joins($node); db_query("DELETE FROM {jsdoc_parameters} WHERE vid = %d", $node->vid); if ($parameters = jsdoc_get_parameters($node)) { $i = 0; foreach ($parameters as $parameter_name => $parameter) { if (is_array($parameter)) { db_query("INSERT INTO {jsdoc_parameters} (vid, nid, weight, name, type, summary, optional, repeating) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d)", $node->vid, $node->nid, $i++, $parameter_name, $parameter['type'], ($parameter['previous_summary']) ? $parameter['previous_summary'] : $parameter['summary'], $parameter['optional'], $parameter['repeating']); } } } db_query("DELETE FROM {jsdoc_examples} WHERE vid = %d", $node->vid); if (is_array(jsdoc_get_examples($node))) { foreach (jsdoc_get_examples($node) as $weight => $example) { $example = preg_replace('%^([\s]*)\n%', '', $example); db_query("INSERT INTO {jsdoc_examples} (vid, nid, weight, example, markedup) VALUES (%d, %d, %d, '%s', '%s')", $node->vid, $node->nid, $weight, $example, _jsdoc_markup_text($example, jsdoc_get_version($node)->nid)); } } } } /** * Implementation of hook_delete */ function jsdoc_object_delete(&$node, $vid=null) { $type = ($vid) ? 'vid' : 'nid'; db_query("DELETE FROM {jsdoc_objects} WHERE $type = %d", $node->$type); db_query("DELETE FROM {jsdoc_variable_hierarchy} WHERE $type = %d", $node->$type); db_query("DELETE FROM {jsdoc_parameters} WHERE $type = %d", $node->$type); db_query("DELETE FROM {jsdoc_examples} WHERE $type = %d", $node->$type); if ($vid) { if (!db_num_rows(db_query("SELECT 1 FROM {node_revisions} WHERE nid = %d AND vid != %d", $node->nid, $vid))) { node_delete($node->nid); } else { db_query("DELETE FROM {node_revisions} WHERE vid = %d", $vid); } } } /** * Implementation of hook_load(). */ function jsdoc_object_load($node) { // TODO: Deal with alias _jsdoc_init(); $additions = db_fetch_object(db_query("SELECT jo.type AS jsdoc_raw_type, jo.returns AS jsdoc_returns, jo.return_summary AS jsdoc_return_summary, jo.updating AS jsdoc_updating, jo.classlike AS jsdoc_classlike, jo.private AS jsdoc_private, jo.private_parent AS jsdoc_private_parent, jo.resource_nid AS jsdoc_resource, jo.resource_nid AS jsdoc_resource_nid, jo.resource_vid AS jsdoc_resource_vid, jo.provide_nid AS jsdoc_provide, jo.provide_nid AS jsdoc_provide_nid, jo.provide_vid AS jsdoc_provide_vid, jo.initialized AS jsdoc_initialized, jo.used AS jsdoc_used, jo.version AS jsdoc_version, source AS jsdoc_source, jo.resource_vid != nr2.vid AS has_revisions FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (jo.resource_vid = nr.vid) LEFT JOIN {node_revisions} nr2 ON (jo.nid = nr2.nid AND jo.vid != nr2.vid) WHERE jo.vid = %d GROUP BY jo.vid", $node->vid)); $additions->jsdoc_used = round($additions->jsdoc_used); $additions->jsdoc_formatted = _jsdoc_format_type($additions->jsdoc_raw_type, $additions->jsdoc_classlike); $node->jsdoc_version = $additions->jsdoc_version; $additions->jsdoc_full_url = 'jsdoc/' . jsdoc_get_project($additions)->title . '/' . jsdoc_get_version($additions)->title . '/' . str_replace('/', '__', jsdoc_get_resource($node)->title) . '/' . $node->title; $additions->jsdoc_url = 'jsdoc/' . jsdoc_get_project($additions)->title . '/' . jsdoc_get_version($additions)->title . '/' . $node->title; 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; } /** * Implementation of hook_access(). */ function jsdoc_variable_access($op, $node) { return jsdoc_object_access($op, $node); } /** * Implementation of hook_form(). */ function jsdoc_variable_form(&$node) { $form = array( '#redirect' => $node->jsdoc_url ); $form['jsdoc_disambiguation'] = array( '#type' => 'fieldset', '#title' => t('Disambiguation'), '#collapsible' => true, '#collapsed' => true ); $form['jsdoc_disambiguation']['jsdoc_disambiguation'] = array( '#type' => 'radios', '#default_value' => ($node->jsdoc_disambiguation == -1) ? -1 : round($node->jsdoc_disambiguation->vid), '#options' => array() ); $disambiguations = $node->jsdoc_disambiguations; if ($node->jsdoc_detail) { array_unshift($disambiguations, $node->jsdoc_detail); } foreach ($disambiguations as $disambiguation) { jsdoc_get_resource($disambiguation); jsdoc_get_provide($disambiguation); $form['jsdoc_disambiguation']['jsdoc_disambiguation']['#options'][$disambiguation->jsdoc_resource->vid] = t('Use the summary and description in ' . $disambiguation->jsdoc_resource->title . ' (' . l('Edit object', 'jsdoc/' . jsdoc_get_project($node)->title . '/' . jsdoc_get_version($node)->title . '/' . $node->title . '/edit/' . $disambiguation->jsdoc_resource->vid) . ')'); } $form['jsdoc_disambiguation']['jsdoc_disambiguation']['#options']['0'] = t('No customization (use default if only one resource)'); $form['jsdoc_disambiguation']['jsdoc_disambiguation']['#options']['-1'] = t('Provide a custom summary and description (below)'); $form['teaser'] = array( '#type' => 'textfield', '#title' => t('Summary'), '#default_value' => $node->teaser ); $form['body'] = array( '#type' => 'textarea', '#title' => t('Description'), '#default_value' => $node->body ); return $form; } /** * Implementation of hook_insert(). */ function jsdoc_variable_insert($node) { db_query("INSERT INTO {jsdoc_variables} (vid, nid, private, private_parent, version) VALUES (%d, %d, %d, %d, %d)", $node->vid, $node->nid, $node->jsdoc_private, $node->jsdoc_private_parent, jsdoc_get_version($node)->nid); } /** * Implementation of hook_insert(). */ function jsdoc_version_insert($node) { $old = jsdoc_version_node_load('HEAD', jsdoc_get_project($node))->nid; db_query("INSERT INTO {jsdoc_versions} (nid, project) VALUES (%d, %d)", $node->nid, jsdoc_get_project($node)->nid); if ($old) { $resources = array(); db_query("UPDATE {jsdoc_resources} SET version = %d WHERE version = %d", $node->nid, $old); $query = db_query("SELECT nid, MAX(vid) AS vid FROM {jsdoc_resources} WHERE version = %d GROUP BY nid", $node->nid); while ($resource = db_fetch_object($query)) { set_time_limit(10); $resource = _jsdoc_node_load($resource->nid, $resource->vid); $resource->revision = true; $resource->jsdoc_new = false; $resource->jsdoc_version = $old; node_save($resource); $resources[$resource->old_vid] = $resource->vid; // Map new values } db_query("UPDATE {jsdoc_resource_hierarchy} SET version = %d WHERE version = %d", $node->nid, $old); $query = db_query("SELECT * FROM {jsdoc_resource_hierarchy} WHERE version = %d", $node->nid); while ($resource = db_fetch_object($query)) { set_time_limit(10); if (!$resources[$resource->parent_vid]) { // Holdover from some old, buggy code continue; } db_query("INSERT INTO {jsdoc_resource_hierarchy} (vid, nid, parent_vid, parent_nid, tid, version) VALUES (%d, %d, %d, %d, %d, %d)", $resources[$resource->vid], $resource->nid, $resources[$resource->parent_vid], $resource->parent_nid, $resource->tid, $old); } $objects = array(); db_query("UPDATE {jsdoc_objects} SET version = %d WHERE version = %d", $node->nid, $old); $query = db_query("SELECT nid, MAX(vid) AS vid FROM {jsdoc_objects} WHERE version = %d GROUP BY nid", $node->nid); while ($object = db_fetch_object($query)) { set_time_limit(10); $object = _jsdoc_node_load($object->nid, $object->vid); $object->revision = true; $object->jsdoc_new = false; $object->jsdoc_version = $old; $object->jsdoc_resource = $object->jsdoc_resource_nid; $object->jsdoc_resource_vid = $resources[$object->jsdoc_resource_vid]; $object->jsdoc_provide = $object->jsdoc_provide_nid; $object->jsdoc_provide_vid = $resources[$object->jsdoc_provide_vid]; node_save($object); $objects[$object->old_vid] = $object->vid; } $variables = array(); db_query("UPDATE {jsdoc_variables} SET version = %d WHERE version = %d", $node->nid, $old); $query = db_query("SELECT nid, MAX(vid) AS vid FROM {jsdoc_variables} WHERE version = %d GROUP BY nid", $node->nid); while ($variable = db_fetch_object($query)) { set_time_limit(10); $variable = _jsdoc_node_load($variable->nid, $variable->vid); $variable->revision = true; $variable->jsdoc_version = $old; $variable->jsdoc_resource = $variable->jsdoc_resource_nid; $variable->jsdoc_resource_vid = $resources[$variable->jsdoc_resource_vid]; node_save($variable); $variables[$variable->old_vid] = $variable->vid; // Map new values } db_query("UPDATE {jsdoc_variable_hierarchy} SET version = %d WHERE version = %d", $node->nid, $old); $query = db_query("SELECT * FROM {jsdoc_variable_hierarchy} WHERE version = %d", $node->nid); while ($variable = db_fetch_object($query)) { set_time_limit(10); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, subtype, version) VALUES (%d, %d, %d, %d, '%s', '%s', %d)", $objects[$variable->vid], $variable->nid, $variables[$variable->parent_vid], $variable->parent_nid, $variable->type, $variable->subtype, $old); } } } /** * Implementation of hook_update(). */ function jsdoc_variable_update($node) { if ($node->revision) { jsdoc_variable_insert($node); return; } if (is_numeric($node->jsdoc_disambiguation)) { $resource_nid = db_result(db_query("SELECT nid FROM {node_revisions} WHERE vid = %d", $node->jsdoc_disambiguation)); $node->jsdoc_disambiguation = (object)array( 'nid' => $resource_nid, 'vid' => $node->jsdoc_disambiguation ); } db_query("UPDATE {jsdoc_variables} SET resource_vid = %d, resource_nid = %d, private = %d, private_parent = %d, version = %d WHERE vid = %d", $node->jsdoc_disambiguation->vid, $node->jsdoc_disambiguation->nid, $node->jsdoc_private, $node->jsdoc_private_parent, jsdoc_get_version($node)->nid, $node->vid); } /** * Implementation of hook_delete */ function jsdoc_variable_delete(&$node, $vid=null) { $type = ($vid) ? 'vid' : 'nid'; if (function_exists('search_wipe')) { search_wipe($node->nid, 'jsdoc_' . jsdoc_get_version($node)->title); } db_query("DELETE FROM {jsdoc_variables} WHERE $type = %d", $node->$type); db_query("DELETE FROM {jsdoc_variable_hierarchy} WHERE parent_$type = %d", $node->$type); if ($vid) { if (!db_num_rows(db_query("SELECT 1 FROM {node_revisions} WHERE nid = %d AND vid != %d", $node->nid, $vid))) { node_delete($node->nid); } else { db_query("DELETE FROM {node_revisions} WHERE vid = %d", $vid); } } $query = db_query("SELECT nr.nid FROM {node_revisions} nr JOIN {jsdoc_objects} j ON (j.vid = nr.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s'", $node->title, $node->title); $results = array(); while ($object = db_fetch_object($query)) { $results[$object->nid] = $object; } foreach ($results as $object) { if (!$vid) { node_delete($object->nid); break; } else { $subquery = db_query("SELECT nid, vid FROM {jsdoc_objects} WHERE nid = %d AND version = %d", $object->nid, jsdoc_get_version($node)->nid); while ($object = db_fetch_object($subquery)) { // Same name in the same version $object = _jsdoc_node_load($object->nid, $object->vid); jsdoc_object_delete($object, $object->vid); } } } } /** * Implementation of hook_access(). */ function jsdoc_version_access($op, $node) { if ($op == 'create') { return user_access('edit jsdoc'); } if ($op == 'delete') { return ($node->title != 'HEAD'); } return jsdoc_object_access($op, $node); } /** * Implementation of hook_form(). */ function jsdoc_version_form(&$node) { $form = array(); $options = array(); $query = db_query("SELECT nid, title FROM {node} WHERE type = 'jsdoc_project' AND status = 1"); while ($project = db_fetch_object($query)) { $options[$project->nid] = $project->title; } $form['jsdoc_project'] = array( '#title' => t('Project'), '#type' => 'select', '#options' => $options, '#weight' => -1 ); $form['title'] = array( '#title' => t('Version Number'), '#type' => 'textfield', '#default_value' => $node->title ); $form['body'] = array( '#title' => t('Release Notes'), '#type' => 'textarea', '#default_value' => $node->body ); return $form; } /** * Implementation of hook_delete(). */ function jsdoc_version_delete($node) { db_query("DELETE FROM {jsdoc_versions} WHERE nid = %d", $node->nid); $query = db_query("SELECT nid, vid FROM {jsdoc_variables} WHERE version = %d", $node->nid); while ($variable = db_fetch_object($query)) { set_time_limit(30); $variable = _jsdoc_node_load($variable->nid, $variable->vid); jsdoc_variable_delete($variable, $variable->vid); } $query = db_query("SELECT nid, vid FROM {jsdoc_resources} WHERE version = %d", $node->nid); while ($resource = db_fetch_object($query)) { set_time_limit(30); $resource = _jsdoc_node_load($resource->nid, $resource->vid); jsdoc_resource_delete($resource, $resource->vid); } } /** * Implementation of hook_access(). */ function jsdoc_resource_access($op, $node) { return jsdoc_object_access($op, $node); } /** * Implementation of hook_form(). */ function jsdoc_resource_form(&$node, &$param) { $type = node_get_types('type', $node); $form = array( '#redirect' => $node->jsdoc_url ); $form['title'] = array( '#type' => 'item', '#title' => t('Resource Name'), '#value' => $node->title, '#weight' => -5 ); $form['teaser'] = array( '#type' => 'textfield', '#title' => t('Summary'), '#required' => TRUE, '#default_value' => $node->teaser, '#weight' => -4 ); $form['body_filter']['#weight'] = -6; $form['body_filter']['body'] = array( '#type' => 'textarea', '#title' => t('Description'), '#default_value' => $node->body, '#required' => FALSE ); $form['body_filter']['filter'] = filter_form($node->format); return $form; } /** * Implementation of hook_resource(). */ function jsdoc_resource_insert($node) { if (!isset($node->jsdoc_new)) { $node->jsdoc_new = true; } db_query("INSERT INTO {jsdoc_resources} (vid, nid, version, new) VALUES (%d, %d, %d, %d)", $node->vid, $node->nid, jsdoc_get_version($node)->nid, $node->jsdoc_new); } /** * Implementation of hook_update(). */ function jsdoc_resource_update($node) { if ($node->revision) { jsdoc_resource_insert($node); return; } db_query("UPDATE {jsdoc_resources} SET version = %d WHERE vid = %d", jsdoc_get_version($node)->nid, $node->vid); } /** * Implementation of hook_delete */ function jsdoc_resource_delete(&$node, $vid=null) { $type = ($vid) ? 'vid' : 'nid'; db_query("DELETE FROM {jsdoc_resources} WHERE $type = %d", $node->$type); db_query("DELETE FROM {jsdoc_resource_hierarchy} WHERE $type = %d OR parent_$type = %d", $node->$type, $node->$type); db_query("DELETE FROM {jsdoc_objects} WHERE resource_$type = %d", $node->$type); db_query("DELETE FROM {jsdoc_objects} WHERE provide_$type = %d", $node->$type); if ($vid) { if (!db_num_rows(db_query("SELECT 1 FROM {node_revisions} WHERE nid = %d AND vid != %d", $node->nid, $vid))) { node_delete($node->nid); } else { db_query("DELETE FROM {node_revisions} WHERE vid = %d", $vid); } } } /** * Implementation of hook_insert(). */ function jsdoc_project_insert($node) { _jsdoc_version_get_or_create('HEAD', $node); } /** * Implementation of hook_load(). */ function jsdoc_project_load($node) { $additions = (object)array( 'jsdoc_versions' => array(), 'jsdoc_version_nids' => array() ); $query = db_query("SELECT nid AS jsdoc_version FROM {jsdoc_versions} WHERE project = %d ORDER BY nid DESC", $node->nid); while ($addition = db_fetch_object($query)) { $additions->jsdoc_version_nids[] = $additions->jsdoc_versions[] = $addition->jsdoc_version; } $nids = $additions->jsdoc_version_nids; $additions->jsdoc_version = $nids[0]; return $additions; } /** * Implementation of hook_load(). */ function jsdoc_version_load($node) { $additions = db_fetch_object(db_query("SELECT project AS jsdoc_project FROM {jsdoc_versions} WHERE nid = %d", $node->nid)); if (arg(0) == 'node' && arg(1) == $node->nid) { drupal_set_title(jsdoc_get_project($additions)->title . ' ' . $node->title); } return $additions; } // Public Functions to Load Data by Type // ===================================== function jsdoc_get_project(&$node) { if (isset($node->jsdoc_project)) { if (is_numeric($node->jsdoc_project)) { $node->jsdoc_project = _jsdoc_node_load($node->jsdoc_project); } return $node->jsdoc_project; } if (isset($node->jsdoc_project_name)) { return _jsdoc_project_get_or_create($node->jsdoc_project_name); } if (isset($node->jsdoc_version)) { $version = jsdoc_get_version($node); $node->jsdoc_project = jsdoc_get_project($version); return $node->jsdoc_project; } } function jsdoc_get_version(&$node) { if (isset($node->jsdoc_version)) { if (is_numeric($node->jsdoc_version)) { $node->jsdoc_version = _jsdoc_node_load($node->jsdoc_version); } return $node->jsdoc_version; } if ($node->type == 'jsdoc_project') { $node->jsdoc_version = _jsdoc_version_get_or_create('HEAD', $node); return $node->jsdoc_version; } elseif (isset($node->jsdoc_project_name)) { $project = _jsdoc_project_get_or_create($node->jsdoc_project_name); return $node->jsdoc_version = _jsdoc_version_get_or_create('HEAD', $project); } } function jsdoc_get_versions(&$node) { if ($node->type != 'jsdoc_project' && isset($node->jsdoc_versions)) { return $node->jsdoc_versions; } $project = jsdoc_get_project($node); if (is_array($project->jsdoc_versions)) { foreach ($project->jsdoc_versions as $i => $version) { if (is_numeric($version)) { $project->jsdoc_versions[$i] = _jsdoc_node_load($version); } } if ($node->type == 'jsdoc_variable' || $node->type == 'jsdoc_object') { $query = db_query("SELECT version FROM {" . $node->type . "s} WHERE nid = %d", $node->nid); $versions = array(); while ($version = db_fetch_object($query)) { foreach ($project->jsdoc_versions as $project_version) { if ($project_version->nid == $version->version) { $versions[] = $project_version; break; } } } return ($node->jsdoc_versions = $versions); } return $project->jsdoc_versions; } } function jsdoc_get_return_summary(&$node) { if (isset($node->jsdoc_return_summary_formatted)) { return $node->jsdoc_return_summary_formatted; } $object = jsdoc_get_variable_object($node); if ($object->type == 'jsdoc_variable') { $summary = db_result(db_query("SELECT jo.return_summary FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (nr.vid = jo.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND jo.return_summary != '' LIMIT 1", $object->title, $object->title)); $node->jsdoc_return_summary = $object->jsdoc_return_summary = $summary; return ($node->jsdoc_return_summary = $object->jsdoc_return_summary = $summary); } return ($node->jsdoc_return_summary_formatted = $object->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 (isset($node->jsdoc_return_types)) { return $node->jsdoc_return_types; } $types = array(); $object = jsdoc_get_variable_object($node); if ($object->type == 'jsdoc_variable') { $query = db_query("SELECT jo.returns FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (nr.vid = jo.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND jo.returns != '' GROUP BY jo.returns", $object->title, $object->title); while ($result = db_fetch_object($query)) { $types[] = $result->returns; } } elseif ($object->type == 'jsdoc_object') { foreach (preg_split('%\s*\|+\s*%', $object->jsdoc_returns) as $type) { if ($type) { $types[] = $type; } } } return ($node->jsdoc_return_types = $object->jsdoc_return_types = $types); } function _jsdoc_sort_vid_weight($a, $b){ if ($a->vid == $b->vid) { return ($a->weight < $b->weight) ? -1 : 1; } return ($a->vid < $b->vid) ? -1 : 1; } function jsdoc_get_parameters(&$node) { if (isset($node->jsdoc_parameters)) { return $node->jsdoc_parameters; } if (!jsdoc_is_function($node)) { return array(); } $object = jsdoc_get_variable_object($node); $object->jsdoc_parameters = array(); $parameters = array(); if ($object->type == 'jsdoc_variable') { $query = db_query("SELECT nr.vid, jp.weight, jp.name, jp.type, jp.summary, jp.optional, jp.repeating FROM {jsdoc_parameters} jp JOIN {jsdoc_objects} jo ON (jo.vid = jp.vid) JOIN {node_revisions} nr ON (nr.vid = jo.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND jo.used != -1 AND jo.version = %d", $object->title, $object->title, jsdoc_get_version($object)->nid); $results = array(); while ($parameter = db_fetch_array($query)) { $results[] = $parameter; } usort($results, '_jsdoc_sort_vid_weight'); foreach ($results as $parameter) { $parameters[$parameter['vid']][$parameter['name']] = $parameter; } $similar = true; $last = array_pop($parameters); foreach ($parameters as $parameter) { if (array_diff(array_keys($last), array_keys($parameter))) { $similar = false; } } if ($similar) { $best = $last; $parameters[] = $last; foreach ($parameters as $parameter) { if (strlen($parameter['summary']) > strlen($best['summary'])) { $best['summary'] = $parameter['summary']; } if (strlen($parameter['type']) > strlen($best['type'])) { $best['type'] = $parameter['type']; $best['optional'] = $parameter['optional']; $best['repeating'] = $parameter['repeating']; } } $parameters = array(); if($best){ foreach ($best as $parameter) { unset($parameter['vid']); $parameters[] = $parameter; } } } else { $parameters = array(); } } else { $query = db_query("SELECT name, type, summary, optional, repeating FROM {jsdoc_parameters} WHERE vid = %d ORDER BY weight", $object->vid); while ($parameter = db_fetch_array($query)) { $parameters[] = $parameter; } } foreach ($parameters as $parameter) { $parameter['summary'] = trim(preg_replace('%(^|
$)%', '', check_markup(str_replace("\n", ' ', $parameter['summary']), variable_get('jsdoc_input_format', 1), FALSE))); $parameter['optional'] = round($parameter['optional']); $parameter['repeating'] = round($parameter['repeating']); $parameter['jsdoc_formatted'] = _jsdoc_format_type($parameter['type'], $parameter['optional'], $parameter['repeating']); $object->jsdoc_parameters[$parameter['name']] = $parameter; } return ($node->jsdoc_parameters = $object->jsdoc_parameters); } function jsdoc_get_examples(&$node, $markedup=false) { if (!isset($node->jsdoc_examples)) { $node->jsdoc_examples = array(); $node->jsdoc_markedup_examples = array(); $query = db_query("SELECT example, markedup FROM {jsdoc_examples} WHERE vid = %d ORDER BY weight", $node->vid); while ($example = db_fetch_object($query)) { $node->jsdoc_examples[] = $example->example; $node->jsdoc_markedup_examples[] = $example->markedup; } } return ($markedup) ? $node->jsdoc_markedup_examples : $node->jsdoc_examples; } function jsdoc_get_parent_mixins(&$node) { return jsdoc_get_parents($node)->mixin; } function jsdoc_get_parent_prototype(&$node) { $parents = jsdoc_get_parents($node); if ($parents->chain && $parents->chain['prototype'] && count($parents->chain['prototype'])) { return $parents->chain['prototype'][0]; } } /** * */ function jsdoc_get_parents(&$node) { if (isset($node->jsdoc_parents)) { return $node->jsdoc_parents; } $node->jsdoc_parents = array(); if ($node->type == "jsdoc_object" || ($node->type == "jsdoc_variable" && jsdoc_get_variable_object($node))) { if ($node->type == "jsdoc_variable") { $object = jsdoc_get_variable_object($node); } else { $object = $node; } $object->jsdoc_parents = array(); $query = db_query("SELECT nr.title, j.type FROM {jsdoc_variable_hierarchy} j JOIN {node_revisions} nr ON (nr.vid = j.parent_vid) WHERE j.type IN ('normal', 'prototype', 'instance') AND j.vid = %d", $object->vid); $results = array(); while ($parent = db_fetch_object($query)) { $results[$parent->title] = $parent; } foreach ($results as $parent) { $object->jsdoc_parents['all'][] = $parent->title; $object->jsdoc_parents[$parent->type][] = $parent->title; natsort($object->jsdoc_parents[$parent->type]); } $query = db_query("SELECT j.type, j.subtype, nr.title AS parent FROM {jsdoc_variable_hierarchy} j JOIN {node_revisions} nr ON (nr.vid = j.parent_vid) WHERE j.vid = %d AND j.type IN ('chain', 'mixin')", $object->vid); while ($chain = db_fetch_object($query)) { if (!$chain->subtype) { $chain->subtype = 'normal'; } $detail->jsdoc_parents['all'][] = $chain->parent; $object->jsdoc_parents[$chain->type][$chain->subtype][] = $chain->parent; natsort($object->jsdoc_parents[$chain->type][$chain->subtype]); } if ($object->jsdoc_parents['all']) { $object->jsdoc_parents['all'] = array_unique($object->jsdoc_parents['all']); natcasesort($object->jsdoc_parents['all']); } else { $object->jsdoc_parents['all'] = array(); } $node->jsdoc_parents = $object->jsdoc_parents; } elseif ($node->type == 'jsdoc_variable') { $node->jsdoc_parents = array(); if (jsdoc_get_variable_object($node)) { $query = db_query("SELECT %d AS vid", jsdoc_get_variable_object($node)->vid); } else { // Select all objects with this as a name $query = db_query("SELECT nr.nid, nr.vid FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE j.version = %d AND nr.title = '%s' AND BINARY nr.title = '%s'", jsdoc_get_version($node)->nid, $node->title, $node->title); } $results = array(); while ($child = db_fetch_object($query)) { if (!$child->nid) { $child->nid = $child->vid; } if ($old = $results[$child->nid]){ $old->vid = max($old->vid, $child->vid); } else { $results[$child->nid] = $child; } } $vids = array(-1); foreach ($results as $child) { $vids[] = $child->vid; } $query = db_query("SELECT nr.title FROM {jsdoc_variable_hierarchy} j JOIN {node_revisions} nr ON (nr.vid = j.parent_vid) WHERE j.vid IN (%s) GROUP BY nr.nid ORDER BY nr.vid DESC, nr.title", implode(", ", array_unique($vids))); while ($parent = db_fetch_object($query)) { $node->jsdoc_parents['all'][] = $parent->title; } } $node->jsdoc_parents = (object)$node->jsdoc_parents; return $node->jsdoc_parents; } function jsdoc_is_initialized(&$node) { if (isset($node->jsdoc_initialized)) { return $node->jsdoc_initialized; } $object = jsdoc_get_variable_object($node); if ($object) { return $object->jsdoc_initialized; } return false; } function jsdoc_get_classlike(&$node) { if (isset($node->jsdoc_classlike)) { return $node->jsdoc_classlike; } if ($node->type == 'jsdoc_variable') { $classlikes = array(); $query = db_query("SELECT j.classlike FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.version = %d", $node->title, $node->title, jsdoc_get_version($node)->nid); while ($result = db_fetch_object($query)) { $classlikes[] = $result->classlike; } $classlikes = array_unique($classlikes); if (count($classlikes) == 1) { return $node->jsdoc_classlike = $classlikes[0]; } } return $node->jsdoc_classlike = false; } function jsdoc_get_teaser(&$node) { if (isset($node->formatted_teaser)) { return $node->formatted_teaser; } if ($node->type == 'jsdoc_variable' && !$node->teaser) { $node->teaser = _jsdoc_resolve_variable($node)->teaser; } return ($node->formatted_teaser = $node->teaser); } function jsdoc_get_body(&$node) { if ($node->type == 'jsdoc_variable' && !$node->body) { return $node->body = _jsdoc_resolve_variable($node)->body; } return $node->body; } function jsdoc_get_format(&$node) { if ($node->type == 'jsdoc_variable' && !$node->format) { return $node->format = _jsdoc_resolve_variable($node)->format; } return $node->format; } function jsdoc_get_full_url(&$node) { if ($node->type == 'jsdoc_variable' && !$node->jsdoc_full_url) { return $node->jsdoc_full_url = _jsdoc_resolve_variable($node)->full_url; } return $node->jsdoc_full_url; } function jsdoc_get_variable_object(&$node) { if ($node->type == 'jsdoc_variable') { if ($object = _jsdoc_resolve_variable($node)->object) { return $object; } else { return $node; } } return $node; } function jsdoc_is_function(&$node) { return (jsdoc_get_type($node) == 'Function' || jsdoc_get_type($node) == 'Constructor'); } function jsdoc_is_private(&$node) { if ($node->type == 'jsdoc_object' || isset($node->jsdoc_private)) { return $node->jsdoc_private; } $query = db_query("SELECT jo.private, jo.private_parent FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (nr.vid = jo.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND jo.version = %d GROUP BY jo.private, jo.private_parent", $node->title, $node->title, jsdoc_get_version($node)->nid); if (db_num_rows($query) == 1) { $result = db_fetch_object($query); $node->jsdoc_private_parent = $result->private_parent; return $node->jsdoc_private = $result->private; } } function jsdoc_has_private_parent(&$node) { if ($node->type != 'jsdoc_object' && !isset($node->jsdoc_private_parent)) { jsdoc_is_private($node); } return $node->jsdoc_private_parent; } function jsdoc_get_type(&$node) { if (isset($node->jsdoc_type)) { return $node->jsdoc_type; } $type = ''; if ($node->type == 'jsdoc_variable') { $types = array(); $query = db_query("SELECT j.type FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.version = %d", $node->title, $node->title, jsdoc_get_version($node)->nid); while ($result = db_fetch_object($query)) { if ($result->type) { $types[] = $result->type; } } $types = array_unique($types); if (count($types)) { $type = implode('|', $types); } } elseif ($node->type == 'jsdoc_object') { $type = $node->jsdoc_raw_type; unset($node->jsdoc_raw_type); } if ($type == 'Function') { if (jsdoc_get_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 (is_numeric($node->jsdoc_resource)) { $node->jsdoc_resource = _jsdoc_node_load($node->jsdoc_resource, $node->jsdoc_resource_vid); } return $node->jsdoc_resource; } function jsdoc_get_source($node, $resolve=false, $depth=0) { if (!$resolve && isset($node->jsdoc_source)) { return $node->jsdoc_source; } if ($depth++ < 2 && $node->type == 'jsdoc_object' && empty($node->jsdoc_source)) { if ($constructor = jsdoc_resolve_constructor($node, true, $depth)) { $node->jsdoc_source = $constructor->source; } } return $node->jsdoc_source; } function jsdoc_get_provide(&$node) { $object = $node; if ($node->type == 'jsdoc_variable') { $object = jsdoc_get_variable_object($node); } if (is_numeric($object->jsdoc_provide)) { $object->jsdoc_provide = _jsdoc_node_load($object->jsdoc_provide, $object->jsdoc_provide_vid); } return $object->jsdoc_provide; } function jsdoc_get_child_instances(&$node) { return jsdoc_get_children($node)->instances; } function jsdoc_get_child_prototypes(&$node) { return jsdoc_get_children($node)->prototypes; } function jsdoc_get_child_variables(&$node) { return jsdoc_get_children($node)->variables; } function jsdoc_get_child_chains(&$node) { return jsdoc_get_children($node)->chains; } function jsdoc_get_child_mixins(&$node) { return jsdoc_get_children($node)->mixins; } function jsdoc_is_namespace(&$node) { if (isset($node->jsdoc_namespace)) { return $node->jsdoc_namespace; } static $namespaces; if (!$namespaces) { $namespaces = cache_get('jsdoc_is_namespace', 'cache')->data; if (!$namespaces) { $namespaces = array(); } else { $namespaces = unserialize($namespaces); } } if (!isset($namespaces[$node->title])) { $namespaces[$node->title] = false; if (jsdoc_get_type($node) == 'Object' || jsdoc_is_initialized($node)) { $query = db_query("SELECT 1 FROM {node_revisions} nr INNER JOIN {jsdoc_objects} jo ON (jo.vid = nr.vid) WHERE jo.type = 'Function' AND nr.title LIKE '%s.%%' AND nr.title NOT LIKE '%%.toString' LIMIT 1", $node->title); if (db_result($query)) { $namespaces[$node->title] = true; } } cache_set('jsdoc_is_namespace', 'cache', serialize($namespaces)); } return ($node->jsdoc_namespace = $namespaces[$node->title]); } function jsdoc_get_children(&$node) { if (isset($node->jsdoc_variables)) { return (object)array( 'instances' => $node->jsdoc_instances, 'prototypes' => $node->jsdoc_prototypes, 'variables' => $node->jsdoc_variables, 'chains' => $node->jsdoc_chains, 'mixins' => $node->jsdoc_mixins ); } $node->jsdoc_instances = array(); $node->jsdoc_prototypes = array(); $node->jsdoc_variables = array(); $node->jsdoc_chains = array( 'prototype' => array(), 'call' => array() ); $node->jsdoc_mixins = array( 'normal' => array(), 'prototype' => array() ); if ($node->type == "jsdoc_variable") { // Find all objects joined normally to this object, // Group and order $query = db_query("SELECT nr.title FROM {jsdoc_variable_hierarchy} j JOIN {jsdoc_objects} jo ON (jo.vid = j.vid) JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE j.parent_vid = %d AND j.type = 'normal' AND jo.version = %d AND jo.used != -1", $node->vid, jsdoc_get_version($node)->nid); $results = array(); while ($object = db_fetch_object($query)) { $results[$object->title] = $object; } ksort($results); $node->jsdoc_variables = array(); foreach ($results as $object){ $object = jsdoc_object_node_load($object->title, jsdoc_get_project($node), jsdoc_get_version($node)); $node->jsdoc_variables[$object->title] = $object; } uksort($node->jsdoc_variables, 'strnatcasecmp'); } elseif ($node->type == "jsdoc_object") { $query = db_query("SELECT j.type, j.subtype, nr.nid, nr.vid FROM {jsdoc_variable_hierarchy} j JOIN {node_revisions} nr ON (nr.vid = j.vid) JOIN {node_revisions} nr2 ON (nr2.vid = j.parent_vid) JOIN {jsdoc_objects} jo ON (jo.vid = nr.vid) WHERE jo.used != -1 AND nr2.title = '%s' AND BINARY nr2.title = '%s' AND j.version = %d", $node->title, $node->title, jsdoc_get_version($node)->nid); $results = array(); while ($join = db_fetch_object($query)) { $results["{$join->nid}|{$join->type}|{$join->subtype}"] = $join; } foreach ($results as $join) { $object = _jsdoc_node_load($join->nid, $join->vid); if (!$object->jsdoc_used) { continue; } if ($join->type == 'instance') { $node->jsdoc_instances[$object->title] = $object; } elseif ($join->type == 'prototype') { $node->jsdoc_prototypes[$object->title] = $object; } elseif ($join->type == 'normal') { $node->jsdoc_variables[$object->title] = $object; } elseif ($join->type == 'chain') { $node->jsdoc_chains[$join->subtype][$object->title] = $object; } elseif ($join->type == 'mixin') { if (!$join->subtype) { $join->subtype = 'normal'; } $node->jsdoc_mixins[$join->subtype][$object->title] = $object; } elseif ($join->type == 'alias') { $node->jsdoc_aliases = $object; } } uksort($node->jsdoc_instances, 'strnatcasecmp'); uksort($node->jsdoc_prototypes, 'strnatcasecmp'); uksort($node->jsdoc_chains['prototype'], 'strnatcasecmp'); uksort($node->jsdoc_chains['call'], 'strnatcasecmp'); uksort($node->jsdoc_mixins['normal'], 'strnatcasecmp'); uksort($node->jsdoc_mixins['prototype'], 'strnatcasecmp'); } return jsdoc_get_children($node); } function jsdoc_get_query_variables($project, $version=null){ if (!$version) { $version = jsdoc_get_version($project); } return db_query("SELECT nr.title, nr.nid, nr.vid, n.type, jo.type AS jsdoc_raw_type, jo.classlike AS jsdoc_classlike, jo.initialized AS jsdoc_initialized FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (nr.vid = jo.vid) JOIN {node} n ON (n.nid = nr.nid) WHERE jo.version = %d AND (nr.title = '%s' OR nr.title LIKE '%s.%%') AND (jo.type = 'Function' OR jo.type = 'Object' OR (jo.type = 'Function' AND jo.initialized = 1)) GROUP BY BINARY nr.title ORDER BY nr.title", $version->nid, $project->title, $project->title); } /** * Implementation of hook_load(). */ function jsdoc_resource_load($node) { $additions = db_fetch_object(db_query("SELECT version AS jsdoc_version FROM {jsdoc_resources} WHERE vid = %d", $node->vid)); $version = jsdoc_get_version($node); $node->jsdoc_version = $additions->jsdoc_version; return $additions; } /** * Implementation of hook_load(). */ function jsdoc_variable_load($node) { // An object might have a "default" disambiguation package. $additions = db_fetch_object(db_query("SELECT version AS jsdoc_version, resource_nid AS jsdoc_resource, resource_nid AS jsdoc_resource_nid, resource_vid AS jsdoc_resource_vid, private AS jsdoc_private FROM {jsdoc_variables} WHERE vid = %d", $node->vid)); $node->jsdoc_version = $additions->jsdoc_version; $additions->jsdoc_url = 'jsdoc/' . jsdoc_get_project($node)->title . '/' . jsdoc_get_version($node)->title . '/' . $node->title; return $additions; } /** * Implementation of hook_view(). */ function jsdoc_project_view($node, $teaser = false, $page = false) { return jsdoc_resource_view($node, $teaser, $page); } /** * Implementation of hook_view(). */ function jsdoc_version_view($node, $teaser = false, $page = false) { return jsdoc_resource_view($node, $teaser, $page); } /** * Implementation of hook_view(). */ function jsdoc_resource_view($node, $teaser = false, $page = false) { $node->title = ''; $node->teaser = ''; $node->body = ''; $node = node_prepare($node); return $node; } /** * Implementation of hook_view(). */ function jsdoc_variable_view($node, $teaser = false, $page = false) { return jsdoc_object_view($node, $teaser, $page); } /** * Get the base objects for this project * * @param $version * The release version */ function jsdoc_root_objects($version) { $objects = array(); // Find all "objects" that aren't private and don't have a period in them $query = db_query("SELECT nr.nid, MAX(nr.vid) AS vid FROM {jsdoc_objects} jo JOIN {node_revisions} nr ON (nr.vid = jo.vid) WHERE nr.title NOT LIKE '%.%' AND nr.title != 'window' AND jo.private = 0 AND jo.version = '%s' GROUP BY nr.nid ORDER BY nr.title", $version); while ($object = db_fetch_object($query)) { if (_jsdoc_object_has_child($version, $object)) { // If we find attached variables, it means this is a root level object $object = _jsdoc_node_load($object->nid, $object->vid); $objects[$object->title] = $object; } } return $objects; } /** * Check to see if there are any variables attached directly to this object * * @param $version * The release version * @param $node * A node of type jsdoc_object */ function _jsdoc_object_has_child($version_nid, $node) { $query = db_query("SELECT 1 FROM {jsdoc_variable_hierarchy} joh JOIN {jsdoc_resources} jro ON (jro.vid = joh.vid) JOIN {node_revisions} nr ON (nr.vid = joh.vid) WHERE parent = %d AND joh.type = 'normal' AND jro.private = 0 AND jro.version = %d GROUP BY nr.nid", $node->vid, $version_nid); if (db_num_rows($query)) { return true; } return false; } /** * Path: admin/settings/jsdoc */ function jsdoc_admin() { $form = array(); $form['jsdoc_dir_location'] = array( '#type' => 'textfield', '#title' => t('Directory to run your file from'), '#default_value' => variable_get('jsdoc_dir_location', ''), '#required' => true ); $form['jsdoc_file_location'] = array( '#type' => 'textfield', '#title' => t('Location of file used to parse your code'), '#default_value' => variable_get('jsdoc_file_location', ''), '#required' => true ); $form['jsdoc_base'] = array( '#type' => 'textfield', '#title' => t('This will be used as a base of our functions'), '#default_value' => variable_get('jsdoc_base', 'dojo'), '#description' => t('Implement hook_get_files() and hook_get_contents($file)'), '#required' => true ); $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); } /** * Get list of all projects */ function jsdoc_projects() { $projects = array(); $query = db_query("SELECT nid FROM {node} WHERE type = 'jsdoc_project' AND status = 1"); while ($result = db_fetch_object($query)) { $projects[] = _jsdoc_node_load($result->nid); } return $projects; } // Node Load Functions // =================== function jsdoc_project_node_load() { $args = func_get_args(); return _jsdoc_memoize('_jsdoc_project_node_load', $args); } /** * Custom version of node_load for projects */ function _jsdoc_project_node_load($project_name) { if($nid = db_result(db_query("SELECT n.nid FROM {node} n JOIN {jsdoc_versions} j ON (j.project = n.nid) WHERE BINARY n.title = '%s'", $project_name))) { return _jsdoc_node_load($nid); } } function jsdoc_version_node_load() { $args = func_get_args(); return _jsdoc_memoize('_jsdoc_version_node_load', $args); } /** * Custom version of node_load for versions */ function _jsdoc_version_node_load($version_name, $project) { if (is_numeric($project)) { $project_nid = $project; } elseif (is_string($project)) { $project_nid = jsdoc_project_node_load($project)->nid; } elseif (is_object($project) && $project->nid) { $project_nid = $project->nid; } if ($nid = db_result(db_query("SELECT n.nid FROM {node} n JOIN {jsdoc_versions} j ON (j.nid = n.nid) WHERE n.type = 'jsdoc_version' AND n.title = '%s' AND BINARY n.title = '%s' AND j.project = %d", $version_name, $version_name, $project_nid))) { return _jsdoc_node_load($nid); } } function jsdoc_resource_node_load() { $args = func_get_args(); return _jsdoc_memoize('_jsdoc_resource_node_load', $args); } /** * Custom version of node_load for resources */ function _jsdoc_resource_node_load($project, $version, $name) { if (is_object($project) && $project->nid) { $project_nid = $project->nid; } elseif (is_numeric($project)) { $project_nid = $project; } elseif (is_string($project)) { $project_nid = jsdoc_project_node_load($project)->nid; } 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, $project_nid)->nid; } if ($version_nid && $resource = db_fetch_object(db_query("SELECT nr.nid, MAX(nr.vid) AS vid FROM {jsdoc_resources} jr JOIN {node_revisions} nr ON (nr.vid = jr.vid) JOIN {node} n ON (n.nid = jr.version) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND jr.version = %d GROUP BY nr.nid", $name, $name, $version_nid))) { return _jsdoc_node_load($resource->nid, $resource->vid); } } function _jsdoc_memoize_flatten(&$item, $key) { if (is_object($item)) { $item = ($item->vid) ? $item->vid : $item->title; } elseif (is_array($item)) { $values = array_values($item); $item = array(); foreach ($values as $value) { if (is_object($value)) { $value = ($value->vid) ? $value->vid : $value->title; } $item[] = $value; } $item = implode(',', $item); } } function _jsdoc_memoize($func, $args) { static $cache; if (!$cache) { $cache = array(); } if (!$cache[$func]) { $cache[$func] = array(); } $cache_key = array_values($args); array_walk($cache_key, '_jsdoc_memoize_flatten'); $cache_key = implode('%', $cache_key); if (array_key_exists($cache_key, $cache[$func])) { return $cache[$func][$cache_key]; } return ($output = $cache[$func][$cache_key] = call_user_func_array($func, $args)); } function jsdoc_object_node_load(){ $args = func_get_args(); return _jsdoc_memoize('_jsdoc_object_node_load', $args); } function _jsdoc_object_node_load($name, $project=false, $version=false, $resource=false, $exact=true) { $nodes = jsdoc_object_nodes_load($name, $project, $version, $resource, $exact); if (count($nodes) == 1) { return $nodes[0]; } elseif (!empty($nodes)) { foreach($nodes as $match) { $project = jsdoc_get_project($match)->title; if ($project == $match->title || strpos($match->title, $project . '.') === 0) { return $match; } } return $match; } return (object)array(); } /** * Custom version of node_load for objects */ function jsdoc_object_nodes_load($name, $project=false, $version=false, $resource=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 project the best that we can if (is_object($project) && $project->nid) { $project_nid = $project->nid; } elseif (is_numeric($project)) { $project_nid = $project; } elseif (is_string($project)) { $project = jsdoc_project_node_load($project); $project_nid = $project->nid; if (!$version) { $version = jsdoc_get_version($project); } } // 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, $project_nid)->nid; } if ($version_nid && (!is_object($version) || !$version->nid)) { $version = _jsdoc_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 (!$project_nid) { // We can't look up the proper version without knowing the project $query = db_query("SELECT nr.nid FROM {node_revisions} nr JOIN {node} n ON (n.nid = nr.nid) WHERE nr.title $title_check AND $binary nr.title $title_check AND n.type = 'jsdoc_variable'", $name, $name); $results2 = array(); while ($result = db_fetch_object($query)) { $results2[$result->nid] = $result; } foreach ($results2 as $result) { $results[] = $result; $current_project_nid = jsdoc_get_project(_jsdoc_node_load($result->nid))->nid; if (!isset($project_nid)) { $project_nid = $current_project_nid; } // Set the project_nid if we can if (!$last_project_nid) { $last_project_nid = $current_project_nid; } elseif ($last_project_nid != $current_project_nid) { $project_nid = null; } } } if (!$project_nid) { foreach ($results as $result) { $node = jsdoc_get_variable_object(_jsdoc_node_load($result->nid)); $version = jsdoc_get_version(jsdoc_get_project($node)); $versioned_query = db_query("SELECT vid FROM {" . $node->type . "s} WHERE nid = %d AND version = %d", $node->nid, $version->nid); if ($result = db_fetch_object($versioned_query)) { $node = jsdoc_get_variable_object(_jsdoc_node_load($node->nid, $result->vid)); $nodes[] = $node; } else { $nodes[] = $node; } } } else { if ($resource) { if (is_object($resource)) { $resources = array($resource->nid); } elseif (is_numeric($resource)) { $resources = array($resource); } elseif (is_array($resource)) { $resources = $resource; } else { $resources = array(jsdoc_resource_node_load($project_nid, $version_nid, $resource)->nid); } $query = db_query("SELECT nr.nid, nr.vid, j.version FROM {node_revisions} nr INNER JOIN {jsdoc_objects} j ON (j.vid = nr.vid) WHERE nr.title $title_check AND $binary nr.title $title_check AND j.version = %d AND (j.resource_nid IN (%s) OR j.provide_nid IN (%s))", $name, $name, $version_nid, implode(',', $resources), implode(',', $resources)); } elseif ($version_nid) { $query = db_query("SELECT nr.nid, nr.vid, j.version FROM {node_revisions} nr INNER JOIN {jsdoc_variables} j ON (j.vid = nr.vid) WHERE nr.title $title_check AND $binary nr.title $title_check AND j.version = %d", $name, $name, $version_nid); } else { $version_nids = array(-1); foreach (jsdoc_get_versions(_jsdoc_node_load($project_nid)) as $version) { $version_nids[] = $version->nid; } $query = db_query("SELECT nr.nid, nr.vid AS vid, j.version FROM {node_revisions} nr INNER JOIN {jsdoc_variables} j ON (j.vid = nr.vid) WHERE nr.title $title_check AND $binary nr.title $title_check AND j.version IN (%s) ORDER BY j.version DESC", $name, $name, implode(',', $version_nids)); } $maxes = array(); while ($result = db_fetch_object($query)) { if (!$maxes[$result->nid] || $maxes[$result->nid]->version < $result->version) { $maxes[$result->nid] = $result; } } foreach ($maxes as $result) { $result = _jsdoc_node_load($result->nid, $result->vid); $nodes[] = jsdoc_get_variable_object($result); } } if (empty($nodes)) { if ($broad || !$project_nid) { return array((object)array( 'title' => $name )); } else { // Try without the project return jsdoc_object_nodes_load($name, false, $version->title, $resource, $exact, true); } } 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/ide */ function jsdoc_ide($ide, $download=false) { if ($ide == 'aptana') { $document = new DomDocument(); $document->encoding = 'utf-8'; $document->standalone = true; $document->formatOutput = true; $javascript = $document->appendChild($document->createElement('javascript')); $overview = $javascript->appendChild($document->createElement('overview')); $overview->appendChild($document->createTextNode('The Dojo Toolkit is awesome')); $header = array(); $footer = array(); foreach (explode("\n", $document->saveXML()) as $line) { if ($line == '') { $footer[] = $line; } else { if (empty($footer)) { $header[] = $line; } else { $footer[] = $line; } } } $base = file_directory_path(); if (!file_exists($base . '/jsdoc/ides/docs/api')) { mkdir($base . '/jsdoc'); mkdir($base . '/jsdoc/ides'); mkdir($base . '/jsdoc/ides/docs'); mkdir($base . '/jsdoc/ides/docs/api'); mkdir($base . '/jsdoc/ides/docs/api/js'); mkdir($base . '/jsdoc/ides/docs/api/css'); mkdir($base . '/jsdoc/ides/docs/api/img'); mkdir($base . '/jsdoc/ides/docs/api/nodes'); } file_save_data(implode("\n", $header), 'jsdoc/ides/scriptdoc.xml.tmp', FILE_EXISTS_REPLACE); $handle = fopen($base . '/jsdoc/ides/scriptdoc.xml.tmp', 'a'); $document->removeChild($javascript); $toc_document = new DomDocument(); $toc_document->encoding = 'utf-8'; $toc_document->formatOutput = true; $toc = $toc_document->appendChild($toc_document->createElement('toc')); $toc->setAttribute('label', 'Dojo API Reference'); $toc->setAttribute('link_to', '../com.aptana.ide.documentation/tocreference.xml#libraries'); $toc = $toc->appendChild($toc_document->createElement('topic')); $toc->setAttribute('label', 'Dojo 1.2'); $root = $toc->appendChild($toc_document->createElement('topic')); $root->setAttribute('label', 'API'); foreach (jsdoc_projects() as $project) { $version = jsdoc_version_node_load('HEAD', $project); if (!file_exists($base . '/jsdoc/ides/docs/api/nodes/' . $project->title)) { mkdir($base . '/jsdoc/ides/docs/api/nodes/' . $project->title); } $namespaces = unserialize(cache_get('jsdoc_namespaces_' . $project->title, 'cache')->data); if (empty($namespaces) || empty($namespaces[$version->nid])) { $_GET['jsdoc_project'] = $project->title; jsdoc_block('view', 1); $namespaces = unserialize(cache_get('jsdoc_namespaces_' . $project->title, 'cache')->data); } foreach ($namespaces[$version->nid] as $node) { $node = _jsdoc_node_load($node[0], $node[1]); _jsdoc_ide_build_toc($toc_document, $root, $project->title, $node->title, 'api'); } $query = db_query("SELECT nr.nid, MAX(nr.vid) AS vid FROM {node_revisions} nr INNER JOIN {jsdoc_variables} j ON (j.vid = nr.vid) WHERE j.version = %d GROUP BY nr.nid", $version->nid); while ($result = db_fetch_object($query)) { set_time_limit(150); $node = _jsdoc_node_load($result->nid, $result->vid); $filename = $base . '/jsdoc/ides/docs/api/nodes/' . $project->title . '/' . $node->title . '.html'; if (file_exists($filename) && (time() - filectime($filename) < 86400)) { continue; } $node = jsdoc_get_variable_object($node); if ($node->type == 'jsdoc_variable') { $content = jsdoc_variable_node_view($node); } else { $content = jsdoc_object_node_view($node); } file_put_contents($filename, theme('jsdoc_ide', $content)); } } file_put_contents($base . '/jsdoc/ides/docs/index.xml', $toc_document->saveXML()); foreach (jsdoc_projects() as $project) { $version = jsdoc_version_node_load('HEAD', $project); $query = jsdoc_get_query_variables($project, $version); while ($result = db_fetch_object($query)) { set_time_limit(150); if (jsdoc_is_namespace($result) || jsdoc_get_type($result) == 'Constructor') { $node = _jsdoc_node_load($result->nid, $result->vid); $class = $document->createElement('class'); $class->setAttribute('type', $node->title); if (jsdoc_get_teaser($node)) { $class->appendChild($document->createElement('description'))->appendChild($document->createTextNode(html_entity_decode(jsdoc_get_teaser($node)))); } $superclasses = array(); $class_mixins = array(); if (jsdoc_get_type($node) != 'Constructor') { $superclasses[] = 'Object'; } else { $superclass = jsdoc_get_parent_prototype($node); if ($constructor = jsdoc_resolve_constructor($node, false)) { $constructor_element = $class->appendChild($document->createElement('constructors'))->appendChild($document->createElement('constructor')); $constructor_element->setAttribute('scope', 'instance'); if (jsdoc_get_teaser($constructor)) { $constructor_element->appendChild($document->createElement('description'))->appendChild($document->createTextNode(html_entity_decode(jsdoc_get_teaser($constructor)))); } _jsdoc_ide_build_parameters(jsdoc_get_parameters($constructor), $document, $constructor_element); } if ($superclass == 'window') { unset($superclass); } if (!$superclass) { $superclass = 'Object'; } $superclasses[] = $superclass; if ($mixins = jsdoc_get_parent_mixins($node)) { if ($mixins['prototype']) { foreach ($mixins['prototype'] as $mixin) { if (preg_match('%\.prototype$%', $mixin)) { $superclasses[] = preg_replace('%\.prototype$%', '', $mixin); } else { $class_mixins['instance'][] = array('static', $mixin); } } } if ($mixins['normal']) { foreach ($mixins['normal'] as $mixin) { if (preg_match('%\.prototype$%', $mixin)) { $class_mixins['static'][] = array('instance', preg_replace('%\.prototype$%', '', $mixin)); } else { $class_mixins['static'][] = array('static', $mixin); } } } } } $class->setAttribute('superclass', implode(' ', array_unique($superclasses))); foreach ($class_mixins as $scope => $class_mixin) { $mixins = $class->appendChild($document->createElement('mixins')); $mixins->setAttribute('scope', $scope); foreach ($class_mixin as $scoped_mixin) { $mixin = $mixins->appendChild($document->createElement('mixin')); $mixin->setAttribute('scope', $scoped_mixin[0]); $mixin->setAttribute('type', $scoped_mixin[1]); } } $properties = array(); $methods = array(); $childrens = jsdoc_get_children($node); foreach ($childrens as $type => $children) { if ($type == 'variables' || $type == 'instances' || $type == 'prototypes') { foreach ($children as $child) { unset($element); if (jsdoc_get_type($child) == 'Function') { if (jsdoc_is_initialized($child)) { continue; } $methods[] = $element = $document->createElement('method'); _jsdoc_ide_build_parameters(jsdoc_get_parameters($child), $document, $element); if ($types = _jsdoc_ide_build_types(jsdoc_get_return_types($child), 'Object')) { $return_types = $element->appendChild($document->createElement('return-types')); foreach ($types as $return_type) { $return_types->appendChild($document->createElement('return-type'))->setAttribute('type', $return_type); } } } elseif (!jsdoc_is_namespace($child) && jsdoc_get_type($child) != 'Constructor') { $properties[] = $element = $document->createElement('property'); $child_type = implode('|', _jsdoc_ide_build_types(jsdoc_get_type($child), 'Object')); $element->setAttribute('type', $child_type); $element->setAttribute('access', 'read-write'); } if ($element) { $element->setAttribute('scope', ($type == 'variables') ? 'static' : 'instance'); $name = preg_replace('%^' . $node->title . '\.%', '', $child->title); $element->setAttribute('name', $name); if (jsdoc_is_private($child)) { $element->setAttribute('visibility', 'internal'); } if (jsdoc_get_teaser($child)) { $element->appendChild($document->createElement('description'))->appendChild($document->createTextNode(html_entity_decode(jsdoc_get_teaser($child)))); } } } } } if ($properties) { $properties_element = $class->appendChild($document->createElement('properties')); foreach ($properties as $property) { $properties_element->appendChild($property); } } if ($methods) { $methods_element = $class->appendChild($document->createElement('methods')); foreach ($methods as $method) { $methods_element->appendChild($method); } } fwrite($handle, "\n" . preg_replace('%(^|\n)%', '$1 ', $document->saveXML($class))); } } } fwrite($handle, "\n" . implode("\n", $footer)); rename($base . '/jsdoc/ides/scriptdoc.xml.tmp', $base . '/jsdoc/ides/scriptdoc.xml'); } } function _jsdoc_ide_build_toc(&$document, &$root, $project, $title, $type, $nid=false) { if ($type == 'api') { $title = explode('.', $title); $found = false; foreach ($root->childNodes as $node) { if ($node->getAttribute('label') == $title[0]) { $found = true; $topic = $node; break; } } if (!$found) { $topic = $root->appendChild($document->createElement('topic')); $topic->setAttribute('href', "docs/api/nodes/$project/{$title[0]}.html"); $topic->setAttribute('label', $title[0]); } foreach ($title as $i => $part) { if (!$i) { continue; } $label = implode('.', array_slice($title, 0, $i+1)); $found = false; foreach ($topic->childNodes as $node) { if ($node->getAttribute('label') == $label) { $found = true; $topic = $node; break; } } if (!$found) { $topic = $topic->appendChild($document->createElement('topic')); $topic->setAttribute('href', "docs/api/nodes/$project/$label.html"); $topic->setAttribute('label', $label); } } } } function _jsdoc_ide_build_parameters($parameters, $document, &$element) { if (!$parameters) { return; } $parameters_element = $element->appendChild($document->createElement('parameters')); foreach ($parameters as $parameter) { $parameter_element = $parameters_element->appendChild($document->createElement('parameter')); $parameter_element->setAttribute('name', $parameter['name']); $parameter_element->setAttribute('type', implode('|', _jsdoc_ide_build_types($parameter['type'], 'Object'))); $parameter_element->setAttribute('usage', ($parameter['optional']) ? 'optional' : (($parameter['repeating']) ? 'one-or-more' : 'required')); if ($parameter['summary']) { $parameter_element->appendChild($document->createElement('description'))->appendChild($document->createTextNode(html_entity_decode($parameter['summary']))); } } } function _jsdoc_ide_build_types($types, $default=null) { if (!is_array($types)) { if (is_string($types)) { if(!$types){ if(!is_null($default)){ return array($default); } return array(); }else{ $types = preg_split('%\s*\|+\s*%', $types); } } else { return array(); } } $return_types = array(); foreach ($types as $i => $type) { $node = jsdoc_object_node_load($type); if (!empty($node->title)) { $type = $node->title; } if (preg_match('%^[a-zA-Z_.$][\w_.$]*(\[\])?$%', $type)) { if ($type == 'Integer' || $type == 'Float' || $type == 'Decimal') { $type = 'Number'; } $return_types[] = $type; } } if (empty($return_types) && !is_null($default)) { $return_types[] = $default; } return $return_types; } /** * Path: jsdoc/jsonp */ function jsdoc_jsonp($batch=false, $json=false) { include_once(_jsdoc_get_base_path() . '/' . drupal_get_path('module', 'jsdoc') . '/lib/json/Encoder.php'); include_once(_jsdoc_get_base_path() . '/' . drupal_get_path('module', 'jsdoc') . '/lib/json/Zend_Exception.php'); include_once(_jsdoc_get_base_path() . '/' . drupal_get_path('module', 'jsdoc') . '/lib/json/Exception.php'); $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['project'], $_JSON['version'], $_JSON['resource'], true)){ $nodes = array($nodes); } } else { $nodes = jsdoc_object_nodes_load($name, $_JSON['project'], $_JSON['version'], $_JSON['resource'], false); } foreach ($nodes as $node) { $formatted = (object)array('name' => $name); if ($_JSON['exact'] && count($nodes) > 1) { $formatted->project = jsdoc_get_project($node)->title; } 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",', '', Zend_Json_Encoder::encode($output)) . ');'; } /** * A menu callback */ function jsdoc_variables_node_view($nodes) { $form = array(); if (count($nodes)) { drupal_set_title($nodes[0]->title); } foreach ($nodes as $node) { $project = jsdoc_get_project($node); $form['projects'][$project->title] = array( '#title' => $project->title, '#type' => 'fieldset' ); if (!empty($project->teaser)) { $form['projects'][$project->title]['summary'] = array( '#title' => t('Summary'), '#value' => $project->teaser, '#weight' => 0 ); } $value = l($node->title, $node->jsdoc_url); if (jsdoc_get_teaser($node)) { $value .= ': ' . jsdoc_get_teaser($node); } $form['projects'][$project->title]['item'] = array( '#value' => $value ); } return drupal_render_form('jsdoc_variables_form', $form); } /** * A menu callback */ function jsdoc_variable_node_view($node) { $form = _jsdoc_node_prepare($node); $resources = jsdoc_get_variable_resources($node); $display_resources = array(); foreach ($resources as $resource_value) { $display_resources[] = (object)array( 'href' => l($resource_value->title, 'jsdoc/' . jsdoc_get_project($node)->title . '/HEAD/' . str_replace('/', '__', $resource_value->title) . '/' . $node->title) ); } $form['resources'] = array( '#value' => theme('jsdoc_object_resources', $node, $display_resources), '#weight' => 10 ); $parameters = _jsdoc_get_parameters_themed($node); $function = (object)array( 'signature' => _jsdoc_build_function_signature($node, $parameters) ); if (count($parameters)) { $function->parameters = $parameters; } $form['function'] = array( '#value' => theme('jsdoc_function_information', $function, array(), _jsdoc_get_object_themed($node, $node->title)), '#weight' => 15 ); $form['parameters'] = array( '#value' => theme('jsdoc_function_parameters', $parameters), '#weight' => 20 ); $children = array(); $variables = jsdoc_get_child_variables($node); if (!empty($variables)) { foreach ($variables as $child) { $title = $child->title; if (!jsdoc_is_namespace($node) && $node->title . '.' == substr($title, 0, strlen($node->title) + 1)) { $title = substr($title, strlen($node->title) + 1); } $children[$title] = _jsdoc_get_object_themed($child, $title); } uksort($children, "strnatcasecmp"); $form['children'] = array( '#value' => theme('jsdoc_object_children', $children), '#weight' => 30 ); } if (function_exists('comment_render') && $node->comment) { $form['comments'] = array( '#value' => comment_render($node), '#weight' => 30 ); } drupal_set_title(theme('jsdoc_object_title', _jsdoc_get_title_themed($node))); return drupal_render_form('jsdoc_variable_form', $form); } /** * A menu callback */ function jsdoc_object_node_view($node, $initialized = null, $allow_private = false) { // Test for the basics if (!$node) { return drupal_not_found(); } if ($node->type == 'jsdoc_object' && $node->jsdoc_used == -1) { return drupal_not_found(); } drupal_set_title(theme('jsdoc_object_title', _jsdoc_get_title_themed($node))); $form = _jsdoc_node_prepare($node); $form['require'] = array( '#value' => theme('jsdoc_object_require', jsdoc_get_provide($node)), '#weight' => -20 ); $form['resource'] = array( '#value' => theme('jsdoc_object_resource', jsdoc_get_resource($node), jsdoc_get_project($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 ); } $prototype_chain = jsdoc_get_prototype_chain($node, array(3, 4)); if(empty($prototype_chain)){ $prototype_chain = array($node); } else { $form['prototype_chain'] = array( '#value' => theme('jsdoc_object_prototype_chain', $prototype_chain), '#weight' => 10 ); } $mixin_chain = array(); foreach ($prototype_chain as $prototype) { if ($prototype->title == 'Object') { continue; } $mixin_chain[] = (object)array( 'in_prototype_chain' => true, 'on_prototype' => true, 'node' => $prototype ); } for ($i = 0; $i < count($mixin_chain); $i++) { $item = $mixin_chain[$i]->node; if ($mixin_chain_mixins_all = jsdoc_get_parent_mixins($item)) { foreach ($mixin_chain_mixins_all as $type => $mixin_chain_mixins) { if ($type == 'prototype' && is_array($mixin_chain_mixins)) { ksort($mixin_chain_mixins); foreach ($mixin_chain_mixins as $mixin) { if (substr($mixin, -10) == '.prototype') { // I don't think this is a bad assumption $mixin = substr($mixin, 0, -10); } $mixin = (object)array( 'in_prototype_chain' => false, 'on_prototype' => true, 'node' => jsdoc_object_node_load($mixin, jsdoc_get_project($node), jsdoc_get_version($node), jsdoc_get_all_provide_nids($node, array(3, 4))), ); if ($i > 0 && $mixin_chain[$i - 1]->node->title == $mixin->node->title) { continue; } if ($node->title != $item->title) { $mixin->mixin_from = $item->title; } array_splice($mixin_chain, $i++, 0, array($mixin)); } } } } } if (!empty($mixin_chain)) { $form['parent_mixins'] = array( '#value' => theme('jsdoc_object_mixins', $mixin_chain), '#weight' => 40 ); } $children = array(); foreach ($mixin_chain as $mixin) { $mixin = $mixin->node; $children_all = jsdoc_get_children($mixin); foreach ($children_all as $type => $items) { $type = substr($type, 0, -1); foreach ($items as $item) { if (is_object($item)) { $title = $item->title; if ($type == 'variable' && $node->title . '.' != substr($title, 0, strlen($node->title) + 1)) { continue; } if (!jsdoc_is_namespace($node) && $mixin->title . '.' == substr($title, 0, strlen($mixin->title) + 1)) { $title = substr($title, strlen($mixin->title) + 1); } if ($children[$title]) { $found = false; if ($children[$title]->inheritance) { foreach ($children[$title]->inheritance as $parent) { if ($parent->title == $mixin->title) { $found = true; break; } } } if (!$found) { if ($mixin->title == $node->title) { $children[$title]->override = true; } else { $children[$title]->inheritance[] = _jsdoc_get_object_themed($mixin); } $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); if ($mixin->title != $node->title) { $children[$title]->inheritance[] = _jsdoc_get_object_themed($mixin); } } if ($type == 'variable') { $children[$title]->normal = true; } else { $children[$title]->$type = true; } if (trim(jsdoc_get_teaser($item)) != '') { $children[$title]->summary = jsdoc_get_teaser($item); } } } } } uksort($children, "strnatcasecmp"); if (jsdoc_is_function($node)) { // TODO: jsdoc_get_parameters_themed goes here $parameters = _jsdoc_get_parameters_themed($node); if (count($parameters)) { $form['parameters'] = array( '#value' => theme('jsdoc_function_parameters', $parameters), '#weight' => 20 ); } $base = false; if (jsdoc_get_type($node) == 'Constructor') { $base = $node->title; } $function = (object)array( 'source' => jsdoc_get_source($node, true), 'markedup' => _jsdoc_markup_code(jsdoc_get_source($node, true), jsdoc_get_version($node)->nid, $base), 'signature' => _jsdoc_build_function_signature($node, $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); } /** * Path: jsdoc/VERSION/NAME/edit */ function jsdoc_object_edit_redirect($node, $resource=false) { if ($resource) { if ($node->jsdoc_detail && jsdoc_get_resource($node->jsdoc_detail)->vid == $resource) { drupal_goto('node/' . $node->jsdoc_detail->nid . '/edit'); return; } foreach ($node->jsdoc_disambiguations as $disambiguation) { if (jsdoc_get_resource($disambiguation)->vid == $resource) { drupal_goto('node/' . $disambiguation->nid . '/edit'); return; } } } drupal_goto('node/' . $node->nid . '/edit'); } 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_project($node), jsdoc_get_version($node), jsdoc_get_all_provide_nids($node, array(3, 4))); $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_project($node), jsdoc_get_version($node)); if ($obj) { $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, 'summary' => jsdoc_get_teaser($node), 'singleton' => jsdoc_is_initialized($node), 'namespace' => jsdoc_is_namespace($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_project($node), jsdoc_get_version($node), jsdoc_get_all_provide_nids($node, array(3, 4))); $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; } $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, $cleaned=true, $depth=0) { if (trim($node->jsdoc_source) || jsdoc_get_parameters($node)) { return ($cleaned) ? _jsdoc_get_object_themed($node, $node->title, $depth) : $node; } $prototype_objects = array(); $prototype_chain = jsdoc_get_prototype_chain($node, array(3, 4)); $prototype_chain[] = $node; foreach ($prototype_chain as $prototype) { $children_all = jsdoc_get_children($prototype); $prototype_object_children = array(); foreach ($children_all as $type => $children) { if ($type == 'prototypes' || $type == 'instances') { foreach ($children as $child) { if (jsdoc_get_type($child) == 'Function') { $title = $child->title; if ($prototype->title . '.' == substr($title, 0, strlen($prototype->title) + 1)) { $title = substr($title, strlen($prototype->title) + 1); } $prototype_object_children[$title] = _jsdoc_get_object_themed($child, $title, $depth); $prototype_object_children[$title]->node = $child; } } } } if (!empty($prototype_object_children)) { $prototype_object = _jsdoc_get_object_themed($prototype, $prototype->title, $depth); $prototype_object->node = $prototype; $prototype_objects[] = (object)array( 'node' => $prototype_object, 'children' => $prototype_object_children ); } } if ($constructor = theme('jsdoc_resolve_constructor', $prototype_objects)) { return ($cleaned) ? $constructor : $constructor->node; } } function _jsdoc_get_type_themed($type, $node, $cleaned){ $obj = jsdoc_object_node_load($type, jsdoc_get_project($node), jsdoc_get_version($node), jsdoc_get_all_provide_nids($node, array(3, 4))); if ($obj) { $type = (object)array( 'title' => $obj->title, 'summary' => jsdoc_get_teaser($obj), 'fields' => false ); if (!$cleaned) { $type->url = $obj->jsdoc_url; $type->a = l($obj->title, $obj->jsdoc_url); } // It will have an nid if it's local. For example type: Function isn't local if ($obj->nid) { $type->fields = array(); $prototype_chain = jsdoc_get_prototype_chain($obj, array(3, 4)); for ($i = 0; $i < count($prototype_chain); $i++) { $item = $prototype_chain[$i]; if ($mixin_chain_mixins_all = jsdoc_get_parent_mixins($item)) { foreach ($mixin_chain_mixins_all as $mixin_type => $mixin_chain_mixins) { if ($mixin_type == 'prototype' && is_array($mixin_chain_mixins)) { ksort($mixin_chain_mixins); foreach ($mixin_chain_mixins as $mixin) { if (substr($mixin, -10) == '.prototype') { // I don't think this is a bad assumption $mixin = substr($mixin, 0, -10); } $mixin = jsdoc_object_node_load($mixin, jsdoc_get_project($item), jsdoc_get_version($item), jsdoc_get_all_provide_nids($item, array(3, 4))); if ($i > 0 && $prototype_chain[$i - 1]->title == $mixin->title) { continue; } array_splice($prototype_chain, $i++, 0, array($mixin)); } } } } } $prototype_chain[] = $obj; foreach ($prototype_chain as $obj) { if ($obj->nid) { $args = jsdoc_get_child_instances($obj); foreach (jsdoc_get_child_prototypes($obj) as $title => $arg) { $args[$title] = $arg; } if (!empty($args)) { foreach ($args as $arg) { $title = $parameter->name . '.' . substr($arg->title, strlen($obj->title) + 1); $field = (object)array( 'title' => $title, 'summary' => jsdoc_get_teaser($arg), 'types' => false ); if (!$cleaned) { $field->url = $arg->jsdoc_url; $field->a = l($title, $arg->jsdoc_url); } $field_types = preg_split('%\|+%', jsdoc_get_type($arg)); 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_project($node), jsdoc_get_version($node), jsdoc_get_all_provide_nids($node, array(3, 4))); $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, $depth=0) { if (isset($node->jsdoc_parameters_themed)) { return $node->jsdoc_parameters_themed; } $parameters = array(); $retrieved_parameters = jsdoc_get_parameters($node); if ($depth++ < 2 && empty($retrieved_parameters) && jsdoc_get_type($node) == 'Constructor') { if ($constructor = jsdoc_resolve_constructor($node, true, $depth)) { $node->jsdoc_parameters = jsdoc_get_parameters($constructor->node); $parameters = $constructor->parameters; } } else { foreach ($retrieved_parameters as $item) { $parameter = (object)$item['jsdoc_formatted']; $parameter->summary = $item['summary']; $parameter->name = $item['name']; $parameter->optional = $item['optional']; $parameter->repeating = $item['repeating']; $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_object_mixin($node, $object) { } /** * Either load or create an object, dealing with versioning and everything * * We look up an object by name and resource, getting the latest nid that we find. * * If there is no object present, we use node_save to create a new node. After * this, we can just exit the block. * * If there is an object present, the first thing to check for is to see if * we need to create a new revision of the object. We do this by checking * to see if we have a new revision, if updating flag is set in the DB, or * if the parameters have been updated. In this case, we create a new version * of the node. * * For a few values, we check to see if they are either absent in the DB or * if the updating flag is set (the user is basically saying for the JS to "win"). * * For the rest of the values, the values from JS win. */ function _jsdoc_object_get_or_create($namespace, $name, $summary, $description, $resource, $provide, $private, $private_parent, $initialized, $classlike, $type, $returns, $return_summary, $source, $parameters, $examples, $aliases, $instance, $prototype, $chains, $mixins) { if (is_object($namespace)) { $project = $namespace; $version = jsdoc_version_node_load('HEAD', $project); } else { $project = _jsdoc_project_get_or_create($namespace); $version = _jsdoc_version_get_or_create('HEAD', $project); } if (!$nid_vid = db_fetch_object(db_query("SELECT nr.nid, MAX(nr.vid) AS vid FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.resource_vid = %d AND j.version = %d GROUP BY nr.nid", $name, $name, $resource->vid, $version->nid))) { // Create a node for this object if we don't already have one $node = (object)array( 'title' => $name, 'type' => 'jsdoc_object', 'teaser' => $summary, // from source 'body' => $description, // immutable by source 'uid' => 1, 'status' => 1, 'comment' => 2, 'promote' => 0, 'sticky' => 0, 'jsdoc_cron' => true, 'jsdoc_project_name' => $namespace, // from source 'jsdoc_resource' => $resource->nid, // from source, 'jsdoc_resource_vid' => $resource->vid, // from source, 'jsdoc_provide' => $provide->nid, // from source 'jsdoc_provide_vid' => $provide->vid, // from source 'jsdoc_private' => $private, // from source 'jsdoc_private_parent' => $private_parent, // from source 'jsdoc_initialized' => $initialized, // from source 'jsdoc_classlike' => $classlike, // immutable by source 'jsdoc_type' => $type, // immutable 'jsdoc_returns' => $returns, // from source 'jsdoc_return_summary' => $return_summary, // from source 'jsdoc_source' => $source, // from source 'jsdoc_parameters' => $parameters, // from source 'jsdoc_examples' => $examples, // from source. Although I'd like to add a screen for new ones 'jsdoc_aliases' => $aliases, // from source 'jsdoc_instance' => $instance, // from source 'jsdoc_prototype' => $prototype, // from source 'jsdoc_chains' => $chains, // from source 'jsdoc_mixins' => $mixins // from source ); if (trim($description)) { $node->format = variable_get('jsdoc_input_format', 1); } node_save($node); $node = _jsdoc_node_load($node->nid); } else { $node = _jsdoc_node_load($nid_vid->nid, $nid_vid->vid); $node->jsdoc_cron = true; $new_version = false; if ($node->jsdoc_updating) { // Who knows if we'll have more conditions in the future? Not me. $new_version = true; } if ($parameters) { jsdoc_get_parameters($node); foreach ($parameters as $parameter_name => $parameter) { if (is_array($node->jsdoc_parameters) && array_key_exists($parameter_name, $node->jsdoc_parameters)) { if (empty($parameter['type']) || !$new_version) { $parameters[$parameter_name]['type'] = $node->jsdoc_parameters[$parameter_name]['type']; // Database writes over source } if (empty($parameter['summary']) || !$new_version) { $parameters[$parameter_name]['summary'] = $node->jsdoc_parameters[$parameter_name]['summary']; // Database writes over source } } } } $node->jsdoc_parameters = $parameters; // Allow overriding of all properties, not just mutable values. // Create a new revision before we do this, though (see logic block above) if ($new_version || !$node->jsdoc_type) { $node->jsdoc_type = $type; } if ($new_version || !trim($node->body)) { $node->body = $description; $node->format = variable_get('jsdoc_input_format', 1); } if ($new_version) { $node->jsdoc_classlike = $classlike; } $node->teaser = $summary; $node->jsdoc_private = $private; $node->jsdoc_private_parent = $private_parent; $node->jsdoc_initialized = $initialized; $node->jsdoc_returns = $returns; $node->jsdoc_return_summary = $return_summary; $node->jsdoc_resource = $resource->nid; $node->jsdoc_resource_vid = $resource->vid; $node->jsdoc_provide = $provide->nid; $node->jsdoc_provide_vid = $provide->vid; $node->jsdoc_source = $source; $node->jsdoc_aliases = $aliases; $node->jsdoc_examples = $examples; $node->jsdoc_instance = $instance; $node->jsdoc_prototype = $prototype; $node->jsdoc_chains = $chains; $node->jsdoc_mixins = $mixins; $node->revision = false; node_save($node); $node = _jsdoc_node_load($node->nid); } return $node; } function _jsdoc_variable_get_or_create($name, $project_name, $private) { if (is_object($name)) { $name = $name->title; } if (is_object($project_name)) { $project = $project_name; $version = jsdoc_version_node_load('HEAD', $project); } else { $project = _jsdoc_project_get_or_create($project_name); $version = _jsdoc_version_get_or_create('HEAD', $project); } if (!$node = db_fetch_object(db_query("SELECT nr.nid, MAX(nr.vid) AS vid FROM {jsdoc_variables} j JOIN {node_revisions} nr ON (j.vid = nr.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.version = %d GROUP BY nr.nid", $name, $name, $version->nid))) { $query = db_query("SELECT nr.nid FROM {jsdoc_variables} j JOIN {node_revisions} nr ON (j.vid = nr.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.version IN (%d) GROUP BY nr.nid", $name, $name, implode(',', $project->jsdoc_version_nids)); if (!$node = db_fetch_object($query)) { $node = (object)array( 'title' => $name, 'jsdoc_version' => $version->nid, 'jsdoc_private' => $private, 'type' => 'jsdoc_variable', 'uid' => 1, 'status' => 1, 'comment' => 2, 'promote' => 0, 'sticky' => 0 ); node_save($node); } else { $node = _jsdoc_node_load($node->nid); $node->revision = true; $node->jsdoc_version = $version; node_save($node); } $node = _jsdoc_node_load($node->nid); } else { $node = _jsdoc_node_load($node->nid, $node->vid); $node->jsdoc_private = $private; node_save($node); $node = _jsdoc_node_load($node->nid); } return $node; } function _jsdoc_resource_get_or_create($name, $project_name) { $project = _jsdoc_project_get_or_create($project_name); $version = _jsdoc_version_get_or_create('HEAD', $project); if (!$node = db_fetch_object(db_query("SELECT nr.nid, MAX(nr.vid) AS vid FROM {jsdoc_resources} j JOIN {node_revisions} nr ON (j.vid = nr.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.version = %d GROUP BY nr.nid", $name, $name, $version->nid))) { $node = (object)array( 'jsdoc_version' => $version->nid, 'title' => $name, 'type' => 'jsdoc_resource', 'uid' => 1, 'status' => 1, 'comment' => 2, 'promote' => 0, 'stick' => 0 ); node_save($node); $is_new = $node->is_new; $node = _jsdoc_node_load($node->nid); if ($is_new) { $node->is_new = $is_new; } } else { $node = _jsdoc_node_load($node->nid, $node->vid); } return $node; } function _jsdoc_project_get_or_create($name) { static $projects; if (!$projects) { $projects = array(); } if ($projects[$name]) { return $projects[$name]; } if (!$nid = db_result(db_query("SELECT nid FROM {node} n WHERE type = 'jsdoc_project' AND status = 1 AND title = '%s' AND BINARY title = '%s'", $name, $name))) { $node = (object)array( 'title' => $name, 'type' => 'jsdoc_project', 'uid' => 1, 'status' => 1, 'comment' => 0, 'promote' => 0, 'stick' => 0 ); node_save($node); $node = _jsdoc_node_load($node->nid); } else { $node = _jsdoc_node_load($nid); } return $projects[$name] = $node; } function _jsdoc_version_get_or_create($name, $project_nid) { if (is_object($project_nid) && $project_nid->nid) { $project_nid = $project_nid->nid; } if (!$nid = db_result(db_query("SELECT n.nid FROM {node} n JOIN {jsdoc_versions} j ON (j.nid = n.nid) WHERE n.type = 'jsdoc_version' AND n.title = '%s' AND BINARY n.title = '%s' AND j.project = %d", $name, $name, $project_nid))) { $node = (object)array( 'jsdoc_project' => $project_nid, 'title' => $name, 'type' => 'jsdoc_version', 'uid' => 1, 'status' => 1, 'comment' => 2, 'promote' => 0, 'stick' => 0 ); node_save($node); } else { $node = _jsdoc_node_load($nid); } return $node; } function _jsdoc_detail_update_joins($node) { if (!isset($node->jsdoc_cron)) return; db_query("DELETE FROM {jsdoc_variable_hierarchy} WHERE nid = %d AND version = %d", $node->nid, jsdoc_get_version($node)->nid); if ($node->jsdoc_aliases) { $parent = _jsdoc_variable_get_or_create($node->jsdoc_aliases, jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, version) VALUES (%d, %d, %d, %d, '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'alias', jsdoc_get_version($node)->nid); } else { if ($node->jsdoc_instance) { $parent = _jsdoc_variable_get_or_create($node->jsdoc_instance, jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, version) VALUES (%d, %d, %d, %d, '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'instance', jsdoc_get_version($node)->nid); // If object foo has function foo.bar and foo.bar uses the variable // this.baz and is uninstantiated, `this` would refer to foo, // making baz a property of foo. So we create a normal join // from foo.bar.baz to foo.baz by removing the second to last object section if (preg_match('%^([^.]+)\.[^.]+\.([^.]+)$%', $node->title, $match)) { $parent = _jsdoc_variable_get_or_create($match[1] . '.' . $match[2], jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, version) VALUES (%d, %d, %d, %d, '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'cascading', jsdoc_get_version($node)->nid); } } if ($node->jsdoc_prototype) { $parent = _jsdoc_variable_get_or_create($node->jsdoc_prototype, jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, version) VALUES (%d, %d, %d, %d, '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'prototype', jsdoc_get_version($node)->nid); } if (!$node->jsdoc_instance && !$node->jsdoc_prototype) { $parts = explode('.', $node->title); if (count($parts) == 1) { $parent = 'window'; } else { array_pop($parts); $parent = implode('.', $parts); } $parent = _jsdoc_variable_get_or_create($parent, jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, version) VALUES (%d, %d, %d, %d, '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'normal', jsdoc_get_version($node)->nid); } if ($node->jsdoc_mixins) { foreach ($node->jsdoc_mixins as $subtype => $mixin) { if ($subtype == 'normal') { $subtype = ''; } array_walk($mixin, '_jsdoc_flatten'); $mixin = array_unique($mixin); foreach ($mixin as $parent) { $parent = _jsdoc_variable_get_or_create($parent, jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, subtype, version) VALUES (%d, %d, %d, %d, '%s', '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'mixin', $subtype, jsdoc_get_version($node)->nid); } } } if ($node->jsdoc_chains) { foreach ($node->jsdoc_chains as $subtype => $chain) { array_walk($chain, '_jsdoc_flatten'); $chain = array_unique($chain); foreach ($chain as $parent) { $parent = _jsdoc_variable_get_or_create($parent, jsdoc_get_project($node), false); db_query("INSERT INTO {jsdoc_variable_hierarchy} (vid, nid, parent_vid, parent_nid, type, subtype, version) VALUES (%d, %d, %d, %d, '%s', '%s', %d)", $node->vid, $node->nid, $parent->vid, $parent->nid, 'chain', $subtype, jsdoc_get_version($node)->nid); } } } } } function _jsdoc_flatten(&$item, $key) { if (is_object($item)) { $item = $item->title; } } function jsdoc_get_all_provide_nids(&$node, $environments) { return jsdoc_get_resources($node, $environments)->jsdoc_provide_nids; } function jsdoc_get_prototype_chain($node, $environments, $output = array(), $visited = array()) { $parents = jsdoc_get_parents($node); $resources = jsdoc_get_resources($node, $environments); if (!in_array($node->nid, $visited)) { $visited[] = $node->nid; array_unshift($output, $node); $prototype = ""; if ($parents->chain && !empty($parents->chain['prototype'])) { $prototype = $parents->chain['prototype'][0]; } if ($prototype) { if ($parent = jsdoc_object_node_load($prototype, jsdoc_get_project($node), jsdoc_get_version($node), jsdoc_get_resources($node, $environments)->jsdoc_provide_nids)) { return jsdoc_get_prototype_chain($parent, $environments, $output, $visited); } } } if (count($output) == 1) { return array(); } array_unshift($output, jsdoc_object_node_load('Object', jsdoc_get_project($node), jsdoc_get_version($node))); return $output; } function jsdoc_get_variable_resources(&$node) { if (isset($node->variable_resources)) { return $node->variable_resources; } $node->variable_resources = array(); if ($node->type == 'jsdoc_variable') { $query = db_query("SELECT j.resource_nid AS nid, j.resource_vid AS vid FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.version = %d", $node->title, $node->title, jsdoc_get_version($node)->nid); if (db_num_rows($query)) { while ($result = db_fetch_object($query)) { $node->variable_resources[] = _jsdoc_node_load($result->nid, $result->vid); } } else { $query = db_query("SELECT j.resource_nid AS nid, j.resource_vid AS vid FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE nr.title LIKE '%s%%' AND BINARY nr.title LIKE '%s%%' AND j.version = %d GROUP BY j.resource_vid", $node->title, $node->title, jsdoc_get_version($node)->nid); while ($result = db_fetch_object($query)) { $node->variable_resources[] = _jsdoc_node_load($result->nid, $result->vid); } } } elseif ($node->type == 'jsdoc_object') { if ($resource = jsdoc_get_resource($node)) { $node->variable_resources[] = $resource; } } return $node->variable_resources; } function jsdoc_current_node($node=null) { static $current; if (is_null($node)) { return $current; } $current = $node; } function jsdoc_get_resources(&$node, $environments) { if (isset($node->jsdoc_resources)) { return (object)array( 'jsdoc_provide_nids' => $node->jsdoc_provide_nids, 'jsdoc_resources' => $node->jsdoc_resources ); } $node->jsdoc_provide_nids = array(); $node->jsdoc_resources = array(); if ($node->type == 'jsdoc_object') { $object = $node; } elseif ($node->type == 'jsdoc_variable' && jsdoc_get_variable_object($node)) { $object = jsdoc_get_variable_object($node); } $provide = jsdoc_get_provide($node); $node_resource = jsdoc_get_resource($node); $version = jsdoc_get_version($node); if ($object && $provide) { $object_provide = jsdoc_get_provide($object); $resource_tree = _jsdoc_get_tree($version, $environments, $provide->nid); $provide_nids = array(); $resources = array(); foreach ($resource_tree as $resource) { $provide_nids[] = $resource->nid; $resource_node = _jsdoc_node_load($resource->nid, $resource->vid); $resource_node->jsdoc_resource_depth = $resource->depth; $resources[] = $resource_node; } if ($provide && !in_array($provide->nid, $provide_nids)) { $provide_nids[] = $provide->nid; $resource_node = _jsdoc_node_load($provide->nid, $provide->vid); $resource_node->jsdoc_resource_depth = -1; $resources[] = $resource_node; } if ($object_provide->nid && !in_array($object_provide->nid, $provide_nids)) { $provide_nids[] = $object_provide->nid; $resource_node = _jsdoc_node_load($object_provide->nid, $object_provide->vid); $resource_node->jsdoc_resource_depth = -1; $resources[] = $resource_node; } $node->jsdoc_provide_nids = $object->jsdoc_provide_nids = array_unique($provide_nids); $node->jsdoc_resources = $object->jsdoc_resources = $resources; } return jsdoc_get_resources($node, $environments); } function _jsdoc_get_tree($version, $environments, $parent = 0, $depth = -1, $visited=array()) { static $children, $parents, $terms; $depth++; if (!isset($parents)) { $parents = array(); $result = db_query("SELECT j.vid, j.nid, j.parent_vid, j.parent_nid, j.project, nr.title AS name FROM {jsdoc_resource_hierarchy} j JOIN {node_revisions} nr ON (j.vid = nr.vid) WHERE j.version = %d AND j.tid IN (%s)", $version->nid, implode(',', $environments)); $results = array(); while ($term = db_fetch_object($result)) { $results["{$term->vid}|{$term->parent_vid}|{$term->title}"] = $term; } foreach ($results as $term) { if ($term->project) { $child = _jsdoc_node_load($term->nid, $term->vid); $parent_node = _jsdoc_node_load($term->parent_nid, $term->parent_vid); $parent_versions = jsdoc_get_versions($parent_node); $found = false; foreach ($parent_versions as $version) { if ($version->title == jsdoc_get_version($child)->title) { if ($parent_vid = db_result(db_query("SELECT vid FROM {jsdoc_resources} WHERE nid = %d AND version = %d", $parent_node->nid, $version->nid))) { $term->parent_vid = $parent_vid; $found = true; } } } if (!$found) { continue; } } if ($term->parent_vid) { $children[$term->parent_nid][] = $term->nid; $parents[$term->nid][] = array($term->parent_nid, $term->parent_vid); } $terms[$term->nid] = $term; } } if ($parents[$parent]) { foreach ($parents[$parent] as $parent) { if (isset($terms[$parent[0]])) { $term = $terms[$parent[0]]; } else { $term = (object)array('nid' => $parent[0], 'vid' => $parent[1]); $term->name = $term->title; } $term->depth = $depth; $term->children = $children[$parent[0]]; $tree[] = $term; if ($parents[$parent[0]]) { if (in_array($parent[0], $visited)) { return $tree; } $visited[] = $parent[0]; $tree = array_merge($tree, _jsdoc_get_tree($version, $environments, $parent[0], $depth, $visited)); } } } return $tree ? $tree : array(); } 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; } /** * Links a node to another node with a taxonomy term. * * @param $vid * The vid of a node_revisions item * @param $parent * The vid of a node_revisions item representing a parent of the vid * @param $tid * The taxonomy term tid to use to link them together */ function _jsdoc_save_hierarchy($provides_node, $requires_node, $tid) { $project = array('', ''); if (jsdoc_get_project($provides_node)->nid != jsdoc_get_project($requires_node)->nid) { $project[0] = ', project'; $project[1] = ', ' . jsdoc_get_project($requires_node)->nid; } db_query("INSERT INTO {jsdoc_resource_hierarchy} (vid, nid, parent_vid, parent_nid, tid, version{$project[0]}) VALUES (%d, %d, %d, %d, %d, %d{$project[1]})", $provides_node->vid, $provides_node->nid, $requires_node->vid, $requires_node->nid, $tid, jsdoc_get_version($provides_node)->nid); } 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->teaser = jsdoc_get_teaser($node); $node->body = jsdoc_get_body($node); $form = node_prepare($node)->content; $form['summary'] = array( '#value' => jsdoc_get_teaser($node), '#weight' => $form['body']['#weight'] - 1 ); $teaser = $form['summary']['#value']; $form['summary']['#value'] = theme('jsdoc_object_summary', $teaser); $form['description']['#value'] = theme('jsdoc_object_description', $teaser, $form['body']['#value']); unset($form['body']); return $form; } function _jsdoc_node_load($param = array(), $revision = NULL, $reset = NULL) { static $by_vid; if (!is_array($by_vid)) { $by_vid = array(); } static $by_nid; if (!is_array($by_nid)) { $by_nid = array(); } if (is_numeric($revision)) { if ($by_vid[$revision]) { return $by_vid[$revision]; } } elseif (is_numeric($param) && $by_nid[$param]) { return $by_nid[$param]; } $node = call_user_func('node_load', $param, $revision, $reset); $by_nid[$node->nid] = $by_vid[$node->vid] = $node; return $node; } function _jsdoc_markup_code($text, $version, $base=false) { static $variables; if (!$variables) { if ($variables = cache_get('jsdoc_variables')) { $variables = unserialize($variables->data); } else { $variables = array(); $query = db_query("SELECT nr.title, j.version FROM {jsdoc_variables} j JOIN {node_revisions} nr ON (nr.vid = j.vid) WHERE j.version = %d ORDER BY nr.title DESC", $version); while ($result = db_fetch_object($query)) { $variables[$result->title] = $result->version; } cache_set('jsdoc_variables', 'cache', serialize($variables)); } } $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 = _jsdoc_node_load($variables[$variable]); $text = str_replace("%$i%", l($match, 'jsdoc/' . jsdoc_get_project($version)->title . '/' . $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 = _jsdoc_node_load($variables[$variable]); $text = str_replace($match[0], l($match[0], 'jsdoc/' . jsdoc_get_project($version)->title . '/' . $version->title . '/' . $variable, array('style' => 'border-bottom: 1px dotted #ccc;'), NULL, NULL, FALSE, TRUE), $text); } } } } return $text; } function _jsdoc_resolve_variable(&$node) { if (isset($node->jsdoc_resolved)) { return $node->jsdoc_resolved; } $resource = jsdoc_get_resource($node); $version = jsdoc_get_version($node); $used = $node->jsdoc_used; $object = false; $title = $node->title; $teaser = $node->teaser; $body = $node->body; $format = $node->format; $object_type = $node->jsdoc_type; $classlike = false; $full_url = $node->jsdoc_url; $query = db_query("SELECT nr.teaser, nr.body, nr.format, nr.nid, nr.vid, j.resource_nid, j.resource_vid AS resource_vid, j.classlike, j.type, j.private FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (j.vid = nr.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.used != -1 AND j.version = %d", $node->title, $node->title, $version->nid); if (db_num_rows($query) == 0) { $project_versions = array(); $projects = jsdoc_projects(); foreach ($projects as $project) { foreach (jsdoc_get_versions($project) as $project_version) { if ($project_version->title == $version->title) { $project_versions[] = $project_version->nid; break; } } } $query = db_query("SELECT nr.teaser, nr.body, nr.format, nr.nid, nr.vid, j.resource_nid, j.resource_vid AS resource_vid, j.classlike, j.type, j.private FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (j.vid = nr.vid) WHERE nr.title = '%s' AND BINARY nr.title = '%s' AND j.used != -1 AND j.version IN (%s)", $node->title, $node->title, implode(',', $project_versions)); } $results = array(); while ($result = db_fetch_object($query)) { if($old = $results[$result->nid]){ $old->vid = max($old->vid, $result->vid); $old->resource_vid = max($old->resource_vid, $result->resource_vid); } else { $results[$result->nid] = $result; } } foreach ($results as $result){ // If there's only one object for a given variable, or if a resource is chosen, use this as the object $resources[] = _jsdoc_node_load($result->resource_nid, $result->resource_vid); if (db_num_rows($query) == 1 || ($resource && $resource->vid == $result->resource_vid)) { $object = _jsdoc_node_load($result->nid, $result->vid); $sumdesc = db_fetch_object(db_query("SELECT j.type, j.classlike, nr.teaser, nr.body, nr.format, nr2.title AS resource FROM {jsdoc_objects} j JOIN {node_revisions} nr ON (nr.vid = j.vid) JOIN {node_revisions} nr2 ON (nr2.vid = j.resource_vid) WHERE j.vid = %d", $result->vid)); if ($sumdesc->teaser && !$teaser) { $teaser = $sumdesc->teaser; } if ($sumdesc->body && !$body) { $body = $sumdesc->body; } if ($sumdesc->format && !$format) { $format = $sumdesc->format; } if ($sumdesc->type && !$object_type) { $object_type = $sumdesc->type; } $classlike = $sumdesc->classlike; } else { if ($result->teaser && !$teaser) { $teaser = $result->teaser; } if ($result->body && !$body) { $body = $result->body; } $format = $result->format; if ($result->type) { if (!$object_type) { $object_type = $result->type; } elseif ($object_type != $result->type) { if ($object_type) { $object_type .= '|'; } $object_type .= $result->type; } } if ($result->classlike) { $classlike = true; } } } return $node->jsdoc_resolved = (object)array( 'teaser' => $teaser, 'body' => $body, 'format' => $format, 'object_type' => $object_type, 'object' => $object, 'classlike' => $classlike, 'full_url' => $full_url ); } 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 = '