~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/extensions/irc/js/lib/command-manager.js

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * The contents of this file are subject to the Mozilla Public
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/MPL/
 
7
 *
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 *
 
13
 * The Original Code is JSIRC Library
 
14
 *
 
15
 * The Initial Developer of the Original Code is New Dimensions Consulting,
 
16
 * Inc. Portions created by New Dimensions Consulting, Inc. are
 
17
 * Copyright (C) 1999 New Dimenstions Consulting, Inc. All
 
18
 * Rights Reserved.
 
19
 *
 
20
 * Contributor(s):
 
21
 *  Robert Ginda, rginda@ndcico.com, original author
 
22
 */
 
23
 
 
24
function getAccessKey (str)
 
25
{
 
26
    var i = str.indexOf("&");
 
27
    if (i == -1)
 
28
        return "";
 
29
    return str[i + 1];
 
30
}
 
31
 
 
32
function CommandRecord (name, func, usage, help, label, flags, keystr, tip,
 
33
                        format)
 
34
{
 
35
    this.name = name;
 
36
    this.func = func;
 
37
    this._usage = usage;
 
38
    this.scanUsage();
 
39
    this.help = help;
 
40
    this.label = label ? label : name;
 
41
    this.format = format;
 
42
    this.labelstr = label.replace ("&", "");
 
43
    this.tip = tip;
 
44
    this.flags = flags;
 
45
    this._enabled = true;
 
46
    this.keyNodes = new Array();
 
47
    this.keystr = keystr;
 
48
    this.uiElements = new Array();
 
49
}
 
50
 
 
51
CommandRecord.prototype.__defineGetter__ ("enabled", cr_getenable);
 
52
function cr_getenable ()
 
53
{
 
54
    return this._enabled;
 
55
}
 
56
 
 
57
CommandRecord.prototype.__defineSetter__ ("enabled", cr_setenable);
 
58
function cr_setenable (state)
 
59
{
 
60
    for (var i = 0; i < this.uiElements.length; ++i)
 
61
    {
 
62
        if (state)
 
63
            this.uiElements[i].removeAttribute ("disabled");
 
64
        else
 
65
            this.uiElements[i].setAttribute ("disabled", "true");
 
66
    }
 
67
    return (this._enabled = state);
 
68
}
 
69
 
 
70
CommandRecord.prototype.__defineSetter__ ("usage", cr_setusage);
 
71
function cr_setusage (usage)
 
72
{
 
73
    this._usage = usage;
 
74
    this.scanUsage();
 
75
}
 
76
 
 
77
CommandRecord.prototype.__defineGetter__ ("usage", cr_getusage);
 
78
function cr_getusage()
 
79
{
 
80
    return this._usage;
 
81
}
 
82
 
 
83
/**
 
84
 * Internal use only.
 
85
 *
 
86
 * Scans the argument spec, in the format "<a1> <a2> [<o1> <o2>]", into an
 
87
 * array of strings.
 
88
 */
 
89
CommandRecord.prototype.scanUsage =
 
90
function cr_scanusage()
 
91
{
 
92
    var spec = this._usage;
 
93
    var currentName = "";
 
94
    var inName = false;
 
95
    var len = spec.length;
 
96
    var capNext = false;
 
97
    
 
98
    this._usage = spec;
 
99
    this.argNames = new Array();
 
100
 
 
101
    for (var i = 0; i < len; ++i)
 
102
    {
 
103
        switch (spec[i])
 
104
        {
 
105
            case '[':
 
106
                this.argNames.push (":");
 
107
                break;
 
108
                
 
109
            case '<':
 
110
                inName = true;
 
111
                break;
 
112
                
 
113
            case '-':
 
114
                capNext = true;
 
115
                break;
 
116
                
 
117
            case '>':
 
118
                inName = false;
 
119
                this.argNames.push (currentName);
 
120
                currentName = "";
 
121
                capNext = false;
 
122
                break;
 
123
                
 
124
            default:
 
125
                if (inName)
 
126
                    currentName += capNext ? spec[i].toUpperCase() : spec[i];
 
127
                capNext = false;
 
128
                break;
 
129
        }
 
130
    }
 
131
}
 
132
 
 
133
/**
 
134
 * Returns the command formatted for presentation as part of online
 
135
 * documentation.
 
136
 */
 
137
CommandRecord.prototype.getDocumentation =
 
138
function cr_getdocs(flagFormatter)
 
139
 
 
140
{
 
141
    var str;
 
142
    str  = getMsg(MSG_DOC_COMMANDLABEL,
 
143
                  [this.label.replace("&", ""), this.name]) + "\n";
 
144
    str += getMsg(MSG_DOC_KEY, this.keystr ? this.keystr : MSG_VAL_NA) + "\n";
 
145
    str += getMsg(MSG_DOC_SYNTAX, [this.name, this.usage]) + "\n";
 
146
    str += MSG_DOC_NOTES + "\n";
 
147
    str += (flagFormatter ? flagFormatter(this.flags) : this.flags) + "\n\n";
 
148
    str += MSG_DOC_DESCRIPTION + "\n";
 
149
    str += wrapText(this.help, 75) + "\n";
 
150
    return str;
 
151
}
 
152
 
 
153
CommandRecord.prototype.argNames = new Array();
 
154
 
 
155
function CommandManager (defaultBundle)
 
156
{
 
157
    this.commands = new Object();
 
158
    this.defaultBundle = defaultBundle;
 
159
}
 
160
 
 
161
CommandManager.prototype.defaultFlags = 0;
 
162
 
 
163
CommandManager.prototype.defineCommands =
 
164
function cmgr_defcmds (cmdary)
 
165
{
 
166
    var len = cmdary.length;
 
167
    var commands = new Object();
 
168
    var bundle = "stringBundle" in cmdary ? cmdary.stringBundle : null;
 
169
    
 
170
    for (var i = 0; i < len; ++i)
 
171
    {
 
172
        var name  = cmdary[i][0];
 
173
        var func  = cmdary[i][1];
 
174
        var flags = cmdary[i][2];
 
175
        var usage;
 
176
        if (3 in cmdary[i])
 
177
            usage = cmdary[i][3];
 
178
        
 
179
        var command = this.defineCommand(name, func, flags, usage, bundle);
 
180
        commands[name] = command;
 
181
 
 
182
    }
 
183
 
 
184
    return commands;
 
185
}
 
186
 
 
187
CommandManager.prototype.defineCommand =
 
188
function cmdmgr_defcmd (name, func, flags, usage, bundle)
 
189
{
 
190
    if (!bundle)
 
191
        bundle = this.defaultBundle;
 
192
 
 
193
    var helpDefault;
 
194
    var labelDefault = name;
 
195
    var aliasFor;
 
196
 
 
197
    if (typeof flags != "number")
 
198
        flags = this.defaultFlags;
 
199
    
 
200
    if (flags & CMD_NO_HELP)
 
201
        helpDefault = MSG_NO_HELP;
 
202
    
 
203
    if (typeof usage != "string")
 
204
        usage = getMsgFrom(bundle, "cmd." + name + ".params", null, "");
 
205
 
 
206
    if (typeof func == "string")
 
207
    {
 
208
        var ary = func.match(/(\S+)/);
 
209
        if (ary)
 
210
            aliasFor = ary[1];
 
211
        else
 
212
            aliasFor = null;
 
213
        helpDefault = getMsg (MSG_DEFAULT_ALIAS_HELP, func); 
 
214
        if (aliasFor)
 
215
            labelDefault = getMsgFrom (bundle, "cmd." + aliasFor + ".label",
 
216
                                       null, name);
 
217
    }
 
218
    
 
219
    var label = getMsgFrom(bundle, "cmd." + name + ".label", null,
 
220
                           labelDefault);
 
221
    var help  = getMsgFrom(bundle, "cmd." + name + ".help", null,
 
222
                           helpDefault);
 
223
    var keystr = getMsgFrom (bundle, "cmd." + name + ".key", null, "");
 
224
    var format = getMsgFrom (bundle, "cmd." + name + ".format", null, null);
 
225
    var tip = getMsgFrom (bundle, "cmd." + name + ".tip", null, "");
 
226
    var command = new CommandRecord (name, func, usage, help, label, flags,
 
227
                                     keystr, tip, format);
 
228
    this.addCommand(command);
 
229
    if (aliasFor)
 
230
        command.aliasFor = aliasFor;
 
231
 
 
232
    return command;
 
233
}
 
