<commit_before>/*
*********************************************************************************************

  System Loader Implementation

    - Implemented to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js

    - <script type="module"> supported

*********************************************************************************************
*/

(function() {

  var isWorker = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
  var isBrowser = typeof window != 'undefined' && !isWorker;
  var Promise = __global.Promise || require('when/es6-shim/Promise');

  // Helpers
  // Absolute URL parsing, from https://gist.github.com/Yaffle/1088850
  function parseURI(url) {
    var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
    // authority = '//' + user + ':' + pass '@' + hostname + ':' port
    return (m ? {
      href     : m[0] || '',
      protocol : m[1] || '',
      authority: m[2] || '',
      host     : m[3] || '',
      hostname : m[4] || '',
      port     : m[5] || '',
      pathname : m[6] || '',
      search   : m[7] || '',
      hash     : m[8] || ''
    } : null);
  }
  function removeDotSegments(input) {
    var output = [];
    input.replace(/^(\.\.?(\/|$))+/, '')
      .replace(/\/(\.(\/|$))+/g, '/')
      .replace(/\/\.\.$/, '/../')
      .replace(/\/?[^\/]*/g, function (p) {
        if (p === '/..')
          output.pop();
        else
          output.push(p);
    });
    return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
  }
  function toAbsoluteURL(base, href) {

    href = parseURI(href || '');
    base = parseURI(base || '');

    return !href || !base ? null : (href.protocol || base.protocol) +
      (href.protocol || href.authority ? href.authority : base.authority) +
      removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
      (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
      href.hash;
  }

  var fetchTextFromURL;
  if (isBrowser || isWorker) {
    fetchTextFromURL = function(url, fulfill, reject) {
      var xhr = new XMLHttpRequest();
      var sameDomain = true;
      if (!('withCredentials' in xhr)) {
        // check if same domain
        var domainCheck = /^(\w+:)?\/\/([^\/]+)/.exec(url);
        if (domainCheck) {
          sameDomain = domainCheck[2] === window.location.host;
          if (domainCheck[1])
            sameDomain &= domainCheck[1] === window.location.protocol;
        }
      }
      if (!sameDomain) {
        xhr = new XDomainRequest();
        xhr.onload = load;
        xhr.onerror = error;
        xhr.ontimeout = error;
      }
      function load() {
        fulfill(xhr.responseText);
      }
      function error() {
        reject(xhr.statusText + ': ' + url || 'XHR error');
      }

      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status === 200 || (xhr.status == 0 && xhr.responseText)) {
            load();
          } else {
            error();
          }
        }
      };
      xhr.open("GET", url, true);
      xhr.send(null);
    }
  }
  else {
    var fs;
    fetchTextFromURL = function(url, fulfill, reject) {
      fs = fs || require('fs');
      return fs.readFile(url, function(err, data) {
        if (err)
          return reject(err);
        else
          fulfill(data + '');
      });
    }
  }

  class SystemLoader extends __global.LoaderPolyfill {

    constructor(options) {
      super(options || {});

      // Set default baseURL and paths
      if (isBrowser || isWorker) {
        var href = __global.location.href.split('#')[0].split('?')[0];
        this.baseURL = href.substring(0, href.lastIndexOf('/') + 1);
      }
      else {
        this.baseURL = process.cwd() + '/';
      }
      this.paths = { '*': '*.js' };
    }

    get global() {
      return isBrowser ? window : (isWorker ? self : __global);
    }

    get strict() { return true; }

    normalize(name, parentName, parentAddress) {
      if (typeof name != 'string')
        throw new TypeError('Module name must be a string');

      var segments = name.split('/');

      if (segments.length == 0)
        throw new TypeError('No module name provided');

      // current segment
      var i = 0;
      // is the module name relative
      var rel = false;
      // number of backtracking segments
      var dotdots = 0;
      if (segments[0] == '.') {
        i++;
        if (i == segments.length)
          throw new TypeError('Illegal module name "' + name + '"');
        rel = true;
      }
      else {
        while (segments[i] == '..') {
          i++;
          if (i == segments.length)
            throw new TypeError('Illegal module name "' + name + '"');
        }
        if (i)
          rel = true;
        dotdots = i;
      }

      for (var j = i; j < segments.length; j++) {
        var segment = segments[j];
        if (segment == '' || segment == '.' || segment == '..')
          throw new TypeError('Illegal module name "' + name + '"');
      }

      if (!rel)
        return name;

      // build the full module name
      var normalizedParts = [];
      var parentParts = (parentName || '').split('/');
      var normalizedLen = parentParts.length - 1 - dotdots;

      normalizedParts = normalizedParts.concat(parentParts.splice(0, parentParts.length - 1 - dotdots));
      normalizedParts = normalizedParts.concat(segments.splice(i, segments.length - i));

      return normalizedParts.join('/');
    }

    locate(load) {
      var name = load.name;

      // NB no specification provided for System.paths, used ideas discussed in https://github.com/jorendorff/js-loaders/issues/25

      // most specific (longest) match wins
      var pathMatch = '', wildcard;

      // check to see if we have a paths entry
      for (var p in this.paths) {
        var pathParts = p.split('*');
        if (pathParts.length > 2)
          throw new TypeError('Only one wildcard in a path is permitted');

        // exact path match
        if (pathParts.length == 1) {
          if (name == p && p.length > pathMatch.length) {
            pathMatch = p;
            break;
          }
        }

        // wildcard path match
        else {
          if (name.substr(0, pathParts[0].length) == pathParts[0] && name.substr(name.length - pathParts[1].length) == pathParts[1]) {
            pathMatch = p;
            wildcard = name.substr(pathParts[0].length, name.length - pathParts[1].length - pathParts[0].length);
          }
        }
      }

      var outPath = this.paths[pathMatch];
      if (wildcard)
        outPath = outPath.replace('*', wildcard);

      // percent encode just '#' in module names
      // according to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js#L238
      // we should encode everything, but it breaks for servers that don't expect it
      // like in (https://github.com/systemjs/systemjs/issues/168)
      if (isBrowser)
        outPath = outPath.replace(/#/g, '%23');

      return toAbsoluteURL(this.baseURL, outPath);
    }

    fetch(load) {
      var self = this;
      return new Promise(function(resolve, reject) {
        fetchTextFromURL(toAbsoluteURL(self.baseURL, load.address), function(source) {
          resolve(source);
        }, reject);
      });
    }

  }

  var System = new SystemLoader();

  // note we have to export before runing "init" below
  if (typeof exports === 'object')
    module.exports = System;

  __global.System = System;

  // <script type="module"> support
  // allow a data-init function callback once loaded
  if (isBrowser) {
    var curScript = document.getElementsByTagName('script');
    curScript = curScript[curScript.length - 1];

    function completed() {
      document.removeEventListener( "DOMContentLoaded", completed, false );
      window.removeEventListener( "load", completed, false );
      ready();
    }

    function ready() {
      var scripts = document.getElementsByTagName('script');

      for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        if (script.type == 'module') {
          var source = script.innerHTML;
          System.module(source)['catch'](function(err) { setTimeout(function() { throw err; }); });
        }
      }
    }

    // DOM ready, taken from https://github.com/jquery/jquery/blob/master/src/core/ready.js#L63
    if (document.readyState === 'complete') {
      setTimeout(ready);
    }
    else if (document.addEventListener) {
      document.addEventListener('DOMContentLoaded', completed, false);
      window.addEventListener('load', completed, false);
    }

    // run the data-init function on the script tag
    if (curScript.getAttribute('data-init'))
      window[curScript.getAttribute('data-init')]();
  }
})();<commit_msg>Fix IE8/IE9 bug that may cause hanging cross-domain requests.<commit_after>/*
*********************************************************************************************

  System Loader Implementation

    - Implemented to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js

    - <script type="module"> supported

*********************************************************************************************
*/

(function() {

  var isWorker = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
  var isBrowser = typeof window != 'undefined' && !isWorker;
  var Promise = __global.Promise || require('when/es6-shim/Promise');

  // Helpers
  // Absolute URL parsing, from https://gist.github.com/Yaffle/1088850
  function parseURI(url) {
    var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
    // authority = '//' + user + ':' + pass '@' + hostname + ':' port
    return (m ? {
      href     : m[0] || '',
      protocol : m[1] || '',
      authority: m[2] || '',
      host     : m[3] || '',
      hostname : m[4] || '',
      port     : m[5] || '',
      pathname : m[6] || '',
      search   : m[7] || '',
      hash     : m[8] || ''
    } : null);
  }
  function removeDotSegments(input) {
    var output = [];
    input.replace(/^(\.\.?(\/|$))+/, '')
      .replace(/\/(\.(\/|$))+/g, '/')
      .replace(/\/\.\.$/, '/../')
      .replace(/\/?[^\/]*/g, function (p) {
        if (p === '/..')
          output.pop();
        else
          output.push(p);
    });
    return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
  }
  function toAbsoluteURL(base, href) {

    href = parseURI(href || '');
    base = parseURI(base || '');

    return !href || !base ? null : (href.protocol || base.protocol) +
      (href.protocol || href.authority ? href.authority : base.authority) +
      removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
      (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
      href.hash;
  }

  var fetchTextFromURL;
  if (isBrowser || isWorker) {
    fetchTextFromURL = function(url, fulfill, reject) {
      var xhr = new XMLHttpRequest();
      var sameDomain = true;
      if (!('withCredentials' in xhr)) {
        // check if same domain
        var domainCheck = /^(\w+:)?\/\/([^\/]+)/.exec(url);
        if (domainCheck) {
          sameDomain = domainCheck[2] === window.location.host;
          if (domainCheck[1])
            sameDomain &= domainCheck[1] === window.location.protocol;
        }
      }
      if (!sameDomain) {
        xhr = new XDomainRequest();
        xhr.onload = load;
        xhr.onerror = error;
        xhr.ontimeout = error;
        // IE8/IE9 bug may hang requests unless all properties are defined.
        // See: http://stackoverflow.com/a/9928073/3949247
        xhr.onprogress = function() {};
        xhr.timeout = 0;
      }
      function load() {
        fulfill(xhr.responseText);
      }
      function error() {
        reject(xhr.statusText + ': ' + url || 'XHR error');
      }

      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status === 200 || (xhr.status == 0 && xhr.responseText)) {
            load();
          } else {
            error();
          }
        }
      };
      xhr.open("GET", url, true);
      xhr.send(null);
    }
  }
  else {
    var fs;
    fetchTextFromURL = function(url, fulfill, reject) {
      fs = fs || require('fs');
      return fs.readFile(url, function(err, data) {
        if (err)
          return reject(err);
        else
          fulfill(data + '');
      });
    }
  }

  class SystemLoader extends __global.LoaderPolyfill {

    constructor(options) {
      super(options || {});

      // Set default baseURL and paths
      if (isBrowser || isWorker) {
        var href = __global.location.href.split('#')[0].split('?')[0];
        this.baseURL = href.substring(0, href.lastIndexOf('/') + 1);
      }
      else {
        this.baseURL = process.cwd() + '/';
      }
      this.paths = { '*': '*.js' };
    }

    get global() {
      return isBrowser ? window : (isWorker ? self : __global);
    }

    get strict() { return true; }

    normalize(name, parentName, parentAddress) {
      if (typeof name != 'string')
        throw new TypeError('Module name must be a string');

      var segments = name.split('/');

      if (segments.length == 0)
        throw new TypeError('No module name provided');

      // current segment
      var i = 0;
      // is the module name relative
      var rel = false;
      // number of backtracking segments
      var dotdots = 0;
      if (segments[0] == '.') {
        i++;
        if (i == segments.length)
          throw new TypeError('Illegal module name "' + name + '"');
        rel = true;
      }
      else {
        while (segments[i] == '..') {
          i++;
          if (i == segments.length)
            throw new TypeError('Illegal module name "' + name + '"');
        }
        if (i)
          rel = true;
        dotdots = i;
      }

      for (var j = i; j < segments.length; j++) {
        var segment = segments[j];
        if (segment == '' || segment == '.' || segment == '..')
          throw new TypeError('Illegal module name "' + name + '"');
      }

      if (!rel)
        return name;

      // build the full module name
      var normalizedParts = [];
      var parentParts = (parentName || '').split('/');
      var normalizedLen = parentParts.length - 1 - dotdots;

      normalizedParts = normalizedParts.concat(parentParts.splice(0, parentParts.length - 1 - dotdots));
      normalizedParts = normalizedParts.concat(segments.splice(i, segments.length - i));

      return normalizedParts.join('/');
    }

    locate(load) {
      var name = load.name;

      // NB no specification provided for System.paths, used ideas discussed in https://github.com/jorendorff/js-loaders/issues/25

      // most specific (longest) match wins
      var pathMatch = '', wildcard;

      // check to see if we have a paths entry
      for (var p in this.paths) {
        var pathParts = p.split('*');
        if (pathParts.length > 2)
          throw new TypeError('Only one wildcard in a path is permitted');

        // exact path match
        if (pathParts.length == 1) {
          if (name == p && p.length > pathMatch.length) {
            pathMatch = p;
            break;
          }
        }

        // wildcard path match
        else {
          if (name.substr(0, pathParts[0].length) == pathParts[0] && name.substr(name.length - pathParts[1].length) == pathParts[1]) {
            pathMatch = p;
            wildcard = name.substr(pathParts[0].length, name.length - pathParts[1].length - pathParts[0].length);
          }
        }
      }

      var outPath = this.paths[pathMatch];
      if (wildcard)
        outPath = outPath.replace('*', wildcard);

      // percent encode just '#' in module names
      // according to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js#L238
      // we should encode everything, but it breaks for servers that don't expect it
      // like in (https://github.com/systemjs/systemjs/issues/168)
      if (isBrowser)
        outPath = outPath.replace(/#/g, '%23');

      return toAbsoluteURL(this.baseURL, outPath);
    }

    fetch(load) {
      var self = this;
      return new Promise(function(resolve, reject) {
        fetchTextFromURL(toAbsoluteURL(self.baseURL, load.address), function(source) {
          resolve(source);
        }, reject);
      });
    }

  }

  var System = new SystemLoader();

  // note we have to export before runing "init" below
  if (typeof exports === 'object')
    module.exports = System;

  __global.System = System;

  // <script type="module"> support
  // allow a data-init function callback once loaded
  if (isBrowser) {
    var curScript = document.getElementsByTagName('script');
    curScript = curScript[curScript.length - 1];

    function completed() {
      document.removeEventListener( "DOMContentLoaded", completed, false );
      window.removeEventListener( "load", completed, false );
      ready();
    }

    function ready() {
      var scripts = document.getElementsByTagName('script');

      for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        if (script.type == 'module') {
          var source = script.innerHTML;
          System.module(source)['catch'](function(err) { setTimeout(function() { throw err; }); });
        }
      }
    }

    // DOM ready, taken from https://github.com/jquery/jquery/blob/master/src/core/ready.js#L63
    if (document.readyState === 'complete') {
      setTimeout(ready);
    }
    else if (document.addEventListener) {
      document.addEventListener('DOMContentLoaded', completed, false);
      window.addEventListener('load', completed, false);
    }

    // run the data-init function on the script tag
    if (curScript.getAttribute('data-init'))
      window[curScript.getAttribute('data-init')]();
  }
})();
<|endoftext|>


<commit_before>/*
* tablesaw: A set of plugins for responsive tables
* Stack and Column Toggle tables
* Copyright (c) 2013 Filament Group, Inc.
* MIT License
*/

var Tablesaw = {
	i18n: {
		modeStack: "Stack",
		modeSwipe: "Swipe",
		modeToggle: "Toggle",
		modeSwitchColumnsAbbreviated: "Cols",
		modeSwitchColumns: "Columns",
		columnToggleButton: "Columns",
		columnToggleError: "No eligible columns.",
		sort: "Sort",
		swipePreviousColumn: "Previous column",
		swipeNextColumn: "Next column"
	},
	// cut the mustard
	mustard:
		"head" in document && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+
		(!window.blackberry || window.WebKitPoint) && // only WebKit Blackberry (OS 6+)
		!window.operamini
};

$(win.document).on("enhance.tablesaw", function() {
	// Extend i18n config, if one exists.
	if (typeof TablesawConfig !== "undefined" && TablesawConfig.i18n) {
		Tablesaw.i18n = $.extend(Tablesaw.i18n, TablesawConfig.i18n || {});
	}

	Tablesaw.i18n.modes = [
		Tablesaw.i18n.modeStack,
		Tablesaw.i18n.modeSwipe,
		Tablesaw.i18n.modeToggle
	];
});

if (Tablesaw.mustard) {
	$(document.documentElement).addClass("tablesaw-enhanced");
}

