diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index b556e7fc63..80a207f711 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -888,7 +888,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphlict-listen' => array( - 'uri' => '/res/0743d3f3/rsrc/js/application/aphlict/behavior-aphlict-listen.js', + 'uri' => '/res/6dde3f43/rsrc/js/application/aphlict/behavior-aphlict-listen.js', 'type' => 'js', 'requires' => array( @@ -1522,7 +1522,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-phabricator-notification-example' => array( - 'uri' => '/res/df97e4b3/rsrc/js/application/uiexample/notification-example.js', + 'uri' => '/res/a6d51998/rsrc/js/application/uiexample/notification-example.js', 'type' => 'js', 'requires' => array( @@ -2499,7 +2499,7 @@ celerity_register_resource_map(array( ), 'phabricator-notification' => array( - 'uri' => '/res/cacd79f1/rsrc/js/application/core/Notification.js', + 'uri' => '/res/c604fbbe/rsrc/js/application/core/Notification.js', 'type' => 'js', 'requires' => array( diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js index b22b4beacb..c57d496cb7 100644 --- a/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js @@ -35,7 +35,7 @@ JX.behavior('aphlict-listen', function(config) { new JX.Notification() .setContent('(Aphlict) [' + type + '] ' + details) - .setClassName('jx-notification-debug') + .alterClassName('jx-notification-debug', true) .setDuration(0) .show(); } @@ -63,7 +63,7 @@ JX.behavior('aphlict-listen', function(config) { !showing_reload) { var reload = new JX.Notification() .setContent('Page updated, click to reload.') - .setClassName('jx-notification-alert') + .alterClassName('jx-notification-alert', true) .setDuration(0); reload.listen('activate', function(e) { JX.$U().go(); }) reload.show(); diff --git a/webroot/rsrc/js/application/core/Notification.js b/webroot/rsrc/js/application/core/Notification.js index b3f4061de3..4d062f94bb 100644 --- a/webroot/rsrc/js/application/core/Notification.js +++ b/webroot/rsrc/js/application/core/Notification.js @@ -8,7 +8,7 @@ */ /** - * Show a notification. Usage: + * Show a notification popup on screen. Usage: * * var n = new JX.Notification() * .setContent('click me!'); @@ -21,49 +21,78 @@ JX.install('Notification', { events : ['activate', 'close'], members : { + _container : null, + _visible : false, + _hideTimer : null, + _duration : 12000, + show : function() { - var self = JX.Notification; - self._show(this); + if (!this._visible) { + this._visible = true; - if (this.getDuration()) { - setTimeout(JX.bind(self, self._hide, this), this.getDuration()); + var self = JX.Notification; + self._show(this); + this._updateTimer(); } + return this; }, - _render : function() { - return JX.$N( - 'div', - { - className: 'jx-notification ' + this.getClassName(), - sigil: 'jx-notification' - }, - this.getContent()); - } - }, - properties : { + hide : function() { + if (this._visible) { + this._visible = false; + + var self = JX.Notification; + self._hide(this); + this._updateTimer(); + } + return this; + }, + + alterClassName : function(name, enable) { + JX.DOM.alterClass(this._getContainer(), name, enable); + return this; + }, + + setContent : function(content) { + JX.DOM.setContent(this._getContainer(), content); + return this; + }, /** - * Optional class name(s) to add to the rendered notification. - * - * @param string Class name(s). - */ - className : null, - - /** - * Notification content. - * - * @param mixed Content. - */ - content : null, - - /** - * Duration before the notification fades away, in milliseconds. If set to - * 0, the notification persists until dismissed. + * Set duration before the notification fades away, in milliseconds. If set + * to 0, the notification persists until dismissed. * * @param int Notification duration, in milliseconds. + * @return this */ - duration : 12000 + setDuration : function(milliseconds) { + this._duration = milliseconds; + this._updateTimer(false); + return this; + }, + _updateTimer : function() { + if (this._hideTimer) { + clearTimeout(this._hideTimer); + this._hideTimer = null; + } + + if (this._visible && this._duration) { + this._hideTimer = setTimeout(JX.bind(this, this.hide), this._duration); + } + }, + + _getContainer : function() { + if (!this._container) { + this._container = JX.$N( + 'div', + { + className: 'jx-notification', + sigil: 'jx-notification' + }); + } + return this._container; + } }, statics : { @@ -74,23 +103,14 @@ JX.install('Notification', { var self = JX.Notification; self._installListener(); - self._active.push({ - object: notification, - render: notification._render() - }); - - // Don't show more than a few notifications at once because it's silly. - while (self._active.length > 5) { - self._hide(self._active[0].object); - } - + self._active.push(notification); self._redraw(); }, _hide : function(notification) { var self = JX.Notification; for (var ii = 0; ii < self._active.length; ii++) { - if (self._active[ii].object === notification) { + if (self._active[ii] === notification) { notification.invoke('close'); self._active.splice(ii, 1); break; @@ -113,17 +133,17 @@ JX.install('Notification', { 'jx-notification', function(e) { // NOTE: Don't kill the event since the user might have clicked a - // link, and we want to follow the link if they did. Istead, invoke + // link, and we want to follow the link if they did. Instead, invoke // the activate event for the active notification and dismiss it if it // isn't handled. var target = e.getNode('jx-notification'); for (var ii = 0; ii < self._active.length; ii++) { var n = self._active[ii]; - if (n.render === target) { - var activation = n.object.invoke('activate'); + if (n._getContainer() === target) { + var activation = n.invoke('activate'); if (!activation.getPrevented()) { - self._hide(n.object); + n.hide(); } return; } @@ -151,9 +171,15 @@ JX.install('Notification', { document.body.appendChild(self._container); } + // Show only a limited number of notifications at once. + var limit = 5; + var notifications = []; for (var ii = 0; ii < self._active.length; ii++) { - notifications.push(self._active[ii].render); + notifications.push(self._active[ii]._getContainer()); + if (!(--limit)) { + break; + } } JX.DOM.setContent(self._container, notifications); diff --git a/webroot/rsrc/js/application/uiexample/notification-example.js b/webroot/rsrc/js/application/uiexample/notification-example.js index e4c9090081..6767b43b64 100644 --- a/webroot/rsrc/js/application/uiexample/notification-example.js +++ b/webroot/rsrc/js/application/uiexample/notification-example.js @@ -7,6 +7,9 @@ */ JX.behavior('phabricator-notification-example', function(config) { + + var sequence = 0; + JX.Stratcom.listen( 'click', 'notification-example', @@ -14,29 +17,51 @@ JX.behavior('phabricator-notification-example', function(config) { e.kill(); var notification = new JX.Notification(); - if (Math.random() > 0.1) { - notification.setContent('It is ' + new Date().toString()); + switch (sequence % 4) { + case 0: + var update = function() { + notification.setContent('It is ' + new Date().toString()); + }; - notification.listen( - 'activate', - function(e) { - if (!confirm("Close notification?")) { - e.kill(); - } - }); - } else { - notification - .setContent('Alert! Click to reload!') - .setDuration(0) - .setClassName('jx-notification-alert'); + update(); + setInterval(update, 1000); - notification.listen( - 'activate', - function(e) { - new JX.$U().go(); - }); + break; + case 1: + notification + .setContent('Permanent alert notification (until clicked).') + .setDuration(0) + .alterClassName('jx-notification-alert', true); + break; + case 2: + notification + .setContent('This notification reacts when you click it.'); + + notification.listen( + 'activate', + function() { + if (!confirm("Close notification?")) { + JX.Stratcom.context().kill(); + } + }); + break; + case 3: + notification + .setDuration(2000) + .setContent('This notification will close after 2 seconds ' + + 'unless you keep clicking it!'); + + notification.listen( + 'activate', + function() { + notification.setDuration(2000); + JX.Stratcom.context().kill(); + }); + break; } - notification.show() + + notification.show(); + sequence++; }); });