Add preliminary support for dynamic content js and css.

Change-Id: I1dbc92356e8d4beb712b8872e9b6cf873bdb70be
diff --git a/tools/droiddoc/templates-sdk/assets/js/resourcecards.js b/tools/droiddoc/templates-sdk/assets/js/resourcecards.js
new file mode 100644
index 0000000..fbba201
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/js/resourcecards.js
@@ -0,0 +1,244 @@
+// Requires jd_tag_helpers.js and the data JS to be loaded.
+
+$(document).ready(function() {
+  $('.resource-widget').each(function() {
+    initResourceWidget(this);
+  });
+});
+
+
+function initResourceWidget(widget) {
+  var $widget = $(widget);
+  var isFlow, isCarousel;
+  isFlow = $widget.hasClass('resource-flow-layout');
+  if (!isFlow) {
+    isCarousel = $widget.hasClass('resource-carousel-layout');
+  }
+
+  // find size of widget by pulling out its class name
+  var sizeCols = 1;
+  var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
+  if (m) {
+    sizeCols = parseInt(m[1], 10);
+  }
+
+  var opts = {
+    source: $widget.data('source'),
+    cardSizes: ($widget.data('cardsizes') || '').split(','),
+    maxResults: parseInt($widget.data('maxresults') || '100'),
+    itemsPerPage: $widget.data('itemsperpage'),
+    sortOrder: $widget.data('sortorder'),
+    query: $widget.data('query'),
+    collectionId: $widget.data('collectionid'),
+    sizeCols: sizeCols
+  };
+
+  // run the search for the set of resources to show
+  var resources = buildResourceList(opts);
+
+  if (isFlow) {
+    drawResourcesFlowWidget($widget, opts, resources);
+  }
+}
+
+
+function drawResourcesFlowWidget($widget, opts, resources) {
+  $widget.empty();
+  var cardSizes = opts.cardSizes || ['4x3'];
+
+  for (var i = 0; i < resources.length; i++) {
+    var resource = resources[i];
+
+    var cardSize = i >= cardSizes.length ? cardSizes[cardSizes.length - 1] : cardSizes[i];
+    cardSize = cardSize.replace(/^\s+|\s+$/,'');
+
+    var $card = $('<a>')
+        .addClass('resource-card resource-card-' + cardSize + ' resource-card-' + resource.type)
+        .attr('href', resource.url);
+
+    $('<img>')
+        .addClass('photo')
+        .attr('src', resource.image || '')
+        .appendTo($card);
+
+    var subtitle = resource.type;
+    if (resource.timestamp) {
+      var d = new Date(resource.timestamp);
+      // TODO: localize, humanize
+      subtitle = (1 + d.getMonth()) + '/' + d.getDate() + '/' + d.getFullYear() + ' on ' + subtitle;
+    }
+
+    $('<div>')
+        .addClass('resource-card-text')
+        .append($('<div>').addClass('icon'))
+        .append($('<div>').addClass('title').text(resource.title))
+        .append($('<div>').addClass('subtitle').text(subtitle))
+        .append($('<div>').addClass('abstract').text(resource.summary))
+        .appendTo($card);
+
+    $card.appendTo($widget);
+  }
+
+  $widget.find('.resource-card .photo').each(function() {
+    var src = $(this).attr('src');
+    if (!src) {
+      $(this).parents('.resource-card').addClass('nophoto');
+      $(this).replaceWith($('<div>')
+          .addClass('photo'));
+    } else {
+      $(this).replaceWith($('<div>')
+          .addClass('photo')
+          .css('background-image', 'url(' + $(this).attr('src') + ')'));
+    }
+  });
+}
+
+
+function buildResourceList(opts) {
+  var maxResults = opts.maxResults || 100;
+
+  switch (opts.source) {
+    case 'query':
+      var query = opts.query || '';
+      var expressions = parseResourceQuery(query);
+      var alreadyAddedResources = {};
+      var allResources = [];
+      for (var i = 0; i < expressions.length; i++) {
+        var clauses = expressions[i];
+
+        // build initial set of resources from first clause
+        var firstClause = clauses[0];
+        var resources = [];
+        switch (firstClause.attr) {
+          case 'type':
+            resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
+            break;
+          case 'lang':
+            resources = ALL_RESOURCES_BY_LANG[firstClause.value];
+            break;
+          case 'tag':
+            resources = ALL_RESOURCES_BY_TAG[firstClause.value];
+            break;
+        }
+        resources = resources || [];
+
+        // use additional clauses to filter corpus
+        if (clauses.length > 1) {
+          var otherClauses = clauses.slice(1);
+          resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
+        }
+
+        // filter out resources already added
+        if (i > 1) {
+          resources = resources.filter(getResourceNotAlreadyAddedFilter(alreadyAddedResources));
+        }
+
+        allResources = allResources.concat(resources);
+        if (allResources.length > maxResults) {
+          break;
+        }
+      }
+      if (opts.sortOrder) {
+        var attr = opts.sortOrder;
+        var desc = attr.charAt(0) == '-';
+        if (desc) {
+          attr = attr.substring(1);
+        }
+        allResources = allResources.sort(function(x,y) {
+          return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
+        });
+      }
+      return allResources.slice(0, maxResults);
+
+    case 'related':
+      // TODO
+      break;
+
+    case 'collection':
+      // TODO
+      break;
+  }
+}
+
+
+function getResourceNotAlreadyAddedFilter(addedResources) {
+  return function(x) {
+    return !!addedResources[x];
+  };
+}
+
+
+function getResourceMatchesClausesFilter(clauses) {
+  return function(x) {
+    return doesResourceMatchClauses(x, clauses);
+  };
+}
+
+
+function doesResourceMatchClauses(resource, clauses) {
+  for (var i = 0; i < clauses.length; i++) {
+    var map;
+    switch (clauses[i].attr) {
+      case 'type':
+        map = IS_RESOURCE_OF_TYPE[clauses[i].value];
+        break;
+      case 'lang':
+        map = IS_RESOURCE_IN_LANG[clauses[i].value];
+        break;
+      case 'tag':
+        map = IS_RESOURCE_TAGGED[clauses[i].value];
+        break;
+    }
+
+    if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+
+function parseResourceQuery(query) {
+  // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
+  var expressions = [];
+  var expressionStrs = query.split(',') || [];
+  for (var i = 0; i < expressionStrs.length; i++) {
+    var expr = expressionStrs[i] || '';
+
+    // Break expression into clauses (clause e.g. 'tag:foo')
+    var clauses = [];
+    var clauseStrs = expr.split(/(?=[\+\-])/);
+    for (var j = 0; j < clauseStrs.length; j++) {
+      var clauseStr = clauseStrs[j] || '';
+
+      // Get attribute and value from clause (e.g. attribute='tag', value='foo')
+      var parts = clauseStr.split(':');
+      var clause = {};
+
+      clause.attr = parts[0].replace(/\s+/g,'');
+      if (clause.attr) {
+        if (clause.attr.charAt(0) == '+') {
+          clause.attr = clause.attr.substring(1);
+        } else if (clause.attr.charAt(0) == '-') {
+          clause.negative = true;
+          clause.attr = clause.attr.substring(1);
+        }
+      }
+
+      if (parts.length > 1) {
+        clause.value = parts[1].replace(/\s+/g,'');
+      }
+
+      clauses.push(clause);
+    }
+
+    if (!clauses.length) {
+      continue;
+    }
+
+    expressions.push(clauses);
+  }
+
+  return expressions;
+}
+