├── .actionScriptProperties
├── .flexLibProperties
├── .gitignore
├── .project
├── LICENSE
├── README.md
└── src
└── main
└── actionscript
├── assets
├── images
│ ├── close.png
│ ├── close_over.png
│ ├── m6d-magnum-sidearm-16x16.png
│ └── m6d-magnum-sidearm-50x50.png
├── sounds
│ └── drop.mp3
└── style
│ ├── dark.css
│ ├── dark.swf
│ ├── light.css
│ └── light.swf
└── com
└── charlesbihis
└── engine
└── notification
├── NotificationConst.as
├── NotificationManager.as
├── event
└── NotificationEvent.as
├── style
└── style.css
└── ui
├── Notification.as
└── NotificationWindow.mxml
/.actionScriptProperties:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.flexLibProperties:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .settings/*
2 | bin/*
3 | docs/*
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | actionscript-notification-engine
4 |
5 |
6 |
7 |
8 |
9 | com.adobe.flexbuilder.project.flexbuilder
10 |
11 |
12 |
13 |
14 |
15 | com.adobe.flexbuilder.project.flexlibnature
16 | com.adobe.flexbuilder.project.actionscriptnature
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012 Charles Bihis
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ActionScript Notification Engine
2 |
3 | A powerful cross-platform notification engine.
4 |
5 | ## Overview
6 |
7 | M6D is a notification engine built on top of Adobe AIR. With a very simple interface, you can drop it into your own desktop AIR project and use it to deliver messenger-style notifications to your users! Think Growl, but for all platforms :)
8 |
9 | This project was first mentioned in my blog post at http://blogs.adobe.com/charles/2012/02/actionscript-notification-engine-open-sourced.html.
10 |
11 | ### Codename
12 |
13 | Project M6D Magnum Sidearm
14 |
15 | ### Features
16 |
17 | M6D supports the following features...
18 |
19 | * Ability to display messenger-style toast notifications as well as compact notifications.
20 | * Variable display length for notifications.
21 | * User-presence logic that detects when the user is at the computer. If the user is away, notifications are held on-screen and queued for when the user returns.
22 | * Ability to replay most recent five notifications.
23 | * Individual notification post settings, such as sticky, replayable, custom image, click URL, compact, etc.
24 | * Smart repositioning logic for sticky posts.
25 | * Ability to see a summary notification if user is away for an extended period of time.
26 | * Support for changing the notification images.
27 | * Support for custom styling as well as changing styles on the fly.
28 |
29 | ### Dependencies
30 | None
31 |
32 | ## Reference
33 |
34 | ### Usage
35 |
36 | To use the library, simply drop in the SWC (or the source) into your project and follow the patterns below...
37 |
38 | // create engine with default settings
39 | var notificationManager:NotificationManager = new NotificationManager("/assets/style/dark.swf", // default style
40 | "/assets/m6d-magnum-sidearm-50x50.png", // default notification image
41 | "/assets/m6d-magnum-sidearm-16x16.png", // default compact notification image
42 | "/assets/sounds/drop.mp3" // (optional) default notification sound
43 | NotificationConst.DISPLAY_LENGTH_DEFAULT, // (optional) default display length
44 | NotificationConst.DISPLAY_LOCATION_AUTO); // (optional) default display location
45 |
46 | // now that we have an engine, let's create a notification and show it
47 | var notification:Notification = new Notification();
48 | notification.title = "Derek ► Jacobim";
49 | notification.message = "What is this? A center for ANTS?!";
50 | notification.image = "/assets/images/profile/derek/avatar.png";
51 | notification.link = "http://www.youtube.com/watch?v=_6GqqIvfSVQ";
52 | notification.isCompact = false;
53 | notification.isSticky = false;
54 | notification.isReplayable = true;
55 | notificationManager.showNotification(notification);
56 |
57 | // we can also show notifications quickly using this API too
58 | notificationManager.show("Derek Zoolander Foundation", "Now open!", "/assets/images/dzf-logo-50x50.png");
59 |
60 | You can also change the engine's default settings on the fly too!
61 |
62 | // let's change the default images, display length, and display location
63 | notificationManager.defaultNotificationImage = "/assets/images/dzf-logo-50x50.png";
64 | notificationManager.defaultCompactNotificationImage = "/assets/images/dzf-logo-16x16.png";
65 | notificationManager.displayLength = NotificationConst.DISPLAY_LENGTH_SHORT;
66 | notificationManager.displayLocation = NotificationConst.DISPLAY_LOCATION_TOP_RIGHT;
67 |
68 | // we can even change the style and sound settings on the fly too!
69 | notificationManager.loadStyle("/assets/style/light.swf");
70 | notificationManager.loadSound("/assets/sounds/bing.mp3");
71 |
72 | ### Demo
73 |
74 | * Live demo: http://blogs.adobe.com/charles/2012/02/actionscript-notification-engine-open-sourced.html
75 | * Demo source: https://github.com/charlesbihis/sandbox/tree/master/actionscript/actionscript-notification-engine-demo
76 |
77 | ### Documentation
78 |
79 | You can find the full ASDocs for the project [here](http://charlesbihis.github.com/actionscript-notification-engine/docs/).
80 |
81 | ## Special Notes
82 |
83 | ### Note on Assets
84 |
85 | There are assets included in the project under the path /src/main/actionscript/assets/. However, since library projects cannot include assets that aren't embedded, these will have to be included in your main project and referenced accordingly. That is, if you try and reference them from the location in the library project, it will fail. You must put them in your own containing project alongside your own assets and reference them as you do your other assets. This includes all non-embedded images, sounds, and stylesheets.
86 |
87 | ## Author
88 |
89 | * Created by Charles Bihis
90 | * Website: [www.whoischarles.com](http://www.whoischarles.com)
91 | * E-mail: [charles@whoischarles.com](mailto:charles@whoischarles.com)
92 | * Twitter: [@charlesbihis](http://www.twitter.com/charlesbihis)
93 |
94 | ## License
95 |
96 | The ActionScript Notification Engine (a.k.a. Project M6D Magnum Sidearm) is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
--------------------------------------------------------------------------------
/src/main/actionscript/assets/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/images/close.png
--------------------------------------------------------------------------------
/src/main/actionscript/assets/images/close_over.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/images/close_over.png
--------------------------------------------------------------------------------
/src/main/actionscript/assets/images/m6d-magnum-sidearm-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/images/m6d-magnum-sidearm-16x16.png
--------------------------------------------------------------------------------
/src/main/actionscript/assets/images/m6d-magnum-sidearm-50x50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/images/m6d-magnum-sidearm-50x50.png
--------------------------------------------------------------------------------
/src/main/actionscript/assets/sounds/drop.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/sounds/drop.mp3
--------------------------------------------------------------------------------
/src/main/actionscript/assets/style/dark.css:
--------------------------------------------------------------------------------
1 | /* CSS file */
2 | @namespace s "library://ns.adobe.com/flex/spark";
3 | @namespace mx "library://ns.adobe.com/flex/mx";
4 |
5 | .notificationContainer
6 | {
7 | backgroundAlpha: 0.0;
8 | }
9 |
10 | .notification
11 | {
12 | cornerRadius: 8;
13 | backgroundColor: #000;
14 | backgroundAlpha: 0.8;
15 | borderStyle: none;
16 | borderColor: #000;
17 | paddingTop: 10;
18 | paddingBottom: 10;
19 | paddingLeft: 15;
20 | paddingRight: 15;
21 | }
22 |
23 | .notificationText
24 | {
25 | color: #FFF;
26 | }
--------------------------------------------------------------------------------
/src/main/actionscript/assets/style/dark.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/style/dark.swf
--------------------------------------------------------------------------------
/src/main/actionscript/assets/style/light.css:
--------------------------------------------------------------------------------
1 | /* CSS file */
2 | @namespace s "library://ns.adobe.com/flex/spark";
3 | @namespace mx "library://ns.adobe.com/flex/mx";
4 |
5 | .notificationContainer
6 | {
7 | backgroundAlpha: 0.0;
8 | }
9 |
10 | .notification
11 | {
12 | cornerRadius: 8;
13 | backgroundColor: #F5F5F5;
14 | backgroundAlpha: 1.0;
15 | borderStyle: solid;
16 | borderColor: #b9b9b9;
17 | paddingTop: 10;
18 | paddingBottom: 10;
19 | paddingLeft: 15;
20 | paddingRight: 15;
21 | }
22 |
23 | .notificationText
24 | {
25 | color: #000;
26 | }
--------------------------------------------------------------------------------
/src/main/actionscript/assets/style/light.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlesbihis/actionscript-notification-engine/87bb2793d9e58fb19a0eac5647113d661b5c3e29/src/main/actionscript/assets/style/light.swf
--------------------------------------------------------------------------------
/src/main/actionscript/com/charlesbihis/engine/notification/NotificationConst.as:
--------------------------------------------------------------------------------
1 | package com.charlesbihis.engine.notification
2 | {
3 | /**
4 | * Class containing frequently used constants.
5 | *
6 | * @langversion ActionScript 3.0
7 | * @playerversion Flash 10
8 | *
9 | * @author Charles Bihis (www.whoischarles.com)
10 | */
11 | public class NotificationConst
12 | {
13 | //////////////
14 | // DEFAULTS //
15 | //////////////
16 |
17 | /**
18 | * Default display length is set to DISPLAY_LENGTH_MEDIUM.
19 | */
20 | public static const DISPLAY_LENGTH_DEFAULT:int = DISPLAY_LENGTH_MEDIUM;
21 |
22 | /**
23 | * Default display location is set to DISPLAY_LOCATION_AUTO.
24 | */
25 | public static const DISPLAY_LOCATION_DEFAULT:String = DISPLAY_LOCATION_AUTO;
26 |
27 |
28 | ////////////////////
29 | // DISPLAY LENGTH //
30 | ////////////////////
31 |
32 | /**
33 | * Short display length displays a notification for a duration of 4 seconds.
34 | */
35 | public static const DISPLAY_LENGTH_SHORT:int = 4;
36 |
37 | /**
38 | * Medium display length displays a notification for a duration of 8 seconds.
39 | */
40 | public static const DISPLAY_LENGTH_MEDIUM:int= 8;
41 |
42 | /**
43 | * Long display length displays a notification for a duration of 12 seconds.
44 | */
45 | public static const DISPLAY_LENGTH_LONG:int = 12;
46 |
47 | /**
48 | * Duration of time inbetween repositioning of posts. Is a prime number larger
49 | * than the DISPLAY_LENGTH_LONG value, so as not to collide with any new notifications.
50 | */
51 | public static const REPOSITION_LENGTH:int = 13;
52 |
53 |
54 | //////////////////////
55 | // DISPLAY LOCATION //
56 | //////////////////////
57 |
58 | /**
59 | * Displays notifications in the top-left corner of the users main screen.
60 | */
61 | public static const DISPLAY_LOCATION_TOP_LEFT:String = "topLeft";
62 |
63 | /**
64 | * Displays notifications in the top-right corner of the users main screen.
65 | */
66 | public static const DISPLAY_LOCATION_TOP_RIGHT:String = "topRight";
67 |
68 | /**
69 | * Displays notifications in the bottom-left corner of the users main screen.
70 | */
71 | public static const DISPLAY_LOCATION_BOTTOM_LEFT:String = "bottomLeft";
72 |
73 | /**
74 | * Displays notifications in the bottom-right corner of the users main screen.
75 | */
76 | public static const DISPLAY_LOCATION_BOTTOM_RIGHT:String = "bottomRight";
77 |
78 | /**
79 | * Auto-detects the users operating system and attempts to set a logical
80 | * default for display location. If the user is on a Mac, this will set
81 | * the display location to DISPLAY_LOCATION_TOP_RIGHT. Otherwise, if
82 | * the user is on another system (e.g. Windows or Linux), the display
83 | * location is set to DISPLAY_LOCATION_BOTTOM_RIGHT.
84 | */
85 | public static const DISPLAY_LOCATION_AUTO:String = "auto";
86 | } // class declaration
87 | } // package
--------------------------------------------------------------------------------
/src/main/actionscript/com/charlesbihis/engine/notification/NotificationManager.as:
--------------------------------------------------------------------------------
1 | package com.charlesbihis.engine.notification
2 | {
3 | import com.charlesbihis.engine.notification.event.NotificationEvent;
4 | import com.charlesbihis.engine.notification.ui.Notification;
5 |
6 | import flash.desktop.NativeApplication;
7 | import flash.events.Event;
8 | import flash.events.EventDispatcher;
9 | import flash.events.IEventDispatcher;
10 | import flash.events.IOErrorEvent;
11 | import flash.media.Sound;
12 | import flash.net.URLRequest;
13 | import flash.system.Capabilities;
14 | import flash.utils.setTimeout;
15 |
16 | import mx.collections.ArrayCollection;
17 | import mx.core.FlexGlobals;
18 | import mx.events.StyleEvent;
19 | import mx.logging.ILogger;
20 | import mx.logging.Log;
21 | import mx.logging.LogEventLevel;
22 | import mx.logging.targets.TraceTarget;
23 | import mx.utils.ObjectUtil;
24 |
25 | /**
26 | * Event broadcast when an active notification has just been closed.
27 | *
28 | * @eventType com.charlesbihis.engine.notification.NotificationEvent.NOTIFICATION_CLOSE
29 | *
30 | * @see com.charlesbihis.engine.notification.NotificationEvent
31 | */
32 | [Event(name="notificationCloseEvent", type="com.charlesbihis.engine.notification.event.NotificationEvent")]
33 |
34 | /**
35 | * Event broadcast when an action to close all active notifications has been invoked.
36 | *
37 | * @eventType com.charlesbihis.engine.notification.NotificationEvent.CLOSE_ALL_NOTIFICATIONS
38 | *
39 | * @see com.charlesbihis.engine.notification.NotificationEvent
40 | */
41 | [Event(name="closeAllNotificationsEvent", type="com.charlesbihis.engine.notification.event.NotificationEvent")]
42 |
43 | /**
44 | * Event broadcast when the user has gone idle.
45 | *
46 | * @eventType com.charlesbihis.engine.notification.NotificationEvent.USER_IS_IDLE
47 | *
48 | * @see com.charlesbihis.engine.notification.NotificationEvent
49 | */
50 | [Event(name="userIsIdleEvent", type="com.charlesbihis.engine.notification.event.NotificationEvent")]
51 |
52 | /**
53 | * Event broadcast when the user has returned from idle.
54 | *
55 | * @eventType com.charlesbihis.engine.notification.NotificationEvent.USER_IS_PRESENT
56 | *
57 | * @see com.charlesbihis.engine.notification.NotificationEvent
58 | */
59 | [Event(name="userIsPresentEvent", type="com.charlesbihis.engine.notification.event.NotificationEvent")]
60 |
61 | /**
62 | * Main service class for Notification Engine.
63 | *
64 | * @langversion ActionScript 3.0
65 | * @playerversion Flash 10
66 | *
67 | * @author Charles Bihis (www.whoischarles.com)
68 | */
69 | public class NotificationManager extends EventDispatcher
70 | {
71 | /**
72 | * Array of strings containing title names of other AIR windows that should be noted
73 | * when displaying notifications. Any title names in this array will be compared against
74 | * our notification display locations to avoid any overlapping.
75 | */
76 | public static var otherWindowsToAvoid:Array = new Array();
77 |
78 | private static const NOTIFICATION_THROTTLE_TIME:int = 500;
79 | private static const NOTIFICATION_IDLE_THRESHOLD:int = 15;
80 | private static const NOTIFICATION_MAX_REPLAY_COUNT:int = 5;
81 | private static const MAX_ACTIVE_TOASTS:int = 5;
82 | private static const MINIMUM_TIME_BETWEEN_NOTIFICATION_SOUNDS:int = 10000; // 10 seconds
83 |
84 | private var log:ILogger = Log.getLogger("com.charlesbihis.engine.notification.NotificationManager");
85 | private var queue:ArrayCollection;
86 | private var previousQueue:ArrayCollection;
87 | private var latestNotificationDisplay:Number;
88 | private var latestNotificationSound:Number;
89 | private var suppressedNotificationCount:int;
90 | private var activeToasts:int;
91 | private var notificationSound:Sound;
92 | private var soundLoaded:Boolean;
93 | private var themeLoaded:Boolean;
94 |
95 | private var _defaultNotificationImage:String;
96 | private var _defaultCompactNotificationImage:String;
97 | private var _displayLocation:String;
98 | private var _displayLength:int;
99 | private var _isUserIdle:Boolean;
100 |
101 | /**
102 | * Constructor to create a valid NotificationManager object. Will
103 | * create a notification engine with the passed in values as defaults settings.
104 | * Can also change these settings afterwards, but the first three at least (defaultStyle,
105 | * defaultNotificationImage, and defaultCompactNotificationImage) are required. The
106 | * rest are optional and can be omitted or left null.
107 | *
108 | * @param defaultStyle Location of compiled stylesheet to use as default style.
109 | * @param defaultNotificationImage Location of 50x50 image to use as default notification image when one isn't specified.
110 | * @param defaultCompactNotificationImage Location of 16x16 image to use as default compact notification image when one isn't specified.
111 | * @param notificationSound (Optional) Location of notification sound to use when displaying a notification. Leave null if no sound desired. Defaults to null.
112 | * @param displayLength (Optional) Length, in seconds, that notifications will stay on the screen. Defaults to NotificationConst.DISPLAY_LENGTH_MEDIUM.
113 | * @param displayLocation (Optional) Location on-screen (i.e. top-left, top-right, bottom-left, or bottom-right) where notifications are to appear. Defaults to NotificationConst.DISPLAY_LOCATION_AUTO.
114 | *
115 | * @see com.charlesbihis.engine.notification.NotificationConst
116 | */
117 | public function NotificationManager(defaultStyle:String, defaultNotificationImage:String, defaultCompactNotificationImage:String, notificationSound:String = null, displayLength:int = NotificationConst.DISPLAY_LENGTH_MEDIUM, displayLocation:String = NotificationConst.DISPLAY_LOCATION_AUTO)
118 | {
119 | // initialize
120 | this.queue = new ArrayCollection();
121 | this.previousQueue = new ArrayCollection();
122 | this.latestNotificationDisplay = 0;
123 | this.latestNotificationSound = 0;
124 | this.suppressedNotificationCount = 0;
125 | this.activeToasts = 0;
126 | this.notificationSound = null;
127 | this.soundLoaded = false;
128 | this.themeLoaded = false;
129 |
130 | // set up logging
131 | var logTarget:TraceTarget = new TraceTarget();
132 | logTarget.level = LogEventLevel.ALL;
133 | logTarget.includeDate = true;
134 | logTarget.includeTime = true;
135 | logTarget.includeCategory = true;
136 | logTarget.includeLevel = true;
137 | Log.addTarget(logTarget);
138 |
139 | // configure default settings
140 | _defaultNotificationImage = defaultNotificationImage;
141 | _defaultCompactNotificationImage = defaultCompactNotificationImage;
142 | _displayLength = displayLength;
143 | _displayLocation = displayLocation;
144 |
145 | // listen for important events
146 | addEventListener(NotificationEvent.NOTIFICATION_CLOSE, notificationCloseHandler);
147 | NativeApplication.nativeApplication.idleThreshold = NOTIFICATION_IDLE_THRESHOLD;
148 | NativeApplication.nativeApplication.addEventListener(Event.USER_IDLE, userIdleHandler);
149 | NativeApplication.nativeApplication.addEventListener(Event.USER_PRESENT, userPresentHandler);
150 |
151 | // if display location is set to "auto", configure default display location based on detected operating system
152 | if (_displayLocation == NotificationConst.DISPLAY_LOCATION_AUTO)
153 | {
154 | log.info("Display location has been set to \"auto\". Configuring based on detected operating system");
155 |
156 | var os:String = flash.system.Capabilities.os;
157 | if (os.indexOf("Mac") >= 0)
158 | {
159 | log.info("Display location has detected an operating system of \"{0}\". Setting default display location to top right.", os);
160 | _displayLocation = NotificationConst.DISPLAY_LOCATION_TOP_RIGHT;
161 | } // if statement
162 | else
163 | {
164 | log.info("Display location has detected an operating system of \"{0}\". Setting default display location to bottom right.", os);
165 | _displayLocation = NotificationConst.DISPLAY_LOCATION_BOTTOM_RIGHT;
166 | } // else statement
167 | } // if statement
168 |
169 | // load default style
170 | var loadStyleEvent:IEventDispatcher = FlexGlobals.topLevelApplication.styleManager.loadStyleDeclarations2(defaultStyle);
171 | loadStyleEvent.addEventListener(StyleEvent.COMPLETE, loadStyleHandler);
172 |
173 | // load notification sound
174 | if (notificationSound != null)
175 | {
176 | this.notificationSound = new Sound(new URLRequest(notificationSound));
177 | this.notificationSound.addEventListener(Event.COMPLETE, loadSoundHandler);
178 | this.notificationSound.addEventListener(IOErrorEvent.IO_ERROR, loadSoundHandler);
179 | } // if statement
180 |
181 | function notificationCloseHandler(event:Event):void
182 | {
183 | activeToasts--;
184 | log.debug("Notification closed. There are now {0} active toasts", activeToasts);
185 | } // notificationCloseHandler
186 | } // NotificationManager
187 |
188 | /**
189 | * Method to show a notification.
190 | *
191 | * @param notification A notification object for which to display
192 | *
193 | * @see com.charlesbihis.engine.notification.Notification
194 | */
195 | public function showNotification(notification:Notification):void
196 | {
197 | log.debug("showNotification() called with notification object: {0}", ObjectUtil.toString(notification));
198 |
199 | // set image to default if none provided
200 | if (notification.image == null || notification.image.length == 0)
201 | {
202 | notification.image = (notification.isCompact ? _defaultCompactNotificationImage : _defaultNotificationImage);
203 | } // if statement
204 |
205 | // place it in the previousQueue for possibly replaying later,
206 | if (notification.isReplayable)
207 | {
208 | previousQueue.addItem(notification);
209 | } // if statement
210 |
211 | // make sure we only store a max of 5 notifications
212 | while (previousQueue.length > NOTIFICATION_MAX_REPLAY_COUNT)
213 | {
214 | previousQueue.removeItemAt(0);
215 | } // while loop
216 |
217 | // queue it so we avoid overlapping notification windows
218 | queue.addItem(notification);
219 |
220 | // start processing the queue
221 | showAll();
222 | } // showNotification
223 |
224 | /**
225 | * Method to show notification using raw, passed-in values. This is really
226 | * a convenience method for quickly showing a notification. Behind the scenes,
227 | * it simply creates a notification object and calls the showNotification() API
228 | * with that object.
229 | *
230 | * @param notificationTitle The title for the notification pop-up.
231 | * @param notificationMessage The message for the notification pop-up.
232 | * @param notificationLink (Optional) The URL, if any, to direct the user to when the notification is clicked. If null, there will be no action on notification click. Defaults to null.
233 | * @param isCompact (Optional) Parameter to set whether or not the notification should be displayed as a compact notification. If this is true, the notification message will not be displayed. Defaults to false.
234 | * @param isSticky (Optional) Parameter to set whether or not the notification is sticky and should remain on-screen until the user manually closes it. Defaults to false.
235 | * @param isReplayable (Optional) Parameter to set whether or not the notification is replayable, meaning whether or not it will show up when the replayLatestFiveNotifications()
API is invoked. This is most often set to true, but would be set to false for certain types of notifications such as system notifications. Defaults to true.
236 | */
237 | public function show(notificationTitle:String, notificationMessage:String, notificationImage:String, notificationLink:String = null, isCompact:Boolean = false, isSticky:Boolean = false, isReplayable:Boolean = true):void
238 | {
239 | var notification:Notification = new Notification();
240 | notification.title = notificationTitle;
241 | notification.message = notificationMessage;
242 | notification.image = notificationImage;
243 | notification.link = notificationLink;
244 | notification.isCompact = isCompact;
245 | notification.isSticky = isSticky;
246 | notification.isReplayable = isReplayable;
247 |
248 | log.debug("show() called with values producing notification object: {0}", ObjectUtil.toString(notification));
249 | showNotification(notification);
250 | } // show
251 |
252 | /**
253 | * Method to replay the latest 5 updates.
254 | */
255 | public function replayLatestFiveUpdates():void
256 | {
257 | log.info("Replaying latest five updates");
258 |
259 | // if there are no recent messages, tell the user
260 | if (previousQueue.length == 0)
261 | {
262 | log.info("No updates to display");
263 | var noUpdatesNotification:Notification = new Notification();
264 | noUpdatesNotification.title = "No Updates to Show";
265 | noUpdatesNotification.isReplayable = false;
266 | showNotification(noUpdatesNotification);
267 |
268 | return;
269 | } // if statement
270 |
271 | for (var i:int = 0; i < previousQueue.length; i++)
272 | {
273 | // must create new notification with same properties since previously closed windows cannot be re-opened
274 | var previousNotification:Notification = previousQueue.getItemAt(i) as Notification;
275 | var notification:Notification = new Notification();
276 | notification.title = previousNotification.title;
277 | notification.message = previousNotification.message;
278 | notification.image = previousNotification.image;
279 | notification.link = previousNotification.link;
280 | notification.isCompact = previousNotification.isCompact;
281 | notification.isSticky = previousNotification.isSticky;
282 | notification.isReplayable = previousNotification.isReplayable;
283 |
284 | log.debug("Replaying notification {0} with values: {1}", i, ObjectUtil.toString(notification));
285 | queue.addItem(notification);
286 | } // for loop
287 |
288 | showAll();
289 | } // replayLatestFiveUpdates
290 |
291 | /**
292 | * Change the style at runtime by caling this API and passing in the location
293 | * of a compiled stylesheet.
294 | *
295 | * @param style Location of a compiled stylesheet to use as the current style for any further notifications.
296 | */
297 | public function loadStyle(style:String):void
298 | {
299 | themeLoaded = false;
300 | log.info("Loading style sheet at location: {0}", style);
301 | var loadStyleEvent:IEventDispatcher = FlexGlobals.topLevelApplication.styleManager.loadStyleDeclarations2(style);
302 | loadStyleEvent.addEventListener(StyleEvent.COMPLETE, loadStyleHandler);
303 | } // loadStyle
304 |
305 | /**
306 | * Load a sound to play when a notification is displayed. If null is passed in, no notification sound
307 | * will be played at all.
308 | *
309 | * @param sound Location of sound file to use as the sound played when a notification is displayed.
310 | */
311 | public function loadSound(sound:String):void
312 | {
313 | // add event listeners
314 | if (notificationSound != null && !notificationSound.hasEventListener(Event.COMPLETE))
315 | {
316 | notificationSound.addEventListener(Event.COMPLETE, loadSoundHandler);
317 | } // if statement
318 | if (notificationSound != null && !notificationSound.hasEventListener(IOErrorEvent.IO_ERROR))
319 | {
320 | this.notificationSound.addEventListener(IOErrorEvent.IO_ERROR, loadSoundHandler);
321 | } // if statement
322 |
323 | // load the sound
324 | soundLoaded = false;
325 | if (sound != null)
326 | {
327 | log.info("Loading sound file at location: {0}", sound);
328 | notificationSound = new Sound(new URLRequest(sound));
329 | } // if statement
330 | else
331 | {
332 | log.info("Disabling sound on notifications");
333 | notificationSound = null;
334 | } // else statement
335 | } // loadSound
336 |
337 | /**
338 | * Clears all notifications from history. Calling replayLatestFiveUpdates
339 | * after calling this method will show no notifications.
340 | */
341 | public function clearLatestFiveUpdates():void
342 | {
343 | log.info("Clearing latest five updates queue");
344 | previousQueue.removeAll();
345 | } // clearLatestFiveUpdates
346 |
347 | /**
348 | * Closes all active, on-screen notifications. Dispatches a
349 | * NotificationEvent.CLOSE_ALL_NOTIFICATIONS
event to do so.
350 | *
351 | * @see com.charlesbihis.engine.notification.event.NotificationEvent#CLOSE_ALL_NOTIFICATIONS
352 | */
353 | public function closeAllNotifications():void
354 | {
355 | log.info("Dispatching NotificationEvent.CLOSE_ALL_NOTIFICATIONS event");
356 | var notificationEvent:NotificationEvent = new NotificationEvent(NotificationEvent.CLOSE_ALL_NOTIFICATIONS);
357 | dispatchEvent(notificationEvent);
358 | } // closeAllNotifications
359 |
360 | /**
361 | * Default image to use in notifications when no image is specified in
362 | * the actual notification object.
363 | */
364 | public function get defaultNotificationImage():String
365 | {
366 | return _defaultNotificationImage;
367 | } // defaultNotificationImage
368 |
369 | /**
370 | * @private
371 | */
372 | public function set defaultNotificationImage(defaultNotificationImage:String):void
373 | {
374 | _defaultNotificationImage = defaultNotificationImage;
375 | } // defaultNotificationImage
376 |
377 | /**
378 | * Default compact image to use in compact notifications when no compact image is
379 | * specified in the actual notification object.
380 | */
381 | public function get defaultCompactNotificationImage():String
382 | {
383 | return _defaultCompactNotificationImage;
384 | } // defaultCompactNotificationImage
385 |
386 | /**
387 | * @private
388 | */
389 | public function set defaultCompactNotificationImage(defaultCompactNotificationImage:String):void
390 | {
391 | _defaultCompactNotificationImage = defaultCompactNotificationImage;
392 | } // defaultCompactNotificationImage
393 |
394 | /**
395 | * Default display length, in seconds, that the notifications will stay on screen for.
396 | */
397 | public function get displayLength():int
398 | {
399 | return _displayLength;
400 | } // displayLength
401 |
402 | /**
403 | * @private
404 | */
405 | public function set displayLength(displayLength:int):void
406 | {
407 | _displayLength = displayLength;
408 | } // displayLength
409 |
410 | /**
411 | * Default location on-screen (i.e. top-left, top-right, bottom-left, or bottom-right)
412 | * where notifications are to appear.
413 | */
414 | public function get displayLocation():String
415 | {
416 | return _displayLocation;
417 | } // displayLocation
418 |
419 | /**
420 | * @private
421 | */
422 | public function set displayLocation(displayLocation:String):void
423 | {
424 | _displayLocation = displayLocation;
425 | } // displayLocation
426 |
427 | /**
428 | * Convenience method for notifications that indicates whether or not the
429 | * user is idle.
430 | */
431 | public function get isUserIdle():Boolean
432 | {
433 | return _isUserIdle;
434 | } // isUserIdle
435 |
436 | /**
437 | * @private
438 | */
439 | private function showAll():void
440 | {
441 | // throttle the notifications!
442 | if (!themeLoaded || (notificationSound != null && !soundLoaded) || new Date().time - latestNotificationDisplay <= NOTIFICATION_THROTTLE_TIME)
443 | {
444 | setTimeout(showAll, NOTIFICATION_THROTTLE_TIME);
445 |
446 | return;
447 | } // if statement
448 |
449 | // maintain only 5 active toasts
450 | if (isUserIdle && queue.length > 0 && (activeToasts >= MAX_ACTIVE_TOASTS || suppressedNotificationCount > 0))
451 | {
452 | // close all active notifications
453 | if (activeToasts >= MAX_ACTIVE_TOASTS && suppressedNotificationCount == 0)
454 | {
455 | log.info("User is idle and max active notification limit has been reached ({0}). Closing all active toasts and queueing all incoming.", MAX_ACTIVE_TOASTS);
456 | var notificationEvent:NotificationEvent = new NotificationEvent(NotificationEvent.CLOSE_ALL_NOTIFICATIONS);
457 | dispatchEvent(notificationEvent);
458 | } // if statement
459 |
460 | // increment the suppressed notification count, remove the notification from the queue, rinse, repeat
461 | suppressedNotificationCount++;
462 | queue.removeItemAt(0);
463 | showAll();
464 |
465 | return;
466 | } // if statement
467 |
468 | // start emptying the queue, one notification at a time
469 | if (queue.length > 0)
470 | {
471 | // show it
472 | var notification:Notification = queue.getItemAt(0) as Notification;
473 | notification.open(this);
474 | log.debug("Showing notification: {0}", ObjectUtil.toString(notification));
475 |
476 | // play sound
477 | if (soundLoaded && notificationSound != null && (new Date().time - latestNotificationSound > MINIMUM_TIME_BETWEEN_NOTIFICATION_SOUNDS))
478 | {
479 | notificationSound.play();
480 | latestNotificationSound = new Date().time;
481 | } // if statement
482 |
483 | // keep track of it
484 | activeToasts++;
485 | log.debug("There are now {0} active toasts", activeToasts);
486 |
487 | // update the latest notification time
488 | latestNotificationDisplay = new Date().time;
489 |
490 | // remove item from the queue
491 | queue.removeItemAt(0);
492 |
493 | // recursively call showAll() until the queue is empty
494 | setTimeout(showAll, NOTIFICATION_THROTTLE_TIME);
495 | } // if statement
496 | } // showAll
497 |
498 | /**
499 | * @private
500 | */
501 | private function userPresentHandler(event:Event):void
502 | {
503 | log.debug("User is back");
504 | _isUserIdle = false;
505 | dispatchEvent(new NotificationEvent(NotificationEvent.USER_IS_PRESENT));
506 |
507 | if (suppressedNotificationCount > 0)
508 | {
509 | // build summary notification
510 | var summaryNotification:Notification = new Notification();
511 | summaryNotification.title = "There were " + (suppressedNotificationCount + MAX_ACTIVE_TOASTS) + " stories posted while you were away";
512 | summaryNotification.isReplayable = false;
513 | log.info("Showing summary notification of {0} missed notifications", (suppressedNotificationCount + MAX_ACTIVE_TOASTS));
514 |
515 | // must reset suppressedNotificationCount back to 0 so that this upcoming
516 | // summary notification will not also be suppressed when shown
517 | suppressedNotificationCount = 0;
518 |
519 | // show it
520 | showNotification(summaryNotification);
521 | } // if statement
522 | } // onPresence
523 |
524 | /**
525 | * @private
526 | */
527 | private function userIdleHandler(event:Event):void
528 | {
529 | log.debug("User is idle");
530 | _isUserIdle = true;
531 | dispatchEvent(new NotificationEvent(NotificationEvent.USER_IS_IDLE));
532 | } // onIdle
533 |
534 | /**
535 | * @private
536 | */
537 | private function loadStyleHandler(event:StyleEvent):void
538 | {
539 | themeLoaded = true;
540 | } // styleLoadHandler
541 |
542 | /**
543 | * @private
544 | */
545 | private function loadSoundHandler(event:Event):void
546 | {
547 | if (event is IOErrorEvent)
548 | {
549 | log.error("Unable to load sound file \"{0}\". Please verify its location", notificationSound);
550 | soundLoaded = false;
551 | } // if statement
552 | else
553 | {
554 | soundLoaded = true;
555 | } // else statement
556 | } // loadSoundHandler
557 | } // class declaration
558 | } // package
559 |
--------------------------------------------------------------------------------
/src/main/actionscript/com/charlesbihis/engine/notification/event/NotificationEvent.as:
--------------------------------------------------------------------------------
1 | package com.charlesbihis.engine.notification.event
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * Event class for all events in notification engine.
7 | *
8 | * @langversion ActionScript 3.0
9 | * @playerversion Flash 10
10 | *
11 | * @author Charles Bihis (www.whoischarles.com)
12 | */
13 | public class NotificationEvent extends Event
14 | {
15 | /**
16 | * Event broadcast when an active notification has just been closed.
17 | *
18 | * @eventType notificationCloseEvent
19 | */
20 | public static const NOTIFICATION_CLOSE:String = "notificationCloseEvent";
21 |
22 | /**
23 | * Event broadcast when an action to close all active notifications has been invoked.
24 | *
25 | * @eventType closeAllNotificationsEvent
26 | */
27 | public static const CLOSE_ALL_NOTIFICATIONS:String = "closeAllNotificationsEvent";
28 |
29 | /**
30 | * Event broadcast when the user has gone idle.
31 | *
32 | * @eventType userIsIdleEvent
33 | */
34 | public static const USER_IS_IDLE:String = "userIsIdleEvent";
35 |
36 | /**
37 | * Event broadcast when the user has returned from idle.
38 | *
39 | * @eventType userIsPresentEvent
40 | */
41 | public static const USER_IS_PRESENT:String = "userIsPresentEvent";
42 |
43 | public function NotificationEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false):void
44 | {
45 | super(type, bubbles, cancelable);
46 | } // NotificationEvent
47 |
48 | public override function clone():Event
49 | {
50 | return new NotificationEvent(type);
51 | } // clone
52 | } // class declaration
53 | } // package
--------------------------------------------------------------------------------
/src/main/actionscript/com/charlesbihis/engine/notification/style/style.css:
--------------------------------------------------------------------------------
1 | /* CSS file */
2 | @namespace s "library://ns.adobe.com/flex/spark";
3 | @namespace mx "library://ns.adobe.com/flex/mx";
4 |
5 | .notificationContainer
6 | {
7 | backgroundAlpha: 0.0;
8 | }
9 |
10 | .notification
11 | {
12 | cornerRadius: 8;
13 | backgroundColor: #000;
14 | backgroundAlpha: 0.8;
15 | borderStyle: none;
16 | borderColor: #000;
17 | paddingTop: 10;
18 | paddingBottom: 10;
19 | paddingLeft: 15;
20 | paddingRight: 15;
21 | }
22 |
23 | .notificationText
24 | {
25 | color: #FFF;
26 | }
--------------------------------------------------------------------------------
/src/main/actionscript/com/charlesbihis/engine/notification/ui/Notification.as:
--------------------------------------------------------------------------------
1 | package com.charlesbihis.engine.notification.ui
2 | {
3 | import com.charlesbihis.engine.notification.NotificationManager;
4 |
5 | import flash.events.EventDispatcher;
6 |
7 | /**
8 | * Wrapper class for a notification object.
9 | *
10 | * @langversion ActionScript 3.0
11 | * @playerversion Flash 10
12 | * @author Charles Bihis (www.whoischarles.com)
13 | */
14 | public class Notification extends EventDispatcher
15 | {
16 | private var _title:String;
17 | private var _message:String;
18 | private var _image:String;
19 | private var _link:String;
20 | private var _isCompact:Boolean;
21 | private var _isSticky:Boolean;
22 | private var _isReplayable:Boolean;
23 |
24 | private var notificationWindow:NotificationWindow;
25 |
26 | /**
27 | * Constructor to create a valid Notification object.
28 | *
29 | * @param title The title that appears in bold at the top of the notification. This is always displayed, whether it be a regular notification or a compact notification.
30 | * @param message The message that appears in the body of the notification. This is only displayed in regular notifications and is not used in compact notifications.
31 | * @param image Location of image to use as the image for the notification. If notification is set to compact (i.e. isCompact=true
), image should be 16x16 pixels. Otherwise, image should be 50x50 pixels.
32 | * @param link The URL, if any, to direct the user to when the notification is clicked. If null, there will be no action on notification click.
33 | * @param isCompact Sets whether the notification is regular or compact.
34 | * @param isSticky Sets whether the notification is sticky and should remain on-screen until the user manually closes it.
35 | * @param isReplayable Sets whether the notification is replayable, meaning whether or not it will show up when the replayLatestFiveNotifications()
API is invoked. This is most often set to true
, but would be set to false
for certain types of notifications such as system notifications.
36 | */
37 | public function Notification(title:String = null, message:String = null, image:String = null, link:String = null, isCompact:Boolean = false, isSticky:Boolean = false, isReplayable:Boolean = true)
38 | {
39 | notificationWindow = new NotificationWindow();
40 | notificationWindow.notificationTitle = title;
41 | notificationWindow.notificationMessage = message;
42 | notificationWindow.notificationImage = image;
43 | notificationWindow.notificationLink = link;
44 | notificationWindow.isCompact = isCompact;
45 | notificationWindow.isSticky = isSticky;
46 | notificationWindow.isReplayable = isReplayable;
47 |
48 | notificationWindow.title = NotificationWindow.NOTIFICATION_IDENTIFIER;
49 | } // Notification
50 |
51 | /**
52 | * Opens the notification. This will make the notification appear on-screen
53 | * with the values specified. If certain values aren't specified and this
54 | * is called, the default engine values will be used.
55 | */
56 | public function open(notificationManager:NotificationManager):void
57 | {
58 | notificationWindow.notificationManager = notificationManager;
59 | notificationWindow.open(false);
60 | } // open
61 |
62 | /**
63 | * The title that appears in bold at the top of the notification. This is always displayed, whether it be a regular notification or a compact notification.
64 | */
65 | public function get title():String
66 | {
67 | return notificationWindow.notificationTitle;
68 | } // title
69 |
70 | /**
71 | * @private
72 | */
73 | public function set title(title:String):void
74 | {
75 | notificationWindow.notificationTitle = title;
76 | } // title
77 |
78 | /**
79 | * The message that appears in the body of the notification. This is only
80 | * displayed in regular notifications and is not used in compact notifications.
81 | */
82 | public function get message():String
83 | {
84 | return notificationWindow.notificationMessage;
85 | } // message
86 |
87 | /**
88 | * @private
89 | */
90 | public function set message(message:String):void
91 | {
92 | notificationWindow.notificationMessage = message;
93 | } // message
94 |
95 | /**
96 | * Location of image to use as the image for the notification. If notification
97 | * is set to compact (i.e. isCompact=true
), image should be 16x16
98 | * pixels. Otherwise, image should be 50x50 pixels. Other sizes are allowed,
99 | * but they will be scaled to fit one of the sizes, and scaling may result in
100 | * less than optimized display of the images.
101 | */
102 | public function get image():String
103 | {
104 | return notificationWindow.notificationImage;
105 | } // image
106 |
107 | /**
108 | * @private
109 | */
110 | public function set image(image:String):void
111 | {
112 | notificationWindow.notificationImage = image;
113 | } // image
114 |
115 | /**
116 | * The URL, if any, to direct the user to when the notification is clicked. If null, there will be no action on notification click.
117 | */
118 | public function get link():String
119 | {
120 | return notificationWindow.notificationLink;
121 | } // link
122 |
123 | /**
124 | * @private
125 | */
126 | public function set link(link:String):void
127 | {
128 | notificationWindow.notificationLink = link;
129 | } // link
130 |
131 | /**
132 | * Sets whether the notification is regular or compact. Regular notifications display a
133 | * 50x50 pixel image, with the notification title in bold at the top, followed by
134 | * the notification message in the body of the notification. Compact notifications display
135 | * a 16x16 pixel image with only the notification title in bold next to it. The notification
136 | * message in compact notifications is not used.
137 | */
138 | public function get isCompact():Boolean
139 | {
140 | return notificationWindow.isCompact;
141 | } // isCompact
142 |
143 | /**
144 | * @private
145 | */
146 | public function set isCompact(isCompact:Boolean):void
147 | {
148 | notificationWindow.isCompact = isCompact;
149 | } // isCompact
150 |
151 | /**
152 | * Sets whether the notification is sticky and should remain on-screen until the user manually closes it.
153 | */
154 | public function get isSticky():Boolean
155 | {
156 | return notificationWindow.isSticky;
157 | } // isSticky
158 |
159 | /**
160 | * @private
161 | */
162 | public function set isSticky(isSticky:Boolean):void
163 | {
164 | notificationWindow.isSticky = isSticky;
165 | } // isSticky
166 |
167 | /**
168 | * Sets whether the notification is replayable, meaning whether or not it will show up when
169 | * the replayLatestFiveNotifications()
API is invoked. This is most often set
170 | * to true
, but would be set to false
for certain types of
171 | * notifications such as system notifications.
172 | */
173 | public function get isReplayable():Boolean
174 | {
175 | return notificationWindow.isReplayable;
176 | } // isReplayable
177 |
178 | /**
179 | * @private
180 | */
181 | public function set isReplayable(isReplayable:Boolean):void
182 | {
183 | notificationWindow.isReplayable = isReplayable;
184 | } // isReplayable
185 | } // class declaration
186 | } // package
--------------------------------------------------------------------------------
/src/main/actionscript/com/charlesbihis/engine/notification/ui/NotificationWindow.mxml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 | = screen.visibleBounds.x);
136 | x -= (isLeft ? -(size.width + TOP_MARGIN) : size.width + TOP_MARGIN))
137 | {
138 | for (y = (isTop ? screen.visibleBounds.y + TOP_MARGIN + TOP_MARGIN : screen.visibleBounds.y + screen.visibleBounds.height - size.height - TOP_MARGIN);
139 | (isTop ? y <= screen.visibleBounds.y + screen.visibleBounds.height - size.height - TOP_MARGIN : y >= screen.visibleBounds.y);
140 | y += (isTop ? 10 : -10))
141 | {
142 | rectangle = new Rectangle(x, isTop ? y - TOP_MARGIN : y, size.width + TOP_MARGIN, size.height + TOP_MARGIN);
143 |
144 | if(!isOccupied(rectangle))
145 | {
146 | spot.x = x;
147 | spot.y = y;
148 | done = true;
149 | break;
150 | } // if statement
151 | } // for loop
152 |
153 | if (done)
154 | {
155 | break;
156 | } // if statement
157 | } // for loop
158 |
159 | return spot;
160 | } // findSpotForMessage
161 |
162 | private function isOccupied(rectangle:Rectangle):Boolean
163 | {
164 | var occupied:Boolean = false;
165 | var isOtherWindow:Boolean = false;
166 | for each (var window:NativeWindow in NativeApplication.nativeApplication.openedWindows)
167 | {
168 | for (var i:int = 0; i < NotificationManager.otherWindowsToAvoid.length; i++)
169 | {
170 | if (window.title.indexOf(NotificationManager.otherWindowsToAvoid[i]) >= 0)
171 | {
172 | isOtherWindow = true;
173 | break;
174 | } // if statement
175 | } // for loop
176 | occupied = occupied || (((window.title.indexOf(NOTIFICATION_IDENTIFIER) >= 0 && window.title.indexOf(notificationId) < 0) || isOtherWindow) && window.bounds.intersects(rectangle) && window.visible);
177 | } // for loop
178 |
179 | return occupied;
180 | } // isOccupied
181 |
182 | private function clickHandler():void
183 | {
184 | if (notificationLink != null && notificationLink.length > 0 && !closeButton.visible)
185 | {
186 | flash.net.navigateToURL(new URLRequest(notificationLink));
187 | } // if statement
188 | } // clickHandler
189 |
190 | private function closeToast(event:Event = null):void
191 | {
192 | // clean up the event listeners
193 | notificationManager.removeEventListener(NotificationEvent.CLOSE_ALL_NOTIFICATIONS, closeToast);
194 | notificationManager.removeEventListener(NotificationEvent.USER_IS_PRESENT, userPresentHandler);
195 | removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
196 | removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
197 |
198 | // If 'close' button was pushed, let's close it
199 | // immediately. Otherwise, let's start the
200 | // fade-out effect and close after it's done playing.
201 | if (event is MouseEvent)
202 | {
203 | event.preventDefault();
204 | finishClose();
205 | } // if statement
206 | else
207 | {
208 | fadeout.addEventListener(EffectEvent.EFFECT_END, finishClose);
209 | fadeout.play();
210 | } // else statement
211 |
212 | function finishClose(event:Event = null):void
213 | {
214 | if (event is EffectEvent)
215 | {
216 | fadeout.removeEventListener(EffectEvent.EFFECT_END, finishClose);
217 | } // if statement
218 |
219 | notificationManager.dispatchEvent(new NotificationEvent(NotificationEvent.NOTIFICATION_CLOSE));
220 | lifetimeTimer.stop();
221 | repositionTimer.stop();
222 | close();
223 | } // finishClose
224 | } // closeToast
225 |
226 | private function mouseOverHandler(event:MouseEvent = null):void
227 | {
228 | // reset lifetime
229 | if (ticks < LIFETIME_RESET_VALUE)
230 | {
231 | ticks = LIFETIME_RESET_VALUE;
232 | } // if statement
233 |
234 | lifetimeTimer.stop();
235 | } // mouseOverHandler
236 |
237 | private function mouseOutHandler(event:MouseEvent = null):void
238 | {
239 | if (!isSticky)
240 | {
241 | lifetimeTimer.start();
242 | } // if statement
243 | } // mouseOutHandler
244 |
245 | private function userPresentHandler(event:NotificationEvent):void
246 | {
247 | // reset lifetime if too low - don't want notifications disappearing immediately after the user returns
248 | if (ticks < LIFETIME_RESET_VALUE)
249 | {
250 | ticks = LIFETIME_RESET_VALUE;
251 | } // if statement
252 | } // userPresentHandler
253 | ]]>
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
--------------------------------------------------------------------------------