234
 
 
235
CommandManager.prototype.installKeys =
 
236
function cmgr_instkeys (document, commands)
 
237
{
 
238
    var parentElem = document.getElementById("dynamic-keys");
 
239
    if (!parentElem)
 
240
    {
 
241
        parentElem = document.createElement("keyset");
 
242
        parentElem.setAttribute ("id", "dynamic-keys");
 
243
        document.firstChild.appendChild (parentElem);
 
244
    }
 
245
 
 
246
    if (!commands)
 
247
        commands = this.commands;
 
248
    
 
249
    for (var c in commands)
 
250
        this.installKey (parentElem, commands[c]);
 
251
}
 
252
 
 
253
/**
 
254
 * Create a <key> node relative to a DOM node.  Usually called once per command,
 
255
 * per document, so that accelerator keys work in all application windows.
 
256
 *
 
257
 * @param parentElem  A reference to the DOM node which should contain the new
 
258
 *                    <key> node.
 
259
 * @param command     reference to the CommandRecord to install.
 
260
 */
 
261
CommandManager.prototype.installKey =
 
262
function cmgr_instkey (parentElem, command)
 
263
{
 
264
    if (!command.keystr)
 
265
        return;
 
266
 
 
267
    var ary = command.keystr.match (/(.*\s)?([\S]+)$/);
 
268
    if (!ASSERT(ary, "couldn't parse key string ``" + command.keystr +
 
269
                "'' for command ``" + command.name + "''"))
 
270
    {
 
271
        return;
 
272
    }
 
273
    
 
274
    var key = document.createElement ("key");
 
275
    key.setAttribute ("id", "key:" + command.name);
 
276
    key.setAttribute ("oncommand", "dispatch('" + command.name +
 
277
                      "', {isInteractive: true});");
 
278
    key.setAttribute ("modifiers", ary[1]);
 
279
    if (ary[2].indexOf("VK_") == 0)
 
280
        key.setAttribute ("keycode", ary[2]);
 
281
    else
 
282
        key.setAttribute ("key", ary[2]);
 
283
    
 
284
    parentElem.appendChild(key);
 
285
    command.keyNodes.push(key);
 
286
}
 
287
 
 
288
CommandManager.prototype.uninstallKeys =
 
289
function cmgr_uninstkeys (commands)
 
290
{
 
291
    if (!commands)
 
292
        commands = this.commands;
 
293
    
 
294
    for (var c in commands)
 
295
        this.uninstallKey (commands[c]);
 
296
}
 
297
 
 
298
CommandManager.prototype.uninstallKey =
 
299
function cmgr_uninstkey (command)
 
300
{
 
301
    for (var i in command.keyNodes)
 
302
    {
 
303
        try
 
304
        {
 
305
            /* document may no longer exist in a useful state. */
 
306
            command.keyNodes[i].parentNode.removeChild(command.keyNodes[i]);
 
307
        }
 
308
        catch (ex)
 
309
        {
 
310
            dd ("*** caught exception uninstalling key node: " + ex);
 
311
        }
 
312
    }
 
313
}
 
314
 
 
315
/**
 
316
 * Register a new command with the manager.
 
317
 */
 
318
CommandManager.prototype.addCommand =
 
319
function cmgr_add (command)
 
320
{
 
321
    this.commands[command.name] = command;
 
322
}
 
