You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by bm...@apache.org on 2013/07/19 01:45:09 UTC

[10/39] Organized WebUI static assets into directories.

http://git-wip-us.apache.org/repos/asf/mesos/blob/5852eb93/src/webui/master/static/js/jquery.pailer.js
----------------------------------------------------------------------
diff --git a/src/webui/master/static/js/jquery.pailer.js b/src/webui/master/static/js/jquery.pailer.js
new file mode 100644
index 0000000..4edc5b4
--- /dev/null
+++ b/src/webui/master/static/js/jquery.pailer.js
@@ -0,0 +1,335 @@
+// A jQuery plugin for PAging and taILing data (i.e., a
+// 'PAILer'). Paging occurs when scrolling reaches the "top" and
+// tailing occurs when scrolling has reached the "bottom".
+
+// A 'read' function must be provided for reading the data (in
+// bytes). This function should expect an "options" object with the
+// fields 'offset' and 'length' set for reading the data. The result
+// from of the function should be a "promise" like value which has a
+// 'success' and 'error' callback which each take a function. An
+// object with at least two fields defined ('offset' and 'data') is
+// expected on success. The length of 'data' may be smaller than the
+// amount requested. If the offset requested is greater than the
+// available offset, the result should be an object with the 'offset'
+// field set to the available offset (i.e., the total length of the
+// data) with an empty 'data' field.
+
+// The plugin prepends, appends, and updates the "html" component of
+// the elements specified in the jQuery selector (e.g., doing
+// $('#data').pailer(...) means that data will be updated within
+// $('#data') via $('#data').prepend(...) and $('#data').append(...)
+// and $('#data').html(...) calls).
+
+// An indicator paragraph element (i.e., <p>) can be specified that
+// the plugin will write text to describing any status/errors that
+// have been encountered.
+
+// Data will automagically get truncated at some specified length,
+// configurable via the 'truncate-length' option. Likewise, the amount
+// of data paged in at a time can be configured via the 'page-size'
+// option.
+
+// Example:
+//   HTML:
+//     <div id="data" style="white-space:pre-wrap;"></div>
+//
+//     <div style="position: absolute; left: 5px; top: 0px;">
+//       <p id="indicator"></p>
+//     </div>
+//  Javascript:
+//    $('#data').pailer({
+//      'read': function(options) {
+//        var settings = $.extend({
+//          'offset': -1,
+//          'length': -1
+//        }, options);
+//        var url = '/url/for/data'
+//          + '?offset=' + settings.offset
+//          + '&length=' + settings.length;
+//        return $.getJSON(url);
+//      },
+//      'indicator': $('#indicator')
+//    });
+
+(function($) {
+  // Helper for escaping html, based on _.escape from underscore.js.
+  function escapeHTML(string) {
+    if (string == null) {
+      return '';
+    }
+
+    var escapes = {
+      '&': '&amp;',
+      '<': '&lt;',
+      '>': '&gt;',
+      '"': '&quot;',
+      "'": '&#x27;',
+      '/': '&#x2F;'
+    };
+    var regex = new RegExp('[' + Object.keys(escapes).join('') + ']', 'g');
+
+    return ('' + string).replace(regex, function (match) {
+      return escapes[match];
+    });
+  }
+
+  function Pailer(read, element, indicator, page_size, truncate_length) {
+    var this_ = this;
+
+    this_.read = read;
+    this_.element = element;
+    this_.indicator = indicator;
+    this_.initialized = false;
+    this_.start = -1;
+    this_.end = -1;
+    this_.paging = false;
+    this_.tailing = true;
+
+    page_size || $.error('Expecting page_size to be defined');
+    truncate_length || $.error('Expecting truncate_length to be defined');
+
+    this_.page_size = page_size;
+    this_.truncate_length = truncate_length;
+
+    this_.element.css('overflow', 'auto');
+
+    this_.element.scroll(function () {
+      var scrollTop = this_.element.scrollTop();
+      var height = this_.element.height();
+      var scrollHeight = this_.element[0].scrollHeight;
+
+      if (scrollTop == 0) {
+        this_.page();
+      } else if (scrollTop + height >= scrollHeight) {
+        if (!this_.tailing) {
+          this_.tailing = true;
+          this_.tail();
+        }
+      } else {
+        this_.tailing = false;
+      }
+    });
+  }
+
+
+  Pailer.prototype.initialize = function() {
+    var this_ = this;
+
+    // Set an indicator while we load the data.
+    this_.indicate('(LOADING)');
+
+    this_.read({'offset': -1})
+      .success(function(data) {
+        this_.indicate('');
+
+        // Get the last page of data.
+        if (data.offset > this_.page_size) {
+          this_.start = this_.end = data.offset - this_.page_size;
+        } else {
+          this_.start = this_.end = 0;
+        }
+
+        this_.initialized = true;
+        this_.element.html('');
+        setTimeout(function() { this_.tail(); }, 0);
+      })
+      .error(function() {
+        this_.indicate('(FAILED TO INITIALIZE ... RETRYING)');
+        setTimeout(function() {
+          this_.indicate('');
+          this_.initialize();
+        }, 1000);
+      });
+  }
+
+
+  Pailer.prototype.page = function() {
+    var this_ = this;
+
+    if (!this_.initialized) {
+      return;
+    }
+
+    if (this_.paging) {
+      return;
+    }
+
+    this_.paging = true;
+    this_.indicate('(PAGING)');
+
+    if (this_.start == 0) {
+      this_.paging = false;
+      this_.indicate('(AT BEGINNING OF FILE)');
+      setTimeout(function() { this_.indicate(''); }, 1000);
+      return;
+    }
+
+    var offset = this_.start - this_.page_size;
+    var length = this_.page_size;
+
+    if (offset < 0) {
+      offset = 0;
+      length = this_.start;
+    }
+
+    // Buffer the data in case what gets read is less than 'length'.
+    var buffer = '';
+
+    var read = function(offset, length) {
+      this_.read({'offset': offset, 'length': length})
+        .success(function(data) {
+          if (data.data.length < length) {
+            buffer += data.data;
+            read(offset + data.data.length, length - data.data.length);
+          } else if (data.data.length > 0) {
+            this_.indicate('(PAGED)');
+            setTimeout(function() { this_.indicate(''); }, 1000);
+
+            // Prepend buffer onto data.
+            data.offset -= buffer.length;
+            data.data = buffer + data.data;
+
+            // Truncate to the first newline (unless this is the beginning).
+            if (data.offset != 0) {
+              var index = data.data.indexOf('\n') + 1;
+              data.offset += index;
+              data.data = data.data.substring(index);
+            }
+
+            this_.start = data.offset;
+
+            var scrollTop = this_.element.scrollTop();
+            var scrollHeight = this_.element[0].scrollHeight;
+
+            this_.element.prepend(escapeHTML(data.data));
+
+            scrollTop += this_.element[0].scrollHeight - scrollHeight;
+            this_.element.scrollTop(scrollTop);
+
+            this_.paging = false;
+          }
+        })
+        .error(function() {
+          this_.indicate('(FAILED TO PAGE ... RETRYING)');
+          setTimeout(function() {
+            this_.indicate('');
+            this_.page();
+          }, 1000);
+        });
+    }
+
+    read(offset, length);
+  }
+
+
+  Pailer.prototype.tail = function() {
+    var this_ = this;
+
+    if (!this_.initialized) {
+      return;
+    }
+
+    this_.read({'offset': this_.end, 'length': this_.truncate_length})
+      .success(function(data) {
+        var scrollTop = this_.element.scrollTop();
+        var height = this_.element.height();
+        var scrollHeight = this_.element[0].scrollHeight;
+
+        // Check if we are still at the bottom (since this event might
+        // have fired before the scroll event has been dispatched).
+        if (scrollTop + height < scrollHeight) {
+          this_.tailing = false;
+          return;
+        }
+
+        if (data.data.length > 0) {
+          // Truncate to the first newline if this is the first time
+          // (and we aren't reading from the beginning of the log).
+          if (this_.start == this_.end && data.offset != 0) {
+            var index = data.data.indexOf('\n') + 1;
+            data.offset += index;
+            data.data = data.data.substring(index);
+            this_.start = data.offset; // Adjust the actual start too!
+          }
+
+          this_.end = data.offset + data.data.length;
+
+          this_.element.append(escapeHTML(data.data));
+
+          scrollTop += this_.element[0].scrollHeight - scrollHeight;
+          this_.element.scrollTop(scrollTop);
+
+          // Also, only if we're at the bottom, truncate data so that we
+          // don't consume too much memory. TODO(benh): Only do
+          // truncations if we've been at the bottom for a while.
+          this_.truncate();
+        }
+
+        // Tail immediately if we got as much data as requested (since
+        // this probably means we've waited around a while). The
+        // alternative here would be to not get data in chunks, but the
+        // potential issue here is that we might end up requesting GB of
+        // log data at a time ... the right solution here might be to do
+        // a request to determine the new ending offset and then request
+        // the proper length.
+        if (data.data.length == this_.truncate_length) {
+          setTimeout(function() { this_.tail(); }, 0);
+        } else {
+          setTimeout(function() { this_.tail(); }, 1000);
+        }
+      })
+      .error(function() {
+        this_.indicate('(FAILED TO TAIL ... RETRYING)');
+        this_.initialized = false;
+        setTimeout(function() {
+          this_.indicate('');
+          this_.initialize();
+        }, 1000);
+      });
+  }
+
+
+  Pailer.prototype.indicate = function(text) {
+    var this_ = this;
+
+    if (this_.indicator) {
+      this_.indicator.text(text);
+    }
+  }
+
+
+  Pailer.prototype.truncate = function() {
+    var this_ = this;
+
+    var length = this_.element.html().length;
+    if (length >= this_.truncate_length) {
+      var index = length - this_.truncate_length;
+      this_.start = this_.end - this_.truncate_length;
+      this_.element.html(this_.element.html().substring(index));
+    }
+  }
+
+  $.fn.pailer = function(options) {
+    var settings = $.extend({
+      'read': function() { return {
+        'success': function() {},
+        'error': function(f) { f(); }
+      }},
+      'page_size': 8 * 4096, // 8 "pages".
+      'truncate_length': 50000
+    }, options);
+
+    this.each(function() {
+      var pailer = $.data(this, 'pailer');
+      if (!pailer) {
+        pailer = new Pailer(settings.read,
+                            $(this),
+                            settings.indicator,
+                            settings.page_size,
+                            settings.truncate_length);
+        $.data(this, 'pailer', pailer);
+        pailer.initialize();
+      }
+    });
+  }
+})(jQuery);