(function() {
	var pluginName = "tablesaw";
	var classes = {
		toolbar: "tablesaw-bar"
	};
	var events = {
		create: "tablesawcreate",
		destroy: "tablesawdestroy",
		refresh: "tablesawrefresh",
		resize: "tablesawresize"
	};
	var defaultMode = "stack";
	var initSelector = "table";
	var initFilterSelector = "[data-tablesaw],[data-tablesaw-mode],[data-tablesaw-sortable]";
	var defaultConfig = {};

	Tablesaw.events = events;

	var Table = function(element) {
		if (!element) {
			throw new Error("Tablesaw requires an element.");
		}

		this.table = element;
		this.$table = $(element);

		// only one <thead> and <tfoot> are allowed, per the specification
		this.$thead = this.$table.children().filter("thead").eq(0);

		// multiple <tbody> are allowed, per the specification
		this.$tbody = this.$table.children().filter("tbody");

		this.mode = this.$table.attr("data-tablesaw-mode") || defaultMode;

		this.$toolbar = null;

		this.attributes = {
			subrow: "data-tablesaw-subrow",
			ignorerow: "data-tablesaw-ignorerow"
		};

		this.init();
	};

	Table.prototype.init = function() {
		if (!this.$thead.length) {
			throw new Error("tablesaw: a <thead> is required, but none was found.");
		}

		if (!this.$thead.find("th").length) {
			throw new Error("tablesaw: no header cells found. Are you using <th> inside of <thead>?");
		}

		// assign an id if there is none
		if (!this.$table.attr("id")) {
			this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000));
		}

		this.createToolbar();

		this._initCells();

		this.$table.data(pluginName, this);

		this.$table.trigger(events.create, [this]);
	};

	Table.prototype.getConfig = function(pluginSpecificConfig) {
		// shoestring extend doesn’t support arbitrary args
		var configs = $.extend(defaultConfig, pluginSpecificConfig || {});
		return $.extend(configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {});
	};

	Table.prototype._getPrimaryHeaderRow = function() {
		return this._getHeaderRows().eq(0);
	};

	Table.prototype._getHeaderRows = function() {
		return this.$thead.children().filter("tr").filter(function() {
			return !$(this).is("[data-tablesaw-ignorerow]");
		});
	};

	Table.prototype._getRowIndex = function($row) {
		return $row.prevAll().length;
	};

	Table.prototype._getHeaderRowIndeces = function() {
		var self = this;
		var indeces = [];
		this._getHeaderRows().each(function() {
			indeces.push(self._getRowIndex($(this)));
		});
		return indeces;
	};

	Table.prototype._getPrimaryHeaderCells = function($row) {
		return ($row || this._getPrimaryHeaderRow()).find("th");
	};

	Table.prototype._$getCells = function(th) {
		var self = this;
		return $(th).add(th.cells).filter(function() {
			var $t = $(this);
			var $row = $t.parent();
			var hasColspan = $t.is("[colspan]");
			// no subrows or ignored rows (keep cells in ignored rows that do not have a colspan)
			return (
				!$row.is("[" + self.attributes.subrow + "]") &&
				(!$row.is("[" + self.attributes.ignorerow + "]") || !hasColspan)
			);
		});
	};

	Table.prototype._getVisibleColspan = function() {
		var colspan = 0;
		this._getPrimaryHeaderCells().each(function() {
			var $t = $(this);
			if ($t.css("display") !== "none") {
				colspan += parseInt($t.attr("colspan"), 10) || 1;
			}
		});
		return colspan;
	};

	Table.prototype.getColspanForCell = function($cell) {
		var visibleColspan = this._getVisibleColspan();
		var visibleSiblingColumns = 0;
		if ($cell.closest("tr").data("tablesaw-rowspanned")) {
			visibleSiblingColumns++;
		}

		$cell.siblings().each(function() {
			var $t = $(this);
			var colColspan = parseInt($t.attr("colspan"), 10) || 1;

			if ($t.css("display") !== "none") {
				visibleSiblingColumns += colColspan;
			}
		});
		// console.log( $cell[ 0 ], visibleColspan, visibleSiblingColumns );

		return visibleColspan - visibleSiblingColumns;
	};

	Table.prototype.isCellInColumn = function(header, cell) {
		return $(header).add(header.cells).filter(function() {
			return this === cell;
		}).length;
	};

	Table.prototype.updateColspanCells = function(cls, header, userAction) {
		var self = this;
		var primaryHeaderRow = self._getPrimaryHeaderRow();

		// find persistent column rowspans
		this.$table.find("[rowspan][data-tablesaw-priority]").each(function() {
			var $t = $(this);
			if ($t.attr("data-tablesaw-priority") !== "persist") {
				return;
			}

			var $row = $t.closest("tr");
			var rowspan = parseInt($t.attr("rowspan"), 10);
			if (rowspan > 1) {
				$row = $row.next();

				$row.data("tablesaw-rowspanned", true);

				rowspan--;
			}
		});

		this.$table
			.find("[colspan],[data-tablesaw-maxcolspan]")
			.filter(function() {
				// is not in primary header row
				return $(this).closest("tr")[0] !== primaryHeaderRow[0];
			})
			.each(function() {
				var $cell = $(this);

				if (userAction === undefined || self.isCellInColumn(header, this)) {
				} else {
					// if is not a user action AND the cell is not in the updating column, kill it
					return;
				}

				var colspan = self.getColspanForCell($cell);

				if (cls && userAction !== undefined) {
					// console.log( colspan === 0 ? "addClass" : "removeClass", $cell );
					$cell[colspan === 0 ? "addClass" : "removeClass"](cls);
				}

				// cache original colspan
				var maxColspan = parseInt($cell.attr("data-tablesaw-maxcolspan"), 10);
				if (!maxColspan) {
					$cell.attr("data-tablesaw-maxcolspan", $cell.attr("colspan"));
				} else if (colspan > maxColspan) {
					colspan = maxColspan;
				}

				// console.log( this, "setting colspan to ", colspan );
				$cell.attr("colspan", colspan);
			});
	};

	Table.prototype._findPrimaryHeadersForCell = function(cell) {
		var $headerRow = this._getPrimaryHeaderRow();
		var $headers = this._getPrimaryHeaderCells($headerRow);
		var headerRowIndex = this._getRowIndex($headerRow);
		var results = [];

		for (var rowNumber = 0; rowNumber < this.headerMapping.length; rowNumber++) {
			if (rowNumber === headerRowIndex) {
				continue;
			}
			for (var colNumber = 0; colNumber < this.headerMapping[rowNumber].length; colNumber++) {
				if (this.headerMapping[rowNumber][colNumber] === cell) {
					results.push($headers[colNumber]);
				}
			}
		}
		return results;
	};

	// used by init cells
	Table.prototype.getRows = function() {
		var self = this;
		return this.$table.find("tr").filter(function() {
			return $(this).closest("table").is(self.$table);
		});
	};

	// used by sortable
	Table.prototype.getBodyRows = function(tbody) {
		return (tbody ? $(tbody) : this.$tbody).children().filter("tr");
	};

	Table.prototype.getHeaderCellIndex = function(cell) {
		var lookup = this.headerMapping[0];
		for (var colIndex = 0; colIndex < lookup.length; colIndex++) {
			if (lookup[colIndex] === cell) {
				return colIndex;
			}
		}

		return -1;
	};

	Table.prototype._initCells = function() {
		var $rows = this.getRows();
		var columnLookup = [];

		$rows.each(function(rowNumber) {
			columnLookup[rowNumber] = [];
		});

		$rows.each(function(rowNumber) {
			var coltally = 0;
			var $t = $(this);
			var children = $t.children();

			children.each(function() {
				var colspan = parseInt(this.getAttribute("colspan"), 10);
				var rowspan = parseInt(this.getAttribute("rowspan"), 10);

				// set in a previous rowspan
				while (columnLookup[rowNumber][coltally]) {
					coltally++;
				}

				columnLookup[rowNumber][coltally] = this;

				// TODO? both colspan and rowspan
				if (colspan) {
					for (var k = 0; k < colspan - 1; k++) {
						coltally++;
						columnLookup[rowNumber][coltally] = this;
					}
				}
				if (rowspan) {
					for (var j = 1; j < rowspan; j++) {
						columnLookup[rowNumber + j][coltally] = this;
					}
				}

				coltally++;
			});
		});

		var headerRowIndeces = this._getHeaderRowIndeces();
		for (var colNumber = 0; colNumber < columnLookup[0].length; colNumber++) {
			for (var headerIndex = 0, k = headerRowIndeces.length; headerIndex < k; headerIndex++) {
				var headerCol = columnLookup[headerRowIndeces[headerIndex]][colNumber];

				var rowNumber = headerRowIndeces[headerIndex];
				var rowCell;

				if (!headerCol.cells) {
					headerCol.cells = [];
				}

				while (rowNumber < columnLookup.length) {
					rowCell = columnLookup[rowNumber][colNumber];

					if (headerCol !== rowCell) {
						headerCol.cells.push(rowCell);
					}

					rowNumber++;
				}
			}
		}

		this.headerMapping = columnLookup;
	};

	Table.prototype.refresh = function() {
		this._initCells();

		this.$table.trigger(events.refresh, [this]);
	};

	Table.prototype._getToolbarAnchor = function() {
		var $parent = this.$table.parent();
		if ($parent.is(".tablesaw-overflow")) {
			return $parent;
		}
		return this.$table;
	};

	Table.prototype._getToolbar = function($anchor) {
		if (!$anchor) {
			$anchor = this._getToolbarAnchor();
		}
		return $anchor.prev().filter("." + classes.toolbar);
	};

	Table.prototype.createToolbar = function() {
		// Insert the toolbar
		// TODO move this into a separate component
		var $anchor = this._getToolbarAnchor();
		var $toolbar = this._getToolbar($anchor);
		if (!$toolbar.length) {
			$toolbar = $("<div>").addClass(classes.toolbar).insertBefore($anchor);
		}
		this.$toolbar = $toolbar;

		if (this.mode) {
			this.$toolbar.addClass("tablesaw-mode-" + this.mode);
		}
	};

	Table.prototype.destroy = function() {
		// Don’t remove the toolbar, just erase the classes on it.
		// Some of the table features are not yet destroy-friendly.
		this._getToolbar().each(function() {
			this.className = this.className.replace(/\btablesaw-mode\-\w*\b/gi, "");
		});

		var tableId = this.$table.attr("id");
		$(document).off("." + tableId);
		$(window).off("." + tableId);

		// other plugins
		this.$table.trigger(events.destroy, [this]);

		this.$table.removeData(pluginName);
	};

	// Collection method.
	$.fn[pluginName] = function() {
		return this.each(function() {
			var $t = $(this);

			if ($t.data(pluginName)) {
				return;
			}

			new Table(this);
		});
	};

	var $doc = $(win.document);
	$doc.on("enhance.tablesaw", function(e) {
		// Cut the mustard
		if (Tablesaw.mustard) {
			$(e.target).find(initSelector).filter(initFilterSelector)[pluginName]();
		}
	});

	// Avoid a resize during scroll:
	// Some Mobile devices trigger a resize during scroll (sometimes when
	// doing elastic stretch at the end of the document or from the
	// location bar hide)
	var isScrolling = false;
	var scrollTimeout;
	$doc.on("scroll.tablesaw", function() {
		isScrolling = true;

		win.clearTimeout(scrollTimeout);
		scrollTimeout = win.setTimeout(function() {
			isScrolling = false;
		}, 300); // must be greater than the resize timeout below
	});

	var resizeTimeout;
	$(win).on("resize", function() {
		if (!isScrolling) {
			win.clearTimeout(resizeTimeout);
			resizeTimeout = win.setTimeout(function() {
				$doc.trigger(events.resize);
			}, 150); // must be less than the scrolling timeout above.
		}
	});
})();
<commit_msg>use cached max colspan to init cells (was resulting in js errors when using modified colspans)<commit_after>/*
* tablesaw: A set of plugins for responsive tables
* Stack and Column Toggle tables
* Copyright (c) 2013 Filament Group, Inc.
* MIT License
*/

var Tablesaw = {
	i18n: {
		modeStack: "Stack",
		modeSwipe: "Swipe",
		modeToggle: "Toggle",
		modeSwitchColumnsAbbreviated: "Cols",
		modeSwitchColumns: "Columns",
		columnToggleButton: "Columns",
		columnToggleError: "No eligible columns.",
		sort: "Sort",
		swipePreviousColumn: "Previous column",
		swipeNextColumn: "Next column"
	},
	// cut the mustard
	mustard:
		"head" in document && // IE9+, Firefox 4+, Safari 5.1+, Mobile Safari 4.1+, Opera 11.5+, Android 2.3+
		(!window.blackberry || window.WebKitPoint) && // only WebKit Blackberry (OS 6+)
		!window.operamini
};

$(win.document).on("enhance.tablesaw", function() {
	// Extend i18n config, if one exists.
	if (typeof TablesawConfig !== "undefined" && TablesawConfig.i18n) {
		Tablesaw.i18n = $.extend(Tablesaw.i18n, TablesawConfig.i18n || {});
	}

	Tablesaw.i18n.modes = [
		Tablesaw.i18n.modeStack,
		Tablesaw.i18n.modeSwipe,
		Tablesaw.i18n.modeToggle
	];
});

if (Tablesaw.mustard) {
	$(document.documentElement).addClass("tablesaw-enhanced");
}