323
 
 
324
CommandManager.prototype.removeCommands = 
 
325
function cmgr_removes (commands)
 
326
{
 
327
    for (var c in commands)
 
328
        this.removeCommand(commands[c]);
 
329
}
 
330
 
 
331
CommandManager.prototype.removeCommand = 
 
332
function cmgr_remove (command)
 
333
{
 
334
    delete this.commands[command.name];
 
335
}
 
336
 
 
337
/**
 
338
 * Register a hook for a particular command name.  |id| is a human readable
 
339
 * identifier for the hook, and can be used to unregister the hook.  If you
 
340
 * re-use a hook id, the previous hook function will be replaced.
 
341
 * If |before| is |true|, the hook will be called *before* the command executes,
 
342
 * if |before| is |false|, or not specified, the hook will be called *after*
 
343
 * the command executes.
 
344
 */
 
345
CommandManager.prototype.addHook =
 
346
function cmgr_hook (commandName, func, id, before)
 
347
{
 
348
    if (!ASSERT(commandName in this.commands,
 
349
                "Unknown command '" + commandName + "'"))
 
350
    {
 
351
        return;
 
352
    }
 
353
    
 
354
    var command = this.commands[commandName];
 
355
    
 
356
    if (before)
 
357
    {
 
358
        if (!("beforeHooks" in command))
 
359
            command.beforeHooks = new Object();
 
360
        command.beforeHooks[id] = func;
 
361
    }
 
362
    else
 
363
    {
 
364
        if (!("afterHooks" in command))
 
365
            command.afterHooks = new Object();
 
366
        command.afterHooks[id] = func;
 
367
    }
 
368
}
 
369
 
 
370
CommandManager.prototype.addHooks =
 
371
function cmgr_hooks (hooks, prefix)
 
372
{
 
373
    if (!prefix)
 
374
        prefix = "";
 
375
    
 
376
    for (var h in hooks)
 
377
    {
 
378
        this.addHook(h, hooks[h], prefix + ":" + h, 
 
379
                     ("_before" in hooks[h]) ? hooks[h]._before : false);
 
380
    }
 
381
}
 
382
 
 
383
CommandManager.prototype.removeHooks =
 
384
function cmgr_remhooks (hooks, prefix)
 
385
{
 
386
    if (!prefix)
 
387
        prefix = "";
 
388
    
 
389
    for (var h in hooks)
 
390
    {
 
391
        this.removeHook(h, prefix + ":" + h, 
 
392
                        ("before" in hooks[h]) ? hooks[h].before : false);
 
393
    }
 
394
}
 
395
 
 
396
CommandManager.prototype.removeHook =
 
397
function cmgr_unhook (commandName, id, before)
 
398
{
 
399
    var command = this.commands[commandName];
 
400
 
 
401
    if (before)
 
402
        delete command.beforeHooks[id];
 
403
    else
 
404
        delete command.afterHooks[id];
 
405
}
 
406
 
 
407
/**
 
408
 * Return an array of all CommandRecords which start with the string
 
409
 * |partialName|, sorted by |label| property.
 
410
 *
 
411
 * @param   partialName Prefix to search for.
 
412
 * @param   flags       logical ANDed with command flags.
 
413
 * @returns array       Array of matching commands, sorted by |label| property.
 
414
 */
 
415
CommandManager.prototype.list =
 
416
function cmgr_list (partialName, flags)
 
