Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/action.js

Issue 10233013: Crawler, second version (Closed)
Patch Set: Created April 12, 2013, 1:38 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/skin/text-x-source.png ('k') | lib/action_platform.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * We avoid using a "let" statement so that we can test this module with js-test -driver, which doesn't support
3 * non-standard JavaScript syntax.
4 */
5 var x = require( "action_platform" );
6 var Action_Platform = x.Action_Platform;
7
8 /**
9 * @namespace The action library, working with both synchronous and asynchronous actions.
10 */
11 Action = {};
12
13 //-------------------------------------------------------
14 // Platform functions
15 //-------------------------------------------------------
16 /*
17 * We forward the basic platform functions into this name space for ease of use and a single place for documentation.
18 */
19 /**
20 * Dispatch a function into a future JavaScript thread. JS is single-threaded, s o the function won't run concurrently
21 * with the present thread. Rather it creates a new JS call stack with the argum ent function at the root of the stack.
22 * The browser implementation is setTimeout( f, 0 ).
23 *
24 * @type {function(function)}
25 */
26 Action.dispatch = Action_Platform.dispatch;
27
28 //-------------------------------------------------------
29
30 /**
31 * The common states of all actions. The ordinary start state is Ready leads to only three transitions: from Ready to
32 * Running, and from Running to both Done and Exception. For actions that are no t fully initialized by their constructors,
33 * there's also the state Init and a transition to Ready.
34 * @enum {number}
35 */
36 Action.State = {
37 /**
38 * An available start state for actions that use more then their constructors for initialization.
39 */
40 Init: 0,
41 /**
42 * The ordinary start state. An action is ready after it is fully initialized.
43 */
44 Ready: 1,
45 /**
46 * The subprogram of the action is currently running. The state is changed imm ediately upon the call to go() or run().
47 */
48 Running: 2,
49 /**
50 * The action completed without exception. In this case no catcher was called. The state is changed after the
51 * subprogram has finished and before calling the finisher.
52 */
53 Done: 3,
54 /**
55 * The action threw an exception. In this case any catcher specified would be called. The state is changed
56 * after the subprogram has finished and before calling the catcher.
57 */
58 Exception: 4
59 };
60
61 //-------------------------------------------------------
62 // Action interfaces
63 //-------------------------------------------------------
64 /**
65 * The base action interface is just a marker.
66 *
67 * This interface declaration is in Closure Compiler syntax, but for use by Inte lliJ IDEA. The compiler would optimize
68 * it out, but it ought to go into a separate file anyway.
69 *
70 * @interface
71 */
72 Action.Action_interface = function()
73 {
74 /**
75 * Every action is either reliable, which means that it's guaranteed to return control to the caller, or unreliable,
76 * which means no such guarantee exists. Unreliable does not mean "never retur ns"; what would be the point of that?
77 *
78 * Reliability is a self-declaration for primitive actions. For composite acti ons, that is, actions that have at least
79 * one other action within themselves, reliability can (often) be inferred.
80 *
81 * @expose
82 * @type {boolean}
83 */
84 this.reliable = null;
85 };
86
87 /**
88 *
89 * @interface
90 * @extends Action.Action_interface
91 */
92 Action.Synchronous_Action_interface = function()
93 {
94 /**
95 * Every synchronous action is, by definition, reliable, since it always retur ns control to its caller. The return
96 * of control can be either ordinary or exceptional, but that distinction is i rrelevant to the meaning of "reliable".
97
98 * @type {boolean}
99 */
100 this.reliable = true;
101 };
102
103 /**
104 * The subprogram of a synchronous action is called 'run', to distinguish it fro m an asynchronous subprogram.
105 */
106 Action.Synchronous_Action_interface.prototype.run = function()
107 {
108 };
109
110 //-------------------------------------------------------
111 /**
112 * @interface
113 * @extends Action.Action_interface
114 */
115 Action.Asynchronous_Action_interface = function()
116 {
117 /**
118 * The default for an asynchronous action is unreliable. While some asynchrono us actions are reliable, its prudent not
119 * to assume that otherwise without specific knowledge.
120 *
121 * @type {boolean}
122 */
123 this.reliable = false;
124 };
125
126 Action.Asynchronous_Action_interface.prototype._go = function()
127 {
128 };
129
130
131 //------------------------------------------------------------------------------ -----------
132 // Join Messaging
133 //------------------------------------------------------------------------------ -----------
134 /*
135 * Join messages are part of the basic action implementation, because the behavi or of a join requires augmenting the
136 * behavior of the finisher. Upon an action completing, it notifies other action s (directly or indirectly) that it has
137 * completed. Thus, in addition to the caller-designated finisher functions, the re may be an additional set of calls
138 * made at this time.
139 *
140 * Some classes generate join messages, such as those that
141 * wait for another action to complete. Some accept join messages, just as Join_ class itself. Yet others, however, the
142 * category of condition transformers, both generate and accept such messages, j ust as the gate for join-with-timeout.
143 * As a consequence, we need to define these interfaces separately, so that, for example, both join actions and
144 * condition transformers can each consistently accept messages
145 */
146
147 /**
148 * Interface for a receiver of join messages.
149 * @interface
150 */
151 Action.JM_Attentive = function()
152 {
153 };
154
155 /**
156 * Receive a notice that a dependent condition has completed well.
157 *
158 * @param id
159 */
160 Action.JM_Attentive.prototype.notice_good = function( id )
161 {
162 };
163
164 /**
165 * Receive a notice that a dependent condition has completed badly.
166 *
167 * @param {*} id
168 * The identifier for the dependent condition in case there's more than one.
169 * @param {*} e
170 * An exception object as it appears in a catch clause.
171 */
172 Action.JM_Attentive.prototype.notice_bad = function( id, e )
173 {
174 };
175
176 /**
177 * Interface for a sender of join messages. This interface is required because r eporters have state that may need to be
178 * queried by a receiver.
179 * @interface
180 */
181 Action.JM_Reporting = function()
182 {
183 };
184
185 /**
186 * Watch the ending of this action.
187 *
188 * @param {Action.JM_Attentive} watcher
189 * The watcher object.
190 * @param {*} their_id
191 * An opaque identifier by which the peer identifies the relation.
192 * @returns {*}
193 * Our identifier for the relation.
194 */
195 Action.JM_Reporting.prototype.watch = function( watcher, their_id )
196 {
197 };
198
199 //-------------------------------------------------------
200 /**
201 * Base class implementation.
202 * @constructor
203 * @implements {Action.JM_Reporting}
204 * @implements {Action.Asynchronous_Action_interface}
205 */
206 Action.Asynchronous_Action = function()
207 {
208 };
209
210 /**
211 * We set up the finisher and catcher function at construction time. The design principle here is that flow of control
212 * is structural and should be set up at the beginning. In addition, it enables run() and go() to be parallel, both
213 * accepting arguments for the body of the action.
214 *
215 * @this {Action.Asynchronous_Action}
216 * @param {function} [finisher]
217 * @param {function} [catcher]
218 */
219 Action.Asynchronous_Action.init = function( finisher, catcher )
220 {
221 this.finisher = finisher;
222 this.catcher = catcher;
223
224 /**
225 * The common state of a asynchronous action
226 * @type {Action.State}
227 * @private
228 */
229 this._state = Action.State.Ready;
230
231 /**
232 * @type {Array.<{watcher,id}>}
233 */
234 this._end_watchers = [];
235
236 /**
237 * The value of an action, used to invoke the finisher as its array of argumen ts.
238 * @type {Array}
239 * @protected
240 */
241 this._argv = [];
242 };
243
244 Object.defineProperty( Action.Asynchronous_Action.prototype, "state", {
245 /**
246 * @this {Action.Asynchronous_Action}
247 */
248 get: function()
249 {
250 return this._state;
251 }
252 } );
253
254 Object.defineProperty( Action.Asynchronous_Action.prototype, "completed", {
255 /**
256 * @this {Action.Asynchronous_Action}
257 */
258 get: function()
259 {
260 return this._state >= Action.State.Done;
261 }
262 } );
263
264 Object.defineProperty( Action.Asynchronous_Action.prototype, "completed_well", {
265 /**
266 * @this {Action.Asynchronous_Action}
267 */
268 get: function()
269 {
270 return this._state == Action.State.Done;
271 }
272 } );
273
274 Object.defineProperty( Action.Asynchronous_Action.prototype, "exception", {
275 /**
276 * @this {Action.Asynchronous_Action}
277 */
278 get: function()
279 {
280 if ( this._state == Action.State.Exception )
281 {
282 return this._exception;
283 }
284 else
285 {
286 throw new Error( "Action is not in an exception state." );
287 }
288 }
289 } );
290
291 /**
292 * Start up the subprogram body for this action instance.
293 *
294 * Any arguments here are passed along unchanged to the action, which may or may not ignore them.
295 */
296 Action.Asynchronous_Action.prototype.go = function()
297 {
298 if ( this._state != Action.State.Ready )
299 {
300 throw new Error( "Call to go() is invalid because the action is not in state 'Ready'." );
301 }
302 this._state = Action.State.Running;
303 this._go.apply( this, arguments );
304 };
305
306 /**
307 * The default action body is to do nothing.
308 */
309 Action.Asynchronous_Action.prototype._go = function()
310 {
311 };
312
313 /**
314 * Cancellation is an ordinary end to an action. We halt execution of the action and end immediately.
315 *
316 * The analog of this method for exceptional end is abort().
317 *
318 * Design Note: Commands the force early end to an action only affect the action itself, at most. They do not affect
319 * the execution of finisher and catcher functions that the action may have been invoked with. Nor do they affect
320 * notices to join actions waiting on the early-terminated action. One of the mo tivations for this behavior is that we
321 * want reliable actions to remain reliable. If cancellation were to cause finis hers, not to run, no action could be
322 * considered reliable.
323 */
324 Action.Asynchronous_Action.prototype.cancel = function()
325 {
326 this.end_well();
327 };
328
329 /**
330 * Abortion in an exceptional end to an action. We halt execution of the action and end immediately.
331 *
332 * The analog of this method for ordinary end is cancel(), which see for more co mmentary.
333 *
334 * @param {*} [e]
335 * An exception object to associate with exceptional termination. If absent, defaults to a new Error object.
336 */
337 Action.Asynchronous_Action.prototype.abort = function( e )
338 {
339 this.end_badly( e ? e : new Error( "Action aborted by external command." ) );
340 };
341
342 /**
343 * The default termination behavior is to do nothing.
344 *
345 * Actions that allocate resources should override this method and release their resources here. This method is always
346 * called when the action ends.
347 *
348 * @protected
349 */
350 Action.Asynchronous_Action.prototype.terminate = function()
351 {
352 };
353
354 /**
355 * Change state to Done and execute the finisher.
356 *
357 * @protected
358 */
359 Action.Asynchronous_Action.prototype.end_well = function()
360 {
361 function good()
362 {
363 this.watcher.notice_good( this.id );
364 }
365
366 /*
367 * We may only complete once.
368 */
369 if ( this.completed )
370 return;
371 /*
372 * Note that there's no exception handling in this function. In order to mimic the behavior of the try-finally
373 * statement, an exception thrown from a finisher is treated as if it had happ ened within a finally block, which is to
374 * say, it throws the exception. There's no need for extra code to do that.
375 *
376 * In addition, the state is left at Done if the finisher throws an exception. In this case, the exception does not
377 * come from the action itself, but from user code. So regardless of how the f inisher terminates, it does not change
378 * that the action completed ordinarily.
379 */
380 this._state = Action.State.Done;
381 this.terminate();
382 this._each_watcher( good );
383 if ( this.finisher ) this.finisher.apply( null, this._argv );
384 };
385
386 /**
387 * Change state to Exception and execute the catcher followed by the finisher.
388 *
389 * @protected
390 * @param e
391 * An exception value
392 */
393 Action.Asynchronous_Action.prototype.end_badly = function( e )
394 {
395 function bad()
396 {
397 this.watcher.notice_bad( this.id, e );
398 }
399
400 /*
401 * We may only complete once.
402 */
403 if ( this.completed )
404 return;
405 /*
406 * In contrast to end_well(), this function does require a try-finally stateme nt. If the catcher throws an
407 * exception, then we still have to execute the finisher anyway.
408 */
409 this._state = Action.State.Exception;
410 /**
411 * The object identified with the exceptional completion of the action.
412 *
413 * @type {*}
414 * @private
415 */
416 this._exception = e;
417 this.terminate();
418 this._each_watcher( bad );
419 try
420 {
421 this._argv.unshift( e );
422 if ( this.catcher ) this.catcher.apply( null, this._argv );
423 }
424 finally
425 {
426 this._argv.shift();
427 if ( this.finisher ) this.finisher.apply( null, this._argv );
428 }
429 };
430
431 /**
432 * Call a function on each watcher.
433 *
434 * @param {function} f
435 * A function to be called on the watcher structure.
436 * @private
437 */
438 Action.Asynchronous_Action.prototype._each_watcher = function( f )
439 {
440 for ( var j = 0 ; j < this._end_watchers.length ; ++j )
441 {
442 try
443 {
444 /**
445 * @type {{watcher:Action.JM_Attentive, id}}
446 */
447 var w = this._end_watchers[ j ];
448 if ( !w )
449 {
450 /*
451 * It's OK for a watcher to be null. All this means is that the watcher withdrew before completion.
452 */
453 continue;
454 }
455 f.call( w );
456 }
457 catch ( e )
458 {
459 /*
460 * The use of this catch block is a defense so that we can ignore exceptio ns. There shouldn't be any, though, but
461 * just in case.
462 */
463 }
464 }
465 };
466
467 /**
468 * Watch the ending of this action.
469 *
470 * @param {Action.JM_Attentive} watcher
471 * The watcher object.
472 * @param {*} their_id
473 * An opaque identifier by which the peer identifies itself.
474 * @returns {number}
475 * Our identifier, which is the index in the _end_watchers array.
476 */
477 Action.Asynchronous_Action.prototype.watch = function( watcher, their_id )
478 {
479 return this._end_watchers.push( { watcher: watcher, id: their_id } ) - 1;
480 };
481
482 //noinspection JSUnusedGlobalSymbols
483 /**
484 * Withdraw a watcher
485 */
486 Action.Asynchronous_Action.prototype.withdraw = function( our_id )
487 {
488 this._end_watchers[ our_id ] = null;
489 };
490
491 //-------------------------------------------------------
492 /**
493 * @interface
494 * @extends Action.Action_interface
495 */
496 Action.Joinable = function()
497 {
498 };
499
500 //------------------------------------------------------------------------------ -----------
501 // ACTIONS
502 //------------------------------------------------------------------------------ -----------
503
504 //-------------------------------------------------------
505 // Defer
506 //-------------------------------------------------------
507 /**
508 * Class constructor for Defer actions, which defer execution of a function (the "trial") until after the current
509 * JavaScript-thread has run to completion.
510 *
511 * @constructor
512 * @extends Action.Asynchronous_Action
513 */
514 Action.Defer_class = function()
515 {
516 /**
517 * @const
518 * @type {boolean}
519 */
520 this.reliable = true;
521 };
522 Action.Defer_class.prototype = new Action.Asynchronous_Action();
523
524 /**
525 *
526 */
527 Action.Defer_class.prototype._go = function()
528 {
529 Action.dispatch( this._body.bind( this ) );
530 };
531
532 /**
533 * The deferred trial is run inside of a try-catch-finally statement.
534 * @private
535 */
536 Action.Defer_class.prototype._body = function()
537 {
538 try
539 {
540 if ( this.trial )
541 this._argv = this.trial();
542 }
543 catch ( e )
544 {
545 /*
546 * Note that because the trial did not return, it had no return value. Thus we have no context for a sensible
547 * return value argument for end_badly().
548 */
549 this.end_badly( e );
550 return;
551 }
552 this.end_well();
553 };
554
555 /**
556 * Instance constructor for standard Defer actions.
557 * @constructor
558 * @param f
559 * @param {function} [finisher]
560 * @param {function} [catcher]
561 */
562 Action.Defer = function( f, finisher, catcher )
563 {
564 Action.Asynchronous_Action.init.call( this, finisher, catcher );
565 this.trial = f;
566 };
567 Action.Defer.prototype = new Action.Defer_class();
568
569 //-------------------------------------------------------
570 /**
571 *
572 * @constructor
573 * @extends Action.Asynchronous_Action
574 */
575 Action.Delay_class = function()
576 {
577 /**
578 * Delay actions always complete, even if cancelled or aborted early.
579 * @const
580 * @type {boolean}
581 */
582 this.reliable = true;
583 };
584 Action.Delay_class.prototype = new Action.Asynchronous_Action();
585
586 /**
587 * Initialization function for use by instance constructors.
588 * @param f
589 * @param duration
590 * @param finisher
591 * @param catcher
592 */
593 Action.Delay_class.init = function( f, duration, finisher, catcher )
594 {
595 Action.Asynchronous_Action.init.call( this, finisher, catcher );
596 this.trial = f;
597 this.duration = duration;
598 };
599
600 Action.Delay_class.prototype._go = function()
601 {
602 this.timer_id = Action_Platform.set_timer( this._body.bind( this ), this.durat ion );
603 };
604
605 Action.Delay_class.prototype._body = function()
606 {
607 if ( this.completed )
608 return;
609 try
610 {
611 if ( this.trial )
612 this._argv = this.trial();
613 }
614 catch ( e )
615 {
616 this.end_badly( e );
617 return;
618 }
619 this.end_well();
620 };
621
622 Action.Delay_class.prototype.terminate = function()
623 {
624 Action_Platform.clear_timer( this.timer_id );
625 };
626
627 Action.Delay = function( f, duration, finisher, catcher )
628 {
629 Action.Delay_class.init.call( this, f, duration, finisher, catcher );
630 };
631 Action.Delay.prototype = new Action.Delay_class();
632
633 //-------------------------------------------------------
634 // Join_class
635 //-------------------------------------------------------
636 /**
637 * Class constructor for objects that join on a single action, perhaps with modi fications.
638 *
639 * The policy for this class is that completion of the joined action causes comp letion of this action. This is a simple
640 * join policy. Joining on more than action requires a more complicated action. This policy, however, is suitable not
641 * only for Join itself, but also for Join_Timeout. Join_Timeout also depends on ly upon a single action; its timer is
642 * internal and doesn't incur the overhead of an action for that simple case.
643 *
644 * @constructor
645 * @extends {Action.Asynchronous_Action}
646 */
647 Action.Join_class = function()
648 {
649 };
650 Action.Join_class.prototype = new Action.Asynchronous_Action();
651
652 /**
653 * Initialization function for instance constructors.
654 *
655 * @this {Action.Join_class}
656 * @param action
657 * @param finisher
658 * @param catcher
659 */
660 Action.Join_class.init = function( action, finisher, catcher )
661 {
662 if ( !action )
663 throw new Error( "Action to be joined may not be null" );
664 Action.Asynchronous_Action.init.call( this, finisher, catcher );
665 this.joined_action = action;
666 };
667
668 /**
669 * The action body for a join is to do nothing when the joined action is not yet completed.
670 * @private
671 */
672 Action.Join_class.prototype._go = function()
673 {
674 this._argv = Array.prototype.slice.call( arguments );
675
676 if ( this.joined_action.completed )
677 {
678 if ( this.joined_action.completed_well )
679 {
680 this.end_well();
681 }
682 else
683 {
684 this.end_badly( this.joined_action.exception );
685 }
686 }
687 else
688 {
689 this.watch_id = this.joined_action.watch( this, null );
690 }
691 };
692
693 /**
694 * A good completion of the joined action yields a good completion for us.
695 */
696 Action.Join_class.prototype.notice_good = function()
697 {
698 this.end_well();
699 };
700
701 /**
702 * A bad completion of the joined action yields a bad completion for us.
703 */
704 Action.Join_class.prototype.notice_bad = function( id, e )
705 {
706 this.end_badly( e );
707 };
708
709 /**
710 * We treat object references as resources and null them out. This is a defense against creating or aggravating
711 * memory leaks.
712 */
713 Action.Join_class.prototype.terminate = function()
714 {
715 this.joined_action.withdraw( this.watch_id );
716 this.joined_action = null;
717 };
718
719 //-------------------------------------------------------
720 // Join
721 //-------------------------------------------------------
722 /**
723 * Join with another action. The completion of the action joined allows the join action to complete.
724 *
725 * @constructor
726 * @param {Action.Asynchronous_Action} action
727 * @param [finisher]
728 * @param [catcher]
729 */
730 Action.Join = function( action, finisher, catcher )
731 {
732 Action.Join_class.init.call( this, action, finisher, catcher );
733 };
734 Action.Join.prototype = new Action.Join_class();
735
736 //-------------------------------------------------------
737 // Join_Timeout
738 //-------------------------------------------------------
739 /**
740 * Join with another action and set a timer that may preemptively complete.
741 *
742 * Note that the timer starts on go() regardless of the state of the joined acti on. If you invoke a Join_Timeout on an
743 * action which never itself is invoked, then the timeout always causes completi on.
744 *
745 * It would be perfectly reasonable to have an action which started a timer when some other action began, but that would
746 * require a subclass of Asynchronous_Action that had a hook at the start of exe cution, rather than merely upon
747 * completion.
748 *
749 * @constructor
750 * @param {Action.Asynchronous_Action} action
751 * @param duration
752 * @param [finisher]
753 * @param [catcher]
754 */
755 Action.Join_Timeout = function( action, duration, finisher, catcher )
756 {
757 Action.Join_class.init.call( this, action, finisher, catcher );
758
759 /**
760 * The identifier of the platform timer. It's used for early termination of th e timer, if needed.
761 * @type {*}
762 */
763 this._timer_id = null;
764
765 this.duration = duration;
766
767 /**
768 * Flag indicating that the action has timed out.
769 * @type {boolean}
770 * @private
771 */
772 this._timed_out = false;
773 };
774 Action.Join_Timeout.prototype = new Action.Join_class();
775
776 /**
777 * Flag indicating that the action has timed out. This is a read-only version of a private property, but with the
778 * caveat that calling it is only valid in a completed state.
779 *
780 * @this {Action.Join_Timeout}
781 * @return {boolean}
782 */
783 Object.defineProperty( Action.Join_Timeout.prototype, "timed_out", {
784 /**
785 * @this {Action.Join_Timeout}
786 */
787 get: function()
788 {
789 if ( this.completed )
790 {
791 return this._timed_out;
792 }
793 else
794 {
795 throw new Error( "Action is not yet completed." );
796 }
797 }
798 } );
799
800 Action.Join_Timeout.prototype._go = function()
801 {
802 Action.Join_class.prototype._go.apply( this, arguments );
803 if ( !this.completed )
804 {
805 this._timer_id = Action_Platform.set_timer( this.ding.bind( this ), this.dur ation );
806 }
807 };
808
809 /**
810 * The timer just went off.
811 */
812 Action.Join_Timeout.prototype.ding = function()
813 {
814 this._timer_id = null;
815 /*
816 * If we've already completed, we don't change the completion state.
817 */
818 if ( this.completed )
819 return;
820 /*
821 * Since we haven't completed at this point, the timer has gone off before the action completed. Timeout is
822 * considered an exceptional completion.
823 */
824 this._timed_out = true;
825 this.end_badly( new Error( "Action timed out." ) );
826 };
827
828 /**
829 * Termination requires clearing any timer that may still be active.
830 *
831 * @override
832 */
833 Action.Join_Timeout.prototype.terminate = function()
834 {
835 /*
836 * Clear the timer so it doesn't ding. Clear the timer id because as an object reference it acts as a resource.
837 */
838 if ( this._timer_id )
839 {
840 Action_Platform.clear_timer( this._timer_id );
841 this._timer_id = null;
842 }
843
844 Action.Join_class.prototype.terminate.call( this );
845 };
846
847 //-------------------------------------------------------
848 /**
849 * @constructor
850 * @implements {Action.JM_Attentive}
851 * @param {Array.Joinable} actions
852 */
853 Action.Join_Conjunction = function( actions )
854 {
855 /**
856 * The conjunction of actions is reliable only if all the actions are reliable .
857 */
858 this.reliable = true;
859 for ( var j = 0 ; j < actions.length ; ++j )
860 {
861 if ( !actions[ j ].reliable )
862 {
863 this.reliable = false;
864 break;
865 }
866 }
867 };
868
869 Action.Join_Conjunction.prototype.notice_good = function()
870 {
871 };
872
873 Action.Join_Conjunction.prototype.notice_bad = function()
874 {
875 };
876
877 /*
878 * The guard around the definition of 'exports' is present because in js-test-dr iver it's not in scope.
879 */
880 if ( typeof exports === 'undefined' )
881 exports = {};
882 exports.Action = Action;
OLDNEW
« no previous file with comments | « chrome/skin/text-x-source.png ('k') | lib/action_platform.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld