Toaster.js 8.9 KB

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