(function() {
	var pluginName = "tablesaw";
	var classes = {
		toolbar: "tablesaw-bar"
	};
	var events = {
		create: "tablesawcreate",
		destroy: "tablesawdestroy",
		refresh: "tablesawrefresh",
		resize: "tablesawresize"
	};
	var defaultMode = "stack";
	var initSelector = "table";
	var initFilterSelector = "[data-tablesaw],[data-tablesaw-mode],[data-tablesaw-sortable]";
	var defaultConfig = {};

	Tablesaw.events = events;

	var Table = function(element) {
		if (!element) {
			throw new Error("Tablesaw requires an element.");
		}

		this.table = element;
		this.$table = $(element);

		// only one <thead> and <tfoot> are allowed, per the specification
		this.$thead = this.$table.children().filter("thead").eq(0);

		// multiple <tbody> are allowed, per the specification
		this.$tbody = this.$table.children().filter("tbody");

		this.mode = this.$table.attr("data-tablesaw-mode") || defaultMode;

		this.$toolbar = null;

		this.attributes = {
			subrow: "data-tablesaw-subrow",
			ignorerow: "data-tablesaw-ignorerow"
		};

		this.init();
	};

	Table.prototype.init = function() {
		if (!this.$thead.length) {
			throw new Error("tablesaw: a <thead> is required, but none was found.");
		}

		if (!this.$thead.find("th").length) {
			throw new Error("tablesaw: no header cells found. Are you using <th> inside of <thead>?");
		}

		// assign an id if there is none
		if (!this.$table.attr("id")) {
			this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000));
		}

		this.createToolbar();

		this._initCells();

		this.$table.data(pluginName, this);

		this.$table.trigger(events.create, [this]);
	};

	Table.prototype.getConfig = function(pluginSpecificConfig) {
		// shoestring extend doesn’t support arbitrary args
		var configs = $.extend(defaultConfig, pluginSpecificConfig || {});
		return $.extend(configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {});
	};

	Table.prototype._getPrimaryHeaderRow = function() {
		return this._getHeaderRows().eq(0);
	};

	Table.prototype._getHeaderRows = function() {
		return this.$thead.children().filter("tr").filter(function() {
			return !$(this).is("[data-tablesaw-ignorerow]");
		});
	};

	Table.prototype._getRowIndex = function($row) {
		return $row.prevAll().length;
	};

	Table.prototype._getHeaderRowIndeces = function() {
		var self = this;
		var indeces = [];
		this._getHeaderRows().each(function() {
			indeces.push(self._getRowIndex($(this)));
		});
		return indeces;
	};

	Table.prototype._getPrimaryHeaderCells = function($row) {
		return ($row || this._getPrimaryHeaderRow()).find("th");
	};

	Table.prototype._$getCells = function(th) {
		var self = this;
		return $(th).add(th.cells).filter(function() {
			var $t = $(this);
			var $row = $t.parent();
			var hasColspan = $t.is("[colspan]");
			// no subrows or ignored rows (keep cells in ignored rows that do not have a colspan)
			return (
				!$row.is("[" + self.attributes.subrow + "]") &&
				(!$row.is("[" + self.attributes.ignorerow + "]") || !hasColspan)
			);
		});
	};

	Table.prototype._getVisibleColspan = function() {
		var colspan = 0;
		this._getPrimaryHeaderCells().each(function() {
			var $t = $(this);
			if ($t.css("display") !== "none") {
				colspan += parseInt($t.attr("colspan"), 10) || 1;
			}
		});
		return colspan;
	};

	Table.prototype.getColspanForCell = function($cell) {
		var visibleColspan = this._getVisibleColspan();
		var visibleSiblingColumns = 0;
		if ($cell.closest("tr").data("tablesaw-rowspanned")) {
			visibleSiblingColumns++;
		}

		$cell.siblings().each(function() {
			var $t = $(this);
			var colColspan = parseInt($t.attr("colspan"), 10) || 1;

			if ($t.css("display") !== "none") {
				visibleSiblingColumns += colColspan;
			}
		});
		// console.log( $cell[ 0 ], visibleColspan, visibleSiblingColumns );

		return visibleColspan - visibleSiblingColumns;
	};

	Table.prototype.isCellInColumn = function(header, cell) {
		return $(header).add(header.cells).filter(function() {
			return this === cell;
		}).length;
	};

	Table.prototype.updateColspanCells = function(cls, header, userAction) {
		var self = this;
		var primaryHeaderRow = self._getPrimaryHeaderRow();

		// find persistent column rowspans
		this.$table.find("[rowspan][data-tablesaw-priority]").each(function() {
			var $t = $(this);
			if ($t.attr("data-tablesaw-priority") !== "persist") {
				return;
			}

			var $row = $t.closest("tr");
			var rowspan = parseInt($t.attr("rowspan"), 10);
			if (rowspan > 1) {
				$row = $row.next();

				$row.data("tablesaw-rowspanned", true);

				rowspan--;
			}
		});

		this.$table
			.find("[colspan],[data-tablesaw-maxcolspan]")
			.filter(function() {
				// is not in primary header row
				return $(this).closest("tr")[0] !== primaryHeaderRow[0];
			})
			.each(function() {
				var $cell = $(this);

				if (userAction === undefined || self.isCellInColumn(header, this)) {
				} else {
					// if is not a user action AND the cell is not in the updating column, kill it
					return;
				}

				var colspan = self.getColspanForCell($cell);

				if (cls && userAction !== undefined) {
					// console.log( colspan === 0 ? "addClass" : "removeClass", $cell );
					$cell[colspan === 0 ? "addClass" : "removeClass"](cls);
				}

				// cache original colspan
				var maxColspan = parseInt($cell.attr("data-tablesaw-maxcolspan"), 10);
				if (!maxColspan) {
					$cell.attr("data-tablesaw-maxcolspan", $cell.attr("colspan"));
				} else if (colspan > maxColspan) {
					colspan = maxColspan;
				}

				// console.log( this, "setting colspan to ", colspan );
				$cell.attr("colspan", colspan);
			});
	};

	Table.prototype._findPrimaryHeadersForCell = function(cell) {
		var $headerRow = this._getPrimaryHeaderRow();
		var $headers = this._getPrimaryHeaderCells($headerRow);
		var headerRowIndex = this._getRowIndex($headerRow);
		var results = [];

		for (var rowNumber = 0; rowNumber < this.headerMapping.length; rowNumber++) {
			if (rowNumber === headerRowIndex) {
				continue;
			}
			for (var colNumber = 0; colNumber < this.headerMapping[rowNumber].length; colNumber++) {
				if (this.headerMapping[rowNumber][colNumber] === cell) {
					results.push($headers[colNumber]);
				}
			}
		}
		return results;
	};

	// used by init cells
	Table.prototype.getRows = function() {
		var self = this;
		return this.$table.find("tr").filter(function() {
			return $(this).closest("table").is(self.$table);
		});
	};

	// used by sortable
	Table.prototype.getBodyRows = function(tbody) {
		return (tbody ? $(tbody) : this.$tbody).children().filter("tr");
	};

	Table.prototype.getHeaderCellIndex = function(cell) {
		var lookup = this.headerMapping[0];
		for (var colIndex = 0; colIndex < lookup.length; colIndex++) {
			if (lookup[colIndex] === cell) {
				return colIndex;
			}
		}

		return -1;
	};

	Table.prototype._initCells = function() {
		var $rows = this.getRows();
		var columnLookup = [];

		$rows.each(function(rowNumber) {
			columnLookup[rowNumber] = [];
		});

		$rows.each(function(rowNumber) {
			var coltally = 0;
			var $t = $(this);
			var children = $t.children();

			children.each(function() {
				var colspan = parseInt(
					this.getAttribute("data-tablesaw-maxcolspan") || this.getAttribute("colspan"),
					10
				);
				var rowspan = parseInt(this.getAttribute("rowspan"), 10);

				// set in a previous rowspan
				while (columnLookup[rowNumber][coltally]) {
					coltally++;
				}

				columnLookup[rowNumber][coltally] = this;

				// TODO? both colspan and rowspan
				if (colspan) {
					for (var k = 0; k < colspan - 1; k++) {
						coltally++;
						columnLookup[rowNumber][coltally] = this;
					}
				}
				if (rowspan) {
					for (var j = 1; j < rowspan; j++) {
						columnLookup[rowNumber + j][coltally] = this;
					}
				}

				coltally++;
			});
		});

		var headerRowIndeces = this._getHeaderRowIndeces();
		for (var colNumber = 0; colNumber < columnLookup[0].length; colNumber++) {
			for (var headerIndex = 0, k = headerRowIndeces.length; headerIndex < k; headerIndex++) {
				var headerCol = columnLookup[headerRowIndeces[headerIndex]][colNumber];

				var rowNumber = headerRowIndeces[headerIndex];
				var rowCell;

				if (!headerCol.cells) {
					headerCol.cells = [];
				}

				while (rowNumber < columnLookup.length) {
					rowCell = columnLookup[rowNumber][colNumber];

					if (headerCol !== rowCell) {
						headerCol.cells.push(rowCell);
					}

					rowNumber++;
				}
			}
		}

		this.headerMapping = columnLookup;
	};

	Table.prototype.refresh = function() {
		this._initCells();

		this.$table.trigger(events.refresh, [this]);
	};

	Table.prototype._getToolbarAnchor = function() {
		var $parent = this.$table.parent();
		if ($parent.is(".tablesaw-overflow")) {
			return $parent;
		}
		return this.$table;
	};

	Table.prototype._getToolbar = function($anchor) {
		if (!$anchor) {
			$anchor = this._getToolbarAnchor();
		}
		return $anchor.prev().filter("." + classes.toolbar);
	};

	Table.prototype.createToolbar = function() {
		// Insert the toolbar
		// TODO move this into a separate component
		var $anchor = this._getToolbarAnchor();
		var $toolbar = this._getToolbar($anchor);
		if (!$toolbar.length) {
			$toolbar = $("<div>").addClass(classes.toolbar).insertBefore($anchor);
		}
		this.$toolbar = $toolbar;

		if (this.mode) {
			this.$toolbar.addClass("tablesaw-mode-" + this.mode);
		}
	};

	Table.prototype.destroy = function() {
		// Don’t remove the toolbar, just erase the classes on it.
		// Some of the table features are not yet destroy-friendly.
		this._getToolbar().each(function() {
			this.className = this.className.replace(/\btablesaw-mode\-\w*\b/gi, "");
		});

		var tableId = this.$table.attr("id");
		$(document).off("." + tableId);
		$(window).off("." + tableId);

		// other plugins
		this.$table.trigger(events.destroy, [this]);

		this.$table.removeData(pluginName);
	};

	// Collection method.
	$.fn[pluginName] = function() {
		return this.each(function() {
			var $t = $(this);

			if ($t.data(pluginName)) {
				return;
			}

			new Table(this);
		});
	};

	var $doc = $(win.document);
	$doc.on("enhance.tablesaw", function(e) {
		// Cut the mustard
		if (Tablesaw.mustard) {
			$(e.target).find(initSelector).filter(initFilterSelector)[pluginName]();
		}
	});

	// Avoid a resize during scroll:
	// Some Mobile devices trigger a resize during scroll (sometimes when
	// doing elastic stretch at the end of the document or from the
	// location bar hide)
	var isScrolling = false;
	var scrollTimeout;
	$doc.on("scroll.tablesaw", function() {
		isScrolling = true;

		win.clearTimeout(scrollTimeout);
		scrollTimeout = win.setTimeout(function() {
			isScrolling = false;
		}, 300); // must be greater than the resize timeout below
	});

	var resizeTimeout;
	$(win).on("resize", function() {
		if (!isScrolling) {
			win.clearTimeout(resizeTimeout);
			resizeTimeout = win.setTimeout(function() {
				$doc.trigger(events.resize);
			}, 150); // must be less than the scrolling timeout above.
		}
	});
})();
<|endoftext|>


<commit_before>// DOMOutputSpec:: interface
// A description of a DOM structure. Can be either a string, which is
// interpreted as a text node, a DOM node, which is interpreted as
// itself, or an array.
//
// An array describes a DOM element. The first element in the array
// should be a string, and is the name of the DOM element. If the
// second element is a non-Array, non-DOM node object, it is
// interpreted as an object providing the DOM element's attributes.
// Any elements after that (including the 2nd if it's not an attribute
// object) are interpreted as children of the DOM elements, and must
// either be valid `DOMOutputSpec` values, or the number zero.
//
// The number zero (pronounced “hole”) is used to indicate the place
// where a ProseMirror node's content should be inserted.

// ::- A DOM serializer knows how to convert ProseMirror nodes and
// marks of various types to DOM nodes.
class DOMSerializer {
  // :: (Object<(node: Node) → DOMOutputSpec>, Object<(mark: Mark) → DOMOutputSpec>)
  // Create a serializer. `nodes` should map node names to functions
  // that take a node and return a description of the corresponding
  // DOM. `marks` does the same for mark names.
  constructor(nodes, marks) {
    // :: Object<(node: Node) → DOMOutputSpec>
    this.nodes = nodes || {}
    // :: Object<(mark: Mark) → DOMOutputSpec>
    this.marks = marks || {}
  }

  // :: (Fragment, ?Object) → dom.DocumentFragment
  // Serialize the content of this fragment to a DOM fragment. When
  // not in the browser, the `document` option, containing a DOM
  // document, should be passed so that the serialize can create
  // nodes.
  serializeFragment(fragment, options = {}, target) {
    if (!target) target = doc(options).createDocumentFragment()

    let top = target, active = null
    fragment.forEach(node => {
      if (active || node.marks.length) {
        if (!active) active = []
        let keep = 0
        for (; keep < Math.min(active.length, node.marks.length); ++keep)
          if (!node.marks[keep].eq(active[keep])) break
        while (keep < active.length) {
          active.pop()
          top = top.parentNode
        }
        while (active.length < node.marks.length) {
          let add = node.marks[active.length]
          active.push(add)
          top = top.appendChild(this.serializeMark(add, options))
        }
      }
      top.appendChild(this.serializeNode(node, options))
    })

    return target
  }

  // :: (Node, ?Object) → dom.Node
  // Serialize this node to a DOM node. This can be useful when you
  // need to serialize a part of a document, as opposed to the whole
  // document. To serialize a whole document, use
  // [`serializeFragment`](#model.DOMSerializer.serializeFragment) on
  // its [`content`](#model.Node.content).
  serializeNode(node, options = {}) {
    return this.renderStructure(this.nodes[node.type.name](node), node, options)
  }

  serializeNodeAndMarks(node, options = {}) {
    let dom = this.serializeNode(node, options)
    for (let i = node.marks.length - 1; i >= 0; i--) {
      let wrap = this.serializeMark(node.marks[i], options)
      wrap.appendChild(dom)
      dom = wrap
    }
    return dom
  }

