source: Dev/trunk/src/client/util/docscripts/lib/parser2/dojo2.inc

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

Added Dojo 1.9.3 release.

File size: 15.4 KB
Line 
1<?php
2
3ini_set('display_errors', 1);
4error_reporting(E_ALL ^ E_NOTICE);
5
6require_once('JavaScriptLanguage.php');
7require_once('JavaScriptParser.php');
8require_once('JavaScriptStatements.php');
9require_once('JavaScriptFunction.php');
10require_once('Dojo.php');
11require_once('DojoCommentBlock.php');
12
13$_dojo_properties_modules = array();
14
15function _dojo_get_namespaces($limit=null){
16        static $namespaces;
17        if (!isset($namespaces)) {
18                $namespaces = array();
19                $files = scandir('modules');
20                foreach ($files as $file) {
21                        if (substr($file, -7) == '.module') {
22                                $namespace = substr($file, 0, -7);
23                                if (!$limit || in_array($namespace, $limit)) {
24                                        include_once('modules/' . $file);
25                                        $namespaces[] = substr($file, 0, -7);
26                                }
27                        }
28                        elseif (substr($file, -18) == '.module.properties') {
29                                $namespace = substr($file, 0, -18);
30                                if (!$limit || in_array($namespace, $limit)) {
31                                        global $_dojo_properties_modules;
32                                        foreach (preg_split('%[\n\r]+%', file_get_contents('modules/' . $file)) as $line) {
33                                                list($line, ) = preg_split('%[!#]%', $line, 2);
34                                                if ($line = trim($line)) {
35                                                        list($key, $line) = explode('=', $line, 2);
36                                                        $key = str_replace('\\ ', ' ', trim($key));
37                                                        $line = preg_replace('%^\s+%', '', $line);
38                                                        if ($key == 'location') {
39                                                                $line = _dojo_ensure_directory($line);
40                                                        }
41                                                        $_dojo_properties_modules[$namespace][$key] = $line;
42                                                }
43                                        }
44                                        $namespaces[] = substr($file, 0, -18);
45                                }
46                        }
47                }
48        }
49        return $namespaces;
50}
51
52function dojo_get_include($node, $provide) {
53        if ($node->jsdoc_project_name == $provide->title) {
54                return 'Included automatically';
55        }
56        else {
57                return 'dojo.require("%s");';
58        }
59}
60
61function _dojo_ensure_directory($directory) {
62        if (!is_dir($directory)) {
63                die("$directory is not a directory\n");
64        }
65        else {
66                if(substr($directory, -1) != '/'){
67                        $directory .= '/';
68                }
69        }
70        return $directory;
71}
72
73function dojo_get_file_time($namespace, $file) {
74        if (function_exists($namespace . '_code_location')) {
75                return filectime(_dojo_ensure_directory(call_user_func($namespace . '_code_location')) . $file);
76        }
77        else {
78                global $_dojo_properties_modules;
79                return filectime($_dojo_properties_modules[$namespace]['location'] . $file);
80        }
81}
82
83function _jsdoc_file_list($dir = false, $recurse = false){
84        $output = array();
85
86        if (!$recurse) {
87                $old_dir = getcwd();
88                if (!is_dir($dir)) {
89                        return array();
90                }
91                chdir($dir);
92                $dir = '.';
93        }
94        $files = scandir($dir);
95
96        foreach ($files as $file) {
97                if ($file{0} == '.') continue;
98                if (is_dir($dir . '/' . $file)) {
99                        if ($recurse) {
100                                $file = $dir . '/' . $file;
101                        }
102                        $output = array_merge($output, _jsdoc_file_list($file, true));
103                }else{
104                        if (substr($file, -3) == '.js' && substr($file, -6) != '.xd.js') {
105                                if ($recurse) {
106                                        $file = $dir . '/' . $file;
107                                }
108                                $output[] = $file;
109                        }
110                }
111        }
112
113        if (!$recurse) {
114                chdir($old_dir);
115        }
116        return $output;
117}
118
119function dojo_get_files($limit=null) {
120                $namespaces = _dojo_get_namespaces($limit);
121                $files = array();
122                foreach ($namespaces as $namespace) {
123                                // skip util directory, parser chokes on util/less
124                                if($namespace == "util") continue;
125                                if (function_exists($namespace . '_code_location')) {
126                                        $location = _dojo_ensure_directory(call_user_func($namespace . '_code_location'));
127                                }
128                                else {
129                                        global $_dojo_properties_modules;
130                                        $location = $_dojo_properties_modules[$namespace]['location'];
131                                }
132                                if (!$location) die($namespace . '_code_location does not return useful result');
133                                $list = _jsdoc_file_list($location);
134                                foreach ($list as $i => $item) {
135                                                // Skip internationalization/tests/demos files
136                                                if (preg_match('%(^|/|\\\\)(nls|tests|demos)(\\\\|/)%', $item)) {
137                                                        unset($list[$i]);
138                                                        continue;
139                                                }
140                                                $list[$i] = array($namespace, $item);
141                                }
142                                $files = array_merge($files, array_values($list));
143                }
144
145                return $files;
146}
147
148function _amd_pseudodoc_fix($matches){
149        // adds a pseudo-block of aliases to trick parser into believing the things inside of define() callbacks
150        // are in fact global
151//      $out = $matches[1];
152//      $args = trim($matches[2]);
153//      if($args != ""){
154//              $pseudo = "){\n\n/*=====\n";
155//              $newargs = array();
156//              foreach(explode(",", $args) as $arg){
157//                      $arg = trim($arg);
158//                      $newargs[] = "_$arg";
159//                      $pseudo .= "\tvar _$arg = $arg;\n";
160//              }
161//              $out .= join(",", $newargs);
162//              $pseudo .= "\n=====*/\n\n";
163//              $out .= $pseudo;
164//      }else{
165//              $out .= "){";
166//      }
167//     
168//      return $out;
169
170        // turn (dojo, dijit) into (this.dojo, this.dijit);
171        $args = $matches[3];
172        $freshargs = preg_replace('/\w+/', "_$0", $args);
173        $newargs = $args; // preg_replace('/\w+/', "this.$0", $args);
174        $body = $matches[4];
175
176        return ";(function" . $freshargs . "{" . $body . "})" . $args . ";";
177       
178}
179
180function _amd_unwrap($text, $namespace, $file_name) {
181        // looking for something like...
182        //
183        //       define("my/module", ["path/to/module", "another/module"]
184        //       define(["path/to/module", "another/module"]
185        //       define(["path/to/module"]
186        //       define([]
187        //
188        // if the mid is missing (which is the usual case), then the mid is given by the namespace + filename
189
190        if (preg_match('/\/\/>>not-amd/', $text) || !preg_match('/define\s*\(\s*([\'"]([^\'"]+)[\'"]\s*,\s*)?\[\s*([^\]]*?)\s*\]/', $text, $matches)) {
191                // not an AMD module
192                //print "not an AMD module\n\n\n"; //for debugging
193                return $text;
194        }
195
196        $mid = trim($matches[2]);
197        if ($mid=="" || empty($mid)) {
198                // prefix the namespace; remove the .js filetype
199                if (preg_match('/(.+)\.js/', $file_name, $midMatches)) {
200                        $mid = $namespace . "/" . $midMatches[1];
201                } else {
202                        //print "not an AMD module\n\n\n";      //for debugging
203                        return $text;
204                }
205        }
206
207        $requires = "";
208        foreach (explode(",", $matches[3]) as $dep) {
209                // regex is looking for the trimmed contents of a quoted string
210                // if a plugin resource is given (e.g., "dojo/text!./path/to/template.html"), then return the plugin (e.g., "dojo/text")
211                if (preg_match('/[\'"]\s*([^\'"!]+)\!?[^\'"]*\s*[\'"]/', $dep, $match)) {
212                        $dep = $match[1];
213                        if (substr($dep, 0, 1)==".") {
214                                // path is relative to mid
215                                $dep = explode("/", $dep);
216                                $ref = explode("/", $mid);
217                                array_pop($ref);
218                                foreach($dep as $part){
219                                        if ($part=="..") {
220                                                array_pop($ref);
221                                        } else if ($part!=".") {
222                                                array_push($ref, $part);
223                                        }
224                                }
225                                $dep = implode(".", $ref);
226                        } else {
227                                // absolute path
228                                $dep = str_replace("/", ".", $dep);
229                        }
230                        // don't dojo.require dojo or the CommonJs module lexical vars
231                        if ($dep!="dojo" && $dep!="require" && $dep!="exports" && $dep!="module") {
232                                $requires.= "dojo.require(\"" . $dep    . "\");\n";
233                        }
234                } else {
235                        // print "failed to find dependency name in what looks like an AMD module (" .   $namespace . "/" . $file_name . ").\n";
236                        // print $matches[0];
237                }
238        }
239        $result = "dojo.provide(\"" . str_replace("/", ".", $mid)       . "\");\n" . $requires; //
240       
241        // FIXME: temporary fix, inject pseudo-docs for AMD style define([], function(a,b,c))
242        // tricks parser into thinking a b and c are aliases to global objects:
243        // FIXME: regexp is really greedy. also breaks define statements in docs if doesn't match first one
244        // FIXME: will only break on define([ "eval!function() { return 'fml'; }" ); so don't do that.
245        $text = preg_replace_callback('/(define\((.*?)function(\s*\(.*?)\{(.*)\}\);)/s', "_amd_pseudodoc_fix", $text, 1);
246       
247        // old re:
248        // '/(define\(.*?function.*\()(.*)\)\s?\{/'
249       
250        //print $result . "\n\n"; //for debugging
251        return $result . $text;
252}
253
254function dojo_get_contents($namespace, $file_name) {
255
256        if (function_exists($namespace . '_code_location')) {
257                $location = _dojo_ensure_directory(call_user_func($namespace . '_code_location'));
258        }
259        else {
260                global $_dojo_properties_modules;
261                $location = $_dojo_properties_modules[$namespace]['location'];
262        }
263
264        $output = array();
265        $output['#debug'] = array();
266
267        $filedata = file_get_contents($location . '/' . $file_name);
268        $output['#raw_source'] = $filedata;
269        $filedata_amd = _amd_unwrap($filedata, $namespace, $file_name);
270        if($filedata_amd != $filedata){
271                $output['#unwrapped_source'] = $filedata_amd;
272        }
273        $filedata = $filedata_amd;
274       
275        $lines = preg_replace('%/\*={3,}|={3,}\*/%', '', $filedata);
276       
277        try{
278                $parser = new JavaScriptParser(JavaScriptLanguage::tokenize($lines));
279        }catch(Exception $e){
280                $output['#debug'][] = "Died parsing $file_name";
281                $output['#debug'][] = $e;
282                return $output;
283        }
284       
285//      print '<pre>';
286//      $statements = $parser->statements();
287//      print $statements[0]->resolve();
288//      print '</pre>';
289//      die();
290       
291        try{
292                $package = new JavaScriptStatements($parser->statements());             
293        }catch(Exception $e){
294                $output['#debug'][] = "Died parsing statements";
295                $output['#debug'][] = $e;
296                return $output;
297        }
298
299        // Handle dojo.provide calls
300        foreach ($package->function_calls(TRUE, 'dojo.provide') as $call) {
301                if ($module = $call->arguments()->getString(0)) {
302                        $output['#provides'] = $module;
303                }
304        }
305
306        $output['#resource'] = $file_name;
307       
308        // Handle dojo.require calls
309        foreach ($package->function_calls(TRUE, 'dojo.require') as $call) {
310                if ($module = $call->arguments()->getString(0)) {
311                        $output['#requires'][] = array('common', $module);
312                }
313        }
314
315        // Handle mixin/extend calls
316        foreach ($package->function_calls(TRUE, 'dojo.mixin', 'dojo.extend', 'lang.extend', 'lang.mixin', 'lang._mixin', 'dojo._mixin') as $call) {
317                $arguments = $call->arguments();
318                $assignment = $call->assignment();
319                $name = $call->name();
320                $root = NULL;
321                if ($constructor = $arguments->getFunction(0)) {
322                        if ($assignment) {
323                                Dojo::roll_out($constructor, $assignment, FALSE, $output);
324                        }
325                }
326                else {
327                        $root = $arguments->getVariable(0, TRUE);
328                        if (endswith($name, 'extend')) {
329                                $output[$root]['type'] = 'Function';
330                        }
331                }
332
333                foreach (array_diff(array_unique(array($assignment, $root)), array(NULL)) as $root) {
334                        $mixin = endswith($name, "mixin");
335                        for ($i = 1; $i < $arguments->length; $i++) {
336                                if ($arguments->getObject($i)) {
337                                        $keys = array();
338                                        foreach ($arguments->getObject($i)->values() as $key => $values) {
339                                                $keys[] = $key;
340                                                $full_name = "$root.$key";
341                                                foreach ($values as $value) {
342                                                        if ($value instanceof JavaScriptVariable) {
343                                                                $key = $mixin ? $full_name : "$root.prototype.$key";
344                                                                if ($key != $value->value()) {
345                                                                        $output[$key]['alias'] = $value->value();
346                                                                }
347                                                        }
348                                                        else {
349                                                                Dojo::roll_out($value, $full_name, FALSE, $output);
350                                                                $output[$full_name][$mixin ? 'attached' : 'prototype'] = $root;
351                                                        }
352                                                }
353                                        }
354                                        Dojo::roll_out_comment_block($arguments->getObject($i), $root, $output, $keys);
355                                }
356                                elseif ($root && $full_name = $arguments->getVariable($i)) {
357                                        if ($mixin) {
358                                                $output[$root]['mixins']['normal'][] = $full_name;
359                                        }
360                                        else {
361                                                $output[$root]['chains']['prototype'][] = $full_name;
362                                        }
363                                }
364                        }
365                }
366        }
367
368        foreach ($package->function_calls(TRUE, 'dojo.declare', 'declare') as $call) {
369                $arguments = $call->arguments();
370                $name = $arguments->getString(0);
371                if (!$name) {
372                        $assignment = $call->assignment();
373                        $output['#debug'][] = "Found declare() without a name? check return value?" + $assignments;
374                        continue;
375                } else{
376                        // $output['#debug'][] = "Found declare w/ $name";
377                }
378                $output[$name]['type'] = 'Function';
379                if ($superclass = $arguments->getVariable(1)) {
380                        if ($superclass != 'null') {
381                                $output[$name]['chains']['prototype'][] = $superclass;
382                                $output[$name]['chains']['call'][] = $superclass;
383                        }
384                }
385                elseif ($superclasses = $arguments->getArray(1)) {
386                        for($i = 0; TRUE; $i++) {
387                                if ($superclass = $superclasses->getVariable($i)) {
388                                        $output[$name]['chains']['prototype'][] = $superclass . ($i ? '.prototype' : '');
389                                        $output[$name]['chains']['call'][] = $superclass;
390                                }
391                                else {
392                                        break;
393                                }
394                        }
395                }
396                if ($mixin = $arguments->getObject(2)) {
397                        $keys = $block_keys = Dojo::$block_keys;
398                        $new_keys = array();
399                        $constructors = array();
400                        // Remember that bad code can have multiple matching keys
401                        foreach ($mixin->values() as $key => $values) {
402                                $new_keys[] = $key;
403                                $full_name = "$name.$key";
404                                foreach ($values as $value) {
405                                        if ($value instanceof JavaScriptFunction) {
406                                                if (in_array($key, array('constructor', 'preamble', 'postscript'))) {
407                                                        $output[$full_name]['constructor'] = $key;
408                                                        $output[$full_name]['prototype'] = $name;
409                                                        $constructors[$full_name] = $value;
410                                                        continue;
411                                                }
412                                        }
413                                        elseif ($value->type() == 'variable') {
414                                                if ($full_name != $value->value()) {
415                                                        $output[$full_name]['alias'] = $value->value();
416                                                }
417                                                continue;
418                                        }
419
420                                        $output[$full_name]['prototype'] = $name;
421                                        $new_keys = array_unique(array_merge($new_keys, Dojo::roll_out($value, $full_name, FALSE, $output, $new_keys)));
422                                }
423                        }
424
425                        foreach ($constructors as $full_name => $constructor) {
426                                $new_keys = array_unique(array_merge($new_keys, Dojo::roll_out($constructor, $name, FALSE, $output, $new_keys)));
427                                foreach ($output[$name] as $key => $value) {
428                                        if ($key != 'chains') {
429                                                $output[$full_name][$key] = $value;
430                                        }
431                                }
432                        }
433
434                        Dojo::roll_out_comment_block($mixin, $name, $output, $new_keys);
435                }
436        }
437
438        // Variable assignments (global)
439        foreach ($package->assignments(TRUE) as $variable) {
440                foreach ($variable->names() as $name) {
441                        $parts = explode('.', $name);
442                        $name = implode('.', array_diff($parts, array('prototype')));
443                        $last = array_pop($parts);
444
445                        $is_prototype = ($last == 'prototype');
446
447                        Dojo::roll_out($variable->value(), $name, FALSE, $output, array(), $is_prototype);
448
449                        if (count($parts) && !$is_prototype) {
450                                $output[$name]['attached'] = implode('.', $parts);
451                        }
452                }
453        }
454
455        // dojo.provide creates new objects if needed
456        if (!empty($output['#provides'])) {
457                $parts = explode('.', $output['#provides']);
458                while (count($parts)) {
459                        if (!array_key_exists(implode('.', $parts), $output)) {
460                                $output[implode('.', $parts)] = array('type' => 'Object');
461                        }
462                        array_pop($parts);
463                }
464        }
465
466        // Set privacy, classlikeness, and clean up the summary a bit
467        foreach ($output as $object_name => $object) {
468                if ($object_name{0} == '#') {
469                        continue;
470                }
471                $parts = explode('.', $object_name);
472                $last = array_pop($parts);
473                if ($last{0} == '_') {
474                        $output[$object_name]['private'] = true;
475                }
476                if (preg_match('%\._+[^A-Z]%', implode('.', $parts), $match)) {
477                                $output[$object_name]['private_parent'] = true;
478                }
479                if (is_array($object['tags'])) {
480                        foreach ($object['tags'] as $tag) {
481                                if ($tag == 'protected') {
482                                        unset($output[$object_name]['private']);
483                                        $output[$object_name]['protected'] = true;
484                                }
485                                elseif ($tag == 'private') {
486                                        unset($output[$object_name]['protected']);
487                                        $output[$object_name]['private'] = true;
488                                }
489                                elseif ($tag == 'deprecated') {
490                                        $output[$object_name]['deprecated'] = true;
491                                }
492                        }
493                        $output[$object_name]['tags'] = array_diff($object['tags'], array('private', 'protected', 'deprecated'));
494                }
495
496                if (isset($object['inferred_type'])) {
497                        if (empty($object['type'])) {
498                                $output[$object_name]['type'] = $object['inferred_type'];
499                        }
500                        unset($output[$object_name]['inferred_type']);
501                }
502
503                if ($object['type'] == 'Function') {
504                        if (preg_match('%^(_*)[A-Z]%', $last, $match)) {
505                                if (strlen($match[1]) < 2) {
506                                        unset($output[$object_name]['private']);
507                                }
508                                $output[$object_name]['classlike'] = true;
509                        }
510                }
511
512                if ($object['prototype'] && $output[$object['prototype']]) {
513                        $output[$object['prototype']]['classlike'] = true;
514                }
515                elseif ($object['instance'] && $output[$object['instance']]) {
516                        $output[$object['instance']]['classlike'] = true;
517                }
518        }
519
520        return $output;
521}
522
523function endswith($haystack, $needle){
524        $length = strlen($needle);
525        $start = $length * -1;
526        return (substr($haystack, $start) === $needle);
527}
Note: See TracBrowser for help on using the repository browser.