checking in patch from Josh Gough, <exv@randomc.com>
muchas gracias!

irc.js
add whois, invite

utils.js
added map and mapObjFunc

chatzilla.xul
add cntxt menu for userlist

commands.js
hook up /away, op, deop, voice, devoice, echo, invite, and kick commands

hadlers.js
onload hack
whois, away, deop, op, voice, devoice, echo, invite, kick command implementations

static.js
more of the onload hack
addEventListener on toolbuttons

listbox.js
selection code

chatzilla.css
selicted list item style
This commit is contained in:
rginda%netscape.com 2000-04-24 00:40:56 +00:00
Родитель 160feec6b5
Коммит f98d2946fb
8 изменённых файлов: 713 добавлений и 46 удалений

Просмотреть файл

@ -386,6 +386,18 @@ function serv_actto (target, msg)
this.messageTo ("PRIVMSG", target, msg, "ACTION");
}
/**
* Abstracts the whois command.
*
* @param target intended user(s).
*/
CIRCServer.prototype.whois =
function serv_whois (target)
{
this.sendData ("WHOIS " + target + "\n");
}
CIRCServer.prototype.onDisconnect =
@ -1017,9 +1029,9 @@ CIRCServer.prototype.onPart =
function serv_part (e)
{
e.channel = new CIRCChannel (this, e.params[1]);
e.channel = new CIRCChannel (this, e.params[1]);
e.user = new CIRCChanUser (e.channel, e.user.nick);
delete e.channel.users[e.user.nick];
e.channel.removeUser(e.user.nick);
e.destObject = e.channel;
e.set = "channel";
@ -1303,6 +1315,22 @@ function chan_adduser (nick, isOp, isVoice)
}
CIRCChannel.prototype.getUser =
function chan_getuser (nick)
{
nick = nick.toLowerCase(); // assumes valid param!
var cuser = this.users[nick];
return cuser; // caller expected to check for undefinededness
}
CIRCChannel.prototype.removeUser =
function chan_removeuser (nick)
{
delete this.users[nick.toLowerCase()]; // see ya
}
CIRCChannel.prototype.getUsersLength =
function chan_userslen ()
{
@ -1372,6 +1400,20 @@ function chan_part ()
}
/**
* Invites a user to a channel.
*
* @param nick the user name to invite.
*/
CIRCChannel.prototype.invite =
function chan_inviteuser (nick)
{
this.parent.sendData("INVITE " + nick + " " + this.name + "\n");
return true;
}
/*
* channel mode
*/
@ -1625,6 +1667,14 @@ function usr_act (msg)
this.parent.actTo (this.nick, msg);
}
CIRCUser.prototype.whois =
function usr_whois ()
{
this.parent.whois (this.nick);
}
/*
* channel user
@ -1654,6 +1704,7 @@ function CIRCChanUser (parent, nick, isOp, isVoice)
this.say = cusr_say;
this.notice = cusr_notice;
this.act = cusr_act;
this.whois = cusr_whois;
this.parent = parent;
this.isOp = (typeof isOp != "undefined") ? isOp : false;
this.isVoice = (typeof isVoice != "undefined") ? isVoice : false;
@ -1771,3 +1822,10 @@ function cusr_act (msg)
this.__proto__.act (msg);
}
function cusr_whois ()
{
this.__proto__.whois ();
}

Просмотреть файл

@ -426,3 +426,41 @@ function getInterfaces (cls)
return rv;
}
/**
* Calls a named function for each element in an array, sending
* the same parameter each call.
*
* @param ary an array of objects
* @param func_name string name of function to call.
* @param data data object to pass to each object.
*/
function mapObjFunc(ary, func_name, data)
{
/*
* WARNING: Caller assumes resonsibility to verify ary
* and func_name
*/
for (var i in ary)
ary[i][func_name](data);
}
/**
* Passes each element of an array to a given function object.
*
* @param func a function object.
* @param ary an array of values.
*/
function map(func, ary) {
/*
* WARNING: Caller assumnes responsibility to verify
* func and ary.
*/
for (var i in ary)
func(ary[i]);
}