  serializeMark(mark, options = {}) {
    return this.renderStructure(this.marks[mark.type.name](mark), null, options)
  }

  renderStructure(structure, node, options) {
    if (typeof structure == "string")
      return doc(options).createTextNode(structure)
    if (structure.nodeType != null)
      return structure
    let dom = doc(options).createElement(structure[0]), attrs = structure[1], start = 1
    if (attrs && typeof attrs == "object" && attrs.nodeType == null && !Array.isArray(attrs)) {
      start = 2
      for (let name in attrs) {
        if (name == "style") dom.style.cssText = attrs[name]
        else if (attrs[name] != null) dom.setAttribute(name, attrs[name])
      }
    }
    for (let i = start; i < structure.length; i++) {
      let child = structure[i]
      if (child === 0) {
        if (!node || node.isLeaf)
          throw new RangeError("Content hole not allowed in a mark or leaf node spec (must produce a single node)")
        if (i < structure.length - 1 || i > start)
          throw new RangeError("Content hole must be the only child of its parent node")
        if (options.onContent)
          options.onContent(node, dom, options)
        else
          this.serializeFragment(node.content, options, dom)
      } else {
        dom.appendChild(this.renderStructure(child, node, options))
      }
    }
    return dom
  }

  // :: (Schema) → DOMSerializer
  // Build a serializer using the [`toDOM`](#model.NodeSpec.toDOM)
  // properties in a schema's node and mark specs.
  static fromSchema(schema) {
    return schema.cached.domSerializer ||
      (schema.cached.domSerializer = new DOMSerializer(this.nodesFromSchema(schema), this.marksFromSchema(schema)))
  }

  // :: (Schema) → Object<(node: Node) → DOMOutputSpec>
  // Gather the serializers in a schema's node specs into an object.
  // This can be useful as a base to build a custom serializer from.
  static nodesFromSchema(schema) {
    return gatherToDOM(schema.nodes)
  }

  // :: (Schema) → Object<(mark: Mark) → DOMOutputSpec>
  // Gather the serializers in a schema's mark specs into an object.
  static marksFromSchema(schema) {
    return gatherToDOM(schema.marks)
  }
}
exports.DOMSerializer = DOMSerializer

function gatherToDOM(obj) {
  let result = {}
  for (let name in obj) {
    let toDOM = obj[name].spec.toDOM
    if (toDOM) result[name] = toDOM
  }
  return result
}

function doc(options) {
  // declare global: window
  return options.document || window.document
}
<commit_msg>Export renderSpec as a static method<commit_after>// DOMOutputSpec:: interface
// A description of a DOM structure. Can be either a string, which is
// interpreted as a text node, a DOM node, which is interpreted as
// itself, or an array.
//
// An array describes a DOM element. The first element in the array
// should be a string, and is the name of the DOM element. If the
// second element is a non-Array, non-DOM node object, it is
// interpreted as an object providing the DOM element's attributes.
// Any elements after that (including the 2nd if it's not an attribute
// object) are interpreted as children of the DOM elements, and must
// either be valid `DOMOutputSpec` values, or the number zero.
//
// The number zero (pronounced “hole”) is used to indicate the place
// where a ProseMirror node's content should be inserted.

// ::- A DOM serializer knows how to convert ProseMirror nodes and
// marks of various types to DOM nodes.
class DOMSerializer {
  // :: (Object<(node: Node) → DOMOutputSpec>, Object<(mark: Mark) → DOMOutputSpec>)
  // Create a serializer. `nodes` should map node names to functions
  // that take a node and return a description of the corresponding
  // DOM. `marks` does the same for mark names.
  constructor(nodes, marks) {
    // :: Object<(node: Node) → DOMOutputSpec>
    this.nodes = nodes || {}
    // :: Object<(mark: Mark) → DOMOutputSpec>
    this.marks = marks || {}
  }

  // :: (Fragment, ?Object) → dom.DocumentFragment
  // Serialize the content of this fragment to a DOM fragment. When
  // not in the browser, the `document` option, containing a DOM
  // document, should be passed so that the serialize can create
  // nodes.
  serializeFragment(fragment, options = {}, target) {
    if (!target) target = doc(options).createDocumentFragment()

    let top = target, active = null
    fragment.forEach(node => {
      if (active || node.marks.length) {
        if (!active) active = []
        let keep = 0
        for (; keep < Math.min(active.length, node.marks.length); ++keep)
          if (!node.marks[keep].eq(active[keep])) break
        while (keep < active.length) {
          active.pop()
          top = top.parentNode
        }
        while (active.length < node.marks.length) {
          let add = node.marks[active.length]
          active.push(add)
          top = top.appendChild(this.serializeMark(add, options))
        }
      }
      top.appendChild(this.serializeNode(node, options))
    })

    return target
  }

  // :: (Node, ?Object) → dom.Node
  // Serialize this node to a DOM node. This can be useful when you
  // need to serialize a part of a document, as opposed to the whole
  // document. To serialize a whole document, use
  // [`serializeFragment`](#model.DOMSerializer.serializeFragment) on
  // its [`content`](#model.Node.content).
  serializeNode(node, options = {}) {
    return this.renderStructure(this.nodes[node.type.name](node), node, options)
  }

  serializeNodeAndMarks(node, options = {}) {
    let dom = this.serializeNode(node, options)
    for (let i = node.marks.length - 1; i >= 0; i--) {
      let wrap = this.serializeMark(node.marks[i], options)
      wrap.appendChild(dom)
      dom = wrap
    }
    return dom
  }

  serializeMark(mark, options = {}) {
    return this.renderStructure(this.marks[mark.type.name](mark), null, options)
  }

  // :: (dom.Document, DOMOutputSpec) → {dom: dom.Node, contentDOM: ?dom.Node}
  // Render an [output spec](##model.DOMOutputSpec).
  static renderSpec(doc, structure) {
    if (typeof structure == "string")
      return {dom: doc.createTextNode(structure)}
    if (structure.nodeType != null)
      return {dom: structure}
    let dom = doc.createElement(structure[0]), contentDOM = null
    let attrs = structure[1], start = 1
    if (attrs && typeof attrs == "object" && attrs.nodeType == null && !Array.isArray(attrs)) {
      start = 2
      for (let name in attrs) {
        if (name == "style") dom.style.cssText = attrs[name]
        else if (attrs[name] != null) dom.setAttribute(name, attrs[name])
      }
    }
    for (let i = start; i < structure.length; i++) {
      let child = structure[i]
      if (child === 0) {
        if (i < structure.length - 1 || i > start)
          throw new RangeError("Content hole must be the only child of its parent node")
        return {dom, contentDOM: dom}
      } else {
        let {dom: inner, contentDOM: innerContent} = DOMSerializer.renderSpec(doc, child)
        dom.appendChild(inner)
        if (innerContent) {
          if (contentDOM) throw new RangeError("Multiple content holes")
          contentDOM = innerContent
        }
      }
    }
    return {dom, contentDOM}
  }

  renderStructure(structure, node, options) {
    let {dom, contentDOM} = DOMSerializer.renderSpec(doc(options), structure)
    if (node && !node.isLeaf) {
      if (!contentDOM) throw new RangeError("No content hole in template for non-leaf node")
      if (options.onContent)
        options.onContent(node, contentDOM, options)
      else
        this.serializeFragment(node.content, options, contentDOM)
    } else if (contentDOM) {
      throw new RangeError("Content hole not allowed in a mark or leaf node spec")
    }
    return dom
  }

  // :: (Schema) → DOMSerializer
  // Build a serializer using the [`toDOM`](#model.NodeSpec.toDOM)
  // properties in a schema's node and mark specs.
  static fromSchema(schema) {
    return schema.cached.domSerializer ||
      (schema.cached.domSerializer = new DOMSerializer(this.nodesFromSchema(schema), this.marksFromSchema(schema)))
  }

  // :: (Schema) → Object<(node: Node) → DOMOutputSpec>
  // Gather the serializers in a schema's node specs into an object.
  // This can be useful as a base to build a custom serializer from.
  static nodesFromSchema(schema) {
    return gatherToDOM(schema.nodes)
  }

  // :: (Schema) → Object<(mark: Mark) → DOMOutputSpec>
  // Gather the serializers in a schema's mark specs into an object.
  static marksFromSchema(schema) {
    return gatherToDOM(schema.marks)
  }
}
exports.DOMSerializer = DOMSerializer

function gatherToDOM(obj) {
  let result = {}
  for (let name in obj) {
    let toDOM = obj[name].spec.toDOM
    if (toDOM) result[name] = toDOM
  }
  return result
}

function doc(options) {
  // declare global: window
  return options.document || window.document
}
<|endoftext|>


<commit_before>var API_ENDPOINT = "http://fun-api.herokuapp.com/shortcode/";

jQuery(function($)
{
    var hash = window.location.hash;
    if (hash)
    {
        $.ajax(
        {
            url: ENDPOINT + hash.substr(1) + ".json",
            type: "get",
            error: function()
            {
                console.log("Error: gif not found.");
            },
            success: function(data)
            {
                $("#main").remove();
                $(".spinner").remove();
                $("#image-share-link").text(window.location);
                $("#image-caption").text(data.name);
                $("#image-view").attr("src", data.original_source);
                $("#shortcode").show();
            }
        });
    }

    $(".slider-list li:first-child").on("click", function()
    {
        $(".focus_me_please").effect("highlight").focus();
        return false;
    });

    function playCurrentCardVideo()
    {
        var lastCardVideo = $(".slider-list .slide.in-deck:last video");
        if (lastCardVideo.length)
        {
            lastCardVideo[0].play();
        }
    }

    var stack = gajus.Swing.Stack(
        {
            "throwOutConfidence": function(offset, element)
            {
                return Math.min(Math.abs(offset) / (element.offsetWidth / 3.0), 1);
            },
            "throwOutDistance": function()
            {
                return 250;
            }
        })
      , throwOutConfidenceElements = {}
    ;
    $(".slider-list .slide:not(:first-child)").each(function(i, e)
    {
        var card = stack.createCard(this);
        (function(card, e)
        {
            setTimeout(function()
            {
                card.throwIn(0, 200);
                $(e).addClass("in-deck");
            }
            , i * 100);
        }
        )(card, e);
    });
    $(".slider-list .slide:last video")[0].play();
    stack.on("dragstart", function (e)
    {
        throwOutConfidenceElements.yes = $(".phone__label__fave").stop();
        throwOutConfidenceElements.no = $(".phone__label__pass").stop();
    });
    stack.on("dragmove", function (e)
    {
        throwOutConfidenceElements[e.throwDirection == gajus.Swing.Card.DIRECTION_RIGHT ? "yes" : "no"].css("opacity", e.throwOutConfidence);
    });
    stack.on("throwout", function (e)
    {
        $(e.target).removeClass("in-deck");
        playCurrentCardVideo();
    });
    stack.on("dragend", function (e)
    {
        if (e.throwOutConfidence != 1)
        {
            throwOutConfidenceElements.yes.animate({ "opacity": 0 }, 300);
            throwOutConfidenceElements.no.animate({ "opacity": 0 }, 300);
        }
    });

    $(".phone__controls .phone__controls__btn.pass").on("click", function()
    {
        var card = stack.getCard($(".slider-list .slide.in-deck:last")[0]);
        if (card)
        {
            $(".phone__label__pass").css("opacity", 1.0).animate({ "opacity": 0 }, 900);
            card.throwOut(gajus.Swing.Card.DIRECTION_LEFT, 0);
        }
        return false;
    });
    $(".phone__controls .phone__controls__btn.fave").on("click", function()
    {
        var card = stack.getCard($(".slider-list .slide.in-deck:last")[0]);
        if (card)
        {
            $(".phone__label__fave").css("opacity", 1.0).animate({ "opacity": 0 }, 900);
            card.throwOut(gajus.Swing.Card.DIRECTION_RIGHT, 0);
        }
        return false;
    });
});<commit_msg>fix web view link<commit_after>var API_ENDPOINT = "http://fun-api.herokuapp.com/shortcode/";