417
{
 
418
    /* returns array of command objects which look like |partialName|, or
 
419
     * all commands if |partialName| is not specified */
 
420
    function compare (a, b)
 
421
    {
 
422
        a = a.labelstr.toLowerCase();
 
423
        b = b.labelstr.toLowerCase();
 
424
 
 
425
        if (a == b)
 
426
            return 0;
 
427
 
 
428
        if (a > b)
 
429
            return 1;
 
430
 
 
431
        return -1;
 
432
    }
 
433
 
 
434
    var ary = new Array();
 
435
    var commandNames = keys(this.commands);
 
436
 
 
437
    /* a command named "eval" wouldn't show up in the result of keys() because
 
438
     * eval is not-enumerable, even if overwritten. */
 
439
    if ("eval" in this.commands && typeof this.commands.eval == "object")
 
440
        commandNames.push ("eval");
 
441
 
 
442
    for (var i in commandNames)
 
443
    {
 
444
        var name = commandNames[i];
 
445
        if (!flags || (this.commands[name].flags & flags))
 
446
        {
 
447
            if (!partialName ||
 
448
                this.commands[name].name.indexOf(partialName) == 0)
 
449
            {
 
450
                if (partialName && 
 
451
                    partialName.length == this.commands[name].name.length)
 
452
                {
 
453
                    /* exact match */
 
454
                    return [this.commands[name]];
 
455
                }
 
456
                else
 
457
                {
 
458
                    ary.push (this.commands[name]);
 
459
                }
 
460
            }
 
461
        }
 
462
    }
 
463
 
 
464
    ary.sort(compare);
 
465
    return ary;
 
466
}
 
467
 
 
468
/**
 
469
 * Return a sorted array of the command names which start with the string
 
470
 * |partialName|.
 
471
 *
 
472
 * @param   partialName Prefix to search for.
 
473
 * @param   flags       logical ANDed with command flags.
 
474
 * @returns array       Sorted Array of matching command names.
 
475
 */
 
476
CommandManager.prototype.listNames =
 
477
function cmgr_listnames (partialName, flags)
 
478
{
 
479
    var cmds = this.list(partialName, flags);
 
480
    var cmdNames = new Array();
 
481
    
 
482
    for (var c in cmds)
 
483
        cmdNames.push (cmds[c].name);
 
484
 
 
485
    cmdNames.sort();
 
486
    return cmdNames;
 
487
}
 
488
 
 
489
/**
 
490
 * Internal use only.
 
491
 *
 
492
 * Called to parse the arguments stored in |e.inputData|, as properties of |e|,
 
493
 * for the CommandRecord stored on |e.command|.
 
494
 *
 
495
 * @params e  Event object to be processed.
 
496
 */
 
497
CommandManager.prototype.parseArguments =
 
498
function cmgr_parseargs (e)
 
499
{
 
500
    var rv = this.parseArgumentsRaw(e);
 
501
    //dd("parseArguments '" + e.command.usage + "' " + 
 
502
    //   (rv ? "passed" : "failed") + "\n" + dumpObjectTree(e));
 
503
    delete e.currentArgIndex;
 
504
    return rv;
 
505
}
 
506
 
 
507
/**
 
508
 * Internal use only.
 
509
 *
 
510
 * Don't call parseArgumentsRaw directly, use parseArguments instead.
 
511
 *
 
512
 * Parses the arguments stored in the |inputData| property of the event object,
 
513
 * according to the format specified by the |command| property.
 
514
 *
 
515
 * On success this method returns true, and propery names corresponding to the
 
516
 * argument names used in the format spec will be created on the event object.
 
517
 * All optional parameters will be initialized to |null| if not already present
 
518
 * on the event.
 
519
 *
 
520
 * On failure this method returns false and a description of the problem
 
521
 * will be stored in the |parseError| property of the event.
 
522
 *
 
523
 * For example...
 
524
 * Given the argument spec "<int> <word> [ <word2> <word3> ]", and given the
 
525
 * input string "411 foo", stored as |e.command.usage| and |e.inputData|
 
526
 * respectively, this method would add the following propertys to the event
 
527
 * object...
 
528
 *   -name---value--notes-   
 
529
 *   e.int    411   Parsed as an integer
 
530
 *   e.word   foo   Parsed as a string
 
531
 *   e.word2  null  Optional parameters not specified will be set to null.
 
532
 *   e.word3  null  If word2 had been provided, word3 would be required too.
 
533
 *
 
534
 * Each parameter is parsed by calling the function with the same name, located
 
535
 * in this.argTypes.  The first parameter is parsed by calling the function
 
536
 * this.argTypes["int"], for example.  This function is expected to act on
 
537
 * e.unparsedData, taking it's chunk, and leaving the rest of the string.
 
538
 * The default parse functions are...
 
539
 *   <word>    parses contiguous non-space characters.
 
540
 *   <int>     parses as an int.
 
541
 *   <rest>    parses to the end of input data.
 
542
 *   <state>   parses yes, on, true, 1, 0, false, off, no as a boolean.
 
543
 *   <toggle>  parses like a <state>, except allows "toggle" as well.
 
544
 *   <...>     parses according to the parameter type before it, until the end
 
545
 *             of the input data.  Results are stored in an array named
 
546
 *             paramnameList, where paramname is the name of the parameter
 
547
 *             before <...>.  The value of the parameter before this will be
 
548
 *             paramnameList[0].
 
549
 *
 
550
 * If there is no parse function for an argument type, "word" will be used by
 
551
 * default.  You can alias argument types with code like...
 
552
 * commandManager.argTypes["my-integer-name"] = commandManager.argTypes["int"];
 
553
 */
 