Просмотреть файл

@ -48,6 +48,20 @@
<html:script src="chrome://chatzilla/content/static.js"/>
<html:script src="chrome://chatzilla/content/handlers.js"/>
<!-- This is to test the popups in the 'quickList' channel name list -->
<popupset>
<popup id="userlistPopup" onclick="onUserListPopupClick(event)">
<menuitem value="Op" code="/op" />
<menuitem value="Deop" code="/deop" />
<menuitem value="Voice" code="/voice" />
<menuitem value="Devoice" code="/devoice" />
<menuitem value="Kick" code="/kick" />
<!-- <menuitem value="Kick With Reason" code="/kick" /> -->
<menuitem value="Whois" code="/whois" />
</popup>
</popupset>
<toolbox>
<menubar>
<menu value="Options">
@ -98,9 +112,9 @@
</toolbox>
<box id="outer-box" align="vertical" flex="1">
<box id="inner-box" align="horizontal" flex="1">
<html:div id="quickList" class="quick-list" flex="0" width-hidden="150px">
</html:div>
<box id="inner-box" align="horizontal" flex="1">
<box id="quickList" class="quick-list" align="vertical"
flex="0" width-hidden="150px" context="userlistPopup" />
<splitter id="main-splitter" align="vertical" collapse="before"/>
<box align="vertical" flex="1">
<html:iframe id="it-doesnt-matter-anyway" class="output-container"

Просмотреть файл

@ -53,7 +53,7 @@ function addCommands(commandObject)
add ("me", "onInputMe",
"<action>",
"Performs an 'action' on the current channel.");
add ("msg", "onInputMsg",
"<user> <msg>",
"Sends a private message <msg> to the user <user>.");
@ -113,4 +113,69 @@ function addCommands(commandObject)
"<new-topic>. If <new-topic> is *not* specified, the current topic " +
"will be displayed");
/* JG: commands below jan 24 2000, update mar 15 */
add ("away", "onInputAway",
"[<reason>]",
"If <reason> is spcecified, sets you away with that message. " +
"Used without <reason>, you are marked back as no longer being away.");
add ("op", "onInputOp",
"<nick>",
"Gives operator status to <nick> on current channel. Requires " +
"operator status.");
add ("deop", "onInputDeop",
"<nick>",
"Removes operator status from <nick> on current channel. " +
"Requires operator status.");
add ("voice", "onInputVoice",
"<nick>",
"Gives voice status to <nick> on current channel. " +
"Requires operator status.");
add ("devoice", "onInputDevoice",
"<nick>",
"Removes voice status from <nick> on current channel. " +
"Requires operator status.");
add ("echo", "onInputEcho",
"<text>",
"Displays <text> in the current view, but does not send it to " +
"the server.");
/* FIXME: JG: not implemented yet */
/*
add ("filter", "onInputFilter",
"<regex>",
"Shows only messages matching <regex> on current channel. When used " $
"with no parameter, the contents are restored.");
*/
add ("invite", "onInputInvite",
"<nick> [<channel>]",
"Invites <nick> to <channel> or current channel if not " +
"supplied. Requires operator status if +i is set.");
add ("kick", "onInputKick",
"[<channel>] <nick>",
"Kicks <nick> from <channel> or current channel if not " +
"supplied. Requires operator status.");
/* NOT implemented yet
add ("server", "onInputServer",
"<server> [<port>]",
"Connects you to <server> using port 6667 if <port> is not" +
" specified.");
*/
/* NOT implemented yet
add ("quit", "onInputExit",
"[<message>]",
"Terminates the connection with the server associated with the" +
" current view, overriding the default quit message with" +
" message if specified.");
*/
}

Просмотреть файл