jQuery(function($)
{
    var hash = window.location.hash;
    if (hash)
    {
        $.ajax(
        {
            url: API_ENDPOINT + hash.substr(1) + ".json",
            type: "get",
            error: function()
            {
                console.log("Error: gif not found.");
            },
            success: function(data)
            {
                $("#main").remove();
                $(".spinner").remove();
                $("#image-share-link").text(window.location);
                $("#image-caption").text(data.name);
                $("#image-view").attr("src", data.original_source);
                $("#shortcode").show();
            }
        });
    }

    $(".slider-list li:first-child").on("click", function()
    {
        $(".focus_me_please").effect("highlight").focus();
        return false;
    });

    function playCurrentCardVideo()
    {
        var lastCardVideo = $(".slider-list .slide.in-deck:last video");
        if (lastCardVideo.length)
        {
            lastCardVideo[0].play();
        }
    }

    var stack = gajus.Swing.Stack(
        {
            "throwOutConfidence": function(offset, element)
            {
                return Math.min(Math.abs(offset) / (element.offsetWidth / 3.0), 1);
            },
            "throwOutDistance": function()
            {
                return 250;
            }
        })
      , throwOutConfidenceElements = {}
    ;
    $(".slider-list .slide:not(:first-child)").each(function(i, e)
    {
        var card = stack.createCard(this);
        (function(card, e)
        {
            setTimeout(function()
            {
                card.throwIn(0, 200);
                $(e).addClass("in-deck");
            }
            , i * 100);
        }
        )(card, e);
    });
    $(".slider-list .slide:last video")[0].play();
    stack.on("dragstart", function (e)
    {
        throwOutConfidenceElements.yes = $(".phone__label__fave").stop();
        throwOutConfidenceElements.no = $(".phone__label__pass").stop();
    });
    stack.on("dragmove", function (e)
    {
        throwOutConfidenceElements[e.throwDirection == gajus.Swing.Card.DIRECTION_RIGHT ? "yes" : "no"].css("opacity", e.throwOutConfidence);
    });
    stack.on("throwout", function (e)
    {
        $(e.target).removeClass("in-deck");
        playCurrentCardVideo();
    });
    stack.on("dragend", function (e)
    {
        if (e.throwOutConfidence != 1)
        {
            throwOutConfidenceElements.yes.animate({ "opacity": 0 }, 300);
            throwOutConfidenceElements.no.animate({ "opacity": 0 }, 300);
        }
    });

    $(".phone__controls .phone__controls__btn.pass").on("click", function()
    {
        var card = stack.getCard($(".slider-list .slide.in-deck:last")[0]);
        if (card)
        {
            $(".phone__label__pass").css("opacity", 1.0).animate({ "opacity": 0 }, 900);
            card.throwOut(gajus.Swing.Card.DIRECTION_LEFT, 0);
        }
        return false;
    });
    $(".phone__controls .phone__controls__btn.fave").on("click", function()
    {
        var card = stack.getCard($(".slider-list .slide.in-deck:last")[0]);
        if (card)
        {
            $(".phone__label__fave").css("opacity", 1.0).animate({ "opacity": 0 }, 900);
            card.throwOut(gajus.Swing.Card.DIRECTION_RIGHT, 0);
        }
        return false;
    });
});<|endoftext|>


<commit_before>const spec_cols = [
  {label: "Column Name"               , id: d => `${d.name}-cn`, format: d => d.name                                    , class:"norm"},
  {label: "Definition"                , id: d => `${d.name}-df`, format: d => d.definition                              , class:"norm"},
  {label: "Type (length)"              , id: d => `${d.name}-tl`, format: d => format_typelens(d.type, d.length)         , class:"norm"},
  {label: "Valid Values"              , id: d => `${d.name}-vv`, format: d => format_vallists_vf(d)                     , class:"norm-vv", title: "Click to turn this into a SAS format statement"},
  {label: "Implementation Guidelines" , id: d => `${d.name}-ig`, format: d => format_igs(d.implementation_guidelines)   , class:"hideable"}
  ]

function vv_click(datum) {
  clickedCell = `td#${datum.name}-vv` ;
  td = d3.select(clickedCell) ;
  currentClass = td._groups[0][0].className ;
  td
    .transition().duration(2000)
    .attr("class", currentClass == "format-vv" ? "norm-vv" : "format-vv")
    .attr("title", currentClass == "format-vv" ? "Click to turn this into a SAS format" : "Click to revert to pretty display")
  ;
}
function headerClick(event) {
  // alert('boobies') ;
  if (d3.selectAll(".hidden").empty()) {
    //nothing is hidden--do the hiding
    d3.selectAll(".hideable")
      .attr('class', 'hidden') ;
  } else {
    d3.selectAll(".hidden")
      .attr('class', 'hideable') ;
    }
}
function format_igs(inarr) {
  if (Array.isArray(inarr)) {
    retval = "" ;
    inarr.forEach(ig => {
      retval += "
" + ig + "
"
    })
  } else {retval = inarr}
  return retval ;
}
function format_typelens(intyp, inlen) {
  if (intyp) {
    typ = intyp ;
    if (inlen) {len = inlen} else {len = '*'} ;
    return typ + "(" + len + ")" ;
  } else {
    return '' ;
  } ;
}
function format_vallists_vf(colspec) {
  // Writes a SAS format value statement for an input array of valid values
  // console.log(Array.isArray(inarr)) ;
  if (Array.isArray(colspec.valid_values)) {
    retval = `<pre class = 'fmt'>value ${makeFormatName(colspec)}` ;
    colspec.valid_values.forEach(d => {
      retval += "\n  '" + d.value + "' = '" + d.meaning.slice(0, 40) + "'"
    })
    retval += "\n;
" ;
    retval += "
" ;
    colspec.valid_values.forEach(d => {
      retval += "
" + d.value + "
"
      retval += "
" + d.meaning + "
"
    })
    retval += "
" ;
  } else {retval = colspec.valid_values}
  return retval ;
}
function makeFormatName(d) {
  // console.log(d) ;
  retval = d.type == "char" ? "$" : "" ;
  retval += d.name.split('-')[0].replace(/[0-9]/g, '').toLowerCase() ;
  return retval.substr(0, 31) ;
}
async function draw_spec(spec_name, with_igs = true) {
  all_specs = await d3.json('./specs.json') ;
  spec = all_specs[spec_name] ;
  our_cols = spec_cols ;
  if (!with_igs) {our_cols.pop()} ;

  wrapper = d3.select("#wrapper") ;
  wrapper.attr("class", "vdw-spec") ;
  wrapper.append("h1").text(spec.name) ;
  wrapper.append("p").html(spec.description) ;
  wrapper.append("h2").text("Standard Variable: " + spec.stdvar) ;
  wrapper.append("h2").text("Columns") ;
  wrapper.append("table")
    .attr("id", "col-tab")
    .append("thead")
      .attr("title", "(click to toggle display of IGs)")
    .append("tr")
    .selectAll("thead")
    .data(spec_cols)
    .enter().append("th")
      .text(d => d.label)
      .on("click", headerClick)
      .attr("class", d => d.class)
  ;
  ;
  const coltab = d3.select("#col-tab") ;
  const tbody = coltab.append("tbody") ;
  spec.columns.forEach(d => {
    tbody.append("tr")
      .selectAll("td")
      .data(spec_cols)
      .enter().append("td")
      .attr("class", column => column.class)
      .attr("title", column => column.title === undefined ? "" : column.title)
      .attr("id", column => column.id(d))
      .html(column => column.format(d))
      .on("click", () => vv_click(d))
    ;
  }) ;

  wrapper.append("h2").text("Primary Key: " + spec.primary_key) ;
  wrapper.append("h2").text("Comments") ;
  spec.comments.forEach((cmt, ind) => {
    wrapper.append("h3").text((ind + 1) + ": " + cmt.title) ;
    wrapper.append("p").html(cmt.comment) ;
  }) ;

}

<commit_msg>Trying out new copy-to-clipboard functionality<commit_after>const spec_cols = [
  {label: "Column Name"               , id: d => `${d.name}-cn`, format: d => d.name                                    , class:"norm"},
  {label: "Definition"                , id: d => `${d.name}-df`, format: d => d.definition                              , class:"norm"},
  {label: "Type (length)"             , id: d => `${d.name}-tl`, format: d => format_typelens(d.type, d.length)         , class:"norm"},
  {label: "Valid Values"              , id: d => `${d.name}-vv`, format: d => format_vallists_vf(d)                     , class:"norm-vv", title: "Click to turn this into a SAS format statement", onclick: vv_click},
  {label: "Implementation Guidelines" , id: d => `${d.name}-ig`, format: d => format_igs(d.implementation_guidelines)   , class:"hideable"}
  ]

function vv_click(datum) {
  // console.log(datum) ;
  clickedCell = `td#${datum.name}-vv` ;
  td = d3.select(clickedCell) ;
  currentClass = td._groups[0][0].className ;
  td
    .transition().duration(2000)
    .attr("class", currentClass == "format-vv" ? "norm-vv" : "format-vv")
    .attr("title", currentClass == "format-vv" ? "Click to turn this into a SAS format" : "Click to revert to pretty display")
  ;

  const clipText = createFormat(datum) ;

  navigator.clipboard.writeText(clipText).then(function() {
      console.log("success!") ;
    }, function() {
      console.log("boo!") ;
    })

}
function headerClick(event) {
  // alert('boobies') ;
  if (d3.selectAll(".hidden").empty()) {
    //nothing is hidden--do the hiding
    d3.selectAll(".hideable")
      .attr('class', 'hidden') ;
  } else {
    d3.selectAll(".hidden")
      .attr('class', 'hideable') ;
    }
}
function format_igs(inarr) {
  if (Array.isArray(inarr)) {
    retval = "" ;
    inarr.forEach(ig => {
      retval += "
" + ig + "
"
    })
  } else {retval = inarr}
  return retval ;
}
function format_typelens(intyp, inlen) {
  if (intyp) {
    typ = intyp ;
    if (inlen) {len = inlen} else {len = '*'} ;
    return typ + "(" + len + ")" ;
  } else {
    return '' ;
  } ;
}
function createFormat(colspec) {
  let retval = ''   ;
  if (Array.isArray(colspec.valid_values)) {
    retval = `value ${makeFormatName(colspec)}` ;
    colspec.valid_values.forEach(d => {
      retval += "\n  '" + d.value + "' = '" + d.meaning + "'"
    })
    retval += "\n;\n" ;
  }
  return retval ;
}

