[483] | 1 | define([ |
---|
| 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 | }else if(pd.match(/^[tb]c-/)){ |
---|
| 248 | style.left = Math.round((view.w - nodeSize.w - 1 - view.l)/2)+"px"; |
---|
| 249 | } |
---|
| 250 | |
---|
| 251 | style.clip = "rect(0px, " + nodeSize.w + "px, " + nodeSize.h + "px, 0px)"; |
---|
| 252 | if(has("ie")){ |
---|
| 253 | if(!this.bgIframe){ |
---|
| 254 | this.clipNode.id = registry.getUniqueId("dojox_widget_Toaster_clipNode"); |
---|
| 255 | this.bgIframe = new BackgroundIframe(this.clipNode); |
---|
| 256 | } |
---|
| 257 | var iframe = this.bgIframe.iframe; |
---|
| 258 | if(iframe){ iframe.style.display="block"; } |
---|
| 259 | } |
---|
| 260 | }, |
---|
| 261 | |
---|
| 262 | onSelect: function(/*Event*/e){ |
---|
| 263 | // summary: |
---|
| 264 | // callback for when user clicks the message |
---|
| 265 | }, |
---|
| 266 | |
---|
| 267 | show: function(){ |
---|
| 268 | // summary:' |
---|
| 269 | // show the Toaster |
---|
| 270 | domStyle.set(this.domNode, 'display', 'block'); |
---|
| 271 | |
---|
| 272 | this._placeClip(); |
---|
| 273 | |
---|
| 274 | if(!this._scrollConnected){ |
---|
| 275 | this._scrollConnected = connect.connect(window, "onscroll", this, this._placeClip); |
---|
| 276 | } |
---|
| 277 | }, |
---|
| 278 | |
---|
| 279 | hide: function(){ |
---|
| 280 | // summary: |
---|
| 281 | // hide the Toaster |
---|
| 282 | |
---|
| 283 | domStyle.set(this.domNode, 'display', 'none'); |
---|
| 284 | |
---|
| 285 | if(this._scrollConnected){ |
---|
| 286 | connect.disconnect(this._scrollConnected); |
---|
| 287 | this._scrollConnected = false; |
---|
| 288 | } |
---|
| 289 | |
---|
| 290 | domStyle.set(this.containerNode, "opacity", 1); |
---|
| 291 | } |
---|
| 292 | }); |
---|
| 293 | |
---|
| 294 | }); |
---|