@ -29,8 +29,11 @@ function onLoad()
initHost(client);
readIRCPrefs();
setOutputStyle (client.DEFAULT_STYLE);
initStatic();
setOutputStyle (client.DEFAULT_STYLE);
/*
Called in a callback once the document loads due to a current bug
*/
//initStatic();
mainStep();
}
@ -52,6 +55,7 @@ function onNotImplemented()
/* toolbaritem click */
function onTBIClick (id)
{
var tbi = document.getElementById (id);
var view = client.viewsArray[tbi.getAttribute("viewKey")];
@ -59,6 +63,28 @@ function onTBIClick (id)
}
/* popup click in user list */
function onUserListPopupClick (e)
{
var code = e.target.getAttribute("code");
var ary = code.substr(1, code.length).match (/(\S+)? ?(.*)/);
var command = ary[1];
var ev = new CEvent ("client", "input-command", client,
"onInputCommand");
ev.command = command;
ev.inputData = ary[2] ? stringTrim(ary[2]) : "";
ev.target = client.currentObject;
getObjectDetails (ev.target, ev);
client.eventPump.addEvent (ev);
}
function onToggleTraceHook()
{
var h = client.eventPump.getHook ("event-tracer");
@ -291,6 +317,7 @@ function onInputCompleteLine(e)
}
client.onInputCommand =
function cli_icommand (e)
{
@ -647,12 +674,12 @@ function cli_izoom (e)
return false;
}
var nick = e.inputData.toLowerCase();
var cuser = e.channel.users[nick];
var cuser = e.channel.getUser(e.inputData);
if (!cuser)
{
client.currentObject.display ("User '" + e.inputData + "' not found.");
client.currentObject.display ("User '" + e.inputData + "' not found.",
"ERROR");
return false;
}
@ -662,31 +689,43 @@ function cli_izoom (e)
}
client.onInputWhoIs =
function cli_whois (e)
/**
* Performs a whois on a user.
*/
client.onInputWhoIs =
function cli_whois (e)
{
if (!e.inputData)
return false;
if (!e.network || !e.network.isConnected())
{
if (!e.network)
client.currentObject.display ("No network selected.", "ERROR");
else
client.currentObject.display ("Network '" + e.network.name +
" is not connected.", "ERROR");
client.currentObject.display ("You must be connected to a network " +
"to use whois", "ERROR");
return false;
}
var nick = e.inputData.match(/\S+/);
e.server.sendData ("whois " + nick + "\n");
return true;
if (!e.inputData)
{
var nicksAry = e.channel.getSelectedUsers();
if (nicksAry)
{
dd ("Sending [" + nicksAry.length + "] nicks the whois call\n");
mapObjFunc(nicksAry, "whois", null);
return true;
}
else
{
return false;
}
}
// Otherwise, there is no guarantee that the username
// is currently a user
var nick = e.inputData.match( /\S+/ );
e.server.whois (nick);
return true;
}
client.onInputTopic =
function cli_itopic (e)
{
@ -720,6 +759,326 @@ function cli_itopic (e)
}
client.onInputAway =
function cli_iaway (e)
{
if (!e.network || !e.network.isConnected())
{
client.currentObject.display ("You must be connected to a network " +
"to use away.", "ERROR");
return false;
}
else if (!e.inputData)
{
e.server.sendData ("AWAY\n");
}
else
{
e.server.sendData ("AWAY " + e.inputData + "\n");
}
return true;
}
/**
* Removes operator status from a user.
*/
client.onInputDeop =
function cli_ideop (e)
{
/* NOTE: See the next function for a well commented explanation
of the general form of these Multiple-Target type functions */
if (!e.channel)
{
client.currentObject.display ("You must be on a channel to use " +
"to use deop.", "ERROR");
return false;
}
if (!e.inputData)
{
var nicksAry = e.channel.getSelectedUsers();
if (nicksAry)
{
mapObjFunc(nicksAry, "setOp", false);
return true;
}
else
{
return false;
}
}
var cuser = e.channel.getUser(e.inputData);
if (!cuser)
{
/* use e.inputData so the case is retained */
client.currentObject.display ("User '" + e.inputData + "' not found.",
"ERROR");
return false;
}
cuser.setOp(false);
return true;
}
/**
* Gives operator status to a channel user.
*/
client.onInputOp =
function cli_iop (e)
{
if (!e.channel)
{
client.currentObject.display ("You must be connected to a network " +
"to use op.", "ERROR");
return false;
}
if (!e.inputData)
{
/* Since no param is passed, check for selection */
var nicksAry = e.channel.getSelectedUsers();
/* If a valid array of user objects, then call the mapObjFunc */
if (nicksAry)
{
/* See test3-utils.js: this simply
applies the setOp function to every item
in nicksAry with the parameter of "true"
each time
*/
mapObjFunc(nicksAry, "setOp", true);
return true;
}
else
{
/* If no input and no selection, return false
to display the usage */
return false;
}
}
/* We do have inputData, so use that, rather than any
other option */
var cuser = e.channel.getUser(e.inputData);
if (!cuser)
{
client.currentObject.display ("User '" + e.inputData + "' not found.",
"ERROR");
return false;
}
cuser.setOp(true);
return true;
}
/**
* Gives voice status to a user.
*/
client.onInputVoice =
function cli_ivoice (e)
{
if (!e.channel)
{
client.currentObject.display ("You must be on a channel " +
"to use voice.", "ERROR");
return false;
}
if (!e.inputData)
{
var nicksAry = e.channel.getSelectedUsers();
if (nicksAry)
{
mapObjFunc(nicksAry, "setVoice", true);
return true;
}
else
{
return false;
}
}
var cuser = e.channel.getUser(e.inputData);
if (!cuser)
{
client.currentObject.display ("User '" + e.inputData + "' not found.",
"ERROR");
return false;
}
cuser.setVoice(true);
return true;
}
/**
* Removes voice status from a user.
*/
client.onInputDevoice =
function cli_devoice (e)
{
if (!e.channel)
{
client.currentObject.display ("You must be on a channel " +
"to use devoice.", "ERROR");
return false;
}
if (!e.inputData)
{
var nicksAry = e.channel.getSelectedUsers();
if (nicksAry)
{
mapObjFunc(nicksAry, "setVoice", false);
return true;
}
else
{
return false;
}
}
var cuser = e.channel.getUser(e.inputData);
if (!cuser)
{
client.currentObject.display ("User '" + e.inputData + "' not found.",
"ERROR");
return false;
}
cuser.setVoice(false);
return true;
}
/**
* Displays input to the current view, but doesn't send it to the server.
*/
client.onInputEcho =
function cli_iecho (e)
{
if (!e.inputData)
{
return false;
}
else
{
client.currentObject.display (e.inputData, "ECHO");
return true;
}
}
client.onInputInvite =
function cli_iinvite (e)
{
if (!e.network || !e.network.isConnected())
{
client.currentObject.display ("You must be connected to a network " +
"to use invite.", "ERROR");
return false;
}
else if (!e.channel)
{
client.currentObject.display
("You must be in a channel to use invite", "ERROR");
return false;
}
if (!e.inputData) {
return false;
}
else
{
var ary = e.inputData.split( /\s+/ );
if (ary.length == 1)
{
e.channel.invite (ary[0]);
}
else
{
var chan = e.server.channels[ary[1].toLowerCase()];
if (chan == undefined)
{
client.currentObject.display ("You must be on " + ary[1] +
" to use invite.", "ERROR");
return false;
}
chan.invite (ary[0]);
}
return true;
}
}
client.onInputKick =
function cli_ikick (e)
{
if (!e.channel)
{
client.currentObject.display ("You must be on a channel to use " +
"kick.", "ERROR");
return false;
}
if (!e.inputData)
{
var nicksAry = e.channel.getSelectedUsers();
if (nicksAry)
{
mapObjFunc(nicksAry, "kick", "");
return true;
}
else
{
return false;
}
}
var ary = e.inputData.match ( /(\S+)? ?(.*)/ );
var cuser = e.channel.getUser(ary[1]);
if (!cuser)
{
client.currentObject.display ("User '" + e.inputData + "' not found.",
"ERROR");
return false;
}
if (ary.length > 2)
{
cuser.kick(ary[2]);
}
else
cuser.kick();
return true;
}
/* 'private' function, should only be used from inside */
CIRCChannel.prototype._addToUserList =
function my_addtolist (user)
@ -1021,7 +1380,7 @@ function my_ckick (e)
e.reason + ")", "KICK", enforcerNick);
}
this.list.listContainer.removeChild (e.user.getDecoratedNick());
this.list.remove (e.lamer.getDecoratedNick());
updateChannel (e.channel);
@ -1110,3 +1469,4 @@ function my_notice (e)
this.display (e.meat, "NOTICE");
}