function format_vallists_vf(colspec) {
  // Writes a SAS format value statement for an input array of valid values
  // console.log(Array.isArray(inarr)) ;
  if (Array.isArray(colspec.valid_values)) {
    retval = `<pre class = 'fmt'>value ${makeFormatName(colspec)}` ;
    colspec.valid_values.forEach(d => {
      retval += "\n  '" + d.value + "' = '" + d.meaning.slice(0, 40) + "'"
    })
    retval += "\n;
" ;
    retval += "
" ;
    colspec.valid_values.forEach(d => {
      retval += "
" + d.value + "
"
      retval += "
" + d.meaning + "
"
    })
    retval += "
" ;
  } else {retval = colspec.valid_values}
  return retval ;
}
function makeFormatName(d) {
  // console.log(d) ;
  retval = d.type == "char" ? "$" : "" ;
  retval += d.name.split('-')[0].replace(/[0-9]/g, '').toLowerCase() ;
  return retval.substr(0, 31) ;
}
async function draw_spec(spec_name, with_igs = true) {
  all_specs = await d3.json('../data/specs.json') ;
  spec = all_specs[spec_name] ;
  our_cols = spec_cols ;
  if (!with_igs) {our_cols.pop()} ;

  wrapper = d3.select("#wrapper") ;
  wrapper.attr("class", "vdw-spec") ;
  wrapper.append("h1").text(spec.name) ;
  wrapper.append("p").html(spec.description) ;
  wrapper.append("h2").text("Standard Variable: " + spec.stdvar) ;
  wrapper.append("h2").text("Columns") ;
  wrapper.append("table")
    .attr("id", "col-tab")
    .append("thead")
      .attr("title", "(click to toggle display of IGs)")
    .append("tr")
    .selectAll("thead")
    .data(spec_cols)
    .enter().append("th")
      .text(d => d.label)
      .on("click", headerClick)
      .attr("class", d => d.class)
  ;
  ;
  const coltab = d3.select("#col-tab") ;
  const tbody = coltab.append("tbody") ;
  spec.columns.forEach(d => {
    tbody.append("tr")
      .selectAll("td")
      .data(spec_cols)
      .enter().append("td")
        .attr("class", column => column.class)
        .attr("title", column => column.title === undefined ? "" : column.title)
        .attr("id", column => column.id(d))
        .html(column => column.format(d))
        .on("click", column => column.onclick === undefined ? null : column.onclick(d))
    ;
  }) ;

  wrapper.append("h2").text("Primary Key: " + spec.primary_key) ;
  wrapper.append("h2").text("Comments") ;
  spec.comments.forEach((cmt, ind) => {
    wrapper.append("h3").text((ind + 1) + ": " + cmt.title) ;
    wrapper.append("p").html(cmt.comment) ;
  }) ;

}

<|endoftext|>


<commit_before>"use strict";
let express = require("express");
let router = express.Router();
const logError = require("../helpers/log-error.js");
let { db, FEATURED_COLLECTION, FEATURED } = require("../helpers/db");

function shuffle(array) {
  const shuffledArray = array.slice();
  console.log("shuffledArray[0].entryUrl", shuffledArray[0].entryUrl)
  for (let i = shuffledArray.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
  }
  return shuffledArray;
}

function getHeroFeature() {
  return shuffle([
    {
      imageCredit: "Unknown",
      imageUrl:
        "https://s3.amazonaws.com/participedia.prod/ee8da85d-d17e-4bfb-a500-0fc2e3dfd72a-In%20the%20Participatory%20Community%20Boards%20the%20plans%20presented%20by%20the%20Housing%20Institute%20are%20shaped.",
      entryTitle:
        'Participatory Slum Upgrading Process in the City of Buenos Aires: The "Villa 20" Case',
      entryUrl: "/case/5988",
    },
    {
      imageCredit: "Unknown",
      imageUrl:
        "https://s3.amazonaws.com/participedia.prod/b5294e0a-e875-4ece-afe1-a242f851a5c3",
      entryTitle:
        "Decommissioning South African Social Services: Participatory Field Research in Delft",
      entryUrl: "/case/5834",
    },
    {
      imageCredit: "Max Bender",
      imageUrl:
        "https://s3.amazonaws.com/participedia.prod/d97cf067-9b0b-4d80-bc38-aee5b8553c8c",
      entryTitle: "George Floyd Protests",
      entryUrl: "/case/6590",
    },
  ])[0];
}

async function getThingStatistic() {
  const stats = {
    cases: 0,
    methods: 0,
    organizations: 0
  };
  const results = await db.any("SELECT type, COUNT(*) as total FROM things WHERE type <> 'collection' GROUP BY type");
  results.map(result => {
    let type = `${result.type}s`;
    if (stats.hasOwnProperty(type)) {
      stats[type] = parseInt(result.total);
    }
  });
  return stats;
}

async function getTotalCountries() {
  const results = await db.any("SELECT country FROM things WHERE type IN ('case', 'organization') and country <> '' GROUP BY country");
  let total = 0;
  if (results && results.length) {
    total = parseInt(results.length);
  }
  return total;
}

async function getTotalContributors() {
  const results = await db.any("SELECT user_id FROM authors GROUP BY user_id");
  let total = 0;
  if (results && results.length) {
    total = parseInt(results.length);
  }
  return total;
}

router.get("/", async function(req, res) {
  let returnType = req.query.returns;
  const language = req.cookies.locale || "en";
  const thingStatsResult = await getThingStatistic();
  const totalCounties = await getTotalCountries();
  const totalContributors = await getTotalContributors();
  const heroFeature = getHeroFeature();

  // Collect Statistics
  const stats = {
    cases: thingStatsResult.cases, // (total entries)
    methods: thingStatsResult.methods, // (total entries)
    organizations: thingStatsResult.organizations, // (total entries)
    countries: totalCounties, // (number of unique countries represented between cases and orgs)
    contributors: totalContributors, // (number of unique users who have created or edited an entry)
  };

  // Collect Featured Collections
  const featuredCollections = await db.any(FEATURED_COLLECTION, {
    language: language
  });

  // Collect Featured Things
  const featuredCasesMethodsOrgs = await db.any(FEATURED, {
    language: language,
    limit: 7,
    sortby: "post_date",
    type: "things",
    userId: req.user ? req.user.id : null,
    facets: "",
    offset: 1
  });

  // Populate response data
  const data = {
    featuredCasesMethodsOrgs: featuredCasesMethodsOrgs,
    featuredCollections: featuredCollections,
    stats: stats,
    heroFeature: heroFeature
  };

  switch (returnType) {
    case "json":
      return res.status(200).json({
        user: req.user || null,
        ...data
      });
    case "html": // fall through
    default:
      return res.status(200).render("home", {
        user: req.user || null,
        ...data,
      });
  }
});

module.exports = router;
<commit_msg>update hero image credits<commit_after>"use strict";
let express = require("express");
let router = express.Router();
const logError = require("../helpers/log-error.js");
let { db, FEATURED_COLLECTION, FEATURED } = require("../helpers/db");

function shuffle(array) {
  const shuffledArray = array.slice();
  console.log("shuffledArray[0].entryUrl", shuffledArray[0].entryUrl)
  for (let i = shuffledArray.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
  }
  return shuffledArray;
}

function getHeroFeature() {
  return shuffle([
    {
      imageCredit: "Housing Institute of Buenos Aires",
      imageUrl:
        "https://s3.amazonaws.com/participedia.prod/ee8da85d-d17e-4bfb-a500-0fc2e3dfd72a-In%20the%20Participatory%20Community%20Boards%20the%20plans%20presented%20by%20the%20Housing%20Institute%20are%20shaped.",
      entryTitle:
        'Participatory Slum Upgrading Process in the City of Buenos Aires: The "Villa 20" Case',
      entryUrl: "/case/5988",
    },
    {
      imageCredit: "Research Team, University of the Western Cape: Professor Laurence Piper, Robyn Pasensie and Sondre Bailey",
      imageUrl:
        "https://s3.amazonaws.com/participedia.prod/b5294e0a-e875-4ece-afe1-a242f851a5c3",
      entryTitle:
        "Decommissioning South African Social Services: Participatory Field Research in Delft",
      entryUrl: "/case/5834",
    },
    {
      imageCredit: "Max Bender",
      imageUrl:
        "https://s3.amazonaws.com/participedia.prod/d97cf067-9b0b-4d80-bc38-aee5b8553c8c",
      entryTitle: "George Floyd Protests",
      entryUrl: "/case/6590",
    },
  ])[0];
}

async function getThingStatistic() {
  const stats = {
    cases: 0,
    methods: 0,
    organizations: 0
  };
  const results = await db.any("SELECT type, COUNT(*) as total FROM things WHERE type <> 'collection' GROUP BY type");
  results.map(result => {
    let type = `${result.type}s`;
    if (stats.hasOwnProperty(type)) {
      stats[type] = parseInt(result.total);
    }
  });
  return stats;
}

async function getTotalCountries() {
  const results = await db.any("SELECT country FROM things WHERE type IN ('case', 'organization') and country <> '' GROUP BY country");
  let total = 0;
  if (results && results.length) {
    total = parseInt(results.length);
  }
  return total;
}

async function getTotalContributors() {
  const results = await db.any("SELECT user_id FROM authors GROUP BY user_id");
  let total = 0;
  if (results && results.length) {
    total = parseInt(results.length);
  }
  return total;
}

router.get("/", async function(req, res) {
  let returnType = req.query.returns;
  const language = req.cookies.locale || "en";
  const thingStatsResult = await getThingStatistic();
  const totalCounties = await getTotalCountries();
  const totalContributors = await getTotalContributors();
  const heroFeature = getHeroFeature();

  // Collect Statistics
  const stats = {
    cases: thingStatsResult.cases, // (total entries)
    methods: thingStatsResult.methods, // (total entries)
    organizations: thingStatsResult.organizations, // (total entries)
    countries: totalCounties, // (number of unique countries represented between cases and orgs)
    contributors: totalContributors, // (number of unique users who have created or edited an entry)
  };

  // Collect Featured Collections
  const featuredCollections = await db.any(FEATURED_COLLECTION, {
    language: language
  });

  // Collect Featured Things
  const featuredCasesMethodsOrgs = await db.any(FEATURED, {
    language: language,
    limit: 7,
    sortby: "post_date",
    type: "things",
    userId: req.user ? req.user.id : null,
    facets: "",
    offset: 1
  });

  // Populate response data
  const data = {
    featuredCasesMethodsOrgs: featuredCasesMethodsOrgs,
    featuredCollections: featuredCollections,
    stats: stats,
    heroFeature: heroFeature
  };

  switch (returnType) {
    case "json":
      return res.status(200).json({
        user: req.user || null,
        ...data
      });
    case "html": // fall through
    default:
      return res.status(200).render("home", {
        user: req.user || null,
        ...data,
      });
  }
});

module.exports = router;
<|endoftext|>


<commit_before><commit_msg>view.Desktop noop: refactor 'desktop.Status' setup<commit_after><|endoftext|>


<commit_before>/* global window:false, require:false, document:false */
require([
    'application'
], function (
    Application
) {
    'use strict';

    window.app = new Application();
});<commit_msg>start the app<commit_after>/* global window:false, require:false, document:false */
require([
    'application'
], function (
    Application
) {
    'use strict';

    window.app = new Application();
    window.app.start();
});<|endoftext|>


