dojo.provide("dojo.widget.Toaster");
dojo.require("dojo.widget.*");
dojo.require("dojo.lfx.*");
dojo.require("dojo.html.iframe");
// This is mostly taken from Jesse Kuhnert's MessageNotifier.
// Modified by Bryan Forbes to support topics and a variable delay.
dojo.widget.defineWidget(
"dojo.widget.Toaster",
dojo.widget.HtmlWidget,
{
// summary
// Message that slides in from the corner of the screen, used for notifications
// like "new email".
templateString: '<div dojoAttachPoint="clipNode"><div dojoAttachPoint="containerNode" dojoAttachEvent="onClick:onSelect"><div dojoAttachPoint="contentNode"></div></div></div>',
templateCssPath: dojo.uri.dojoUri("src/widget/templates/Toaster.css"),
// messageTopic: String
// Name of topic; anything published to this topic will be displayed as a message.
// Message format is either String or an object like
// {message: "hello word", type: "ERROR", delay: 500}
messageTopic: "",
// messageTypes: Enumeration
// Possible message types.
messageTypes: {
MESSAGE: "MESSAGE",
WARNING: "WARNING",
ERROR: "ERROR",
FATAL: "FATAL"
},
// defaultType: String
// If message type isn't specified (see "messageTopic" parameter),
// then display message as this type.
// Possible values in messageTypes enumeration ("MESSAGE", "WARNING", "ERROR", "FATAL")
defaultType: "MESSAGE",
// css classes
clipCssClass: "dojoToasterClip",
containerCssClass: "dojoToasterContainer",
contentCssClass: "dojoToasterContent",
messageCssClass: "dojoToasterMessage",
warningCssClass: "dojoToasterWarning",
errorCssClass: "dojoToasterError",
fatalCssClass: "dojoToasterFatal",
// positionDirection: String
// Position from which message slides into screen, one of
// ["br-up", "br-left", "bl-up", "bl-right", "tr-down", "tr-left", "tl-down", "tl-right"]
positionDirection: "br-up",
// positionDirectionTypes: Enumeration
// Possible values for positionDirection parameter
positionDirectionTypes: ["br-up", "br-left", "bl-up", "bl-right", "tr-down", "tr-left", "tl-down", "tl-right"],
// showDelay: Integer
// Number of milliseconds to show message
// TODO: this is a strange name. "duration" makes more sense
showDelay: 2000,
postCreate: function(){
this.hide();
dojo.html.setClass(this.clipNode, this.clipCssClass);
dojo.html.addClass(this.containerNode, this.containerCssClass);
dojo.html.setClass(this.contentNode, this.contentCssClass);
if(this.messageTopic){
dojo.event.topic.subscribe(this.messageTopic, this, "_handleMessage");
}
if(!this.positionDirection || !dojo.lang.inArray(this.positionDirectionTypes, this.positionDirection)){
this.positionDirection = this.positionDirectionTypes.BRU;
}
},
_handleMessage: function(msg){
if(dojo.lang.isString(msg)){
this.setContent(msg);
}else{
this.setContent(msg["message"], msg["type"], msg["delay"]);
}
},
setContent: function(msg, messageType, delay){
// summary
// sets and displays the given message and show duration
// msg: String
// the message
// messageType: Enumeration
// type of message; possible values in messageTypes array ("MESSAGE", "WARNING", "ERROR", "FATAL")
// delay: Integer
// number of milliseconds to display message
var delay = delay||this.showDelay;
// sync animations so there are no ghosted fades and such
if(this.slideAnim && this.slideAnim.status() == "playing"){
dojo.lang.setTimeout(50, dojo.lang.hitch(this, function(){
this.setContent(msg, messageType);
}));
return;
}else if(this.slideAnim){
this.slideAnim.stop();
if(this.fadeAnim) this.fadeAnim.stop();
}
if(!msg){
dojo.debug(this.widgetId + ".setContent() incoming content was null, ignoring.");
return;
}
if(!this.positionDirection || !dojo.lang.inArray(this.positionDirectionTypes, this.positionDirection)){
dojo.raise(this.widgetId + ".positionDirection is an invalid value: " + this.positionDirection);
}
// determine type of content and apply appropriately
dojo.html.removeClass(this.containerNode, this.messageCssClass);
dojo.html.removeClass(this.containerNode, this.warningCssClass);
dojo.html.removeClass(this.containerNode, this.errorCssClass);
dojo.html.removeClass(this.containerNode, this.fatalCssClass);
dojo.html.clearOpacity(this.containerNode);
if(msg instanceof String || typeof msg == "string"){
this.contentNode.innerHTML = msg;
}else if(dojo.html.isNode(msg)){
this.contentNode.innerHTML = dojo.html.getContentAsString(msg);
}else{
dojo.raise("Toaster.setContent(): msg is of unknown type:" + msg);
}
switch(messageType){
case this.messageTypes.WARNING:
dojo.html.addClass(this.containerNode, this.warningCssClass);
break;
case this.messageTypes.ERROR:
dojo.html.addClass(this.containerNode, this.errorCssClass);
break
case this.messageTypes.FATAL:
dojo.html.addClass(this.containerNode, this.fatalCssClass);
break;
case this.messageTypes.MESSAGE:
default:
dojo.html.addClass(this.containerNode, this.messageCssClass);
break;
}
// now do funky animation of widget appearing from
// bottom right of page and up
this.show();
var nodeSize = dojo.html.getMarginBox(this.containerNode);
// sets up initial position of container node and slide-out direction
if(this.positionDirection.indexOf("-up") >= 0){
this.containerNode.style.left=0+"px";
this.containerNode.style.top=nodeSize.height + 10 + "px";
}else if(this.positionDirection.indexOf("-left") >= 0){
this.containerNode.style.left=nodeSize.width + 10 +"px";
this.containerNode.style.top=0+"px";
}else if(this.positionDirection.indexOf("-right") >= 0){
this.containerNode.style.left = 0 - nodeSize.width - 10 + "px";
this.containerNode.style.top = 0+"px";
}else if(this.positionDirection.indexOf("-down") >= 0){
this.containerNode.style.left = 0+"px";
this.containerNode.style.top = 0 - nodeSize.height - 10 + "px";
}else{
dojo.raise(this.widgetId + ".positionDirection is an invalid value: " + this.positionDirection);
}
this.slideAnim = dojo.lfx.html.slideTo(
this.containerNode,
{ top: 0, left: 0 },
450,
null,
dojo.lang.hitch(this, function(nodes, anim){
dojo.lang.setTimeout(dojo.lang.hitch(this, function(evt){
// we must hide the iframe in order to fade
// TODO: figure out how to fade with a BackgroundIframe
if(this.bgIframe){
this.bgIframe.hide();
}
// can't do a fadeHide because we're fading the
// inner node rather than the clipping node
this.fadeAnim = dojo.lfx.html.fadeOut(
this.containerNode,
1000,
null,
dojo.lang.hitch(this, function(evt){
this.hide();
})).play();
}), delay);
})).play();
},
_placeClip: function(){
var scroll = dojo.html.getScroll();
var view = dojo.html.getViewport();
var nodeSize = dojo.html.getMarginBox(this.containerNode);
// sets up the size of the clipping node
this.clipNode.style.height = nodeSize.height+"px";
this.clipNode.style.width = nodeSize.width+"px";
// sets up the position of the clipping node
if(this.positionDirection.match(/^t/)){
this.clipNode.style.top = scroll.top+"px";
}else if(this.positionDirection.match(/^b/)){
this.clipNode.style.top = (view.height - nodeSize.height - 2 + scroll.top)+"px";
}
if(this.positionDirection.match(/^[tb]r-/)){
this.clipNode.style.left = (view.width - nodeSize.width - 1 - scroll.left)+"px";
}else if(this.positionDirection.match(/^[tb]l-/)){
this.clipNode.style.left = 0 + "px";
}
this.clipNode.style.clip = "rect(0px, " + nodeSize.width + "px, " + nodeSize.height + "px, 0px)";
if(dojo.render.html.ie){
if(!this.bgIframe){
this.bgIframe = new dojo.html.BackgroundIframe(this.containerNode);
this.bgIframe.setZIndex(this.containerNode);
}
this.bgIframe.onResized();
this.bgIframe.show();
}
},
onSelect: function(e) {
// summary: callback for when user clicks the message
},
show: function(){
dojo.widget.Toaster.superclass.show.call(this);
this._placeClip();
if(!this._scrollConnected){
this._scrollConnected = true;
dojo.event.connect(window, "onscroll", this, "_placeClip");
}
},
hide: function(){
dojo.widget.Toaster.superclass.hide.call(this);
if(this._scrollConnected){
this._scrollConnected = false;
dojo.event.disconnect(window, "onscroll", this, "_placeClip");
}
dojo.html.setOpacity(this.containerNode, 1.0);
}
}
);