Просмотреть файл

@ -84,9 +84,11 @@ function initStatic()
obj = document.getElementById("input");
obj.onkeyup = onInputKeyUp;
//obj = document.getElementById("tb[*client*]");
client.quickList = new CListBox(document.getElementById("quickList"));
client.quickList.selectedItemCallback = quicklistCallback;
var saveDir = client.PRINT_DIRECTION;
client.PRINT_DIRECTION = 1;
client.display ("Welcome to ChatZilla...\n" +
@ -242,6 +244,17 @@ function mainStep()
}
function quicklistCallback (element, ndx, ary)
{
/* Check whether the selected attribute == true */
if (element.getAttribute("selected") == "true")
{
/* extract the nick data from the element */
/* Hmmm, nice walk ... */
ary.push(element.childNodes[0].childNodes[2].childNodes[0].nodeValue);
}
}
function getObjectDetails (obj, rv)
{
if (!rv)
@ -296,6 +309,8 @@ function setOutputStyle (styleSheet)
{
var oc = top.frames[0].document;
top.frames[0].setClientOutput = setClientOutput;
oc.close();
oc.open();
oc.write ("<html><head>");
@ -315,12 +330,21 @@ function setOutputStyle (styleSheet)
"TYPE='text/css' MEDIA='screen'>");
oc.write ("</head>" +
"<body><div id='output' class='output-window'></div></body>" +
"<body onload='setClientOutput(document)'>" +
"<div id='output' class='output-window'></div></body>" +
"</html>");
client.output = oc.getElementById ("output");
}
function setClientOutput(doc)
{
client.output = doc.getElementById("output");
/* continue processing now: */
initStatic();
}
function updateNetwork(obj)
{
var o = new Object();
@ -650,8 +674,9 @@ function getTBForObject (source, create)
{
var views = document.getElementById ("views-tbar");
var tbi = document.createElement ("toolbaritem");
tbi.setAttribute ("onclick", "onTBIClick('" + id + "')");
//tbi.setAttribute ("onclick", "onTBIClick('" + id + "')");
tbi.addEventListener("click", onTBIClickTempHandler, false);
tb = document.createElement ("titledbutton");
tb.setAttribute ("class", "activity-button");
tb.setAttribute ("id", id);
@ -669,6 +694,21 @@ function getTBForObject (source, create)
}
/*
* This is used since setAttribute is funked up right now.
*/
function onTBIClickTempHandler (e)
{
var tbid = "tb[" + e.target.value + "]";
var tbi = document.getElementById (tbid);
var view = client.viewsArray[tbi.getAttribute("viewKey")];
setCurrentObject (view.source);
}
function deleteToolbutton (tb)
{
var i, key = Number(tb.getAttribute("viewKey"));
@ -712,7 +752,7 @@ function filterOutput (msg, msgtype)
return msg;
}
}
client.sayToCurrentTarget =
function cli_say(msg)
@ -965,6 +1005,32 @@ function user_display(message, msgtype, sourceNick)
}
/**
* Retrieves the selected nicks from the associated
* gui list object. This simply calls list.getSelectedNicks
* and then promotes each string into an actual instance
* of CIRCChanUser.
*/
CIRCChannel.prototype.getSelectedUsers =
function my_getselectedusers ()
{
/* retrieve raw text list from list object first */
var ary = this.list.getSelectedItems();
if (ary && ary.length > 0)
{
for (var i in ary)
{
/* promote each string to chan user object */
ary[i] = this.getUser(ary[i]);
}
}
/* USAGE NOTE: If the return value is non-null, the caller
can assume the array is valid, and NOT
need to check the length */
return ary.length > 0 ? ary : null;
}
CIRCChannel.prototype.display =
function chan_display (message, msgtype, nick)
{

Просмотреть файл

@ -27,7 +27,7 @@
function CListBox ()
{
this.listContainer = document.createElement ("html:span");
this.listContainer = document.createElement ("html:div");
this.listContainer.setAttribute ("class", "list");
}
@ -51,6 +51,7 @@ function lbox_chandler (e)
var lm = this.listManager;
e.target = this;
if (lm && typeof lm.onClick == "function")
lm.onClick ({realTarget: this, event: e});
@ -58,32 +59,64 @@ function lbox_chandler (e)
CListBox.prototype.onClick =
function lbox_chandler (e)
{
{
dd ("onclick: \n" + dumpObjectTree (e, 1));
/* Check for the button number first */
/* FIXME: are there constants for this stuff? */
if (e.event.which == 3)
{
return;
}
/*
* If the ctrlKey is _not_ set, unselect all currently
* selected items
*/
if (!e.event.ctrlKey)
{
this.enumerateElements(this._unselectCallback, e.realTarget);
}
/* Check to see whether the item is currently selected */
var isSelected = e.realTarget.getAttribute("selected");
/* toggle the value */
if ("true" == isSelected)
e.realTarget.setAttribute("selected", "false");
else
e.realTarget.setAttribute("selected", "true");
}
CListBox.prototype._unselectCallback =
function (element, ndx, realTarget)
{
if (element != realTarget)
element.setAttribute("selected", "false");
}
CListBox.prototype.add =
function lbox_add (stuff, tag)
{
var option = document.createElement ("html:a");
/* NOTE: JG using a div here makes the item span the full
length and looks better when selected */
var option = document.createElement ("html:div");
option.setAttribute ("class", "list-option");
option.appendChild (stuff);
option.appendChild (document.createElement("html:br"));
option.onclick = this.clickHandler;
option.listManager = this;
option.tag = tag;
this.listContainer.appendChild (option);
return true;
return true;
}
CListBox.prototype.prepend =
function lbox_prepend (stuff, oldstuff, tag)
{
if (!this.listContainer.firstChild)
return this.add (stuff, tag);
@ -91,11 +124,10 @@ function lbox_prepend (stuff, oldstuff, tag)
if (!nextOption)
return false;
var option = document.createElement ("html:a");
var option = document.createElement ("html:div");
option.setAttribute ("class", "list-option");
option.appendChild (stuff);
option.appendChild (document.createElement("html:br"));
option.onclick = this.clickHandler;
option.listManager = this;
option.tag = tag;
@ -136,15 +168,42 @@ function lbox_remove (stuff)
}
CListBox.prototype.enumerateElements =
function lbox_enum (callback)
/* NOTE: JG: added data param so arbitrary data can be passed. */
function lbox_enum (callback, data)
{
var i = 0;
var current = this.listContainer.firstChild;
while (current)
{
callback (current, i++);
callback (current, i++, data);
current = current.nextSibling;
}
}
/**
* Using enumerateElements, this is used just to fill an array
* with selected nick names.
* @returns an array of selected nicknames
*/
CListBox.prototype.getSelectedItems =
function lbox_getselecteditems ()
{
var ary = [];
this.enumerateElements(this.selectedItemCallback, ary);
return ary;
}
/**
* used to build the array of values returned by getSelectedItems.
*/
CListBox.prototype.selectedItemCallback =
function (item, idx, data)
{
return item;
}

Просмотреть файл

@ -170,6 +170,13 @@ toolbar {
}
/* JG: Selection colors, in accord with standard mozilla chrome colors */
.list-option[selected="true"] {
background: rgb(51,102,153);
font-weight: bold;
color: white;
}
/* text in the quicklist */
.option-text {
font-weight: bold;