<commit_before>define(function(require, exports, module) {
  'use strict';
  var flight = require('flight');
  var L = require('leaflet');
  require('L.Control.Locate');
  module.exports = flight.component(function map() {
    this.defaultAttrs({
      tileUrl: 'http://a{s}.acetate.geoiq.com/tiles/acetate-hillshading/{z}/{x}/{y}.png',
      tileAttribution: '©2012 Esri & Stamen, Data from OSM and Natural Earth',
      tileSubdomains: '0123',
      tileMinZoom: 2,
      tileMaxZoom: 18
    });

    this.defineIconStyles = function() {
      // define icon styles
      var path = L.Icon.Default.imagePath;
      if (!path) {
        path = L.Icon.Default.imagePath = 'lib/leaflet/images';
      }
      this.grayIcon = L.icon({
        iconUrl: path + '/marker-icon-gray.png',
        shadowUrl: path + '/marker-shadow.png'
      });

      this.defaultIcon = L.icon({
        iconUrl: path + '/marker-icon.png',
        shadowUrl: path + '/marker-shadow.png'
      });
    };

    this.configureMap = function(ev, config) {
      var mapConfig = config.map;

      // if list or facets are emabled, give the map less space
      if (config.facets) {
        this.$node.css('left', '+=300');
      }
      if (config.list) {
        this.$node.css('left', '+=300');
      }

      this.map.setView(mapConfig.center, mapConfig.zoom);
      if (mapConfig.maxZoom){
        this.map.options.maxZoom = mapConfig.maxZoom;
      }
      if (mapConfig.maxBounds){
        this.map.setMaxBounds(mapConfig.maxBounds);
      }

      // Add the location control which will zoom to current
      // location
      L.control.locate().addTo(this.map);

      // set feature attribute to be used as preview text to config
      this.featurePreviewAttr = config.map.preview_attribute;
    };

    this.loadData = function(ev, data) {
      this.defineIconStyles();

      var setupFeature = function(feature, layer) {
        this.attr.features[feature.geometry.coordinates] = layer;

        // bind popup to feature with specified preview attribute
        this.bindPopupToFeature(
          layer,
          feature.properties[this.featurePreviewAttr]);

        layer.on({
          click: this.emitClick.bind(this),
          mouseover: this.emitHover.bind(this),
          mouseout: this.clearHover.bind(this)
        });
      }.bind(this);

      if (this.attr.layer) {
        this.attr.features = {};
        this.map.removeLayer(this.attr.layer);
      }

      this.attr.layer = L.geoJson(data, {onEachFeature: setupFeature});
      this.attr.layer.addTo(this.map);
    };

    this.emitClick = function(e) {
      this.trigger(document, 'selectFeature', e.target.feature);
    };

    this.emitHover = function(e) {
      this.trigger(document, 'hoverFeature', e.target.feature);
    };

    this.clearHover = function(e) {
      this.trigger(document, 'clearHoverFeature', e.target.feature);
    };

    this.selectFeature = function(ev, feature) {
      if (this.previouslyClicked) {
        this.previouslyClicked.setIcon(this.defaultIcon);
        this.trigger(document, 'deselectFeature', this.currentFeature);
      }
      if (feature) {
        this.currentFeature = feature;
        var layer = this.attr.features[feature.geometry.coordinates];
        layer.setIcon(this.grayIcon);
        this.previouslyClicked = layer;

        // re-bind popup to feature with specified preview attribute
        this.bindPopupToFeature(
          layer,
          feature.properties[this.featurePreviewAttr]);

        this.trigger('panTo', {lng: feature.geometry.coordinates[0],
                               lat: feature.geometry.coordinates[1]});
      } else {
        this.previouslyClicked = null;
      }
    };

    this.deselectFeature = function(ev, feature) {
      if (this.previouslyClicked) {
        this.previouslyClicked.setIcon(this.defaultIcon);
      }
      var layer = this.attr.features[feature.geometry.coordinates];
      // re-bind popup to feature with specified preview attribute
      this.bindPopupToFeature(
        layer,
        feature.properties[this.featurePreviewAttr]);
      this.previouslyClicked = null;
    };

    this.bindPopupToFeature = function(layer, feature){
      layer.bindPopup(
        feature,
        {
          closeButton: false,
          offset: L.point(0, -40)
        });
    };

    this.hoverFeature = function(ev, feature) {
      if (feature) {
        var layer = this.attr.features[feature.geometry.coordinates];
        layer.openPopup();
      }
    };

    this.clearHoverFeature = function(ev, feature) {
      if (feature) {
        var layer = this.attr.features[feature.geometry.coordinates];
        layer.closePopup();
      }
    };

    this.panTo = function(ev, latlng) {
      this.map.panTo(latlng);
    };

    this.after('initialize', function() {
      this.map = L.map(this.node, {});

      this.attr.features = {};

      L.tileLayer(this.attr.tileUrl, {
        attribution: this.attr.tileAttribution,
        subdomains: this.attr.tileSubdomains,
        minZoom: this.attr.tileMinZoom,
        maxZoom: this.attr.tileMaxZoom
      }).addTo(this.map);

      this.on(document, 'config', this.configureMap);
      this.on(document, 'data', this.loadData);
      this.on(document, 'dataFiltered', this.loadData);

      this.on(document, 'selectFeature', this.selectFeature);
      this.on(document, 'deselectFeature', this.deselectFeature);
      this.on(document, 'hoverFeature', this.hoverFeature);
      this.on(document, 'clearHoverFeature', this.clearHoverFeature);
      this.on('panTo', this.panTo);
    });
  });
});
<commit_msg>re-size map in a separate tick<commit_after>define(function(require, exports, module) {
  'use strict';
  var flight = require('flight');
  var L = require('leaflet');
  require('L.Control.Locate');
  module.exports = flight.component(function map() {
    this.defaultAttrs({
      tileUrl: 'http://a{s}.acetate.geoiq.com/tiles/acetate-hillshading/{z}/{x}/{y}.png',
      tileAttribution: '©2012 Esri & Stamen, Data from OSM and Natural Earth',
      tileSubdomains: '0123',
      tileMinZoom: 2,
      tileMaxZoom: 18
    });

    this.defineIconStyles = function() {
      // define icon styles
      var path = L.Icon.Default.imagePath;
      if (!path) {
        path = L.Icon.Default.imagePath = 'lib/leaflet/images';
      }
      this.grayIcon = L.icon({
        iconUrl: path + '/marker-icon-gray.png',
        shadowUrl: path + '/marker-shadow.png'
      });

      this.defaultIcon = L.icon({
        iconUrl: path + '/marker-icon.png',
        shadowUrl: path + '/marker-shadow.png'
      });
    };

    this.configureMap = function(ev, config) {
      // if list or facets are emabled, give the map less space
      var addition = 0;
      if (config.facets) {
        addition += 300;
      }
      if (config.list) {
        addition += 300;
      }

      if (addition > 0) {
        window.setTimeout(function() {
          this.$node.css('left', '+=' + addition);
        }.bind(this), 50);
      }

      var mapConfig = config.map;

      this.map.setView(mapConfig.center, mapConfig.zoom);
      if (mapConfig.maxZoom){
        this.map.options.maxZoom = mapConfig.maxZoom;
      }
      if (mapConfig.maxBounds){
        this.map.setMaxBounds(mapConfig.maxBounds);
      }

      // Add the location control which will zoom to current
      // location
      L.control.locate().addTo(this.map);

      // set feature attribute to be used as preview text to config
      this.featurePreviewAttr = config.map.preview_attribute;
    };

    this.loadData = function(ev, data) {
      this.defineIconStyles();

      var setupFeature = function(feature, layer) {
        this.attr.features[feature.geometry.coordinates] = layer;

        // bind popup to feature with specified preview attribute
        this.bindPopupToFeature(
          layer,
          feature.properties[this.featurePreviewAttr]);

        layer.on({
          click: this.emitClick.bind(this),
          mouseover: this.emitHover.bind(this),
          mouseout: this.clearHover.bind(this)
        });
      }.bind(this);

      if (this.attr.layer) {
        this.attr.features = {};
        this.map.removeLayer(this.attr.layer);
      }

      this.attr.layer = L.geoJson(data, {onEachFeature: setupFeature});
      this.attr.layer.addTo(this.map);
    };

    this.emitClick = function(e) {
      this.trigger(document, 'selectFeature', e.target.feature);
    };

    this.emitHover = function(e) {
      this.trigger(document, 'hoverFeature', e.target.feature);
    };

    this.clearHover = function(e) {
      this.trigger(document, 'clearHoverFeature', e.target.feature);
    };

    this.selectFeature = function(ev, feature) {
      if (this.previouslyClicked) {
        this.previouslyClicked.setIcon(this.defaultIcon);
        this.trigger(document, 'deselectFeature', this.currentFeature);
      }
      if (feature) {
        this.currentFeature = feature;
        var layer = this.attr.features[feature.geometry.coordinates];
        layer.setIcon(this.grayIcon);
        this.previouslyClicked = layer;

        // re-bind popup to feature with specified preview attribute
        this.bindPopupToFeature(
          layer,
          feature.properties[this.featurePreviewAttr]);

        this.trigger('panTo', {lng: feature.geometry.coordinates[0],
                               lat: feature.geometry.coordinates[1]});
      } else {
        this.previouslyClicked = null;
      }
    };

    this.deselectFeature = function(ev, feature) {
      if (this.previouslyClicked) {
        this.previouslyClicked.setIcon(this.defaultIcon);
      }
      var layer = this.attr.features[feature.geometry.coordinates];
      // re-bind popup to feature with specified preview attribute
      this.bindPopupToFeature(
        layer,
        feature.properties[this.featurePreviewAttr]);
      this.previouslyClicked = null;
    };

    this.bindPopupToFeature = function(layer, feature){
      layer.bindPopup(
        feature,
        {
          closeButton: false,
          offset: L.point(0, -40)
        });
    };

    this.hoverFeature = function(ev, feature) {
      if (feature) {
        var layer = this.attr.features[feature.geometry.coordinates];
        layer.openPopup();
      }
    };

    this.clearHoverFeature = function(ev, feature) {
      if (feature) {
        var layer = this.attr.features[feature.geometry.coordinates];
        layer.closePopup();
      }
    };

    this.panTo = function(ev, latlng) {
      this.map.panTo(latlng);
    };

    this.after('initialize', function() {
      this.map = L.map(this.node, {});

      this.attr.features = {};

      L.tileLayer(this.attr.tileUrl, {
        attribution: this.attr.tileAttribution,
        subdomains: this.attr.tileSubdomains,
        minZoom: this.attr.tileMinZoom,
        maxZoom: this.attr.tileMaxZoom
      }).addTo(this.map);

      this.on(document, 'config', this.configureMap);
      this.on(document, 'data', this.loadData);
      this.on(document, 'dataFiltered', this.loadData);

      this.on(document, 'selectFeature', this.selectFeature);
      this.on(document, 'deselectFeature', this.deselectFeature);
      this.on(document, 'hoverFeature', this.hoverFeature);
      this.on(document, 'clearHoverFeature', this.clearHoverFeature);
      this.on('panTo', this.panTo);
    });
  });
});
<|endoftext|>