554
CommandManager.prototype.parseArgumentsRaw =
 
555
function parse_parseargsraw (e)
 
556
{
 
557
    var argc = e.command.argNames.length;
 
558
    
 
559
    function initOptionals()
 
560
    {
 
561
        for (var i = 0; i < argc; ++i)
 
562
        {
 
563
            if (e.command.argNames[i] != ":" && 
 
564
                e.command.argNames[i] != "..."  && 
 
565
                !(e.command.argNames[i] in e))
 
566
            {
 
567
                e[e.command.argNames[i]] = null;
 
568
            }
 
569
 
 
570
            if (e.command.argNames[i] == "...")
 
571
            {
 
572
                var paramName = e.command.argNames[i - 1];
 
573
                if (paramName == ":")
 
574
                    paramName = e.command.argNames[i - 2];
 
575
                var listName = paramName + "List";
 
576
                if (!(listName in e))
 
577
                    e[listName] = [ e[paramName] ];
 
578
            }
 
579
        }
 
580
    }
 
581
        
 
582
    if ("inputData" in e && e.inputData)
 
583
    {
 
584
        /* if data has been provided, parse it */
 
585
        e.unparsedData = e.inputData;
 
586
        var parseResult;
 
587
        var currentArg;
 
588
        e.currentArgIndex = 0;
 
589
 
 
590
        if (argc)
 
591
        {
 
592
            currentArg = e.command.argNames[e.currentArgIndex];
 
593
        
 
594
            while (e.unparsedData)
 
595
            {
 
596
                if (currentArg != ":")
 
597
                {
 
598
                    if (!this.parseArgument (e, currentArg))
 
599
                        return false;
 
600
                }
 
601
                if (++e.currentArgIndex < argc)
 
602
                    currentArg = e.command.argNames[e.currentArgIndex];
 
603
                else
 
604
                    break;
 
605
            }
 
606
 
 
607
            if (e.currentArgIndex < argc && currentArg != ":")
 
608
            {
 
609
                /* parse loop completed because it ran out of data.  We haven't
 
610
                 * parsed all of the declared arguments, and we're not stopped
 
611
                 * at an optional marker, so we must be missing something
 
612
                 * required... */
 
613
                e.parseError = getMsg(MSG_ERR_REQUIRED_PARAM, 
 
614
                                      e.command.argNames[e.currentArgIndex]);
 
615
                return false;
 
616
            }
 
617
        }
 
618
        
 
619
        if (e.unparsedData)
 
620
        {
 
621
            /* parse loop completed with unparsed data, which means we've
 
622
             * successfully parsed all arguments declared.  Whine about the
 
623
             * extra data... */
 
624
            display (getMsg(MSG_EXTRA_PARAMS, e.unparsedData), MT_WARN);
 
625
        }
 
626
    }
 
627
 
 
628
    var rv = this.isCommandSatisfied(e);
 
629
    if (rv)
 
630
        initOptionals();
 
631
    return rv;
 
632
}
 