http://git-wip-us.apache.org/repos/asf/mesos/blob/5852eb93/src/webui/master/static/js/relative-date.js
----------------------------------------------------------------------
diff --git a/src/webui/master/static/js/relative-date.js b/src/webui/master/static/js/relative-date.js
new file mode 100644
index 0000000..b33ada5
--- /dev/null
+++ b/src/webui/master/static/js/relative-date.js
@@ -0,0 +1,48 @@
+// Retrieved from:
+// https://github.com/azer/relative-date/blob/master/lib/relative-date.js.
+
+var relativeDate = (function(undefined){
+
+  var SECOND = 1000,
+      MINUTE = 60 * SECOND,
+      HOUR = 60 * MINUTE,
+      DAY = 24 * HOUR,
+      WEEK = 7 * DAY,
+      YEAR = DAY * 365,
+      MONTH = YEAR / 12;
+
+  var formats = [
+    [ 0.7 * MINUTE, 'just now' ],
+    [ 1.5 * MINUTE, 'a minute ago' ],
+    [ 60 * MINUTE, 'minutes ago', MINUTE ],
+    [ 1.5 * HOUR, 'an hour ago' ],
+    [ DAY, 'hours ago', HOUR ],
+    [ 2 * DAY, 'yesterday' ],
+    [ 7 * DAY, 'days ago', DAY ],
+    [ 1.5 * WEEK, 'a week ago'],
+    [ MONTH, 'weeks ago', WEEK ],
+    [ 1.5 * MONTH, 'a month ago' ],
+    [ YEAR, 'months ago', MONTH ],
+    [ 1.5 * YEAR, 'a year ago' ],
+    [ Number.MAX_VALUE, 'years ago', YEAR ]
+  ];
+
+  function relativeDate(input,reference){
+    !reference && ( reference = (new Date).getTime() );
+    reference instanceof Date && ( reference = reference.getTime() );
+    input instanceof Date && ( input = input.getTime() );
+
+    var delta = reference - input,
+        format, i, len;
+
+    for(i = -1, len=formats.length; ++i < len; ){
+      format = formats[i];
+      if(delta < format[0]){
+          return format[2] == undefined ? format[1] : Math.round(delta/format[2]) + ' ' + format[1];
+      }
+    };
+  }
+
+  return relativeDate;
+
+})();