source: Dev/branches/rest-dojo-ui/client/dojox/widget/Toaster.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 9.0 KB
Line 
1define([
2        "dojo/_base/declare", // declare
3        "dojo/_base/lang", // lang.getObject...
4        "dojo/_base/connect", // connect.connect, connect.subscribe
5        "dojo/_base/fx", // fx.fadeOut
6        "dojo/dom-style", // domStyle.set
7        "dojo/dom-class", // domClass.add
8        "dojo/dom-geometry", // domGeometry.getMarginBox
9        "dijit/registry",    // registry.getUniqueId()
10        "dijit/_WidgetBase",
11        "dijit/_TemplatedMixin",
12        "dijit/BackgroundIframe",
13        "dojo/fx",
14        "dojo/has",
15        "dojo/_base/window",
16        "dojo/window"
17], function(declare, lang, connect, baseFx, domStyle, domClass, domGeometry, registry, WidgetBase, Templated, BackgroundIframe, coreFx, has, baseWindow, window){
18
19        lang.getObject("dojox.widget", true);
20       
21        var capitalize = function(/* String */w){
22            return w.substring(0,1).toUpperCase() + w.substring(1);
23        };
24
25        return declare("dojox.widget.Toaster", [WidgetBase, Templated], {
26                // summary:
27                //              Message that slides in from the corner of the screen, used for notifications
28                //              like "new email".
29
30                templateString: '<div class="dijitToasterClip" dojoAttachPoint="clipNode"><div class="dijitToasterContainer" dojoAttachPoint="containerNode" dojoAttachEvent="onclick:onSelect"><div class="dijitToasterContent" dojoAttachPoint="contentNode"></div></div></div>',
31
32                // messageTopic: String
33                //              Name of topic; anything published to this topic will be displayed as a message.
34                //              Message format is either String or an object like
35                //              {message: "hello word", type: "error", duration: 500}
36                messageTopic: "",
37
38                // messageTypes: Enumeration
39                //              Possible message types.
40                messageTypes: {
41                        MESSAGE: "message",
42                        WARNING: "warning",
43                        ERROR: "error",
44                        FATAL: "fatal"
45                },
46
47                // defaultType: String
48                //              If message type isn't specified (see "messageTopic" parameter),
49                //              then display message as this type.
50                //              Possible values in messageTypes enumeration ("message", "warning", "error", "fatal")
51                defaultType: "message",
52
53                // positionDirection: String
54                //              Position from which message slides into screen, one of
55                //              ["br-up", "br-left", "bl-up", "bl-right", "tr-down", "tr-left", "tl-down", "tl-right"]
56                positionDirection: "br-up",
57
58                // positionDirectionTypes: Array
59                //              Possible values for positionDirection parameter
60                positionDirectionTypes: ["br-up", "br-left", "bl-up", "bl-right", "tr-down", "tr-left", "tl-down", "tl-right"],
61
62                // duration: Integer
63                //              Number of milliseconds to show message
64                duration: 2000,
65
66                // slideDuration: Integer
67                //              Number of milliseconds for the slide animation, increasing will cause the Toaster
68                //    to slide in more slowly.
69                slideDuration: 500,
70
71                //separator: String
72                //              String used to separate messages if consecutive calls are made to setContent before previous messages go away
73                separator: "<hr></hr>",
74
75                postCreate: function(){
76                        this.inherited(arguments);
77                        this.hide();
78
79                        // place node as a child of body for positioning
80                        baseWindow.body().appendChild(this.domNode);
81
82                        if(this.messageTopic){
83                                connect.subscribe(this.messageTopic, this, "_handleMessage");
84                        }
85                },
86
87                _handleMessage: function(/*String|Object*/message){
88                        if(lang.isString(message)){
89                                this.setContent(message);
90                        }else{
91                                this.setContent(message.message, message.type, message.duration);
92                        }
93                },
94
95                setContent: function(/*String|Function*/message, /*String*/messageType, /*int?*/duration){
96                        // summary:
97                        //              sets and displays the given message and show duration
98                        // message:
99                        //              the message. If this is a function, it will be called with this toaster widget as the only argument.
100                        // messageType:
101                        //              type of message; possible values in messageTypes enumeration ("message", "warning", "error", "fatal")
102                        // duration:
103                        //              duration in milliseconds to display message before removing it. Widget has default value.
104                        duration = duration||this.duration;
105                        // sync animations so there are no ghosted fades and such
106                        if(this.slideAnim){
107                                if(this.slideAnim.status() != "playing"){
108                                        this.slideAnim.stop();
109                                }
110                                if(this.slideAnim.status() == "playing" || (this.fadeAnim && this.fadeAnim.status() == "playing")){
111                                        setTimeout(lang.hitch(this, function(){
112                                                this.setContent(message, messageType, duration);
113                                        }), 50);
114                                        return;
115                                }
116                        }
117
118                        // determine type of content and apply appropriately
119                        for(var type in this.messageTypes){
120                                domClass.remove(this.containerNode, "dijitToaster" + capitalize(this.messageTypes[type]));
121                        }
122
123                        domStyle.set(this.containerNode, "opacity", 1);
124
125                        this._setContent(message);
126
127                        domClass.add(this.containerNode, "dijitToaster" + capitalize(messageType || this.defaultType));
128
129                        // now do funky animation of widget appearing from
130                        // bottom right of page and up
131                        this.show();
132                        var nodeSize = domGeometry.getMarginBox(this.containerNode);
133                        this._cancelHideTimer();
134                        if(this.isVisible){
135                                this._placeClip();
136                                //update hide timer if no sticky message in stack
137                                if(!this._stickyMessage) {
138                                        this._setHideTimer(duration);
139                                }
140                        }else{
141                                var style = this.containerNode.style;
142                                var pd = this.positionDirection;
143                                // sets up initial position of container node and slide-out direction
144                                if(pd.indexOf("-up") >= 0){
145                                        style.left=0+"px";
146                                        style.top=nodeSize.h + 10 + "px";
147                                }else if(pd.indexOf("-left") >= 0){
148                                        style.left=nodeSize.w + 10 +"px";
149                                        style.top=0+"px";
150                                }else if(pd.indexOf("-right") >= 0){
151                                        style.left = 0 - nodeSize.w - 10 + "px";
152                                        style.top = 0+"px";
153                                }else if(pd.indexOf("-down") >= 0){
154                                        style.left = 0+"px";
155                                        style.top = 0 - nodeSize.h - 10 + "px";
156                                }else{
157                                        throw new Error(this.id + ".positionDirection is invalid: " + pd);
158                                }
159                                this.slideAnim = coreFx.slideTo({
160                                        node: this.containerNode,
161                                        top: 0, left: 0,
162                                        duration: this.slideDuration});
163                                this.connect(this.slideAnim, "onEnd", function(nodes, anim){
164                                                //we build the fadeAnim here so we dont have to duplicate it later
165                                                // can't do a fadeHide because we're fading the
166                                                // inner node rather than the clipping node
167                                                this.fadeAnim = baseFx.fadeOut({
168                                                        node: this.containerNode,
169                                                        duration: 1000});
170                                                this.connect(this.fadeAnim, "onEnd", function(evt){
171                                                        this.isVisible = false;
172                                                        this.hide();
173                                                });
174                                                this._setHideTimer(duration);
175                                                this.connect(this, 'onSelect', function(evt){
176                                                        this._cancelHideTimer();
177                                                        //force clear sticky message
178                                                        this._stickyMessage=false;
179                                                        this.fadeAnim.play();
180                                                });
181
182                                                this.isVisible = true;
183                                        });
184                                this.slideAnim.play();
185                        }
186                },
187
188                _setContent: function(message){
189                        if(lang.isFunction(message)){
190                                message(this);
191                                return;
192                        }
193                        if(message && this.isVisible){
194                                message = this.contentNode.innerHTML + this.separator + message;
195                        }
196                        this.contentNode.innerHTML = message;
197                },
198                _cancelHideTimer:function(){
199                        if (this._hideTimer){
200                                clearTimeout(this._hideTimer);
201                                this._hideTimer=null;
202                        }
203                },
204
205                _setHideTimer:function(duration){
206                        this._cancelHideTimer();
207                        //if duration == 0 we keep the message displayed until clicked
208                        if(duration>0){
209                                this._cancelHideTimer();
210                                this._hideTimer=setTimeout(lang.hitch(this, function(evt){
211                                        // we must hide the iframe in order to fade
212                                        // TODO: figure out how to fade with a BackgroundIframe
213                                        if(this.bgIframe && this.bgIframe.iframe){
214                                                this.bgIframe.iframe.style.display="none";
215                                        }
216                                        this._hideTimer=null;
217                                        //force clear sticky message
218                                        this._stickyMessage=false;
219                                        this.fadeAnim.play();
220                                }), duration);
221                        }
222                        else
223                                this._stickyMessage=true;
224                },
225
226                _placeClip: function(){
227                        var view = window.getBox();
228
229                        var nodeSize = domGeometry.getMarginBox(this.containerNode);
230
231                        var style = this.clipNode.style;
232                        // sets up the size of the clipping node
233                        style.height = nodeSize.h+"px";
234                        style.width = nodeSize.w+"px";
235
236                        // sets up the position of the clipping node
237                        var pd = this.positionDirection;
238                        if(pd.match(/^t/)){
239                                style.top = view.t+"px";
240                        }else if(pd.match(/^b/)){
241                                style.top = (view.h - nodeSize.h - 2 + view.t)+"px";
242                        }
243                        if(pd.match(/^[tb]r-/)){
244                                style.left = (view.w - nodeSize.w - 1 - view.l)+"px";
245                        }else if(pd.match(/^[tb]l-/)){
246                                style.left = 0 + "px";
247                        }
248
249                        style.clip = "rect(0px, " + nodeSize.w + "px, " + nodeSize.h + "px, 0px)";
250                        if(has("ie")){
251                                if(!this.bgIframe){
252                                        this.clipNode.id = registry.getUniqueId("dojox_widget_Toaster_clipNode");
253                                        this.bgIframe = new BackgroundIframe(this.clipNode);
254                                }
255                                var iframe = this.bgIframe.iframe;
256                                if(iframe){ iframe.style.display="block"; }
257                        }
258                },
259
260                onSelect: function(/*Event*/e){
261                        // summary: callback for when user clicks the message
262                },
263
264                show: function(){
265                        // summary: show the Toaster
266                        domStyle.set(this.domNode, 'display', 'block');
267
268                        this._placeClip();
269
270                        if(!this._scrollConnected){
271                                this._scrollConnected = connect.connect(window, "onscroll", this, this._placeClip);
272                        }
273                },
274
275                hide: function(){
276                        // summary: hide the Toaster
277
278                        domStyle.set(this.domNode, 'display', 'none');
279
280                        if(this._scrollConnected){
281                                connect.disconnect(this._scrollConnected);
282                                this._scrollConnected = false;
283                        }
284
285                        domStyle.set(this.containerNode, "opacity", 1);
286                }
287        });
288
289});
Note: See TracBrowser for help on using the repository browser.