633
 
 
634
/**
 
635
 * Returns true if |e| has the properties required to call the command
 
636
 * |command|.
 
637
 *
 
638
 * If |command| is not provided, |e.command| is used instead.
 
639
 *
 
640
 * @param e        Event object to test against the command.
 
641
 * @param command  Command to test.
 
642
 */
 
643
CommandManager.prototype.isCommandSatisfied =
 
644
function cmgr_isok (e, command)
 
645
{
 
646
    if (typeof command == "undefined")
 
647
        command = e.command;
 
648
    else if (typeof command == "string")
 
649
        command = this.commands[command];
 
650
    
 
651
    if (!command.enabled)
 
652
        return false;
 
653
 
 
654
    for (var i = 0; i < command.argNames.length; ++i)
 
655
    {
 
656
        if (command.argNames[i] == ":")
 
657
             return true;
 
658
        
 
659
        if (!(command.argNames[i] in e))
 
660
        {
 
661
            e.parseError = getMsg(MSG_ERR_REQUIRED_PARAM, command.argNames[i]);
 
662
            //dd("command '" + command.name + "' unsatisfied: " + e.parseError);
 
663
            return false;
 
664
        }
 
665
    }
 
666
 
 
667
    //dd ("command '" + command.name + "' satisfied.");
 
668
    return true;
 
669
}
 
670
 
 
671
/**
 
672
 * Internal use only.
 
673
 * See parseArguments above and the |argTypes| object below.
 
674
 *
 
675
 * Parses the next argument by calling an appropriate parser function, or the
 
676
 * generic "word" parser if none other is found.
 
677
 *
 
678
 * @param e     event object.
 
679
 * @param name  property name to use for the parse result.
 
680
 */
 
681
CommandManager.prototype.parseArgument =
 
682
function cmgr_parsearg (e, name)
 
683
{
 
684
    var parseResult;
 
685
    
 
686
    if (name in this.argTypes)
 
687
        parseResult = this.argTypes[name](e, name, this);
 
688
    else
 
689
        parseResult = this.argTypes["word"](e, name, this);
 
690
 
 
691
    if (!parseResult)
 
692
        e.parseError = getMsg(MSG_ERR_INVALID_PARAM,
 
693
                              [name, e.unparsedData]);
 
694
 
 
695
    return parseResult;
 
696
}
 
697
 
 
698
CommandManager.prototype.argTypes = new Object();
 
699
 
 
700
/**
 
701
 * Convenience function used to map a list of new types to an existing parse
 
702
 * function.
 
703
 */
 
704
CommandManager.prototype.argTypes.__aliasTypes__ =
 
705
function at_alias (list, type)
 
706
{
 
707
    for (var i in list)
 
708
    {
 
709
        this[list[i]] = this[type];
 
710
    }
 
711
}
 
712
 
 
713
/**
 
714
 * Internal use only.
 
715
 *
 
716
 * Parses an integer, stores result in |e[name]|.
 
717
 */
 
718
CommandManager.prototype.argTypes["int"] =
 
719
function parse_int (e, name)
 
720
{
 
721
    var ary = e.unparsedData.match (/(\d+)(?:\s+(.*))?$/);
 
722
    if (!ary)
 
723
        return false;
 
724
    e[name] = Number(ary[1]);
 
725
    e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
 
726
    return true;
 
727
}
 
728
 
 
729
/**
 
730
 * Internal use only.
 
731
 *
 
732
 * Parses a word, which is defined as a list of nonspace characters.
 
733
 *
 
734
 * Stores result in |e[name]|.
 
735
 */
 
736
CommandManager.prototype.argTypes["word"] =
 
737
function parse_word (e, name)
 
