source: Dev/trunk/src/client/util/jsdoc/jsdoc.module @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

  • Property svn:executable set to *
File size: 85.4 KB
Line 
1<?php
2// $Id$
3
4/**
5* @file
6* Documentation front-end for JavaScript projects.
7*
8* Allows JavaScript projects to parse their code and return an object structure
9* that will be used to generate custom Drupal nodes.
10*/
11
12// Public API (intended for use in a JS source page)
13// =================================================
14
15// Implemented global hook functions
16// =================================
17
18/**
19 * Implementation of hook_node_info().
20 */
21function jsdoc_node_info() {
22  return array(
23                'jsdoc_doc' => array('name' => t('Documentation'), 'module' => 'jsdoc_doc', 'description' => t('A JavaScript documentation file')),
24    'jsdoc_object' => array('name' => t('JavaScript Object'), 'module' => 'jsdoc_object', 'description' => t('An implemented JavaScript object'))
25  );
26}
27
28/**
29 * Implementation of hook_perm().
30 */
31function jsdoc_perm() {
32  return array('edit jsdoc');
33}
34
35/**
36 * Implementation of hook_menu().
37 */
38function jsdoc_menu($may_cache) {
39  $items = array();
40  $version = isset($_SESSION['jsdoc_version']) ? $_SESSION['jsdoc_version'] : null;
41
42  if ($may_cache) {
43    $items[] = array(
44      'path' => 'admin/settings/jsdoc',
45      'title' => t('JavaScript Documentation'),
46      'callback' => 'drupal_get_form',
47      'callback arguments' => array('jsdoc_admin'),
48      'access' => user_access('access administration pages'),
49      'type' => MENU_NORMAL_ITEM
50    );
51
52    $items[] = array(
53      'path' => 'jsdoc/jsonp',
54      'type' => MENU_CALLBACK,
55      'callback' => 'jsdoc_jsonp',
56      'access' => true
57    );
58
59    $items[] = array(
60      'path' => 'jsdoc/feedback',
61      'type' => MENU_CALLBACK,
62      'callback' => 'jsdoc_feedback',
63      'access' => true
64    );
65
66  $items[] = array(
67    'path' => 'jsdoc/feedback/review',
68    'type' => MENU_CALLBACK,
69    'callback' => 'jsdoc_feedback_review',
70    'access' => true
71  );
72
73    $items[] = array(
74      'path' => 'jsdoc/404',
75      'title' => t('Page not found'),
76      'type' => MENU_CALLBACK,
77      'callback' => 'jsdoc_404',
78      'access' => true
79    );
80  }
81  else {
82    if (arg(0) == 'jsdoc' && arg(1) != 'jsonp') {
83      $version = false;
84      $resource = false;
85      $name = '';
86      $path = '';
87
88      $switch = false;
89      $args = array();
90      for ($i = 0; $arg = arg($i); $i++){
91        if ($arg == '.switch') {
92          $switch = arg($i + 1);
93          break;
94        }
95        $args[] = $arg;
96      }
97
98      if (count($args) == 4) {
99        // Old URL
100        $version = $args[2];
101        $name = $args[3];
102        drupal_goto("jsdoc/$version/$name");
103        return;
104      }
105      if ($args[2]) {
106        // If we have 2 arguments, assume that we don't have a resource or project
107        // So basically, we assume that version is more important than project
108        $version = $args[1];
109        $name = $args[2];
110        $path = "jsdoc/$version/$name";
111      }
112      elseif ($args[1]) {
113        // Allow the user to only pass the name
114        $name = $args[1];
115        $path = "jsdoc/$name";
116      }
117
118      $node = jsdoc_get_closest($name, $version);
119
120      if (!$node || !$node->nid) {
121        return;
122      }
123      else {
124        jsdoc_current_node($node);
125
126        $item = array(
127          'path' => $path,
128          'title' => t('View'),
129          'access' => node_access('view', $node),
130          'type' => MENU_CALLBACK
131        );
132
133        $version  = jsdoc_get_version($node);
134
135        $path = array('jsdoc', $version->title, $name);
136        if ($switch) {
137          $path[1] = $switch;
138          drupal_goto(implode('/', $path));
139          return;
140        }
141
142        $versions = jsdoc_get_versions($node, $name);
143        if (count($versions) > 1) {
144          $path = explode('/', $node->jsdoc_url);
145          foreach (array_reverse($versions) as $release) {
146            $path[2] = $release->title;
147
148            $items[] = array(
149              'path' => $_GET['q'] . '/.switch/' . $release->title,
150              'title' => $project->title . ' ' . $release->title,
151              'type' => ($release->nid == $version->nid) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK
152            );
153          }
154        }
155
156        $item['callback'] = ($name == $node->title) ? 'jsdoc_object_node_view' : 'jsdoc_child_node_view';
157        $item['callback arguments'] = ($name == $node->title) ? array($node) : array($node, $name);
158      }
159      $items[] = $item;
160    }
161  }
162
163  return $items;
164}
165
166/**
167 * Impelementation of hook_block
168 */
169function jsdoc_block($op='list', $delta=0) {
170  if ($op == 'list') {
171    if (module_exists('search')) {
172      $block[0]['info'] = t('JavaScript Documentation Search');
173    }
174    $block[1]['info'] = t('JavaScript Namespace List');
175    return $block;
176  }
177  elseif ($op == 'view') {
178    if ($delta == 0 && module_exists('search')) {
179      $block['subject'] = 'Search';
180      $block['content'] = preg_replace('%ignore.*endignore%s', theme('jsdoc_search', 'search'), drupal_get_form('_jsdoc_block_search'));
181      return $block;
182    }
183    elseif ($delta == 1) {
184      $namespaces = array();
185
186      $current = jsdoc_current_node();
187      list($current_namespace,) = explode('.', $current->title, 2);
188
189      if (!($version = jsdoc_get_version($current))) {
190        foreach (jsdoc_get_versions() as $version) {
191          if ($version->title != 'HEAD') {
192            break;
193          }
194        }
195      }
196
197      if ($content = cache_get("jsdoc_namespaces_{$version->title}:$current_namespace")) {
198        $content = $content->data;
199      }
200      else {
201        $query = db_query("SELECT n.nid, n.title, version.title AS version FROM {node} AS n JOIN {jsdoc_objects} j ON (j.nid = n.nid) JOIN {node} AS version ON (version.nid = j.did) WHERE j.did = %d", $version->nid);
202        while ($node = db_fetch_object($query)) {
203          if (strpos($node->title, '._') !== false) {
204            continue;
205          }
206          $parts = explode('.', $node->title);
207          if (count($parts)) {
208            for ($i = 1; $i <= count($parts); $i++) {
209              $parent = implode('.', array_slice($parts, 0, $i));
210              if (!$namespaces[$parent]) {
211                $namespaces[$parent] = (object)array(
212                  'title' => $parent,
213                  'children' => array()
214                );
215              }
216            }
217
218            array_pop($parts);
219            $parent = implode('.', $parts);
220            if ($parent) {
221              $children = &$namespaces[$parent]->children;
222            }
223            else {
224              $children = &$namespaces;
225            }
226            $children[$node->title]->jsdoc_url = 'jsdoc/' . $node->version . '/' . $node->title;
227            $children[$node->title]->url = url('jsdoc/' . $node->version . '/' . $node->title);
228            $children[$node->title]->a = l($node->title, 'jsdoc/' . $node->version . '/' . $node->title);
229          }
230        }
231
232        uksort($namespaces, 'strnatcasecmp');
233        foreach ($namespaces as $namespace) {
234          if ($namespace->children) {
235            uksort($namespace->children, 'strnatcasecmp');
236          }
237        }
238
239        $content = theme('jsdoc_namespaces', $namespaces, $version->title, $current->title);
240        cache_set("jsdoc_namespaces_{$version->title}:$current_namespace", 'cache', $content, time() + 86400);
241      }
242
243      //$namespaces = unserialize(cache_get('jsdoc_namespaces', 'cache')->data);
244      $block['subject'] = 'Objects';
245      $block['content'] = $content;
246      return $block;
247    }
248  }
249}
250
251function _jsdoc_block_search() {
252  if (module_exists('search')) {
253    return array(
254      'ignore' => array(
255        '#value' => 'ignore'
256      ),
257      'search' => array(
258        '#type' => 'textfield'
259      ),
260      'go' => array(
261        '#type' => 'submit',
262        '#value' => 'Go'
263      ),
264      'endignore' => array(
265        '#value' => 'endignore'
266      )
267    );
268  }
269}
270
271function _jsdoc_block_search_submit($form_id, $form) {
272  // TODO
273  $search = $form['search'];
274  if (strpos($search, ' ') === false) {
275    $query = db_query("SELECT title FROM {node} WHERE type = 'jsdoc_object' AND title = '%s' GROUP BY BINARY title", $search);
276    if (db_num_rows($query) == 1) {
277      $object = jsdoc_object_node_load(db_result($query));
278      watchdog('search', t('%keys (@type).', array('%keys' => $search, '@type' => 'JavaScript Documentation')), WATCHDOG_NOTICE, l(t('result'), $object->jsdoc_url));
279      drupal_goto($object->jsdoc_url);
280      return;
281    }
282    else {
283      $query = db_query("SELECT title FROM {node} WHERE type = 'jsdoc_object' AND title LIKE '%%%s%%' GROUP BY BINARY title", $search);
284      if (db_num_rows($query) == 1) {
285        $object = jsdoc_object_node_load(db_result($query));
286        watchdog('search', t('%keys (@type).', array('%keys' => $search, '@type' => 'JavaScript Documentation')), WATCHDOG_NOTICE, l(t('result'), $object->jsdoc_url));
287        drupal_goto($object->jsdoc_url);
288        return;
289      }
290    }
291  }
292
293  drupal_goto('search/jsdoc/' . $search);
294}
295
296/**
297 * shutdown function to make sure we always mark the last node processed.
298 */
299function jsdoc_update_shutdown() {
300  global $jsdoc_last_change, $jsdoc_last_nid;
301
302  if ($jsdoc_last_change && $jsdoc_last_nid) {
303    variable_set('jsdoc_cron_last', $jsdoc_last_change);
304    variable_set('jsdoc_cron_last_nid', $jsdoc_last_nid);
305  }
306}
307
308/**
309 * Implementation of hook_update_index().
310 *
311 * Handle node status the way that the node module does... through remembering nids, last change time,
312 * and last comment change time.
313 *
314 * We need to save things by version/node pair though. That way, we can keep things so that the latest
315 * update is always the latest version of the node.
316 */
317function jsdoc_update_index() {
318  global $jsdoc_last_change, $jsdoc_last_nid;
319
320  register_shutdown_function('jsdoc_update_shutdown');
321
322  $jsdoc_last_change = variable_get('jsdoc_cron_last', 0);
323  $jsdoc_last_nid = variable_get('jsdoc_cron_last_nid', 0);
324  $limit = (int)variable_get('search_cron_limit', 500);
325
326  // $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);
327  $result = db_query_range("SELECT n.changed as last_change, n.nid FROM {node} n WHERE n.type = 'jsdoc_object' AND ((n.changed = %d AND n.nid > %d) OR n.changed > %d) ORDER BY n.changed ASC, n.nid ASC", $jsdoc_last_change, $jsdoc_last_nid, $jsdoc_last_change, 0, $limit);
328  while ($node = db_fetch_object($result)) {
329    $node = node_load($node->nid);
330    $type = 'jsdoc_' . jsdoc_get_version($node)->title;
331
332    $items = array($node);
333    foreach (jsdoc_get_methods($node) as $method) {
334      $method->title = "{$node->title}.{$method->name}";
335      $items[] = $method;
336    }
337    foreach (jsdoc_get_properties($node) as $property) {
338      $property->title = "{$node->title}.{$property->name}";
339      $items[] = $property;
340    }
341    foreach ($items as $item) {
342      $text = '<h1>'. implode(' ', _jsdoc_build_terms($item->title)) . '</h1> ' . jsdoc_get_teaser($item) . ' ' . jsdoc_get_body($item);
343
344      if (!($sid = db_result(db_query("SELECT sid FROM {jsdoc_variables} WHERE variable = '%s'", $item->title)))) {
345        $sid = db_next_id('{jsdoc_variables}_sid');
346        db_query("INSERT INTO {jsdoc_variables} (sid, variable, private) VALUES (%d, '%s', %d)", $sid, $item->title, jsdoc_is_private($item) || jsdoc_has_private_parent($item));
347      }
348
349      search_index($sid, $type, $text);
350    }
351
352    $jsdoc_last_change = $node->changed;
353    $jsdoc_last_nid = $node->nid;
354  }
355
356  variable_set('jsdoc_cron_last', $jsdoc_last_change);
357  variable_set('jsdoc_cron_last_nid', $jsdoc_last_nid);
358}
359
360/**
361 * Implementation of hook_search().
362 */
363function jsdoc_search($op = 'search', $keys = null) {
364  switch ($op) {
365    case 'name':
366      return t('JavaScript Documentation');
367    case 'search':
368      // Get all projects
369      // Get all versions marked 'HEAD' in each project
370      $version = search_query_extract($keys, 'version');
371      if (empty($version)) {
372        $version = jsdoc_get_version()->title;
373      }
374      $keys = search_query_insert($keys, 'version');
375
376      $private = search_query_extract($keys, 'private');
377      $keys = search_query_insert($keys, 'private');
378
379      $where = '1';
380      if (!$private) {
381        $where = "j.private = 0";
382      }
383
384      $find = do_search(
385        '(' . implode('), (', _jsdoc_build_terms($keys)) . ')',
386        'jsdoc_' . $version,
387        "INNER JOIN {jsdoc_variables} j ON (j.sid = i.sid)",
388        $where,
389        array(),
390        'i.relevance + (15 / CHAR_LENGTH(j.variable)) AS score',
391        "INNER JOIN {jsdoc_variables} j ON (j.sid = i.sid)"
392      );
393      $results = array();
394      foreach ($find as $item) {
395        $variable = db_result(db_query("SELECT variable FROM {jsdoc_variables} WHERE sid = %d", $item->sid));
396        $actual = $node = jsdoc_object_node_load($variable, $version);
397        if (!$node->nid) {
398          $parts = explode('.', $variable);
399          array_pop($parts);
400          $node = jsdoc_object_node_load(implode('.', $parts), $version);
401          $actual = _jsdoc_resolve_child($node, $variable);
402        }
403
404        $info = jsdoc_node_info();
405        $results[] = array(
406          'url' => $actual->jsdoc_url,
407          'link' => url($actual->jsdoc_url),
408          'type' => "Version $version",
409          'title' => $actual->title,
410          'user' => '',
411          'date' => '',
412          'node' => $node,
413          'score' => $item->score,
414          'snippet' => (jsdoc_get_teaser($actual)) ? search_excerpt($keys, jsdoc_get_teaser($actual)) : ''
415        );
416      }
417
418      $term = trim(search_query_insert(search_query_insert($keys, 'private'), 'version'));
419      if (count($results) == 1 && $results[0]['title'] == $term) {
420        return drupal_goto($results[0]['url']);
421      }
422
423      return $results;
424  }
425}
426
427/**
428 * Implementation of hook_search_validate()
429 */
430function jsdoc_search_validate($form_id, $form_values, $form) {
431  $keys = $form_values['processed_keys'];
432
433  $version = $form_values['jsdoc_version'];
434  $keys = search_query_insert($keys, 'version', $version);
435
436  if ($form_values['jsdoc_private']) {
437    $keys = search_query_insert($keys, 'private', 'true');
438  }
439  else {
440    $keys = search_query_insert($keys, 'private');
441  }
442
443  if (!empty($keys)) {
444    form_set_value($form['basic']['inline']['processed_keys'], trim($keys));
445  }
446}
447
448/**
449 * Implementation of hook_form_alter().
450 */
451function jsdoc_form_alter($form_id, &$form) {
452  if ($form['type']['#value'] == 'jsdoc_object') {
453    unset($form['taxonomy']);
454  }
455  elseif ($form_id == 'search_form' && $form['module']['#value'] == 'jsdoc') {
456    foreach (jsdoc_get_versions() as $version) {
457      $options[$version->title] = $version->title;
458    }
459
460    if (empty($options)) {
461      $options = array('' => '-----');
462    }
463
464    $form['jsdoc_version'] = array(
465      '#title' => t('Version'),
466      '#type' => 'select',
467      '#options' => $options,
468      '#default_value' => jsdoc_get_version()->title
469    );
470
471    if ($version = search_query_extract($form['basic']['inline']['keys']['#default_value'], 'version')) {
472      $form['jsdoc_version']['#default_value'] = $version;
473    }
474
475    $private = search_query_extract($form['basic']['inline']['keys']['#default_value'], 'private');
476
477    $form['jsdoc_private'] = array(
478      '#title' => t('Include Private Variables'),
479      '#type' => 'checkbox',
480      '#default_value' => $private
481    );
482
483    $form['#validate']['jsdoc_search_validate'] = array();
484  }
485}
486
487/**
488 * Implementation of hook_help().
489 */
490function jsdoc_help($section = '') {
491  $output = '';
492 
493  switch ($section) {
494    case 'admin/help#jsdoc':
495      return '<p>' . t('View and edit JavaScript Documentation') . '</p>';
496    case 'admin/modules#jsdoc':
497      return t('View and edit JavaScript Documentation');
498      case 'admin/settings/jsdoc/ignore':
499        return t("
500  <p>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.</p>
501  <p>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:</p>
502  <ul>
503    <li><em>variable</em>: The name of the variable</li>
504    <li><strong>Resource Specific</strong></li>
505    <li><em>#requires</em>: The string literal '#requires'</li>
506    <li><em>#provides</em>: The string literal '#provides'. It should match the requires statements of other files</li>
507    <li><em>resource</em>: Your code might load different files depending on different conditions. A resource is a string to indicate the location of a file</li>
508    <li><em>environment</em>: An indicator of where your code is running. Only one is available during run-time. Must correspond to the entry in \"JavaScript Environments\"</li>
509    <li><em>condition</em>: An indicator of a condition under which your code is running. Multiple conditions are available at run-time. Must correspond to an entry in \"JavaScript Conditions\"</li>
510    <li><strong>Object Specific</strong></li>
511    <li><em>private</em>: True if this object isn't meant to be public</li>
512    <li><em>type</em>: Object type. If it's a function, set to 'Function'</li>
513    <li><em>summary</em>: A short description of what the item does.</li>
514    <li><em>description</em>: An extended description of what the item does.</li>
515    <li><strong>Function Specific</strong></li>
516    <li><em>aliases</em>: The name of a function/variable this references. In code, it would have been fn1 = fn2, where fn1 aliases fn2</li>
517    <li><em>instance</em>: The name of a function that the 'this' variable refers to. Useful if a function is declared in a constructor.</li>
518    <li><em>initialized</em>: True if this variable references an initialized function.</li>
519    <li><em>classlike</em>: True if a function acts like a class.</li>
520    <li><em>prototype</em>: This object is attached via the prototype of this function</li>
521    <li><em>returns</em>: Object type of the function return.</li>
522    <li><em>return summary</em>: Summary of the function return.</li>
523    <li><em>chains</em>: The string literal 'chains'</li>
524    <li><em>chain-type</em>: call, or prototype. Basically, either doing this.call(parent); or this.prototype = new parent; respectively</li>
525    <li><em>function</em>: A function name</li>
526    <li><em>parameters</em>: The string literal 'parameters'</li>
527    <li><em>parameter</em>: The name of the parameter</li>
528    <li><em>optional</v>: True if the parameter is documented as optional</li>
529    <li><em>repeating</em>: True if the parameter is documented as repeating</li>
530    <li><em>type</em>: Object type of the parameter.</li>
531    <li><em>source</em>: Source Code (if it's a function)</li>
532  </ul>
533  <pre>array(
534  'variable' => array(
535    '#requires' => array(
536      array('environment/condition', 'resource')
537    )
538    'type' => '',   
539    'source' => '',
540    'summary' => '',
541    'description' => '',
542    'aliases' => '',
543    'instance' => '',
544    'initialized' => boolean,
545    'prototype' => '',
546    'returns' => '',
547    'return_summary' => '',
548    'chains' => array(
549      array('chain-type', 'function')
550    ),
551    'parameters' => array(
552      'parameter' => array(
553        'optional' => boolean,
554        'repeating' => boolean,
555        'type' => '',
556        'summary' => ''
557      )
558    )
559  )
560)</pre>");
561  }
562
563  return '';
564}
565
566// Views
567// =====
568
569function jsdoc_doc_form(&$node) {
570        $form = array(
571                '#attributes' => array(
572                        'enctype' => 'multipart/form-data'
573                )
574        );
575
576        $form['title'] = array(
577                '#type' => 'textfield',
578                '#title' => t('Version Number'),
579                '#default_value' => $node->title
580        );
581
582        $form['counts'] = array(
583                '#type' => 'item',
584                '#title' => t('Objects'),
585                '#value' => ($node->nid ? db_result(db_query("SELECT COUNT(*) FROM {jsdoc_objects} WHERE did = %d", $node->nid)) : 0)
586        );
587
588        $form['upload'] = array(
589                '#title' => t('Documentation File'),
590                '#type' => 'file'
591        );
592
593        $form['download'] = array(
594                '#title' => t('Documentation URL'),
595                '#type' => 'textfield'
596        );
597
598        return $form;
599}
600
601function jsdoc_doc_view($node, $teaser = FALSE, $page = FALSE) {
602  $node->content['counts'] = array(
603    '#type' => 'item',
604    '#title' => t('Objects'),
605    '#value' => db_result(db_query("SELECT COUNT(*) FROM {jsdoc_objects} WHERE did = %d", $node->nid))
606  );
607
608  return $node;
609}
610
611function jsdoc_doc_update($node) {
612  jsdoc_doc_insert($node);
613}
614
615function jsdoc_doc_insert($node) {
616  $start = time();
617  if ($file = file_check_upload()) {
618    $doc = fopen($file->filepath, 'r');
619  }
620  elseif ($node->download) {
621    $doc = fopen($node->download, 'r');
622  }
623  else {
624    return;
625  }
626
627  $buffer = array();
628  while (!feof($doc)) {
629    set_time_limit(30);
630
631    $line = stream_get_line($doc, 9999, "\n");
632    if (preg_match('%^\t"([^"]+)": \{%', $line, $match)) {
633      $id = $match[1];
634      $in = true;
635    }
636    elseif (preg_match('%^\t\}%', $line, $match)) {
637      $in = false;
638      $json = json_decode('{' . implode("\n", $buffer) . '}');
639
640      if ($nid = db_result(db_query("SELECT n.nid FROM {node} AS n INNER JOIN {jsdoc_objects} j ON (j.nid = n.nid) WHERE BINARY n.title = '%s' AND j.did = %d", $id, $node->nid))) {
641        $object = node_load($nid);
642        $object->json = $json;
643      }
644      else {
645        $object = (object)array(
646          'title' => $id,
647          'type' => 'jsdoc_object',
648          'version' => $node,
649          'json' => $json
650        );
651      }
652
653      node_save($object);
654      node_load($object->nid, NULL, TRUE);
655      $buffer = array();
656    }
657    elseif($in) {
658      $buffer[] = $line;
659    }
660  }
661
662  // cache_set("jsdoc_namespaces_{$version->title}
663
664  $query = db_query("DELETE FROM {cache} WHERE cid LIKE 'jsdoc_namespaces_%s:%%'", $node->title);
665
666  $query = db_query("SELECT n.nid FROM {node} AS n INNER JOIN {jsdoc_objects} AS j ON (j.nid = n.nid) WHERE type = 'jsdoc_object' AND j.did = %d AND changed < %d", $node->nid, $start);
667  while ($nid = db_fetch_object($query)) {
668    set_time_limit(30);
669    node_delete($nid->nid);
670  }
671}
672
673function jsdoc_doc_delete(&$node) {
674  $query = db_query("SELECT nid FROM {jsdoc_objects} WHERE did = %d", $node->nid);
675  while ($nid = db_fetch_object($query)) {
676    set_time_limit(30);
677    node_delete($nid->nid);
678  }
679}
680
681// Implemented node-type hook functions
682// ====================================
683
684/**
685 * Implementation of hook_access().
686 */
687function jsdoc_object_access($op, $node) {
688  if ($op == 'update') {
689    return user_access('edit jsdoc');
690  }
691  elseif ($op == 'view') {
692    return user_access('access content');
693  }
694
695  return false;
696}
697
698/**
699 * Implementation of hook_insert().
700 */
701function jsdoc_object_insert($node) {
702  db_query("INSERT INTO {jsdoc_objects} (nid, did, json) VALUES (%d, %d, '%s')", $node->nid, $node->version->nid, serialize($node->json));
703}
704
705/**
706 * Implementation of hook_update().
707 */
708function jsdoc_object_update($node) {
709  db_query("UPDATE {jsdoc_objects} SET json = '%s' WHERE nid = %d", serialize($node->json), $node->nid);
710}
711
712/**
713 * Implementation of hook_delete
714 */
715function jsdoc_object_delete(&$node) {
716  $type = 'jsdoc_' . $node->title;
717  $query = db_query("SELECT sid FROM {jsdoc_variables} WHERE variable = '%s'", $node->title);
718  if ($sid = db_fetch_object($query)) {
719    search_wipe($sid->sid, $type);
720  }
721  db_query("DELETE FROM {jsdoc_objects} WHERE nid = %d", $node->nid);
722}
723
724function _jsdoc_convert_object(&$node, $object, $parent = FALSE, $method = FALSE) {
725  $node->teaser = $object->summary;
726  $node->body = $object->description;
727
728  if (!$node->title && $object->name) {
729    $node->title = $object->name;
730  }
731
732  if (!$node->jsdoc_version) {
733    $node->jsdoc_version = $parent->jsdoc_version;
734  }
735
736  if ($method) {
737    $node->jsdoc_raw_type = 'Function';
738  }
739  else {
740    $node->jsdoc_raw_type = $object->type ? $object->type : 'Object';
741  }
742
743  $node->jsdoc_classlike = $object->classlike;
744
745  $node->jsdoc_formatted = _jsdoc_format_type($node->jsdoc_raw_type, $node->jsdoc_classlike);
746
747  $returndescription = 'return-description';
748  $node->jsdoc_return_summary = $object->$returndescription;
749
750  $returntypes = 'return-types';
751  $node->jsdoc_return_types = $object->$returntypes;
752
753  $node->jsdoc_examples = $object->examples;
754
755  $node->jsdoc_resources = $object->resources;
756
757  $node->jsdoc_provides = $object->provides;
758
759  $node->jsdoc_superclass = $object->superclass;
760
761  $node->jsdoc_mixins = $object->mixins;
762
763  if (!empty($object->tags)) {
764    $node->jsdoc_tags = array();
765    foreach (explode(' ', $object->tags) as $tag) {
766      $node->jsdoc_tags[$tag] = TRUE;
767    }
768  }
769
770  $node->jsdoc_private = $object->private;
771
772  $node->jsdoc_protected = $object->protected;
773
774  $node->jsdoc_deprecated = $object->deprecated;
775
776  $node->jsdoc_private_parent = $object->privateparent;
777
778  $node->jsdoc_scope = $object->scope ? $object->scope : 'normal';
779
780  if ($node->jsdoc_raw_type == 'Function') {
781    if (!$node->jsdoc_classlike) {
782      $parameters = $object->parameters;
783    }
784    else {
785      if (is_array($object->methods)) {
786        $constructors = array();
787        foreach ($object->methods as $method) {
788          if ($method->constructor) {
789            $constructors[$method->constructor] = $method;
790          }
791        }
792        foreach (array('constructor', 'preamble', 'postscript') as $constructor) {
793          if ($method = $constructors[$constructor]) {
794            $node->jsdoc_constructor = $method;
795            if (!empty($method->parameters)) {
796              $parameters = $method->parameters;
797              break;
798            }
799          }
800        }
801        if (!$node->jsdoc_constructor && !empty($constructors)) {
802          $node->jsdoc_constructor = reset($constructors);
803        }
804      }
805    }
806
807    $node->jsdoc_parameters = $parameters;
808  }
809
810  if (!empty($object->methods)) {
811    foreach ($object->methods as $method) {
812      if (!$method->constructor) {
813        $method->jsdoc_url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $node->title . '.' . $method->name;
814        $node->jsdoc_methods[] = $method;
815      }
816    }
817  }
818
819  if (!empty($object->properties)) {
820    foreach ($object->properties as $property) {
821      $property->jsdoc_url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $node->title . '.' . $property->name;
822      $node->jsdoc_properties[] = $property;
823    }
824  }
825
826  if ($object->jsdoc_url) {
827    $node->jsdoc_url = $object->jsdoc_url;
828  }
829  else {
830    $node->jsdoc_url = 'jsdoc/' . jsdoc_get_version($node)->title . '/' . $node->title;
831  }
832
833  unset($node->json);
834
835  return $node;
836}
837
838/**
839 * Implementation of hook_load().
840 */
841function jsdoc_object_load($node, $method = FALSE) {
842  // TODO: Deal with alias
843  _jsdoc_init();
844
845  $additions = db_fetch_object(db_query("SELECT json, did FROM {jsdoc_objects} WHERE nid = %d", $node->nid));
846  $additions->title = $node->title;
847  $additions->json = unserialize($additions->json);
848  $node->jsdoc_version = $additions->jsdoc_version = node_load($additions->did);
849
850  _jsdoc_convert_object($additions, $additions->json, $node, $method);
851
852  return $additions;
853}
854
855/**
856 * Implementation of hook_view().
857 */
858function jsdoc_object_view($node, $teaser = false, $page = false) {
859  $node->teaser = '';
860  $node->body = '';
861  if (!$teaser) {
862    $node->body = l($node->title, $node->jsdoc_url);
863  }
864  $node->title = t('Object');
865  $node->taxonomy = array();
866  $node = node_prepare($node, $teaser);
867  return $node;
868}
869
870/**
871 * Path: admin/settings/jsdoc
872 */
873function jsdoc_admin() {
874  $form = array();
875 
876  $formats = filter_formats();
877  $options = array();
878  foreach ($formats as $format) {
879    $options[$format->format] = $format->name;
880  }
881
882  $form['jsdoc_input_format'] = array(
883    '#title' => t('Default Input Format'),
884    '#type' => 'radios',
885    '#options' => $options,
886    '#required' => true,
887    '#default_value' => variable_get('jsdoc_input_format', 1)
888  );
889
890  return system_settings_form($form);
891}
892
893function jsdoc_get_version(&$node = FALSE) {
894  if (!$node) {
895    foreach (jsdoc_get_versions() as $version) {
896      if ($version->title != 'HEAD') {
897        return $version;
898      }
899    }
900  }
901  return $node->jsdoc_version;
902}
903
904function jsdoc_get_versions($node = FALSE, $title = NULL) {
905  static $cache = array();
906  $versions = array();
907
908  if ($node) {
909    if ($node->title) {
910      $cache_key = $node->title;
911      if ($cache[$cache_key]) {
912        return $cache[$cache_key];
913      }
914    }
915    $with_json = ($title) ? ', j.json' : '';
916    $query = db_query("SELECT j.did AS nid $with_json FROM {jsdoc_objects} j INNER JOIN {node} n ON (n.nid = j.nid) WHERE n.title = '%s' GROUP BY j.did", $node->title);
917  }
918  else {
919    $cache_key = '%ALL%';
920    if (isset($cache[$cache_key])) {
921      return $cache[$cache_key];
922    }
923    $query = db_query("SELECT nid FROM {node} WHERE type = 'jsdoc_doc' ORDER BY nid DESC");
924  }
925
926  while ($version = db_fetch_object($query)) {
927    if ($version->json && $node->title != $title) {
928      $parts = explode('.', $title);
929      if (count($parts) > 1) {
930        $last = array_pop($parts);
931        $parent = implode('.', $parts);
932        $found = FALSE;
933        $json = unserialize($version->json);
934        $properties = array();
935        if ($json->methods) {
936          $properties = array_merge($properties, $json->methods);
937        }
938        if ($json->properties) {
939          $properties = array_merge($properties, $json->properties);
940        }
941        foreach ($properties as $property) {
942          if ($property->scope == 'normal' && $property->name == $title) {
943            $found = TRUE;
944            break;
945          }
946          elseif ($property->name == $last) {
947            $found = TRUE;
948            break;
949          }
950        }
951        if (!$found) {
952          continue;
953        }
954      }
955    }
956    $versions[] = node_load($version->nid);
957  }
958
959  if ($cache_key) {
960    $cache[$cache_key] = $versions;
961  }
962
963  return $versions;
964}
965
966function jsdoc_get_return_summary(&$node) {
967  if (isset($node->jsdoc_return_summary_formatted)) {
968    return $node->jsdoc_return_summary_formatted;
969  }
970
971  return ($node->jsdoc_return_summary_formatted = trim(preg_replace('%(^<p>|</p>$)%', '', str_replace("\n", ' ', $object->jsdoc_return_summary))));
972}
973
974function jsdoc_get_return_types(&$node) {
975  if (empty($node->jsdoc_return_types)) {
976    return array();
977  }
978  return $node->jsdoc_return_types;
979}
980
981function jsdoc_get_parameters(&$node) {
982  if (isset($node->jsdoc_formatted_parameters)) {
983    return $node->jsdoc_formatted_parameters;
984  }
985
986  if (empty($node->jsdoc_parameters)) {
987    $node->jsdoc_parameters = array();
988  }
989
990  foreach ($node->jsdoc_parameters as $i => $parameter) {
991    $node->jsdoc_parameters[$i]->jsdoc_formatted = _jsdoc_format_type($parameter->type, false, $parameter->usage == 'optional', $parameter->usage == 'repeating' || $parameter->usage == 'one-or-more');
992  }
993
994  return ($node->jsdoc_formatted_parameters = $node->jsdoc_parameters);
995}
996
997function jsdoc_get_examples(&$node, $markedup=false) {
998  if (empty($node->jsdoc_examples)) {
999    return array();
1000  }
1001  if (isset($node->jsdoc_formatted_examples)) {
1002    return $node->jsdoc_formatted_examples;
1003  }
1004
1005  $examples = array();
1006  foreach ($node->jsdoc_examples as $example) {
1007    $examples[] = _jsdoc_markup_text($example, jsdoc_get_version($node), $node->title);
1008  }
1009
1010  return ($node->jsdoc_formatted_examples = $examples);
1011}
1012
1013function jsdoc_get_parent_prototype(&$node) {
1014  if ($node->jsdoc_superclass) {
1015    return jsdoc_object_node_load($node->jsdoc_superclass, jsdoc_get_version($node));
1016  }
1017}
1018
1019function jsdoc_is_initialized(&$node) {
1020  return false;
1021}
1022
1023function jsdoc_get_tags(&$node) {
1024  if (empty($node->jsdoc_tags)) {
1025    return array();
1026  }
1027  return $node->jsdoc_tags;
1028}
1029
1030function jsdoc_is_classlike(&$node) {
1031  return $node->jsdoc_classlike;
1032}
1033
1034function jsdoc_get_teaser(&$node) {
1035  if (isset($node->formatted_teaser)) {
1036    return $node->formatted_teaser;
1037  }
1038
1039  return ($node->formatted_teaser = $node->teaser);
1040}
1041
1042function jsdoc_get_body(&$node) {
1043  $version = jsdoc_get_version($node)->title;
1044  $body = trim($node->body);
1045  $variables = _jsdoc_get_variables();
1046  foreach (array_unique(preg_split('%\s+%', strip_tags($body))) as $variable) {
1047    if ($variables[$variable] && $variable != $node->title) {
1048      $body = str_replace($variable, l($variable, 'jsdoc/' . $version . '/' . $variable), $body);
1049    }
1050  }
1051  return $body;
1052}
1053
1054function jsdoc_is_function(&$node) {
1055  return (jsdoc_get_type($node) == 'Function' || jsdoc_get_type($node) == 'Constructor');
1056}
1057
1058function jsdoc_is_private(&$node) {
1059  return $node->jsdoc_private;
1060}
1061
1062function jsdoc_is_protected(&$node) {
1063  return $node->jsdoc_protected;
1064}
1065
1066function jsdoc_is_deprecated(&$node) {
1067  return $node->jsdoc_deprecated;
1068}
1069
1070function jsdoc_has_private_parent(&$node) {
1071  foreach (jsdoc_get_namespace_parents($node) as $parent) {
1072    if ($parent->jsdoc_private) {
1073      return true;
1074    }
1075  }
1076
1077  return $node->jsdoc_private_parent;
1078}
1079
1080function jsdoc_get_type(&$node) {
1081  if (isset($node->jsdoc_type)) {
1082    return $node->jsdoc_type;
1083  }
1084
1085  if (is_object($node->type)) {
1086    $type = $node->type->title;
1087  }
1088  else {
1089    $type = $node->jsdoc_raw_type;
1090    unset($node->jsdoc_raw_type);
1091  }
1092
1093  if ($type == 'Function') {
1094    if (jsdoc_is_classlike($node)) {
1095      $type = 'Constructor';
1096    }
1097    elseif (count(jsdoc_get_child_instances($node, true)) || count(jsdoc_get_child_prototypes($node, true))) {
1098      $type = 'Constructor';
1099    }
1100  }
1101
1102  return ($node->jsdoc_type = $type);
1103}
1104
1105function jsdoc_get_resource(&$node) {
1106  if (count($node->jsdoc_resources) == 1) {
1107    return $node->jsdoc_resources[0];
1108  }
1109}
1110
1111function jsdoc_get_source($node, $resolve=false, $depth=0) {
1112  return '';
1113}
1114
1115function jsdoc_get_provide(&$node) {
1116  if (count($node->jsdoc_provides) == 1) {
1117    return $node->jsdoc_provides[0];
1118  }
1119}
1120
1121function jsdoc_get_mixins(&$node, $type = NULL) {
1122  if (empty($node->jsdoc_mixins)) {
1123    return array();
1124  }
1125  if (isset($node->jsdoc_resolved_mixins)) {
1126    return $type ? $node->jsdoc_resolved_mixins[$type] : $node->jsdoc_resolved_mixins;
1127  }
1128
1129  $version = jsdoc_get_version($node);
1130  $resolved_mixins = array('prototype' => array(), 'instance' => array(), 'normal' => array());
1131  foreach ($node->jsdoc_mixins as $from => $mixins) {
1132    foreach ($mixins as $mixin) {
1133      $location = $mixin->location;
1134      if (substr($location, -10) == '.prototype') {
1135        $location = substr($location, 0, -10);
1136      }
1137      $mixin_node = jsdoc_object_node_load($location, $version);
1138      if ($mixin_node->nid) {
1139        $resolved_mixins[$from][] = (object)array(
1140          'attached' => $node,
1141          'scope' => $mixin->scope,
1142          'node' => $mixin_node
1143        );
1144      }
1145    }
1146  }
1147
1148  $node->jsdoc_resolved_mixins = $resolved_mixins;
1149
1150  return jsdoc_get_mixins($node, $type);
1151}
1152
1153function _jsdoc_get_attributes($attribute, &$node, $is_method, $scope) {
1154  $attribute = 'jsdoc_' . $attribute;
1155
1156  $items = array();
1157  if (!empty($node->$attribute)) {
1158    foreach ($node->$attribute as $item){
1159      $converted = (object)array(
1160        'title' => "{$node->title}.{$item->name}",
1161        'name' => $item->name
1162      );
1163      if ($scope) {
1164        $scopes = explode('-', $item->scope);
1165        if (in_array($scope, $scopes)) {
1166          $items[] = _jsdoc_convert_object($converted, $item, $node, $is_method);
1167        }
1168      }
1169      else {
1170        $items[] = _jsdoc_convert_object($converted, $item, $node, $is_method);
1171      }
1172    }
1173  }
1174
1175  if (!$scope || $scope == 'normal') {
1176    $query = db_query("SELECT n.nid, n.title FROM {node} AS n INNER JOIN {jsdoc_objects} AS j ON (j.nid = n.nid) WHERE n.title LIKE '%s.%%' AND j.did = %d", $node->title, jsdoc_get_version($node)->nid);
1177    while ($result = db_fetch_object($query)) {
1178      $parts = explode('.', $result->title);
1179      $name = array_pop($parts);
1180      if (implode('.', $parts) == $node->title) {
1181        $item = node_load($result->nid);
1182        $item->name = $name;
1183        if ($is_method) {
1184          if ($item->jdsoc_raw_type == 'Function') {
1185            $items[] = $item;
1186          }
1187        }
1188        else {
1189          $items[] = $item;
1190        }
1191      }
1192    }
1193  }
1194
1195  return $items;
1196}
1197
1198function jsdoc_get_methods(&$node, $scope = NULL) {
1199  return _jsdoc_get_attributes('methods', $node, TRUE, $scope);
1200}
1201
1202function jsdoc_get_properties(&$node, $scope = NULL) {
1203  return _jsdoc_get_attributes('properties', $node, FALSE, $scope);
1204}
1205
1206function jsdoc_get_child_instances() {
1207  return array();
1208}
1209
1210function jsdoc_get_child_prototypes() {
1211  return array();
1212}
1213
1214function jsdoc_is_namespace(&$node) {
1215  return !!$node->nid && $node->jsdoc_raw_type != 'Function';
1216}
1217
1218
1219// Node Load Functions
1220// ===================
1221
1222/**
1223 * Custom version of node_load for versions
1224 */
1225function jsdoc_version_node_load($version_name) {
1226  return node_load(array(
1227    'type' => 'jsdoc_doc',
1228    'title' => $version_name
1229  ));
1230}
1231
1232function jsdoc_object_node_load($name, $version=false, $exact=true, $broad=false) {
1233  $nodes = jsdoc_object_nodes_load($name, $version, $exact, $broad);
1234  if (count($nodes) == 1) {
1235    return $nodes[0];
1236  }
1237  elseif (!empty($nodes)) {
1238    $version = jsdoc_get_version();
1239    foreach($nodes as $match) {
1240      if ($match->version->nid == $version->nid) {
1241        return $match;
1242      }
1243    }
1244  }
1245  return (object)array();
1246}
1247
1248/**
1249 * Custom version of node_load for objects
1250 */
1251function jsdoc_object_nodes_load($name, $version=false, $exact=true, $broad=false) {
1252  static $cache = array();
1253
1254  $lower_name = strtolower($name);
1255
1256  $override_name = false;
1257  $name_override = $name;
1258
1259  if ($lower_name == 'bool') {
1260    $name = 'Boolean';
1261  }
1262  elseif ($lower_name == 'int' || $lower_name == 'integer') {
1263    $override_name = true;
1264    $name_override = 'Integer';
1265    $name = 'Number';
1266  }
1267  elseif ($lower_name == 'float' || $lower_name == 'decimal') {
1268    $override_name = true;
1269    $name_override = ucfirst($lower_name);
1270    $name = 'Number';
1271  }
1272
1273  $global_vars = array('Array', 'Boolean', 'Date', 'Error', 'Function', 'Number', 'Object', 'RegExp', 'String');
1274  $lower_global_vars = array('array', 'boolean', 'date', 'error', 'function', 'number', 'object', 'regexp', 'string');
1275  if (($pos = array_search($lower_name, $lower_global_vars)) !== false) {
1276    $name = $global_vars[$pos];
1277  }
1278
1279  if (in_array($name, $global_vars)) {
1280    return array((object)array(
1281      'title' => ($override_name) ? $name_override : $name,
1282      'jsdoc_url' => 'http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:' . $name
1283    ));
1284  }
1285  elseif ($lower_name == 'window') {
1286    return array((object)array(
1287      'title' => $name,
1288      'jsdoc_url' => 'http://developer.mozilla.org/en/docs/DOM:window'
1289    ));
1290  }
1291  elseif ($lower_name == 'documentelement' || $lower_name == 'document') {
1292    return array((object)array(
1293      'title' => $name,
1294      'jsdoc_url' => 'http://developer.mozilla.org/en/docs/DOM:document'
1295    ));
1296  }
1297  elseif (in_array($lower_name, array('node', 'htmlelment', 'domnode'))) {
1298    return array((object)array(
1299      'title' => $name,
1300      'jsdoc_url' => 'http://developer.mozilla.org/en/docs/DOM:element'
1301    ));
1302  }
1303  elseif ($name == 'Constructor') {
1304    return array((object)array(
1305      'title' => $name,
1306      'jsdoc_url' => 'http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Class-Based_vs._Prototype-Based_Languages#Defining_a_Class'
1307    ));
1308  }
1309
1310  // Resolve the version the best that we can
1311  if (is_object($version) && $version->nid) {
1312    $version_nid = $version->nid;
1313  }
1314  elseif (is_string($version)) {
1315    $version_nid = jsdoc_version_node_load($version)->nid;
1316  }
1317  elseif (is_numeric($version)) {
1318    $version_nid = $version;
1319  }
1320
1321  if ($version_nid && (!is_object($version) || !$version->nid)) {
1322    $version = node_load($version_nid);
1323  }
1324
1325  if ($name) {
1326    $cache_key = sprintf('%s,%s,%s,%s', $name, $version_nid, $exact, $broad);
1327    if (isset($cache[$cache_key])) {
1328      $nodes = array();
1329      foreach ($cache[$cache_key] as $nid) {
1330        $nodes[] = node_load($nid);
1331      }
1332      if (empty($nodes)) {
1333        if ($broad) {
1334          return array((object)array(
1335            'title' => $name
1336          ));
1337        }
1338        else {
1339          return array();
1340        }
1341      }
1342      return $nodes;
1343    }
1344
1345    // We have a few situations:
1346    //    name
1347    //    project, name
1348    //    project, version, name
1349    //    project, version, resource, name
1350
1351    // If we're missing the version, assume the latest release
1352    // If we're missing the project, look up stuff by name
1353
1354    $binary = ($exact) ? 'BINARY' : '';
1355    $title_check = ($exact) ? "= '%s'" : "LIKE '%%%s%%'";
1356    $nodes = array();
1357    $results = array();
1358
1359    if ($version_nid) {
1360      $query = db_query("SELECT n.nid FROM {node} n INNER JOIN {jsdoc_objects} j ON (j.nid = n.nid) WHERE n.title $title_check AND $binary n.title $title_check AND j.did = %d", $name, $name, $version_nid);
1361    }
1362    else {
1363      $version_nids = array(-1);
1364      foreach (jsdoc_get_versions() as $version) {
1365        $version_nids[] = $version->nid;
1366      }
1367      $query = db_query("SELECT n.nid, j.did, version.title AS version FROM {node} n INNER JOIN {jsdoc_objects} j ON (j.nid = n.nid) INNER JOIN {node} version ON (version.nid = j.did) WHERE n.title $title_check AND $binary n.title $title_check AND j.did IN (%s) ORDER BY version.created DESC", $name, $name, implode(',', $version_nids));
1368    }
1369
1370    while ($result = db_fetch_object($query)) {
1371      if ($result->version) {
1372        if ($result->version != 'HEAD') {
1373          $nodes[] = node_load($result->nid);
1374          break;
1375        }
1376      }
1377      else {
1378        $nodes[] = node_load($result->nid);
1379      }
1380    }
1381
1382    $cache[$cache_key] = array_map('_jsdoc_extract_nids', $nodes);
1383  }
1384
1385  if (empty($nodes)) {
1386    if ($broad) {
1387      return array((object)array(
1388        'title' => $name
1389      ));
1390    }
1391    else {
1392      return array();
1393    }
1394  }
1395
1396  return $nodes;
1397}
1398
1399function _jsdoc_extract_nids($node) {
1400  return $node->nid;
1401}
1402
1403function jsdoc_parameter_weight_sort($a, $b){
1404  return ($a['weight'] == $b['weight']) ? 0 : ($a['weight'] < $b['weight']) ? -1 : 1;
1405}
1406
1407/**
1408 * Path: jsdoc/404
1409 */
1410function jsdoc_404() {
1411  $args = explode('/', $_REQUEST['destination']);
1412  if (count($args) == 1 && $args[0] != 'jsdoc') {
1413    global $_menu;
1414    unset($_menu['items']);
1415    menu_set_active_item('jsdoc/' . $args[0]);
1416    print theme('page', menu_execute_active_handler());
1417    exit();
1418  }
1419
1420  return '<p>' . t('The page you requested was not found.') . '</p>';
1421}
1422
1423/**
1424 * Path: jsdoc/feedback
1425 */
1426function jsdoc_feedback() {
1427  $count = 0;
1428  if ($_POST['page'] && $_POST['feedback']) {
1429    $feedback = array();
1430    if ($cached = cache_get('jsdoc_feedback')) {
1431      $feedback = unserialize($cached->data);
1432    }
1433
1434    if (!isset($feedback[$_POST['page']][$_POST['feedback']])) {
1435      $feedback[$_POST['page']][$_POST['feedback']] = 0;
1436    }
1437
1438    $count = ++$feedback[$_POST['page']][$_POST['feedback']];
1439
1440    cache_set('jsdoc_feedback', 'cache', serialize($feedback));
1441  }
1442
1443  print $count;
1444}
1445
1446function jsdoc_feedback_review() {
1447  drupal_set_title(t('Feedback Review'));
1448
1449  $feedback = array();
1450  if ($cached = cache_get('jsdoc_feedback')) {
1451    $feedback = unserialize($cached->data);
1452  }
1453
1454  $by_count = array();
1455  foreach ($feedback as $page => $stats) {
1456    $total = 0;
1457    foreach ($stats as $type => $count) {
1458      $total += $count;
1459    }
1460    $by_count[$total][] = array(
1461      'page' => $page,
1462      'feedback' => $stats
1463    );
1464  }
1465
1466  krsort($by_count);
1467
1468  $output = '<ul>';
1469  foreach ($by_count as $pages) {
1470    foreach ($pages as $page) {
1471      $output .= '<li>' . l($page['page'], sprintf('jsdoc/%s', $page['page'])) . '<ul>';
1472      foreach ($page['feedback'] as $type => $count) {
1473        $output .= '<li>' . $type . ': ' . $count . '</li>';
1474      }
1475      $output .= '</ul></li>';
1476    }
1477  }
1478  $output .= '</ul>';
1479
1480  return $output;
1481}
1482
1483/**
1484 * Path: jsdoc/jsonp
1485 */
1486function jsdoc_jsonp($batch=false, $json=false) {
1487  $recursion = false;
1488  if ($json) {
1489    $recursion = true;
1490    $_JSON = $json;
1491  }
1492  else {
1493    $_JSON = array();
1494    foreach (explode('&', getenv('QUERY_STRING')) as $query_string) {
1495      list($key, $value) = explode('=', $query_string, 2);
1496      $value = urldecode($value);
1497      if (isset($_JSON[$key])) {
1498        if (!is_array($_JSON[$key])) {
1499          $_JSON[$key] = array($_JSON[$key]);
1500        }
1501        $_JSON[$key][] = $value;
1502      }
1503      else {
1504        $_JSON[$key] = $value;
1505      }
1506    }
1507  }
1508
1509  $names = array();
1510  if ($batch) {
1511    $names = (is_array($_JSON['names'])) ? $_JSON['names'] : array($_JSON['names']);
1512  }
1513  else {
1514    $names = array($_JSON['name']);
1515  }
1516
1517  $attributes = ($_JSON['attributes']) ? $_JSON['attributes'] : array('summary', 'type', 'returns', 'parameters');
1518  if (!is_array($attributes)) {
1519    $attributes = array($attributes);
1520  }
1521
1522  foreach ($names as $name) {
1523    if (empty($name)) {
1524      continue;
1525    }
1526    if ($_JSON['exact']) {
1527      if ($nodes = jsdoc_object_node_load($name, $_JSON['version'], true)){
1528        $nodes = array($nodes);
1529      }
1530    }
1531    else {
1532      $nodes = jsdoc_object_nodes_load($name, $_JSON['version'], false);
1533    }
1534    foreach ($nodes as $node) {
1535      $formatted = (object)array('name' => $name);
1536      foreach ($attributes as $attribute) {
1537        switch ($attribute) {
1538          case 'summary':
1539            $formatted->summary = jsdoc_get_teaser($node);
1540            break;
1541          case 'type':
1542            $formatted->type = jsdoc_get_type($node);
1543            break;
1544          case 'returns':
1545            $formatted->returns = array(
1546              'types' => array(),
1547              'summary' => jsdoc_get_return_summary($node)
1548            );
1549            foreach (jsdoc_get_return_types($node) as $type) {
1550              $themed = _jsdoc_get_type_themed($type->type, $node, true);
1551              if ($themed) {
1552                $formatted->returns['types'][] = $themed;
1553              }
1554              else {
1555                $formatted->returns['types'][] = array(
1556                  'title' => $type
1557                );
1558              }
1559            }
1560          case 'parameters':
1561            $formatted->parameters = _jsdoc_get_parameters_themed($node, true);
1562            if (!count($formatted->parameters)) {
1563              unset($formatted->parameters);
1564            }
1565            break;
1566        }
1567      }
1568      if ($_JSON['recursive']) {
1569        $children = array_keys(jsdoc_get_child_variables($node));
1570        if (!empty($children))
1571          $formatted->children = jsdoc_jsonp(true, array(
1572            'attributes' => $_JSON['attributes'],
1573            'names' => $children,
1574            'recursive' => $_JSON['recursive']
1575          ));
1576      }
1577      $output[] = $formatted;
1578    }
1579  }
1580
1581  $found = array();
1582  foreach ($output as $object) {
1583    $found[] = $object->name;
1584  }
1585  foreach (array_diff($names, $found) as $name) {
1586    $output[] = (object)array(
1587      'name' => $name
1588    );
1589  }
1590
1591  if ($recursion) {
1592    return $output;
1593  }
1594  print $_GET['callback'] . '(' . str_replace('"__className":"stdClass",', '', json_encode($output)) . ');';
1595}
1596
1597function jsdoc_get_closest($name, $version) {
1598  $node = jsdoc_object_node_load($name, $version);
1599  if (!$node->nid) {
1600    $parts = explode('.', $name);
1601    while (!$node->nid) {
1602      array_pop($parts);
1603      if (empty($parts)) {
1604        break;
1605      }
1606      $node = jsdoc_object_node_load(implode('.', $parts), $version);
1607    }
1608  }
1609  return $node;
1610}
1611
1612function _jsdoc_resolve_child($node, $name) {
1613  list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($node, $name);
1614
1615  $actual = (object)array(
1616    'title' => $name,
1617    'jsdoc_version' => jsdoc_get_version($node)
1618  );
1619
1620  foreach ($children as $possible) {
1621    if ($possible->title == $name || $node->title . '.' . $possible->title == $name) {
1622      $actual = $possible->node;
1623      $actual->type = $possible->type;
1624      unset($actual->jsdoc_type);
1625      break;
1626    }
1627  }
1628
1629  $actual->format = $node->format;
1630  $actual->title = $name;
1631
1632  if (empty($actual->teaser) && empty($actual->body)) {
1633    $actual->teaser = '&nbsp;';
1634    $actual->body = '&nbsp;';
1635  }
1636
1637  return $actual;
1638}
1639
1640/**
1641 * A menu callback
1642 * Will only ever be a function or property
1643 */
1644function jsdoc_child_node_view($node, $name) {
1645  $actual = _jsdoc_resolve_child($node, $name);
1646
1647  drupal_set_title(theme('jsdoc_object_title', _jsdoc_get_title_themed($actual)));
1648
1649  $form = _jsdoc_node_prepare($actual);
1650
1651  if ($provide = jsdoc_get_provide($actual)) {
1652    $form['require'] = array(
1653      '#value' => theme('jsdoc_object_require', $provide),
1654      '#weight' => -20
1655    );
1656  }
1657
1658  if ($resource = jsdoc_get_resource($actual)) {
1659    $form['resource'] = array(
1660      '#value' => theme('jsdoc_object_resource', jsdoc_get_resource($node))
1661    );
1662  }
1663
1664  $examples = array();
1665  foreach (jsdoc_get_examples($actual, true) as $weight => $example) {
1666    $examples[] = theme('jsdoc_object_example', $example, $weight + 1);
1667  }
1668  if (!empty($examples)) {
1669    $form['examples'] = array(
1670      '#value' => theme('jsdoc_object_examples', $examples),
1671      '#weight' => 35
1672    );
1673  }
1674
1675  if (jsdoc_is_function($actual)) {
1676    $parameters = _jsdoc_get_parameters_themed($actual);
1677
1678    if (count($parameters)) {
1679      $form['parameters'] = array(
1680        '#value' => theme('jsdoc_function_parameters', $parameters, _jsdoc_get_object_themed($actual, $actual->title)),
1681        '#weight' => 20
1682      );
1683    }
1684
1685    $function = (object)array(
1686      'source' => jsdoc_get_source($actual, true),
1687      'markedup' => _jsdoc_markup_code(jsdoc_get_source($actual, true), jsdoc_get_version($node)->nid, $actual->title, FALSE),
1688      'signature' => _jsdoc_build_function_signature($actual, $parameters)
1689    );
1690    if (count($parameters)) {
1691      $function->parameters = $parameters;
1692    }
1693
1694    $form['function'] = array(
1695      '#value' => theme('jsdoc_function_information', $function, $children, _jsdoc_get_object_themed($actual, $actual->title)),
1696      '#weight' => 30
1697    );
1698  }
1699  elseif (($object = jsdoc_object_node_load(jsdoc_get_type($actual), jsdoc_get_version($node))) && $object->nid) {
1700    list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($object);
1701
1702    $theme_children = array();
1703    foreach ($children as $child) {
1704      if ($child->instance || $child->prototype) {
1705        $theme_children[] = $child;
1706      }
1707    }
1708
1709    if (count($theme_children)) {
1710      $form['children'] = array(
1711        '#value' => theme('jsdoc_object_children', $theme_children),
1712        '#weight' => 40
1713      );
1714    }
1715  }
1716
1717  return drupal_render_form('jsdoc_child_form', $form);
1718}
1719
1720/**
1721 * Climbs up the prototype chain through mixins
1722 *
1723 * Move down the prototype chain
1724 * The prototype chains of the parents have already been calculated
1725 * For each one, inject its resolved mixins above the current class
1726 */
1727function _jsdoc_resolve_mixins($node, $from) {
1728  $resolved_mixins = array();
1729
1730  $all_mixins = array();
1731  if ($from == 'prototype') {
1732    $prototype_chain = jsdoc_get_prototype_chain($node);
1733    foreach ($prototype_chain as $parent) {
1734      if (!$parent->nid) { continue; }
1735      $all_mixins[] = $parent;
1736    }
1737  }
1738  $all_mixins[] = $node;
1739
1740  foreach ($all_mixins as $parent) {
1741    foreach (jsdoc_get_mixins($parent, $from) as $mixin) {
1742      foreach (_jsdoc_resolve_mixins($mixin->node, $from) as $other_mixin) {
1743        if (!$other_mixin->node->nid) { continue; }
1744        $resolved_mixins[] = $other_mixin;
1745      }
1746      $resolved_mixins[] = $mixin;
1747    }
1748    $resolved_mixins[] = (object)array(
1749      'attached' => $node,
1750      'scope' => 'prototype',
1751      'node' => $parent
1752    );
1753  }
1754
1755  for ($i = 0; $i < count($resolved_mixins); $i++) {
1756    for ($j = $i + 1; $j < count($resolved_mixins); $j++) {
1757      if ($resolved_mixins[$i]->node->title == $resolved_mixins[$j]->node->title) {
1758        array_splice($resolved_mixins, $i, 1);
1759        continue 2;
1760      }
1761    }
1762  }
1763
1764  return $resolved_mixins;
1765}
1766
1767function jsdoc_get_oo_info(&$node, $child=NULL) {
1768  if (isset($node->jsdoc_oo_info)) {
1769    return $node->jsdoc_oo_info;
1770  }
1771
1772  $returned_mixin_chain = array();
1773  $mixin_chain = array();
1774
1775  if (jsdoc_is_classlike($node)) {
1776    $prototype_chain = jsdoc_get_prototype_chain($node);
1777
1778    // Start out with simply the prototype chain
1779    $prototype_chain_values = array();
1780    foreach ($prototype_chain as $prototype) {
1781      if ($prototype->nid) {
1782        $prototype_chain_values[] = $prototype->title;
1783      }
1784    }
1785
1786    // Resolve prototype-level items
1787    foreach (_jsdoc_resolve_mixins($node, 'prototype') as $mixin) {
1788      $mixin_chain[] = (object)array(
1789        'attached' => $mixin->attached,
1790        'scope' => 'prototype',
1791        'from' => $mixin->scope,
1792        'methods' => jsdoc_get_methods($mixin->node, $mixin->scope),
1793        'properties' => jsdoc_get_properties($mixin->node, $mixin->scope),
1794        'in_prototype_chain' => in_array($mixin->node->title, $prototype_chain_values),
1795        'node' => $mixin->node
1796      );
1797    }
1798
1799    $returned_mixin_chain = array();
1800    $returned_mixin_titles = array();
1801    foreach ($mixin_chain as $mixin) {
1802      if ($mixin->in_prototype_chain || $mixin->attached->title == $node->title) {
1803        if ($mixin->node->title != $node->title && !$returned_mixin_titles[$mixin->node->title]) {
1804          $returned_mixin_titles[$mixin->node->title] = TRUE;
1805          $returned_mixin_chain[] = $mixin;
1806        }
1807      }
1808    }
1809
1810    $mixin_chain[] = (object)array(
1811      'scope' => 'prototype',
1812      'from' => 'prototype',
1813      'methods' => jsdoc_get_methods($node, 'prototype'),
1814      'properties' => jsdoc_get_properties($node, 'prototype'),
1815      'in_prototype_chain' => true,
1816      'node' => $node
1817    );
1818
1819    // Now with instance-level items
1820
1821    foreach (_jsdoc_resolve_mixins($node, 'instance') as $mixin) {
1822      $mixin_chain[] = (object)array(
1823        'scope' => 'instance',
1824        'from' => $mixin->scope,
1825        'methods' => jsdoc_get_methods($mixin->node, $mixin->scope),
1826        'properties' => jsdoc_get_properties($mixin->node, $mixin->scope),
1827        'in_prototype_chain' => in_array($mixin->node->title, $prototype_chain_values),
1828        'node' => $mixin->node
1829      );
1830    }
1831
1832    $mixin_chain[] = (object)array(
1833      'scope' => 'instance',
1834      'from' => 'instance',
1835      'methods' => jsdoc_get_methods($node, 'instance'),
1836      'properties' => jsdoc_get_properties($node, 'instance'),
1837      'in_prototype_chain' => false,
1838      'node' => $node
1839    );
1840
1841    array_pop($prototype_chain);
1842  }
1843
1844  $mixin_chain[] = (object)array(
1845    'scope' => 'normal',
1846    'from' => 'instance',
1847    'methods' => jsdoc_get_methods($node, 'normal'),
1848    'properties' => jsdoc_get_properties($node, 'normal'),
1849    'in_prototype_chain' => false,
1850    'node' => $node
1851  );
1852
1853  $child_attribute = false;
1854  if ($child) {
1855    list($child_attribute) = array_slice(explode('.', $child), -1);
1856  }
1857  $children = array();
1858  foreach ($mixin_chain as $mixin) {
1859    foreach (array('methods', 'properties') as $child_type) {
1860      foreach ($mixin->$child_type as $item) {
1861        list($item_attribute) = array_slice(explode('.', $item->title), -1);
1862
1863        if ($child_attribute && $item_attribute != $child_attribute) {
1864          continue;
1865        }
1866
1867        if ($mixin->scope == 'normal') {
1868          $title = $item->title;
1869        }
1870        else {
1871          $title = $item_attribute;
1872        }
1873
1874        if (jsdoc_get_type($item) == 'Constructor' && ($resolved = jsdoc_resolve_constructor($item))) {
1875          $item->jsdoc_parameters = $resolved->jsdoc_parameters;
1876          $item->jsdoc_return_summary = $resolved->jsdoc_return_summary;
1877          $item->jsdoc_return_types = $resolved->jsdoc_return_types;
1878          $item->teaser = $resolved->teaser;
1879        }
1880
1881        if ($children[$title]) {
1882          $found = false;
1883          if ($children[$title]->inheritance) {
1884            foreach ($children[$title]->inheritance as $parent) {
1885              if ($parent->title == $mixin->node->title) {
1886                $found = true;
1887                break;
1888              }
1889            }
1890          }
1891          if (!$found) {
1892            if ($mixin->node->title == $node->title) {
1893              $children[$title]->override = true;
1894            }
1895            else {
1896              $children[$title]->inheritance[] = _jsdoc_get_object_themed($mixin->node);
1897            }
1898            $inherited = _jsdoc_get_object_themed($item, $title);
1899            foreach ($inherited as $key => $value) {
1900              if ($key == 'summary' && trim($value) == '') {
1901                continue;
1902              }
1903              $children[$title]->$key = $value;
1904            }
1905          }
1906        }
1907        else {
1908          $children[$title] = _jsdoc_get_object_themed($item, $title);
1909          $children[$title]->node = $item;
1910          if ($mixin->node->title != $node->title && ($mixin->scope != 'normal' || implode('.', array_slice(explode('.', $title), 0, -1)) != $node->title)) {
1911            $children[$title]->inheritance[] = _jsdoc_get_object_themed($mixin->node);
1912          }
1913        }
1914
1915        $scope = $mixin->scope;
1916        $children[$title]->$scope = TRUE;
1917
1918        if ($title == $item->title) {
1919          $children[$title]->actual_title = $title;
1920        }
1921        else {
1922          $children[$title]->actual_title = $mixin->node->title . '.' . $title;
1923        }
1924
1925        $type = $item->jsdoc_scope;
1926        $children[$title]->$type = true;
1927
1928        if (trim(jsdoc_get_teaser($item)) != '') {
1929          $children[$title]->summary = jsdoc_get_teaser($item);
1930        }
1931      }
1932    }
1933  }
1934  uksort($children, "strnatcasecmp");
1935
1936  return ($node->jsdoc_oo_info = array($prototype_chain, $returned_mixin_chain, $children));
1937}
1938
1939/**
1940 * A menu callback
1941 * Will only ever be a constructor or namespace
1942 */
1943function jsdoc_object_node_view($node) {
1944  ini_set('memory_limit', '128M');
1945  // Test for the basics
1946  if (!$node) {
1947    return drupal_not_found();
1948  }
1949
1950  drupal_set_title(theme('jsdoc_object_title', _jsdoc_get_title_themed($node)));
1951
1952  if (empty($node->teaser) && empty($node->body)) {
1953    $node->teaser = '&nbsp;';
1954    $node->body = '&nbsp;';
1955  }
1956
1957  $form = _jsdoc_node_prepare($node);
1958
1959  if ($provide = jsdoc_get_provide($node)) {
1960    $form['require'] = array(
1961      '#value' => theme('jsdoc_object_require', $provide),
1962      '#weight' => -20
1963    );
1964  }
1965
1966  if ($resource = jsdoc_get_resource($node)) {
1967    $form['resource'] = array(
1968      '#value' => theme('jsdoc_object_resource', jsdoc_get_resource($node))
1969    );
1970  }
1971
1972  $examples = array();
1973  foreach (jsdoc_get_examples($node, true) as $weight => $example) {
1974    $examples[] = theme('jsdoc_object_example', $example, $weight + 1);
1975  }
1976  if (!empty($examples)) {
1977    $form['examples'] = array(
1978      '#value' => theme('jsdoc_object_examples', $examples),
1979      '#weight' => 35
1980    );
1981  }
1982
1983  list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($node);
1984
1985  if (jsdoc_is_classlike($node)) {
1986    $form['prototype_chain'] = array(
1987      '#value' => theme('jsdoc_object_prototype_chain', $prototype_chain),
1988      '#weight' => 10
1989    );
1990  }
1991
1992  if (!empty($mixin_chain)) {
1993    $form['parent_mixins'] = array(
1994      '#value' => theme('jsdoc_object_mixins', $mixin_chain),
1995      '#weight' => 40
1996    );
1997  }
1998
1999  if (jsdoc_is_function($node)) {
2000    $actual = $node;
2001    if (jsdoc_is_classlike($node)) {
2002      $actual = jsdoc_resolve_constructor($node);
2003    }
2004
2005    $parameters = _jsdoc_get_parameters_themed($actual);
2006
2007    if (count($parameters)) {
2008      $form['parameters'] = array(
2009        '#value' => theme('jsdoc_function_parameters', $parameters, _jsdoc_get_object_themed($node, $node->title)),
2010        '#weight' => 20
2011      );
2012    }
2013
2014    $base = false;
2015    if (jsdoc_get_type($node) == 'Constructor') {
2016      $base = $node->title;
2017    }
2018
2019    $function = (object)array(
2020      'source' => jsdoc_get_source($actual, true),
2021      'markedup' => _jsdoc_markup_code(jsdoc_get_source($actual, true), jsdoc_get_version($node)->nid, $actual->title, $base),
2022      'signature' => _jsdoc_build_function_signature($actual, $parameters)
2023    );
2024    if (count($parameters)) {
2025      $function->parameters = $parameters;
2026    }
2027
2028    $form['function'] = array(
2029      '#value' => theme('jsdoc_function_information', $function, $children, _jsdoc_get_object_themed($node, $node->title)),
2030      '#weight' => 30
2031    );
2032  }
2033
2034  if (count($children)) {
2035    $form['children'] = array(
2036      '#value' => theme('jsdoc_object_children', $children),
2037      '#weight' => 40
2038    );
2039  }
2040
2041  if (function_exists('comment_render') && $node->comment) {
2042    $form['comments'] = array(
2043      '#value' => comment_render($node),
2044      '#weight' => 50
2045    );
2046  }
2047
2048  return drupal_render_form('jsdoc_object_form', $form);
2049}
2050
2051function _jsdoc_get_title_themed($node) {
2052  $themed = _jsdoc_get_object_themed($node);
2053  $themed->crumbs = array();
2054
2055  if ($type = jsdoc_get_type($node)) {
2056    $type = jsdoc_object_node_load($type, jsdoc_get_version($node));
2057    $themed->type = (object)array(
2058      'title' => $type->title,
2059      'url' => $type->jsdoc_url
2060    );
2061    if ($type->jsdoc_url) {
2062      $themed->type->a = l($type->title, $type->jsdoc_url);
2063    }
2064    else {
2065      $themed->type->a = $type->title;
2066    }
2067  }
2068
2069  $themed->return = (object)array(
2070    'types' => array(),
2071    'summary' => jsdoc_get_return_summary($node)
2072  );
2073  foreach (jsdoc_get_return_types($node) as $type) {
2074    $themed->return->types[] = _jsdoc_get_type_themed($type->type, $node, false);
2075  }
2076
2077  $parts = explode('.', $node->title);
2078  $title_text = "";
2079  $end = array_pop($parts);
2080  foreach ($parts as $part) {
2081    if (!empty($title_text)) {
2082      $title_text .= '.';
2083    }
2084    $title_text .= $part;
2085
2086    $obj = jsdoc_object_node_load($title_text, jsdoc_get_version($node));
2087    if ($obj->nid) {
2088      $themed->crumbs[] = l($part, $obj->jsdoc_url);
2089    }
2090    else {
2091      $themed->crumbs[] = $part;
2092    }
2093  }
2094  $themed->crumbs[] = $end;
2095
2096  drupal_set_breadcrumb($themed->crumbs);
2097
2098  return $themed; 
2099}
2100
2101function _jsdoc_get_object_themed(&$node, $title=null, $depth=0) {
2102  if (is_null($title)) {
2103    $title = $node->title;
2104  }
2105  if (isset($node->jsdoc_object_themed)) {
2106    return $node->jsdoc_object_themed;
2107  }
2108
2109  $object = $node->jsdoc_object_themed = (object)array(
2110    'title' => $title,
2111    'url' => $node->jsdoc_url,
2112    'tags' => jsdoc_get_tags($node),
2113    'summary' => jsdoc_get_teaser($node),
2114    'singleton' => jsdoc_is_initialized($node),
2115    'namespace' => jsdoc_is_namespace($node) && !jsdoc_is_classlike($node),
2116    'private' => jsdoc_is_private($node),
2117    'protected' => jsdoc_is_protected($node),
2118    'deprecated' => jsdoc_is_deprecated($node),
2119    'private_parent' => jsdoc_has_private_parent($node)
2120  );
2121
2122  if ($object->url) {
2123    $object->a = l($node->title, $node->jsdoc_url);
2124  }
2125  else {
2126    $object->a = $object->title;
2127  }
2128
2129  if ($type = jsdoc_get_type($node)) {
2130    $optional = false;
2131    if (strpos($type, '?') !== false){
2132      $optional = true;
2133      $type = str_replace('?', '', $type);
2134    }
2135    $repeating = false;
2136    if (strpos($type, '...') !== false){
2137      $repeating = true;
2138      $type = str_replace('...', '', $type);
2139    }
2140
2141    $type = jsdoc_object_node_load($type, jsdoc_get_version($node));
2142
2143    $object->type = (object)array(
2144      'title' => $type->title,
2145      'url' => $type->jsdoc_url,
2146      'optional' => $optional,
2147      'repeating' => $repeating
2148    );
2149
2150    if ($type->jsdoc_url) {
2151      $object->type->a = l($type->title, $type->jsdoc_url);
2152    }
2153    else {
2154      $object->type->a = $type->title;
2155    }
2156
2157    if (jsdoc_is_function($node)) {
2158      $parameters = _jsdoc_get_parameters_themed($node, false, $depth);
2159      if (!empty($parameters)) {
2160        $object->parameters = $parameters;
2161      }
2162
2163      $object->return = (object)array(
2164        'types' => array(),
2165        'summary' => jsdoc_get_return_summary($node)
2166      );
2167      foreach (jsdoc_get_return_types($node) as $type) {
2168        $object->return->types[] = _jsdoc_get_type_themed($type->type, $node, false);
2169      }
2170
2171      $signature = _jsdoc_build_function_signature($node, $parameters);
2172      if (!empty($signature)) {
2173        $object->signature = $signature;
2174      }
2175
2176      $source = jsdoc_get_source($node, true, $depth);
2177      if (!empty($source)) {
2178        $object->source = $source;
2179      }
2180    }
2181  }
2182
2183  return ($node->jsdoc_object_themed = $object);
2184}
2185
2186function jsdoc_resolve_constructor(&$node) {
2187  if ($node->jsdoc_resolved_constructor) {
2188    return $node->jsdoc_resolved_constructor;
2189  }
2190
2191  if ($node->jsdoc_constructor) {
2192    $constructor = (object)array();
2193    _jsdoc_convert_object($constructor, $node->jsdoc_constructor, $node, TRUE);
2194    return ($node->jsdoc_resolved_constructor = $constructor);
2195  }
2196
2197  $superclass = jsdoc_get_parent_prototype($node);
2198  while ($superclass) {
2199    $constructor = (object)array();
2200    _jsdoc_convert_object($constructor, $superclass->jsdoc_constructor, $node, TRUE);
2201    if ($constructor->jsdoc_parameters) {
2202      return ($node->jsdoc_resolved_constructor = $constructor);
2203    }
2204    $superclass = jsdoc_get_parent_prototype($superclass);
2205  }
2206
2207  return ($node->jsdoc_resolved_constructor = $node->jsdoc_constructor);
2208}
2209
2210function _jsdoc_get_fields($type, &$node) {
2211  // I basically want to find the children of this as
2212  // if it appeared in the normal API.
2213  static $cache = array();
2214  $version = jsdoc_get_version($node);
2215  if (isset($cache[$version->title][$type])) {
2216    return $cache[$version->title][$type];
2217  }
2218
2219  $fields = $cache[$version->title][$type] = array();
2220  $obj = jsdoc_object_node_load($type, $version, TRUE, TRUE);
2221  // It will have an nid if it's local. For example type: Function isn't local
2222  if ($obj && $obj->nid) {
2223    list($prototype_chain, $mixin_chain, $children) = jsdoc_get_oo_info($obj);
2224
2225    foreach ($children as $child) {
2226      $title = $child->title;
2227
2228      $field = (object)array(
2229        'title' => $title,
2230        'summary' => $child->summary,
2231        'types' => false
2232      );
2233      if (!$cleaned) {
2234        $field->url = 'jsdoc/' . $version->title . '/' . $obj->title . '.' . $child->title;
2235        $field->a = l($title, $field->url);
2236      }
2237
2238      $field_types = preg_split('%\|+%', jsdoc_get_type($child));
2239      foreach ($field_types as $field_type) {
2240        $found = 0;
2241        $field_type = str_replace('?', '', $field_type, $found);
2242        $optional = ($found > 0);
2243        $found = 0;
2244        $field_type = str_replace('...', '', $field_type, $found);
2245        $repeating = ($found > 0);
2246
2247        $field_obj = jsdoc_object_node_load($field_type, $version);
2248        $field_type = (object)array(
2249          'title' => $field_obj->title,
2250          'repeating' => $repeating,
2251          'optional' => $optional,
2252          'summary' => jsdoc_get_teaser($field_obj)
2253          );
2254        if (!$cleaned) {
2255          $field_type->url = $field_obj->jsdoc_url;
2256          $field_type->a = l($field_obj->title, $field_obj->jsdoc_url);
2257        }
2258        $field->types[] = $field_type;
2259      }
2260
2261      $fields[$field->title] = $field;
2262    }
2263
2264    if (empty($fields)) {
2265      return;
2266    }else{
2267      uksort($fields, 'strnatcasecmp');
2268      return ($cache[$version->title][$type] = $fields);
2269    }
2270  }
2271}
2272
2273function _jsdoc_get_type_themed($type, $node, $cleaned){
2274  $obj = jsdoc_object_node_load($type, jsdoc_get_version($node), TRUE, TRUE);
2275  if ($obj) {
2276    $type = (object)array(
2277      'title' => $obj->title,
2278      'summary' => jsdoc_get_teaser($obj),
2279      'fields' => false
2280    );
2281    if (!$cleaned && $obj->jsdoc_url) {
2282      $type->url = $obj->jsdoc_url;
2283      $type->a = l($obj->title, $obj->jsdoc_url);
2284    }
2285
2286    return $type;
2287  }
2288}
2289
2290function _jsdoc_get_parameters_themed(&$node, $cleaned=false) {
2291  if (isset($node->jsdoc_parameters_themed)) {
2292    return $node->jsdoc_parameters_themed;
2293  }
2294
2295  $parameters = $node->jsdoc_parameters_themed = array();
2296  $retrieved_parameters = jsdoc_get_parameters($node);
2297
2298  foreach ($retrieved_parameters as $item) {
2299    $parameter = (object)$item->jsdoc_formatted;
2300    $parameter->summary = $item->summary;
2301    $parameter->name = $item->name;
2302    $parameter->optional = $item->usage == 'optional';
2303    $parameter->repeating = $item->usage == 'repeating' || $item->usage == 'one-or-more';
2304    $parameter->types = false;
2305    if ($cleaned) {
2306      unset($parameter->html_type_prefix);
2307      unset($parameter->html_type_suffix);
2308      unset($parameter->separator);
2309    }
2310
2311    $type = $item->type;
2312    $types = array();
2313    if (strpos($type, '|') !== false) {
2314      $types = preg_split('%\s*\|+\s*%', $type);
2315    }
2316    elseif ($type) {
2317      $types = array($type);
2318    }
2319
2320    foreach ($types as $type) {
2321      if ($type = _jsdoc_get_type_themed($type, $node, $cleaned)) {
2322        $type->fields = _jsdoc_get_fields($type->title, $node);
2323        $parameter->types[] = $type;
2324      }
2325    }
2326
2327    if (empty($parameter->types)) {
2328      unset($parameter->html_type_prefix);
2329      unset($parameter->html_type_suffix);
2330      unset($parameter->separator);
2331    }
2332
2333    unset($parameter->type);
2334    $parameters[] = $parameter;
2335  }
2336
2337  return ($node->jsdoc_parameters_themed = $parameters);
2338}
2339
2340function _jsdoc_format_type($type, $classlike = false, $optional = false, $repeating = false) {
2341  $output = array();
2342  $name = '';
2343
2344  if ($type) {
2345    $output['html_type_prefix'] = '/*';
2346    $output['html_type_suffix'] = '*/';
2347    $output['type'] = $type;
2348    if ($type == 'Function' && $classlike) {
2349      $output['type'] = 'Constructor';
2350    }
2351    if ($optional) {
2352      $output['type'] .= '?';
2353    }
2354    if ($repeating) {
2355      $output['type'] .= '...';
2356    }
2357  }
2358  if ($type) {
2359    $output['separator'] .= ' ';
2360  }
2361
2362  return $output;
2363}
2364
2365function _jsdoc_flatten(&$item, $key) {
2366  if (is_object($item)) {
2367    $item = $item->title;
2368  }
2369}
2370
2371function jsdoc_get_namespace_parents(&$node) {
2372  if (isset($node->jsdoc_namespace_parents)) {
2373    return $node->jsdoc_namespace_parents;
2374  }
2375
2376  $parents = array();
2377
2378  $parts = explode('.', $node->title);
2379  array_pop($parts);
2380  while (count($parts)) {
2381    $parents[] = jsdoc_object_node_load(implode('.', $parts), jsdoc_get_version($node));
2382    array_pop($parts);
2383  }
2384
2385  return ($node->jsdoc_namespace_parents = $parents);
2386}
2387
2388function jsdoc_get_prototype_chain(&$node) {
2389  if (isset($node->jsdoc_prototype_chain)) {
2390    return $node->jsdoc_prototype_chain;
2391  }
2392
2393  $chain = array($node);
2394
2395  $superclass = $node->jsdoc_superclass;
2396  while ($superclass) {
2397    $superclass = jsdoc_object_node_load($superclass, jsdoc_get_version($node));
2398    $chain[] = $superclass;
2399    $superclass = $superclass->jsdoc_superclass;
2400  }
2401
2402  $chain[] = jsdoc_object_node_load('Object');
2403
2404  return ($node->jsdoc_prototype_chain = array_reverse($chain));
2405}
2406
2407function jsdoc_current_node($node=null) {
2408  static $current;
2409  if (is_null($node)) {
2410    return $current;
2411  }
2412  $current = $node;
2413}
2414
2415function _jsdoc_build_function_signature($node, $parameters) {
2416  if (isset($node->jsdoc_function_signature)) {
2417    return $node->jsdoc_function_signature;
2418  }
2419
2420  if (jsdoc_get_type($node) != 'Function' && jsdoc_get_type($node) != 'Constructor') {
2421    return;
2422  }
2423
2424  $signature = '(';
2425
2426  if ($parameters) {
2427    foreach ($parameters as $weight => $parameter) {
2428      if ($weight) {
2429        $signature .= ', ';
2430      }
2431      $types = array();
2432      if ($parameter->types) {
2433        foreach ($parameter->types as $type) {
2434          $types[] = $type->a;
2435        }
2436      }
2437      $signature .= $parameter->html_type_prefix . implode('|', $types);
2438      if ($parameter->optional) {
2439        $signature .= '?';
2440      }
2441      if ($parameter->repeating) {
2442        $signature .= '...';
2443      }
2444      $signature .= $parameter->html_type_suffix . $parameter->separator . $parameter->name;
2445    }
2446  }
2447
2448  $signature .= ')';
2449
2450  return ($node->jsdoc_function_signature = $signature);
2451}
2452
2453function _jsdoc_resource_expand($tree, $child, $depth, &$resources){
2454  $output = array();
2455  foreach ($tree as $leaf) {
2456    if ($leaf->depth == $depth && in_array($child, $leaf->children) && !in_array($leaf->vid, $resources)) {
2457      $resources[] = $leaf->vid;
2458      $output[] = $leaf;
2459    }
2460  }
2461
2462  return $output;
2463}
2464
2465function _jsdoc_file_location() {
2466  static $location;
2467
2468  if (!isset($location)) {
2469    $location = variable_get('jsdoc_file_location', false);
2470  }
2471
2472  return $location;
2473}
2474
2475function _jsdoc_dir_location() {
2476  static $location;
2477
2478  if (!isset($location)) {
2479    $location = variable_get('jsdoc_dir_location', false);
2480  }
2481
2482  return $location;
2483}
2484
2485function _jsdoc_base() {
2486  static $base;
2487
2488  if (!isset($base)) {
2489    $base = variable_get('jsdoc_base', 'jsdoc');
2490  }
2491
2492  return $base;
2493}
2494
2495function _jsdoc_build_terms($title) {
2496  $output = array($title, array());
2497  $parts = explode('.', $title);
2498  foreach ($parts as $part) {
2499    if (preg_match('%^[_.$]*([a-zA-Z][a-z0-9_.$]*(?:[A-Z][a-z0-9_.$]*)+)%', $part, $match)) {
2500      if (preg_match_all('%(^[a-zA-Z][a-z0-9_.$]*|[A-Z][a-z0-9_.$]*)%', $match[1], $cased)) {
2501        $output[1] = array_merge($output[1], $cased[0]);
2502      }
2503    }
2504  }
2505  $output[1] = implode(' ', $output[1]);
2506  $output[2] = implode(' ', $parts);
2507  return $output;
2508}
2509
2510function _jsdoc_get_base_path() {
2511  static $path;
2512
2513  if (!isset($path)) {
2514    $path = getcwd();
2515  }
2516
2517  return $path;
2518}
2519
2520function _jsdoc_cron_chdir($enter=false) {
2521  static $location;
2522
2523  if ($enter) {
2524    $location = _jsdoc_get_base_path();
2525    if (!is_dir(_jsdoc_dir_location())){
2526        print t('Check Drupal logs for errors');
2527        watchdog('jsdoc', _jsdoc_dir_location() . t(' should be a directory'), WATCHDOG_ERROR);
2528        return false;
2529    }
2530    if (!@chdir(_jsdoc_dir_location())) {
2531        print t('Check Drupal logs for errors');
2532        watchdog('jsdoc', _jsdoc_dir_location() . t(' could not be accessed'), WATCHDOG_ERROR);
2533        return false;
2534    }
2535    include_once(_jsdoc_file_location());
2536  }
2537  else {
2538    chdir($location);
2539  }
2540}
2541
2542// Private Utility Functions
2543// =========================
2544
2545function _jsdoc_theme_clone($node) {
2546  $clone = drupal_clone($node);
2547  foreach ($clone as $key => $value) {
2548    if (substr($key, 0, 6) == 'jsdoc_') {
2549      unset($clone->$key);
2550    }
2551  }
2552  return $clone;
2553}
2554
2555function _jsdoc_node_prepare($node) {
2556  $node->format = variable_get('jsdoc_input_format', 1);
2557
2558  $form = node_prepare($node)->content;
2559  $form['summary']['#weight'] = $form['body']['#weight'] - 1;
2560
2561  $teaser = jsdoc_get_teaser($node);
2562  if (!$node->body || $node->body == t('n/a')) {
2563    $node->body = $teaser;
2564    $teaser = '';
2565  }
2566  $body = jsdoc_get_body($node);
2567  if ($body == t('n/a')) {
2568    $body = '';
2569  }
2570
2571  $form['summary']['#value'] = theme('jsdoc_object_summary', $teaser);
2572  $form['description']['#value'] = theme('jsdoc_object_description', $teaser, $body);
2573
2574  unset($form['body']);
2575
2576  return $form;
2577}
2578
2579function _jsdoc_get_variables() {
2580  static $variables;
2581  if (empty($variables)) {
2582    if ($variables = cache_get('jsdoc_variables')) {
2583      $variables = unserialize($variables->data);
2584    }
2585    else {
2586      $variables = array();
2587      $query = db_query("SELECT variable FROM {jsdoc_variables}");
2588      while ($variable = db_fetch_object($query)) {
2589        if (preg_match('%^(dojo|dijit|dojox)\.%', $variable->variable)) {
2590          $variables[$variable->variable] = TRUE;
2591        }
2592      }
2593      cache_set('jsdoc_variables', 'cache', serialize($variables), time() + 86400);
2594    }
2595  }
2596
2597  return $variables;
2598}
2599
2600function _jsdoc_markup_code($text, $version, $exclude, $base=false) {
2601  $variables = _jsdoc_get_variables();
2602
2603  $min = 99;
2604  $lines = preg_split('%\r?\n%', $text);
2605  foreach ($lines as $line) {
2606    if (!empty($line)) {
2607      if (preg_match('%^\t+%', $line, $match)) {
2608        if (strlen($match[0]) < $min) {
2609          $min = strlen($match[0]);
2610        }
2611      }
2612    }
2613  }
2614  if ($min) {
2615    foreach ($lines as $i => $line) {
2616      $lines[$i] = preg_replace('%^\t{' . $min . '}%', '', $line);
2617    }
2618  }
2619  $text = implode("\n", $lines);
2620
2621  _jsdoc_init();
2622  $language = 'javascript';
2623  if (preg_match('%(^\W*<|>\W*$)%', $text)) {
2624    $language = 'html4strict';
2625  }
2626  $highlighter =& new GeSHi($text, $language);
2627  $highlighter->enable_classes();
2628  $highlighter->enable_keyword_links(false);
2629  $highlighter->set_overall_style('color: #666;', true);
2630  $highlighter->set_tab_width(4);
2631  $highlighter->add_keyword_group(4, '', true, array('Math', 'Error', 'Array'));
2632  $text = $highlighter->parse_code();
2633  $replaced = array();
2634  if (preg_match_all('%[\w.$]+(?:\.(?:<span class="me\d+">)?[\w.$]+(?:</span>)?)+%', $text, $matches)) {
2635    natcasesort(array_unique($matches[0]));
2636    $matches = array_reverse($matches[0]);
2637    foreach ($matches as $i => $match) {
2638      $variable = preg_replace('%<[^>]+>%', '', $match);
2639      if ($variables && $variables[$variable] && $variable != $exclude) {
2640        $replaced[$i] = $match;
2641        $text = str_replace($match, "%$i%", $text);
2642      }
2643    }
2644    foreach ($matches as $i => $match) {
2645      $variable = preg_replace('%<[^>]+>%', '', $match);
2646      if ($variables && $variables[$variable]) {
2647        $text = str_replace("%$i%", l($match, 'jsdoc/' . $version->title . '/' . $variable, array('style' => 'border-bottom: 1px dotted #ccc;'), NULL, NULL, FALSE, TRUE), $text);
2648      }
2649    }
2650  }
2651  if ($base && preg_match_all('%<span class="kw1">this</span>(\.(?:<span class="me\d+">[\w.$]+</span>)+)%', $text, $matches, PREG_SET_ORDER)) {
2652    foreach ($matches as $match) {
2653      $variable = $base . preg_replace('%<[^>]+>%', '', $match[1]);
2654      if ($variables && $variables[$variable]) {
2655        $version = node_load($variables[$variable]);
2656        $text = str_replace($match[0], l($match[0], 'jsdoc/' . $version->title . '/' . $variable, array('style' => 'border-bottom: 1px dotted #ccc;'), NULL, NULL, FALSE, TRUE), $text);
2657      }
2658    }
2659  }
2660  return $text;
2661}
2662
2663function _jsdoc_markup_text($text, $version, $exclude, $format = false){
2664  if (!$format) {
2665    $format = variable_get('jsdoc_input_format', 1);
2666  }
2667
2668  $lines = array();
2669  $tabbed = FALSE;
2670  foreach (explode("\n", $text) as $line) {
2671    if ($line{0} == "\t") {
2672      if (!$tabbed) {
2673        if (empty($lines)) {
2674          $lines[] = "";
2675        }
2676        $lines[] = "";
2677      }
2678      $tabbed = TRUE;
2679    }
2680    else {
2681      $tabbed = FALSE;
2682    }
2683    $lines[] = $line;
2684  }
2685  $text = implode("\n", $lines);
2686
2687  $tokens = array();
2688  if (preg_match_all('%`([^`]+)`%', $text, $matches)) {
2689    foreach (array_unique($matches[0]) as $i => $match) {
2690      $code = substr($match, 1, -1);
2691      $tokens[$i] = "<code>$code</code>";
2692      $text = str_replace($match, "!$i!", $text);
2693    }
2694  }
2695
2696  $text = check_markup($text, $format, FALSE);
2697
2698  if (preg_match_all('%(?:\s*(?:<pre>|<code>)\s*)+(.*?)(?:\s*(?:</pre>|</code>)\s*)+%s', $text, $matches, PREG_SET_ORDER)) {
2699    foreach ($matches as $match) {
2700      $match[1] = str_replace('&lt;', '<', str_replace('&gt;', '>', $match[1]));
2701      $text = str_replace($match[0], _jsdoc_markup_code($match[1], $version, $exclude), $text);
2702    }
2703  }
2704
2705  foreach ($tokens as $i => $token) {
2706    $text = str_replace("!$i!", $token, $text);
2707  }
2708
2709  return $text;
2710}
2711
2712function _jsdoc_init() {
2713  include_once(_jsdoc_get_base_path() . '/' . drupal_get_path('module', 'jsdoc') . '/lib/geshi/geshi.php');
2714  drupal_add_css(drupal_get_path('module', 'jsdoc') . '/jsdoc.css');
2715}
2716
2717// Theme functions
2718// ===============
2719
2720/**
2721 * When an object occurs in more than one file, we want
2722 * to show this to the user so that they can choose
2723 * the likely location until we disambiguate it.
2724 */
2725function theme_jsdoc_object_resources($node, $resources) {
2726  $output = '<div class="jsdoc_object_resources">';
2727  $output .= '<fieldset>';
2728  $output .= '<legend>Appears in Resources</legend>';
2729  $output .= '<div class="fieldset-wrapper">';
2730  foreach ($resources as $resource) {
2731    $output .= '<div class="form-item">';
2732    $output .= $resource->href;
2733    $output .= '</div>';
2734  }
2735  $output .= '</div>';
2736  $output .= '</fieldset>';
2737  $output .= '</div>';
2738
2739  return $output;
2740}
2741
2742/**
2743 * Theme the object type block.
2744 */
2745function theme_jsdoc_object_title($title) {
2746  $output = implode('.', $title->crumbs);
2747  if ($title->type) {
2748    $output .= ': ' . $title->type->a;
2749  }
2750  return $output;
2751}
2752
2753/**
2754 * Theme the statement used to require this file
2755 */
2756function theme_jsdoc_object_require($require) {
2757  return '<div class="form-item"><label>To Include:</label>include "' . $require . '";</div>';
2758}
2759
2760/**
2761 * Theme the location this file exists in
2762 */
2763function theme_jsdoc_object_resource($resource) {
2764  return '<div class="form-item"><label>Found in:</label> ' . $resource . '</div>';
2765}
2766
2767/**
2768 * Theme the list of mixins for a given object
2769 */
2770function theme_jsdoc_object_mixins($mixins) {
2771  $output = '<div class="form-item"><label>Mixins:</label>';
2772  $counter = 0;
2773  foreach ($mixins as $mixin) {
2774    if ($mixin->in_prototype_chain) {
2775      continue;
2776    }
2777    if ($counter) {
2778      $output .= ', ';
2779    }
2780    if (!$mixin->from == 'normal') {
2781      $output .= '[static] ';
2782    }
2783    $output .= l($mixin->node->title, $mixin->node->jsdoc_url);
2784    if ($mixin->mixin_from) {
2785      $output .= '<small> (from ' . $mixin->mixin_from . ')</small>';
2786    }
2787
2788    ++$counter;
2789  }
2790  if(!$counter){ $output .= 'None'; }
2791  return $output . '</div>';
2792}
2793
2794function theme_jsdoc_object_children($children) {
2795  $output = '<div class="jsdoc_object_children">';
2796  $output .= '<fieldset>';
2797  $output .= '<legend>Fields</legend>';
2798  $output .= '<div class="fieldset-wrapper">';
2799
2800  foreach ($children as $child) {
2801    $output .= '<div class="form-item">';
2802    $output .= l($child->title, $child->url);
2803    if ($child->type && $key!='Functions' && $key!="Constructors") {
2804      $output .= ': <small>' . $child->type->a . '</small> ';
2805    }
2806    if($child->signature){
2807      $output .= $child->signature;
2808    }
2809    else if($key=="Functions"){
2810      $output .= '() ';
2811    }
2812    if ($child->inheritance) {
2813      $output .= ' <div><small>';
2814      if ($child->override) {
2815        $output .= ' Overrides ';
2816      }
2817      else {
2818        $output .= ' Defined by ';
2819      }
2820      foreach ($child->inheritance as $i => $parent) {
2821        if ($i) {
2822          $output .= ', ';
2823        }
2824        $output .= l($parent->title, $parent->url);
2825      }
2826      $output .= '</small></div>';
2827    }
2828    if ($child->summary) {
2829      $output .= '<div>' . str_replace("<", "&lt;", str_replace(">", "&gt;", $child->summary)) . '</div>';
2830    }
2831    $output .= '</div>';
2832  }
2833 
2834  $output .= '</div>';
2835  $output .= '</fieldset>';
2836  $output .= '</div>';
2837
2838  return $output;
2839}
2840
2841/**
2842 * Theme the prototype chain
2843 */
2844function theme_jsdoc_object_prototype_chain($chain) {
2845  $output = '<div class="form-item"><label>Prototype Chain:</label>' ;
2846  foreach ($chain as $i => $node) {
2847    if ($i) {
2848      $output .= ' &raquo; ';
2849    }
2850    $output .= l($node->title, $node->jsdoc_url);
2851  }
2852  return $output .= '</div>';
2853}
2854
2855function theme_jsdoc_object_parent($node) {
2856  return '<div class="form-item"><label>Parent Object:</label>' . $node->object_type_link . '</div>';
2857}
2858
2859/**
2860 *  Theme the function signature and source.
2861 *
2862 *  One of the things to note is that we pass fields
2863 *  because the constructor isn't always listed as the
2864 *  constructor because of the famous dojo.declare
2865 */
2866function theme_jsdoc_function_information($function, $fields, $obj) {
2867  $output .= '<div class="jsdoc_function_information">';
2868  $output .= '<fieldset class="collapsible">';
2869  $output .= '<legend>' . 'Function Information' . '</legend>';
2870  $output .= '<div class="fieldset-wrapper">';
2871  $output .= '<div class="form-item">';
2872  $output .= 'function ' . $function->signature;
2873  $output .= '</div>';
2874  $output .= '</div>';
2875  $output .= '</fieldset>';
2876  $output .= '</div>';
2877
2878  return $output;
2879}
2880
2881function theme_jsdoc_function_parameters($parameters, $node) {
2882  $output = '<div class="jsdoc_function_parameters">';
2883  $output .= '<fieldset>';
2884  $output .= '<legend>' . 'Parameters' . '</legend>';
2885  $output .= '<div class="fieldset-wrapper">';
2886  foreach ($parameters as $parameter) {
2887    $output .= '<div class="form-item"><label>';
2888    $types = array();
2889    if ($parameter->types) {
2890      foreach ($parameter->types as $type) {
2891        $types[] = '<em>' . $type->a . '</em>';
2892      }
2893    }
2894    if (count($types)) {
2895      $output .= implode('|', $types) . ' ';
2896    }
2897    $output .= $parameter->name;
2898    $output .= '</label>';
2899    $output .= $parameter->summary;
2900    if (count($types) && $parameter->types[0]->fields) {
2901      if ($parameter->summary && $parameter->types[0]->summary) {
2902        $output .= '<br><br>';
2903      }
2904      $output .= $parameter->types[0]->summary;
2905      if ($parameter->types[0]->summary) {
2906        $output .= '<br>';
2907      }
2908      foreach ($parameter->types[0]->fields as $i => $field) {
2909        $output .= '<br>';
2910
2911        $field_types = array();
2912        $repeating = false;
2913        $optional = false;
2914        if ($field->types) {
2915          foreach ($field->types as $type) {
2916            if ($type->repeating) {
2917              $repeating = true;
2918            }
2919            if ($type->optional) {
2920              $optional = true;
2921            }
2922            $field_types[] = '<em>' . $type->a . '</em>';
2923          }
2924        }
2925
2926        $output .= '&nbsp; &nbsp; ';
2927        if (count($field_types)) {
2928          $output .= implode('|', $field_types) . ' ';
2929        }
2930        $output .= $field->a;
2931        if ($repeating) {
2932          $output .= ' <small><em>repeating</em></small>';
2933        }
2934        if ($optional) {
2935          $output .= ' <small><em>optional</em></small>';
2936        }
2937        if ($field->summary) {
2938          $output .= ': ' . $field->summary;
2939        }
2940      }
2941    }
2942    $output .= '</div>';
2943  }
2944  $output .= '</div>';
2945  $output .= '</fieldset>';
2946  $output .= '</div>';
2947
2948  return $output;
2949}
2950
2951function theme_jsdoc_object_summary($summary) {
2952  return '';
2953}
2954
2955function theme_jsdoc_object_description($summary, $description) {
2956  return $summary . $description;
2957}
2958
2959function theme_jsdoc_object_example($example, $weight) {
2960  return '<div class="form-item"><label>Example #' . $weight . '</label>' . $example . '</div>';
2961}
2962
2963function theme_jsdoc_object_examples($examples) {
2964  $output = '<div class="jsdoc_object_examples">';
2965  $output .= '<fieldset>';
2966  $output .= '<legend>Examples</legend>';
2967  $output .= '<div class="fieldset-wrapper">';
2968  $output .= implode('', $examples);
2969  $output .= '</div></fieldset></div>';
2970  return $output;
2971}
2972
2973function theme_jsdoc_search($name) {
2974  return '<div class="form-item"><input class="form-text" name="' . $name . '" /> <input class="form-submit" type="submit" name="op" value="Go" /></div>';
2975}
2976
2977function theme_jsdoc_namespaces($namespaces, $version) {
2978  $output = '<ul>';
2979  foreach ($namespaces as $namespace) {
2980    $output .= "<li>{$namespace->a}";
2981    if (!empty($namespace->children)) {
2982      $output .= '<ul>';
2983      foreach ($namespace->children as $child) {
2984        $output .= "<li>{$child->a}</li>";
2985      }
2986      $output .= '</ul>';
2987    }
2988    $output .= "</li>";
2989  }
2990  $output .= '</ul>';
2991  return $output;
2992}
2993
2994function theme_jsdoc_resource_source_link($project, $resource) {
2995  return $resource;
2996}
2997
2998function theme_jsdoc_resolve_constructor($objects) {
2999}
3000
3001function theme_jsdoc_ide($content){
3002  return theme('page', $content);
3003}
Note: See TracBrowser for help on using the repository browser.