Index: trunk/js/Msg.js
===================================================================
--- trunk/js/Msg.js	(revision 622)
+++ trunk/js/Msg.js	(revision 636)
@@ -48,5 +48,5 @@
     // Merge options with defaults
     var options = this.o = $.extend({
-        container: '.sc-msg:last',
+        container: '.sc-msg:first',
         above_msg: '',
         gotohash: true
@@ -67,27 +67,47 @@
 * @param    string message  The message to display.
 * @param    string class    The class to apply to the message div (when using codebase CSS, it is useful to use one of: sc-msg-success, sc-msg-notice, sc-msg-warning, sc-msg-error)
+* @param    string fadeout_delay    If set to an integer or the special value 'auto', remove the message automatically after the specified duration (auto = calculate duration based on string length).
 * @author   Quinn Comendant <quinn@strangecode.com>
 * @version  1.0
 * @since    18 Mar 2014 23:24:15
 */
-Strangecode.Msg.prototype.raise = function (message, msg_class) {
+Strangecode.Msg.prototype.raise = function (message, msg_class, fadeout_delay) {
+    var options = this.o;
     // If an "above msg" is provided, and one doesn't already exist, add it to the message box.
-    if (this.o.above_msg.length && !$(this.o.container).find('.sc-above').length) {
-        $(this.o.container).append($('<div class="sc-js-msg sc-above"></div>').text(this.o.above_msg));
+    if (options.above_msg.length && !$(options.container).find('.sc-above').length) {
+        $(options.container).append($('<div class="sc-js-msg sc-above"></div>').text(options.above_msg));
     }
     // Use 'sc-msg-error' as default class string.
     var msg_class = (typeof msg_class === 'undefined') ? 'sc-msg-error' : msg_class;
     // Append this raised message to the sc-msg stack, if it doesn't exist already.
-    if (!$(this.o.container).find(':contains("' + message + '")').length) {
-        $(this.o.container).append($('<div data-alert class="alert-box sc-js-msg ' + msg_class + '"></div>').hide().html(message).append('<a href="#" class="close">Ã</a>'));
+    var msghash = Strangecode.Msg.hash(message);
+    if (!$(options.container).find('[data-msghash="' + msghash + '"]').length) {
+        $(options.container).append($('<div data-alert class="alert-box sc-js-msg ' + msg_class + '" data-msghash="' + msghash + '"></div>').hide().html(message).append('<a href="#" class="close">Ã</a>'));
         $('.sc-js-msg').slideDown('fast');
         if ($.fn.foundation) {
             $(document).foundation('reflow');
         }
+        var fadeout_delay = (typeof fadeout_delay === 'undefined') ? false : fadeout_delay;
+        if (fadeout_delay) {
+            if (fadeout_delay == 'auto') {
+                var num_words = message.trim().split(/\s+/).length;
+                // Average english reading speed is about 200 words per minute = 300ms per word.
+                fadeout_delay = num_words * 300;
+                fadeout_delay = fadeout_delay < 3000 ? 3000 : fadeout_delay;
+            }
+            setTimeout(function () {
+                $(options.container).find('[data-msghash="' + msghash + '"]').slideUp('slow', function () {
+                    $(this).remove();
+                });
+                if ($.fn.foundation) {
+                    $(document).foundation('reflow');
+                }
+            }, fadeout_delay);
+        }
     }
 
-    if (this.o.gotohash) {
+    if (options.gotohash) {
         $(document.body).animate({
-            'scrollTop': $(this.o.container).offset().top
+            'scrollTop': $(options.container).offset().top
         }, 'fast');
     }
@@ -107,5 +127,18 @@
 */
 Strangecode.Msg.prototype.clear = function (msg_class) {
+    var options = this.o;
     var msg_class = (typeof msg_class === 'undefined') ? 'sc-js-msg' : msg_class;
-    $(this.o.container).find('.' + msg_class).remove();
+    $(options.container).find('.' + msg_class).remove();
 };
+
+// String hashing function based on https://stackoverflow.com/a/7616484
+Strangecode.Msg.hash = function(str) {
+    var hash = 0, i, chr;
+    if (str.length === 0) return hash;
+    for (i = 0; i < str.length; i++) {
+        chr = str.charCodeAt(i);
+        hash = ((hash << 5) - hash) + chr;
+        hash |= 0;
+    }
+    return hash;
+};