738
{
 
739
    var ary = e.unparsedData.match (/(\S+)(?:\s+(.*))?$/);
 
740
    if (!ary)
 
741
        return false;
 
742
    e[name] = ary[1];
 
743
    e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
 
744
    return true;
 
745
}
 
746
 
 
747
/**
 
748
 * Internal use only.
 
749
 *
 
750
 * Parses a "state" which can be "true", "on", "yes", or 1 to indicate |true|,
 
751
 * or "false", "off", "no", or 0 to indicate |false|.
 
752
 *
 
753
 * Stores result in |e[name]|.
 
754
 */
 
755
CommandManager.prototype.argTypes["state"] =
 
756
function parse_state (e, name)
 
757
{
 
758
    var ary =
 
759
        e.unparsedData.match (/(true|on|yes|1|false|off|no|0)(?:\s+(.*))?$/i);
 
760
    if (!ary)
 
761
        return false;
 
762
    if (ary[1].search(/true|on|yes|1/i) != -1)
 
763
        e[name] = true;
 
764
    else
 
765
        e[name] = false;
 
766
    e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
 
767
    return true;
 
768
}
 
769
 
 
770
/**
 
771
 * Internal use only.
 
772
 *
 
773
 * Parses a "toggle" which can be "true", "on", "yes", or 1 to indicate |true|,
 
774
 * or "false", "off", "no", or 0 to indicate |false|.  In addition, the string
 
775
 * "toggle" is accepted, in which case |e[name]| will be the string "toggle".
 
776
 *
 
777
 * Stores result in |e[name]|.
 
778
 */
 
779
CommandManager.prototype.argTypes["toggle"] =
 
780
function parse_toggle (e, name)
 
781
{
 
782
    var ary = e.unparsedData.match
 
783
        (/(toggle|true|on|yes|1|false|off|no|0)(?:\s+(.*))?$/i);
 
784
 
 
785
    if (!ary)
 
786
        return false;
 
787
    if (ary[1].search(/toggle/i) != -1)
 
788
        e[name] = "toggle";
 
789
    else if (ary[1].search(/true|on|yes|1/i) != -1)
 
790
        e[name] = true;
 
791
    else
 
792
        e[name] = false;
 
793
    e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
 
794
    return true;
 
795
}
 
796
 
 
797
/**
 
798
 * Internal use only.
 
799
 *
 
800
 * Returns all unparsed data to the end of the line.
 
801
 *
 
802
 * Stores result in |e[name]|.
 
803
 */
 
804
CommandManager.prototype.argTypes["rest"] =
 
805
function parse_rest (e, name)
 
806
{
 
807
    e[name] = e.unparsedData;
 
808
    e.unparsedData = "";
 
809
    return true;
 
810
}
 
811
 
 
812
/**
 
813
 * Internal use only.
 
814
 *
 
815
 * Parses the rest of the unparsed data the same way the previous argument was
 
816
 * parsed.  Can't be used as the first parameter.  if |name| is "..." then the
 
817
 * name of the previous argument, plus the suffix "List" will be used instead.
 
818
 *
 
819
 * Stores result in |e[name]| or |e[lastName + "List"]|.
 
820
 */
 
821
CommandManager.prototype.argTypes["..."] =
 
822
function parse_repeat (e, name, cm)
 
823
{
 
824
    ASSERT (e.currentArgIndex > 0, "<...> can't be the first argument.");
 
825
    
 
826
    var lastArg = e.command.argNames[e.currentArgIndex - 1];
 
827
    if (lastArg == ":")
 
828
        lastArg = e.command.argNames[e.currentArgIndex - 2];
 
829
 
 
830
    var listName = lastArg + "List";
 
831
    e[listName] = [ e[lastArg] ];
 
832
    
 
833
    while (e.unparsedData)
 
834
    {
 
835
        if (!cm.parseArgument(e, lastArg))
 
836
            return false;
 
837
        e[listName].push(e[lastArg]);
 
838
    }    
 
839
 
 
840
    e[lastArg] = e[listName][0];
 
841
    return true;
 
842
}