gecko-dev/calendar/base/content/calendar-attendee-list.xml

363 lines
13 KiB
XML

<?xml version="1.0"?>
<!--
- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Calendar attendee code.
-
- The Initial Developer of the Original Code is
- Mozilla Corp
- Portions created by the Initial Developer are Copyright (C) 2006
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Joey Minta <jminta@gmail.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL or the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK *****
-->
<bindings id="calendar-attendee-bindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="calendar-attendee-list"
extends="chrome://global/content/bindings/richlistbox.xml#richlistbox" xbl:inherits="flex">
<implementation>
<!-- The listitem that is currently blank. (There should always be one
- and only one.)
-->
<field name="mBlankAttendee">null</field>
<!-- we should ignore focus events during this -->
<field name="mLoading">null</field>
<!-- An array of calIAttendees objects-->
<property name="attendees">
<getter><![CDATA[
var atts = new Array();
for (var i = 0; i < this.childNodes.length; i++) {
// Filter out blank rows, which have null ids.
if (this.childNodes[i].attendee.id) {
atts.push(this.childNodes[i].attendee);
}
}
return atts;
]]></getter>
<setter><![CDATA[
this.mLoading = true;
while (this.lastChild) {
this.removeChild(this.lastChild);
}
for each (att in val) {
this.addAttendee(att);
}
// Add our blank attendee
this.addAttendee(null, true);
this.mLoading = false;
return val;
]]></setter>
</property>
<!-- Called to add a new row to the listbox. If aAttendee is non-null
- then we will populate the textbox of that row with that attendee's
- information. if it is null, we simply create a blank textbox. If
- aDontFocus is false (or null/undefined) then we will also focus
- the box we create.
-->
<method name="addAttendee">
<parameter name="aAttendee"/>
<parameter name="aDontFocus"/>
<body><![CDATA[
if (!aAttendee && this.mBlankAttendee) {
this.mBlankAttendee.focusText();
return;
}
var attElem = document.createElement("calendar-attendee-item");
this.appendChild(attElem);
if (aAttendee) {
attElem.attendee = aAttendee;
} else {
attElem.attendee = Components.classes["@mozilla.org/calendar/attendee;1"]
.createInstance(Components.interfaces.calIAttendee);
this.mBlankAttendee = attElem;
}
if (!aDontFocus) {
attElem.focusText();
}
]]></body>
</method>
<!-- Removes the row in the listbox whose attendee has the same id as
- aAttendee
-->
<method name="removeAttendee">
<parameter name="aAttendee"/>
<body><![CDATA[
for (var i = this.childNodes.length; i >= 0; i--) {
if (this.childNodes[i].id == aAttendee.id) {
this.removeChild(this.childNodes[i]);
}
}
]]></body>
</method>
<!-- Call this if you are in doubt about whether or not there is still
- a blank row in the listbox. (There should always be one, so that
- a user can easily add a new attendee.) If there isn't such a row
- this function will add it for you.
-->
<method name="checkBlankAttendee">
<parameter name="aDontFocus"/>
<body><![CDATA[
if (this.mLoading) {
return;
}
var blankAtts = new Array();
for (var i = 0; i < this.childNodes.length; i++) {
if (!this.childNodes[i].attendee.id) {
blankAtts.push(this.childNodes[i]);
}
}
if (blankAtts.length == 0) {
// add a new blank attendee
this.mBlankAttendee = null;
this.addAttendee(null, aDontFocus);
} else if (blankAtts.length > 1) {
for (var i = 1; i < blankAtts.length; i++) {
this.removeChild(blankAtts[i]);
}
this.mBlankAttendee = blankAtts[0];
}
]]></body>
</method>
</implementation>
</binding>
<binding id="calendar-attendee-item"
extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox align="center" flex="1">
<xul:textbox anonid="attendee-textbox" flex="1"
class="attendee-textbox"
oninput="adjustId(this.value)"
onkeypress="checkEnter(event)"/>
</xul:hbox>
</content>
<implementation>
<!-- This is a list of objects corresponding to the event listeners that
- we're going to add to the textbox. We need references to them so
- that we can remove them later.
-->
<field name="mListenerMap">null</field>
<field name="mAttendee">null</field>
<field name="mShowingInstructions">false</field>
<constructor><![CDATA[
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
// It should come as no surprise to everyone familiar with mozilla that
// focus code sucks. Work around some scoping tricks here. (Listening
// for an event gives you a |this| object that corresponds to the
// textbox, not the richlistitem, so things like mShowingInstructions
// will be undefined, which is bad.)
var attElem = this;
// The first time we get focus, gecko actually sends us a focus *and*
// a blur event, which is really bad. This is a horrible hack to fix
// that.
var ignoreFirst = true;
function clear() {
attElem.clearInstructions.call(attElem);
}
function show() {
if (ignoreFirst) {
ignoreFirst = false;
// Ending up here is very very bad. It means the textbox has
// been blurred! Refocus, after the app has time to get its
// head on straight.
function reFocus() {
textbox.focus();
}
setTimeout(reFocus, 0);
return;
}
attElem.showInstructions.call(attElem);
attElem.finishTyping.call(attElem);
}
this.mListenerMap = [{event:"focus", func: clear},
{event:"blur", func: show}];
for each (var listener in this.mListenerMap) {
textbox.addEventListener(listener.event, listener.func, true);
}
]]></constructor>
<destructor><![CDATA[
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
for each (var listener in this.mListenerMap) {
textbox.removeEventListener(listener.event, listener.func, true);
}
]]></destructor>
<!-- The calIAttendee object associated with this listitem -->
<property name="attendee">
<getter><![CDATA[
return this.mAttendee;
]]></getter>
<setter><![CDATA[
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
if (val.id) {
if (val.id.toLowerCase().substr(0, 7) == "mailto:") {
textbox.value = val.id.toLowerCase().split("mailto:")[1];
} else {
textbox.value = val.id;
}
} else {
this.showInstructions();
}
var myAttendee;
if (!val.isMutable) {
myAttendee = val.clone();
} else {
myAttendee = val;
}
this.mAttendee = myAttendee;
return val;
]]></setter>
</property>
<!-- check whether the latest keypress is 'enter', and if so, move to the
- next row.
-->
<method name="checkEnter">
<parameter name="aEvent"/>
<body><![CDATA[
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
this.adjustId(textbox.value);
// Go to the next attendee on enter
if (aEvent.keyCode != Components.interfaces.nsIDOMKeyEvent.DOM_VK_RETURN) {
return;
}
// Don't let the outer dialog that we're likely contained in close.
aEvent.preventDefault();
// Focus the next listitem
document.commandDispatcher.advanceFocus();
]]></body>
</method>
<!-- Set the id of the calIAttendee object associated with this listitem
- according to the current textbox value.
-->
<method name="adjustId">
<parameter name="aValue"/>
<body><![CDATA[
if (!aValue || aValue == "") {
this.mAttendee.id = null;
return;
}
if (aValue.toLowerCase().indexOf("mailto:") == -1) {
aValue = "mailto:"+aValue;
}
this.mAttendee.id = aValue;
this.finishTyping();
]]></body>
</method>
<!-- Focus the textbox of this item -->
<method name="focusText">
<body><![CDATA[
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
textbox.focus();
]]></body>
</method>
<!-- Call this to when the user is no longer typing in the textbox. If
- the textbox is blank, we'll restore the original grey instructions.
-->
<method name="showInstructions">
<body><![CDATA[
if (this.mShowingInstructions) {
return;
}
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
if (textbox.value && textbox.value != "") {
return;
}
this.mShowingInstructions = true;
textbox.value = calGetString("calendar", "attendeeInstructions");
textbox.setAttribute("instructions", "true");
]]></body>
</method>
<!-- Call this when a user starts paying attention to this textbox.
- (onfocus). We'll remove the grey instructions so they can type.
-->
<method name="clearInstructions">
<body><![CDATA[
if (!this.mShowingInstructions || this.mAttendee.id) {
return;
}
var textbox = document.getAnonymousElementByAttribute(this, "anonid", "attendee-textbox");
textbox.value = "";
textbox.removeAttribute("instructions");
// It's important that we don't set this until here, since setting the
// the textbox's value actually focuses it.
this.mShowingInstructions = false;
]]></body>
</method>
<!-- Called when a user stops caring about this textbox (onblur) to check
- if we're still blank or not, and update the entire list as needed.
-->
<method name="finishTyping">
<parameter name="aDontFocus"/>
<body><![CDATA[
if (aDontFocus == undefined) {
aDontFocus = true;
}
// Mild assumption about our place in the DOM
this.parentNode.checkBlankAttendee(aDontFocus);
]]></body>
</method>
</implementation>
</binding>
</bindings>