<commit_before>'use strict'
/**
 * Provides a Solid web client class for performing LDP CRUD operations.
 * @module web
 */
var DEFAULT_ACCEPT = 'text/turtle;q=0.8,*/*;q=0.5'
var DEFAULT_MIME_TYPE = 'text/turtle'
var defaultConfig = require('../config-default')

var composePatchQuery = require('./util/web-util').composePatchQuery
var SolidResponse = require('./models/response')
var XMLHttpRequest = require('./util/xhr')
var HttpError = require('standard-http-error')
var vocab = require('solid-namespace')

/**
 * Provides a collection of Solid/LDP web operations (CRUD)
 * @class SolidWebClient
 * @constructor
 * @param rdf {RDF} RDF library (like rdflib.js or rdf-ext) for parsing
 * @param [config] {Object} Config hashmap
 */
function SolidWebClient (rdf, config) {
  this.rdf = rdf
  this.vocab = vocab(rdf)
  this.config = config || defaultConfig
}

/**
 * Creates a Solid container with the specified name.
 * Uses PUT instead of POST to guarantee the container name (and uses
 * conditional HTTP headers to fail with a `409 Conflict` error if
 * a container with that name already exists).
 * @method createContainer
 * @param parentUrl {String} Parent directory/container in which to create
 * @param name {String} Container name (slug / URL fragment), no trailing
 *   slash needed.
 * @param [options] Options hashmap (optional, see `solidRequest()`)
 * @param [data] {String} Optional RDF data payload (additional triples
 *   that will be added to the container's metadata)
 * @throw {HttpError} Throws an error if a resource or container with the
 *   same name already exists
 * @return {Promise<SolidResponse>}
 */
SolidWebClient.prototype.createContainer =
  function createContainer (parentUrl, name, options, data) {
    return this.post(parentUrl, data, name, true)
  }

/**
 * Creates and returns the appropriate Solid wrapper for the XHR response.
 * @method createResponse
 * @param xhrResponse {XMLHttpRequest} XHR Response
 * @param method {String} HTTP verb
 * @return {SolidResponse} A SolidResponse
 */
SolidWebClient.prototype.createResponse =
  function createResponse (xhrResponse, method) {
    return new SolidResponse(this.rdf, xhrResponse, method)
  }
/**
 * Returns the current window's location (for use with `needsProxy()`)
 * if used in browser, or `null` if used from Node.
 * @method currentUrl
 * @return {String|Null}
 */
SolidWebClient.prototype.currentUrl = function currentUrl () {
  if (typeof window !== 'undefined') {
    return window.location.href
  } else {
    return null
  }
}
/**
 * Deletes an existing resource or container.
 * @method del
 * @param url {String} URL of the resource or container to be deleted
 * @return {Promise<SolidResponse>} Result of the HTTP Delete operation (returns
 *   true on success, or an anonymous error object on failure)
 */
SolidWebClient.prototype.del = function del (url) {
  return this.solidRequest(url, 'DELETE')
}
/**
 * Retrieves a resource or container by making an HTTP GET call.
 * @method get
 * @param url {String} URL of the resource or container to fetch
 * @param [options={}] Options hashmap (see `solidRequest()` docs)
 * @return {Promise<SolidResponse>} Result of the HTTP
 *   GET operation, or an error object
 */
SolidWebClient.prototype.get = function get (url, options = {}) {
  options.headers = options.headers || {}
  // If no explicit Accept: header specified, set one
  if (!options.headers['Accept']) {
    options.headers['Accept'] = DEFAULT_ACCEPT
  }
  return this.solidRequest(url, 'GET', options)
}
/**
 * Checks to see if a Solid resource exists, and returns useful resource
 *   metadata info.
 * @method head
 * @param url {String} URL of a resource or container
 * @param [options] Options hashmap (see `solidRequest()` docs)
 * @return {Promise} Result of an HTTP HEAD operation (returns a meta object)
 */
SolidWebClient.prototype.head = function head (url, options) {
  return this.solidRequest(url, 'HEAD', options)
}
/**
 * Loads a list of given RDF graphs via an async `Promise.all()`,
 * which resolves to an array of uri/parsed-graph hashes.
 * @method loadParsedGraphs
 * @param locations {Array<String>} Array of graph URLs to load
 * @param [options] Options hashmap (see `solidRequest()` docs)
 * @return {Promise<Array<Object>>}
 */
SolidWebClient.prototype.loadParsedGraphs =
  function loadParsedGraphs (locations, options) {
    var web = this
    var loadPromises = locations.map(function (location) {
      var responseUrl // may differ from location if redirected
      return web.get(location, options)
        .then(function (response) {
          responseUrl = response.url
          return response.parsedGraph()
        })
        .catch(function () {
          // Suppress the error, no need to reject, just return null graph
          return null
        })
        .then(function (parsedGraph) {
          return {
            uri: responseUrl,
            value: parsedGraph
          }
        })
    })
    return Promise.all(loadPromises)
  }
/**
 * Determines whether the web client needs to fall back onto a Proxy url,
 * to avoid being blocked by CORS
 * @method needsProxy
 * @param url {String}
 * @return {Boolean}
 */
SolidWebClient.prototype.needsProxy = function needsProxy (url) {
  var currentUrl = this.currentUrl()
  var currentIsHttps = currentUrl && currentUrl.slice(0, 6) === 'https:'
  var targetIsHttp = url && url.slice(0, 5) === 'http:'
  return currentIsHttps && targetIsHttp
}
/**
 * Issues an HTTP OPTIONS request. Useful for discovering server capabilities
 * (`Accept-Patch:`, `Updates-Via:` for websockets, etc).
 * @method options
 * @param url {String} URL of a resource or container
 * @return {Promise<SolidResponse>} Result of an HTTP OPTIONS operation
 */
SolidWebClient.prototype.options = function options (url) {
  return this.solidRequest(url, 'OPTIONS')
}
/**
 * Partially edits an RDF-type resource by performing a PATCH operation.
 *   Accepts arrays of individual statements (in Turtle format) as params.
 *   For example:
 *   [ '<a> <b> <c> .', '<d> <e> <f> .']
 * @method patch
 * @param url {String} URL of the resource to be edited
 * @param toDel {Array<String>} Triples to remove from the resource
 * @param toIns {Array<String>} Triples to insert into the resource
 * @param [options] Options hashmap
 * @return {Promise<SolidResponse>} Result of PATCH operation
 */
SolidWebClient.prototype.patch = function patch (url, toDel, toIns, options) {
  var data = composePatchQuery(toDel, toIns)
  var mimeType = 'application/sparql-update'
  options = options || {}
  options.headers = options.headers || {}
  options.headers['Content-Type'] = mimeType
  return this.solidRequest(url, 'PATCH', options, data)
}
/**
 * Creates a new resource by performing
 *   a Solid/LDP POST operation to a specified container.
 * @param url {String} URL of the container to post to
 * @param data {Object} Data/payload of the resource to be created
 * @param slug {String} Suggested URL fragment for the new resource
 * @param isContainer {Boolean} Is the object being created a Container
 *            or Resource?
 * @param mimeType {String} Content Type of the data/payload
 * @method post
 * @return {Promise<SolidResponse>} Result of XHR POST (returns parsed response
 *     meta object) or an anonymous error object with status code
 */
SolidWebClient.prototype.post =
  function post (url, data, slug, isContainer, mimeType) {
    var resourceType
    if (isContainer) {
      resourceType = this.vocab.ldp('BasicContainer')
      // Force the right mime type for containers only
      mimeType = 'text/turtle'
    } else {
      resourceType = this.vocab.ldp('Resource')
      mimeType = mimeType || DEFAULT_MIME_TYPE
    }
    var options = {}
    options.headers = {}
    options.headers['Link'] = resourceType + '; rel="type"'
    options.headers['Content-Type'] = mimeType
    if (slug && slug.length > 0) {
      options.headers['Slug'] = slug
    }
    return this.solidRequest(url, 'POST', options, data)
  }
/**
 * Turns a given URL into a proxied version, using a proxy template
 * @method proxyUrl
 * @param url {String} Intended URL
 * @param proxyUrlTemplate {String}
 * @return {String}
 */
SolidWebClient.prototype.proxyUrl = function proxyUrl (url, proxyUrlTemplate) {
  proxyUrlTemplate = proxyUrlTemplate || this.config.proxyUrl
  return proxyUrlTemplate.replace('{uri}', encodeURIComponent(url))
}
/**
 * Updates an existing resource or creates a new resource by performing
 *   a Solid/LDP PUT operation to a specified container
 * @method put
 * @param url {String} URL of the resource to be updated/created
 * @param data {Object} Data/payload of the resource to be created or updated
 * @param [mimeType] {String} MIME Type of the resource to be created
 * @param [options] Options hashmap, see docs for `solidResponse()`
 * @return {Promise<SolidResponse>} Result of PUT operation (returns parsed
 *     response meta object if successful, rejects with an anonymous error
 *     status object if not successful)
 */
SolidWebClient.prototype.put = function put (url, data, mimeType, options) {
  options = options || {}
  options.headers = options.headers || {}
  mimeType = mimeType || DEFAULT_MIME_TYPE
  options.headers['Content-Type'] = mimeType
  return this.solidRequest(url, 'PUT', options, data)
}
/**
 * Sends a generic XHR request with the appropriate Solid headers,
 * and returns a promise that resolves to a parsed response.
 * @method solidRequest
 * @param url {String} URL of the request
 * @param method {String} HTTP Verb ('GET', 'PUT', etc)
 * @param [options] Options hashmap
 * @param [options.noCredentials=false] {Boolean} Don't use `withCredentials`
 * @param [options.forceProxy=false] {Boolean} Enforce using proxy URL if true
 * @param [options.headers={}] {Object} HTTP headers to send along
 *          with request
 * @param [options.proxyUrl=config.proxyUrl] {String} Proxy URL to use for
 *          CORS Requests.
 * @param [options.timeout=config.timeout] {Number} Request timeout in
 *          milliseconds.
 * @param [data] {Object} Optional data / payload
 * @throws {HttpError} Rejects with `httpError.HttpError` of the appropriate
 *   type
 * @return {Promise<SolidResponse>}
 */
SolidWebClient.prototype.solidRequest =
  function solidRequest (url, method, options, data) {
    options = options || {}
    options.headers = options.headers || {}
    options.proxyUrl = options.proxyUrl || this.config.proxyUrl
    options.timeout = options.timeout || this.config.timeout
    if (this.needsProxy(url) || options.forceProxy) {
      url = this.proxyUrl(url)
    }
    var webClient = this
    return new Promise(function (resolve, reject) {
      var http = new XMLHttpRequest()
      http.open(method, url)
      if (!options.noCredentials) {
        http.withCredentials = true
      }
      for (var header in options.headers) {  // Add in optional headers
        http.setRequestHeader(header, options.headers[header])
      }
      if (options.timeout) {
        http.timeout = options.timeout
      }
      http.onload = function () {
        if (this.status >= 200 && this.status < 300) {
          resolve(webClient.createResponse(this, method))
        } else {
          reject(new HttpError(this.status, this.statusText, {xhr: this}))
        }
      }
      http.onerror = function () {
        reject(new HttpError(this.status, this.statusText, {xhr: this}))
      }
      if (typeof data === 'undefined' || !data) {
        http.send()
      } else {
        http.send(data)
      }
    })
  }

/**
 * Returns a web client instance (convenience constructor method).
 * Usage:
 *
 *   ```
 *   var rdf = require('rdflib')  // or other compatible library
 *   var webClient = require('solid-web-client')(rdf)
 *   ```
 * @param rdf
 * @param config
 * @returns {SolidWebClient}
 */
function getClient (rdf, config) {
  return new SolidWebClient(rdf, config)
}

module.exports = getClient
module.exports.SolidWebClient = SolidWebClient
<commit_msg>Inject auth client, send Authorization header if present (#18)<commit_after>'use strict'
/**
 * Provides a Solid web client class for performing LDP CRUD operations.
 * @module web
 */
var DEFAULT_ACCEPT = 'text/turtle;q=0.8,*/*;q=0.5'
var DEFAULT_MIME_TYPE = 'text/turtle'
var defaultConfig = require('../config-default')

var composePatchQuery = require('./util/web-util').composePatchQuery
var SolidResponse = require('./models/response')
var XMLHttpRequest = require('./util/xhr')
var HttpError = require('standard-http-error')
var vocab = require('solid-namespace')

/**
 * Provides a collection of Solid/LDP web operations (CRUD)
 * @class SolidWebClient
 * @constructor
 * @param rdf {RDF} RDF library (like rdflib.js or rdf-ext) for parsing
 * @param [config] {Object} Config hashmap
 * @param [config.auth] {ClientAuthOIDC} Solid OIDC auth client instance
 */
function SolidWebClient (rdf, config = {}) {
  this.rdf = rdf
  this.vocab = vocab(rdf)
  this.config = Object.assign({}, defaultConfig, config)
  this.auth = config.auth
}

/**
 * Creates a Solid container with the specified name.
 * Uses PUT instead of POST to guarantee the container name (and uses
 * conditional HTTP headers to fail with a `409 Conflict` error if
 * a container with that name already exists).
 * @method createContainer
 * @param parentUrl {String} Parent directory/container in which to create
 * @param name {String} Container name (slug / URL fragment), no trailing
 *   slash needed.
 * @param [options] Options hashmap (optional, see `solidRequest()`)
 * @param [data] {String} Optional RDF data payload (additional triples
 *   that will be added to the container's metadata)
 * @throw {HttpError} Throws an error if a resource or container with the
 *   same name already exists
 * @return {Promise<SolidResponse>}
 */
SolidWebClient.prototype.createContainer =
  function createContainer (parentUrl, name, options, data) {
    return this.post(parentUrl, data, name, true)
  }

/**
 * Creates and returns the appropriate Solid wrapper for the XHR response.
 * @method createResponse
 * @param xhrResponse {XMLHttpRequest} XHR Response
 * @param method {String} HTTP verb
 * @return {SolidResponse} A SolidResponse
 */
SolidWebClient.prototype.createResponse =
  function createResponse (xhrResponse, method) {
    return new SolidResponse(this.rdf, xhrResponse, method)
  }
/**
 * Returns the current window's location (for use with `needsProxy()`)
 * if used in browser, or `null` if used from Node.
 * @method currentUrl
 * @return {String|Null}
 */
SolidWebClient.prototype.currentUrl = function currentUrl () {
  if (typeof window !== 'undefined') {
    return window.location.href
  } else {
    return null
  }
}
/**
 * Deletes an existing resource or container.
 * @method del
 * @param url {String} URL of the resource or container to be deleted
 * @return {Promise<SolidResponse>} Result of the HTTP Delete operation (returns
 *   true on success, or an anonymous error object on failure)
 */
SolidWebClient.prototype.del = function del (url) {
  return this.solidRequest(url, 'DELETE')
}
/**
 * Retrieves a resource or container by making an HTTP GET call.
 * @method get
 * @param url {String} URL of the resource or container to fetch
 * @param [options={}] Options hashmap (see `solidRequest()` docs)
 * @return {Promise<SolidResponse>} Result of the HTTP
 *   GET operation, or an error object
 */
SolidWebClient.prototype.get = function get (url, options = {}) {
  options.headers = options.headers || {}
  // If no explicit Accept: header specified, set one
  if (!options.headers['Accept']) {
    options.headers['Accept'] = DEFAULT_ACCEPT
  }
  return this.solidRequest(url, 'GET', options)
}
/**
 * Checks to see if a Solid resource exists, and returns useful resource
 *   metadata info.
 * @method head
 * @param url {String} URL of a resource or container
 * @param [options] Options hashmap (see `solidRequest()` docs)
 * @return {Promise} Result of an HTTP HEAD operation (returns a meta object)
 */
SolidWebClient.prototype.head = function head (url, options) {
  return this.solidRequest(url, 'HEAD', options)
}
/**
 * Loads a list of given RDF graphs via an async `Promise.all()`,
 * which resolves to an array of uri/parsed-graph hashes.
 * @method loadParsedGraphs
 * @param locations {Array<String>} Array of graph URLs to load
 * @param [options] Options hashmap (see `solidRequest()` docs)
 * @return {Promise<Array<Object>>}
 */
SolidWebClient.prototype.loadParsedGraphs =
  function loadParsedGraphs (locations, options) {
    var web = this
    var loadPromises = locations.map(function (location) {
      var responseUrl // may differ from location if redirected
      return web.get(location, options)
        .then(function (response) {
          responseUrl = response.url
          return response.parsedGraph()
        })
        .catch(function () {
          // Suppress the error, no need to reject, just return null graph
          return null
        })
        .then(function (parsedGraph) {
          return {
            uri: responseUrl,
            value: parsedGraph
          }
        })
    })
    return Promise.all(loadPromises)
  }
/**
 * Determines whether the web client needs to fall back onto a Proxy url,
 * to avoid being blocked by CORS
 * @method needsProxy
 * @param url {String}
 * @return {Boolean}
 */
SolidWebClient.prototype.needsProxy = function needsProxy (url) {
  var currentUrl = this.currentUrl()
  var currentIsHttps = currentUrl && currentUrl.slice(0, 6) === 'https:'
  var targetIsHttp = url && url.slice(0, 5) === 'http:'
  return currentIsHttps && targetIsHttp
}
/**
 * Issues an HTTP OPTIONS request. Useful for discovering server capabilities
 * (`Accept-Patch:`, `Updates-Via:` for websockets, etc).
 * @method options
 * @param url {String} URL of a resource or container
 * @return {Promise<SolidResponse>} Result of an HTTP OPTIONS operation
 */
SolidWebClient.prototype.options = function options (url) {
  return this.solidRequest(url, 'OPTIONS')
}
/**
 * Partially edits an RDF-type resource by performing a PATCH operation.
 *   Accepts arrays of individual statements (in Turtle format) as params.
 *   For example:
 *   [ '<a> <b> <c> .', '<d> <e> <f> .']
 * @method patch
 * @param url {String} URL of the resource to be edited
 * @param toDel {Array<String>} Triples to remove from the resource
 * @param toIns {Array<String>} Triples to insert into the resource
 * @param [options] Options hashmap
 * @return {Promise<SolidResponse>} Result of PATCH operation
 */
SolidWebClient.prototype.patch = function patch (url, toDel, toIns, options) {
  var data = composePatchQuery(toDel, toIns)
  var mimeType = 'application/sparql-update'
  options = options || {}
  options.headers = options.headers || {}
  options.headers['Content-Type'] = mimeType
  return this.solidRequest(url, 'PATCH', options, data)
}
/**
 * Creates a new resource by performing
 *   a Solid/LDP POST operation to a specified container.
 * @param url {String} URL of the container to post to
 * @param data {Object} Data/payload of the resource to be created
 * @param slug {String} Suggested URL fragment for the new resource
 * @param isContainer {Boolean} Is the object being created a Container
 *            or Resource?
 * @param mimeType {String} Content Type of the data/payload
 * @method post
 * @return {Promise<SolidResponse>} Result of XHR POST (returns parsed response
 *     meta object) or an anonymous error object with status code
 */
SolidWebClient.prototype.post =
  function post (url, data, slug, isContainer, mimeType) {
    var resourceType
    if (isContainer) {
      resourceType = this.vocab.ldp('BasicContainer')
      // Force the right mime type for containers only
      mimeType = 'text/turtle'
    } else {
      resourceType = this.vocab.ldp('Resource')
      mimeType = mimeType || DEFAULT_MIME_TYPE
    }
    var options = {}
    options.headers = {}
    options.headers['Link'] = resourceType + '; rel="type"'
    options.headers['Content-Type'] = mimeType
    if (slug && slug.length > 0) {
      options.headers['Slug'] = slug
    }
    return this.solidRequest(url, 'POST', options, data)
  }
/**
 * Turns a given URL into a proxied version, using a proxy template
 * @method proxyUrl
 * @param url {String} Intended URL
 * @param proxyUrlTemplate {String}
 * @return {String}
 */
SolidWebClient.prototype.proxyUrl = function proxyUrl (url, proxyUrlTemplate) {
  proxyUrlTemplate = proxyUrlTemplate || this.config.proxyUrl
  return proxyUrlTemplate.replace('{uri}', encodeURIComponent(url))
}
/**
 * Updates an existing resource or creates a new resource by performing
 *   a Solid/LDP PUT operation to a specified container
 * @method put
 * @param url {String} URL of the resource to be updated/created
 * @param data {Object} Data/payload of the resource to be created or updated
 * @param [mimeType] {String} MIME Type of the resource to be created
 * @param [options] Options hashmap, see docs for `solidResponse()`
 * @return {Promise<SolidResponse>} Result of PUT operation (returns parsed
 *     response meta object if successful, rejects with an anonymous error
 *     status object if not successful)
 */
SolidWebClient.prototype.put = function put (url, data, mimeType, options) {
  options = options || {}
  options.headers = options.headers || {}
  mimeType = mimeType || DEFAULT_MIME_TYPE
  options.headers['Content-Type'] = mimeType
  return this.solidRequest(url, 'PUT', options, data)
}
/**
 * Sends a generic XHR request with the appropriate Solid headers,
 * and returns a promise that resolves to a parsed response.
 * @method solidRequest
 * @param url {String} URL of the request
 * @param method {String} HTTP Verb ('GET', 'PUT', etc)
 * @param [options] Options hashmap
 * @param [options.noCredentials=false] {Boolean} Don't use `withCredentials`
 * @param [options.forceProxy=false] {Boolean} Enforce using proxy URL if true
 * @param [options.headers={}] {Object} HTTP headers to send along
 *          with request
 * @param [options.proxyUrl=config.proxyUrl] {String} Proxy URL to use for
 *          CORS Requests.
 * @param [options.timeout=config.timeout] {Number} Request timeout in
 *          milliseconds.
 * @param [data] {Object} Optional data / payload
 * @throws {HttpError} Rejects with `httpError.HttpError` of the appropriate
 *   type
 * @return {Promise<SolidResponse>}
 */
SolidWebClient.prototype.solidRequest =
  function solidRequest (url, method, options, data) {
    options = options || {}
    options.headers = options.headers || {}
    if (this.auth && this.auth.accessToken) {
      options.headers['Authorization'] = 'Bearer ' + this.auth.accessToken
    }
    options.proxyUrl = options.proxyUrl || this.config.proxyUrl
    options.timeout = options.timeout || this.config.timeout
    if (this.needsProxy(url) || options.forceProxy) {
      url = this.proxyUrl(url)
    }
    var webClient = this
    return new Promise(function (resolve, reject) {
      var http = new XMLHttpRequest()
      http.open(method, url)
      if (!options.noCredentials) {
        http.withCredentials = true
      }
      for (var header in options.headers) {  // Add in optional headers
        http.setRequestHeader(header, options.headers[header])
      }
      if (options.timeout) {
        http.timeout = options.timeout
      }
      http.onload = function () {
        if (this.status >= 200 && this.status < 300) {
          resolve(webClient.createResponse(this, method))
        } else {
          reject(new HttpError(this.status, this.statusText, {xhr: this}))
        }
      }
      http.onerror = function () {
        reject(new HttpError(this.status, this.statusText, {xhr: this}))
      }
      if (typeof data === 'undefined' || !data) {
        http.send()
      } else {
        http.send(data)
      }
    })
  }

/**
 * Returns a web client instance (convenience constructor method).
 * Usage:
 *
 *   ```
 *   var rdf = require('rdflib')  // or other compatible library
 *   var webClient = require('solid-web-client')(rdf)
 *   ```
 * @param rdf
 * @param config
 * @returns {SolidWebClient}
 */
function getClient (rdf, config) {
  return new SolidWebClient(rdf, config)
}

module.exports = getClient
module.exports.SolidWebClient = SolidWebClient
<|endoftext|>


<commit_before>var cacheName = 'v1';

// Install the ServiceWorker
self.addEventListener('install', function(event) {
    event.waitUntil(
        // Open a cache
        caches.open(cacheName).then(function(cache) {
            // Define  what we want to cache
            return cache.addAll([
                '/',
                'index.html',
                'offline.html',
                'calc.htmk',
                'featured.html',
                'manifest.json',
                'assets/images/icons/favicon.opng',
                'assets/css/main.css',
                'assets/js/main.js',
                'assets/js/app.js',
                'assets/images/icons/icon-48.png',
                // '/assets/images/icons/icon-96.png',
                'assets/images/icons/icon-144.png',
                'assets/images/icons/icon-192.png',
                // '/assets/images/icons/icon-256.png',
                'assets/vendor/materialize/css/materialize.min.css',
                'assets/vendor/materialize/js/materialize.min.js',
                'assets/vendor/materialize/icons/material.css',
                'assets/vendor/materialize/icons/MaterialIcons-Regular.ttf',
                'assets/vendor/font-awesome/css/font-awesome.min.css',
                'assets/vendor/waves/css/waves.min.css',
                'assets/vendor/waves/js/waves.min.js',
                'assets/vendor/jquery/jquery.min.js'
            ]);
        })
    );
});

self.addEventListener('fetch', function(event) {
    event.respondWith(caches.match(event.request).catch(function() {
        return fetch(event.request);
    }).then(function(response) {
        caches.open(cacheName).then(function(cache) {
            cache.put(event.request, response);
        });
        return response.clone();
    }).catch(function() {
        return caches.match('offline.html');
    }));
});
<commit_msg>Update<commit_after>var cacheName = 'v1';

// Install the ServiceWorker
self.addEventListener('install', function(event) {
    event.waitUntil(
        // Open a cache
        caches.open(cacheName).then(function(cache) {
            // Define  what we want to cache
            return cache.addAll([
                '/',
                'index.html',
                'offline.html',
                'calc.html',
                'featured.html',
                'manifest.json',
                'assets/images/icons/favicon.opng',
                'assets/css/main.css',
                'assets/js/main.js',
                'assets/js/app.js',
                'assets/images/icons/icon-48.png',
                // '/assets/images/icons/icon-96.png',
                'assets/images/icons/icon-144.png',
                'assets/images/icons/icon-192.png',
                // '/assets/images/icons/icon-256.png',
                'assets/vendor/materialize/css/materialize.min.css',
                'assets/vendor/materialize/js/materialize.min.js',
                'assets/vendor/materialize/icons/material.css',
                'assets/vendor/materialize/icons/MaterialIcons-Regular.ttf',
                'assets/vendor/font-awesome/css/font-awesome.min.css',
                'assets/vendor/waves/css/waves.min.css',
                'assets/vendor/waves/js/waves.min.js',
                'assets/vendor/jquery/jquery.min.js'
            ]);
        })
    );
});

self.addEventListener('fetch', function(event) {
    event.respondWith(caches.match(event.request).catch(function() {
        return fetch(event.request);
    }).then(function(response) {
        caches.open(cacheName).then(function(cache) {
            cache.put(event.request, response);
        });
        return response.clone();
    }).catch(function() {
        return caches.match('offline.html');
    }));
});
<|endoftext|>


<commit_before>import { applyProp, attributes } from 'incremental-dom';
import debounce from 'debounce';
import render from './render';
import skate from 'skatejs';

const $debounce = Symbol();

// Defaults all properties to be linked to an attribute unless explicitly
// specified.
function createAttributeLinks (opts) {
  const props = opts.properties;
  Object.keys(props).forEach(function (name) {
    let prop = props[name];

    if (!prop) {
      prop = props[name] = {};
    }

    if (typeof prop.attribute === 'undefined') {
      prop.attribute = true;
      attributes[name] = applyProp;
    }
  });
}

// Creates properties that add / remove event listeners. This is mostly for
// seemless integration with React.
function createEventProperties (opts) {
  const props = opts.properties;
  opts.listeners.forEach(function (name) {
    props[`on${name}`] = {
      attribute: false,
      set (elem, data) {
        if (data.newValue === data.oldValue) {
          return;
        }

        if (data.oldValue) {
          elem.removeEventListener(name, data.oldValue);
        }

        if (data.newValue) {
          elem.addEventListener(name, data.newValue);
        }
      }
    };
  });
}

// Normalises options to safe defaults.
function ensureOpts (opts) {
  if (!opts.listeners) {
    opts.listeners = [];
  }

  if (!opts.properties) {
    opts.properties = {};
  }

  if (!opts.slots) {
    opts.slots = [];
  }
}

// Creates a safe default for the render option for properties.
function normalizePropertyRender (render) {
  if (typeof render === 'undefined') {
    return function (elem, data) {
      return data.newValue !== data.oldValue;
    };
  }
  if (typeof render === 'function') {
    return render;
  }
  return function () {
    return !!render;
  };
}

// Re-renders the component when a property is set, debouncing it so that it is
// only rendered once on the tail end of consecutive subsequent property sets.
function setStateOnPropertySet (opts) {
  const props = opts.properties;
  Object.keys(props).forEach(function (name) {
    const prop = props[name];
    const set = prop.set;
    const render = normalizePropertyRender(prop.render);
    prop.set = function (elem, data) {
      set && set(elem, data);
      if (render(elem, data)) {
        let deb = elem[$debounce];
        !deb && (deb = elem[$debounce] = debounce(skate.render, 1));
        deb(elem);
      }
    };
  });
}

// Wraps the provided render function with our custom renderer that does all
// the diffing / patching.
function wrapRender (opts) {
  opts.render = render(opts);
}

// Proxies skate() ensuring everything is set up before the component is
// registered and returned.
export default function (name, opts) {
  ensureOpts(opts);
  createAttributeLinks(opts);
  setStateOnPropertySet(opts);
  createEventProperties(opts);
  wrapRender(opts);
  return skate(name, opts);
}
<commit_msg>Explicitly import the version of Skate we expect until we can depend on a semver.<commit_after>import { applyProp, attributes } from 'incremental-dom';
import debounce from 'debounce';
import render from './render';
import skate from '../node_modules/skatejs/lib/index';

const $debounce = Symbol();

// Defaults all properties to be linked to an attribute unless explicitly
// specified.
function createAttributeLinks (opts) {
  const props = opts.properties;
  Object.keys(props).forEach(function (name) {
    let prop = props[name];

    if (!prop) {
      prop = props[name] = {};
    }

    if (typeof prop.attribute === 'undefined') {
      prop.attribute = true;
      attributes[name] = applyProp;
    }
  });
}

// Creates properties that add / remove event listeners. This is mostly for
// seemless integration with React.
function createEventProperties (opts) {
  const props = opts.properties;
  opts.listeners.forEach(function (name) {
    props[`on${name}`] = {
      attribute: false,
      set (elem, data) {
        if (data.newValue === data.oldValue) {
          return;
        }

        if (data.oldValue) {
          elem.removeEventListener(name, data.oldValue);
        }

        if (data.newValue) {
          elem.addEventListener(name, data.newValue);
        }
      }
    };
  });
}

// Normalises options to safe defaults.
function ensureOpts (opts) {
  if (!opts.listeners) {
    opts.listeners = [];
  }

  if (!opts.properties) {
    opts.properties = {};
  }

  if (!opts.slots) {
    opts.slots = [];
  }
}

// Creates a safe default for the render option for properties.
function normalizePropertyRender (render) {
  if (typeof render === 'undefined') {
    return function (elem, data) {
      return data.newValue !== data.oldValue;
    };
  }
  if (typeof render === 'function') {
    return render;
  }
  return function () {
    return !!render;
  };
}

// Re-renders the component when a property is set, debouncing it so that it is
// only rendered once on the tail end of consecutive subsequent property sets.
function setStateOnPropertySet (opts) {
  const props = opts.properties;
  Object.keys(props).forEach(function (name) {
    const prop = props[name];
    const set = prop.set;
    const render = normalizePropertyRender(prop.render);
    prop.set = function (elem, data) {
      set && set(elem, data);
      if (render(elem, data)) {
        let deb = elem[$debounce];
        !deb && (deb = elem[$debounce] = debounce(skate.render, 1));
        deb(elem);
      }
    };
  });
}

// Wraps the provided render function with our custom renderer that does all
// the diffing / patching.
function wrapRender (opts) {
  opts.render = render(opts);
}

// Proxies skate() ensuring everything is set up before the component is
// registered and returned.
export default function (name, opts) {
  ensureOpts(opts);
  createAttributeLinks(opts);
  setStateOnPropertySet(opts);
  createEventProperties(opts);
  wrapRender(opts);
  return skate(name, opts);
}
<|endoftext|>


<commit_before>var cacheName = 'v1';

// Install the ServiceWorker
self.addEventListener('install', function(event) {
    event.waitUntil(
        // Open a cache
        caches.open(cacheName).then(function(cache) {
            // Define  what we want to cache
            return cache.addAll([
                '/',
                'index.html',
                'offline.html',
                'calc.html',
                'featured.html',
                'manifest.json',
                'assets/images/icons/favicon.opng',
                'assets/css/main.css',
                'assets/js/main.js',
                'assets/js/app.js',
                'assets/images/icons/icon-48.png',
                // '/assets/images/icons/icon-96.png',
                'assets/images/icons/icon-144.png',
                'assets/images/icons/icon-192.png',
                // '/assets/images/icons/icon-256.png',
                'assets/vendor/materialize/css/materialize.min.css',
                'assets/vendor/materialize/js/materialize.min.js',
                'assets/vendor/materialize/icons/material.css',
                'assets/vendor/materialize/icons/MaterialIcons-Regular.ttf',
                'assets/vendor/font-awesome/css/font-awesome.min.css',
                'assets/vendor/waves/css/waves.min.css',
                'assets/vendor/waves/js/waves.min.js',
                'assets/vendor/jquery/jquery.min.js'
            ]);
        })
    );
});

self.addEventListener('fetch', function(event) {
    event.respondWith(caches.match(event.request).catch(function() {
        return fetch(event.request);
    }).then(function(response) {
        caches.open(cacheName).then(function(cache) {
            cache.put(event.request, response);
        });
        return response.clone();
    }).catch(function() {
        return caches.match('offline.html');
    }));
});
<commit_msg>t03 ServiceWorker<commit_after>var cacheName = 'v1';

// Install the ServiceWorker
self.addEventListener('install', function(event) {
    event.waitUntil(
        // Open a cache
        caches.open(cacheName).then(function(cache) {
            // Define  what we want to cache
            return cache.addAll([
                '/',
                'index.html',
                'offline.html',
                'calc.html',
                'featured.html',
                'manifest.json',
                'assets/images/icons/favicon.opng',
                'assets/css/main.css',
                'assets/js/main.js',
                'assets/js/app.js',
                'assets/images/icons/icon-48.png',
                // '/assets/images/icons/icon-96.png',
                'assets/images/icons/icon-144.png',
                'assets/images/icons/icon-192.png',
                // '/assets/images/icons/icon-256.png',
                'assets/vendor/materialize/css/materialize.min.css',
                'assets/vendor/materialize/js/materialize.min.js',
                'assets/vendor/materialize/icons/material.css',
                'assets/vendor/materialize/icons/MaterialIcons-Regular.ttf',
                'assets/vendor/font-awesome/css/font-awesome.min.css',
                'assets/vendor/waves/css/waves.min.css',
                'assets/vendor/waves/js/waves.min.js',
                'assets/vendor/jquery/jquery.min.js'
            ]);
        })
    );
});

// self.addEventListener('fetch', function(event) {
//     event.respondWith(caches.match(event.request).catch(function() {
//         return fetch(event.request);
//     }).then(function(response) {
//         caches.open(cacheName).then(function(cache) {
//             cache.put(event.request, response);
//         });
//         return response.clone();
//     }).catch(function() {
//         return caches.match('offline.html');
//     }));
// });

self.addEventListener('fetch', function(event) {

  event.respondWith(
    // Look for something in the cache that matches the request
    caches.match(event.request).then(function(response) {

      // If we find something, return it
      // Otherwise, use the network instead
      return response || fetch(event.request);
    })
  );
});<|endoftext|>


<commit_before>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import 'whatwg-fetch';
import React from 'react';
import ReactDOM from 'react-dom';
import deepForceUpdate from 'react-deep-force-update';
import queryString from 'query-string';
import { createPath } from 'history/PathUtils';
import App from './components/App';
import createFetch from './createFetch';
import history from './history';
import { updateMeta } from './DOMUtils';
import router from './router';

// Global (context) variables that can be easily accessed from any React component
// https://facebook.github.io/react/docs/context.html
const context = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: (...styles) => {
    // eslint-disable-next-line no-underscore-dangle
    const removeCss = styles.map(x => x._insertCss());
    return () => {
      removeCss.forEach(f => f());
    };
  },
  // Universal HTTP client
  fetch: createFetch(fetch, {
    baseUrl: window.App.apiUrl,
  }),
};

const container = document.getElementById('app');
let currentLocation = history.location;
let appInstance;

// Switch off the native scroll restoration behavior and handle it manually
// https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration
const scrollPositionsHistory = {};
if (window.history && 'scrollRestoration' in window.history) {
  window.history.scrollRestoration = 'manual';
}

// Re-render the app when window.location changes
async function onLocationChange(location, action) {
  // Remember the latest scroll position for the previous location
  scrollPositionsHistory[currentLocation.key] = {
    scrollX: window.pageXOffset,
    scrollY: window.pageYOffset,
  };
  // Delete stored scroll position for next page if any
  if (action === 'PUSH') {
    delete scrollPositionsHistory[location.key];
  }
  currentLocation = location;

  const isInitialRender = !action;
  try {
    // Traverses the list of routes in the order they are defined until
    // it finds the first route that matches provided URL path string
    // and whose action method returns anything other than `undefined`.
    const route = await router.resolve({
      ...context,
      pathname: location.pathname,
      query: queryString.parse(location.search),
    });

    // Prevent multiple page renders during the routing process
    if (currentLocation.key !== location.key) {
      return;
    }

    if (route.redirect) {
      history.replace(route.redirect);
      return;
    }

    const renderReactApp = isInitialRender ? ReactDOM.hydrate : ReactDOM.render;
    appInstance = renderReactApp(
      <App context={context}>{route.component}</App>,
      container,
      () => {
        if (isInitialRender) {
          const elem = document.getElementById('css');
          if (elem) elem.parentNode.removeChild(elem);
          return;
        }

        document.title = route.title;

        updateMeta('description', route.description);
        // Update necessary tags in <head> at runtime here, ie:
        // updateMeta('keywords', route.keywords);
        // updateCustomMeta('og:url', route.canonicalUrl);
        // updateCustomMeta('og:image', route.imageUrl);
        // updateLink('canonical', route.canonicalUrl);
        // etc.

        let scrollX = 0;
        let scrollY = 0;
        const pos = scrollPositionsHistory[location.key];
        if (pos) {
          scrollX = pos.scrollX;
          scrollY = pos.scrollY;
        } else {
          const targetHash = location.hash.substr(1);
          if (targetHash) {
            const target = document.getElementById(targetHash);
            if (target) {
              scrollY = window.pageYOffset + target.getBoundingClientRect().top;
            }
          }
        }

        // Restore the scroll position if it was saved into the state
        // or scroll to the given #hash anchor
        // or scroll to top of the page
        window.scrollTo(scrollX, scrollY);

        // Google Analytics tracking. Don't send 'pageview' event after
        // the initial rendering, as it was already sent
        if (window.ga) {
          window.ga('send', 'pageview', createPath(location));
        }
      },
    );
  } catch (error) {
    if (__DEV__) {
      throw error;
    }

    console.error(error);

    // Do a full page reload if error occurs during client-side navigation
    if (!isInitialRender && currentLocation.key === location.key) {
      window.location.reload();
    }
  }
}

// Handle client-side navigation by using HTML5 History API
// For more information visit https://github.com/mjackson/history#readme
history.listen(onLocationChange);
onLocationChange(currentLocation);

// Enable Hot Module Replacement (HMR)
if (module.hot) {
  module.hot.accept('./router', () => {
    if (appInstance && appInstance.updater.isMounted(appInstance)) {
      // Force-update the whole tree, including components that refuse to update
      deepForceUpdate(appInstance);
    }

    onLocationChange(currentLocation);
  });
}
<commit_msg>Fixes scroll to anchor on initial page load (#1518)<commit_after>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import 'whatwg-fetch';
import React from 'react';
import ReactDOM from 'react-dom';
import deepForceUpdate from 'react-deep-force-update';
import queryString from 'query-string';
import { createPath } from 'history/PathUtils';
import App from './components/App';
import createFetch from './createFetch';
import history from './history';
import { updateMeta } from './DOMUtils';
import router from './router';

// Global (context) variables that can be easily accessed from any React component
// https://facebook.github.io/react/docs/context.html
const context = {
  // Enables critical path CSS rendering
  // https://github.com/kriasoft/isomorphic-style-loader
  insertCss: (...styles) => {
    // eslint-disable-next-line no-underscore-dangle
    const removeCss = styles.map(x => x._insertCss());
    return () => {
      removeCss.forEach(f => f());
    };
  },
  // Universal HTTP client
  fetch: createFetch(fetch, {
    baseUrl: window.App.apiUrl,
  }),
};

const container = document.getElementById('app');
let currentLocation = history.location;
let appInstance;

const scrollPositionsHistory = {};

// Re-render the app when window.location changes
async function onLocationChange(location, action) {
  // Remember the latest scroll position for the previous location
  scrollPositionsHistory[currentLocation.key] = {
    scrollX: window.pageXOffset,
    scrollY: window.pageYOffset,
  };
  // Delete stored scroll position for next page if any
  if (action === 'PUSH') {
    delete scrollPositionsHistory[location.key];
  }
  currentLocation = location;

  const isInitialRender = !action;
  try {
    // Traverses the list of routes in the order they are defined until
    // it finds the first route that matches provided URL path string
    // and whose action method returns anything other than `undefined`.
    const route = await router.resolve({
      ...context,
      pathname: location.pathname,
      query: queryString.parse(location.search),
    });

    // Prevent multiple page renders during the routing process
    if (currentLocation.key !== location.key) {
      return;
    }

    if (route.redirect) {
      history.replace(route.redirect);
      return;
    }

    const renderReactApp = isInitialRender ? ReactDOM.hydrate : ReactDOM.render;
    appInstance = renderReactApp(
      <App context={context}>{route.component}</App>,
      container,
      () => {
        if (isInitialRender) {
          // Switch off the native scroll restoration behavior and handle it manually
          // https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration
          if (window.history && 'scrollRestoration' in window.history) {
            window.history.scrollRestoration = 'manual';
          }

          const elem = document.getElementById('css');
          if (elem) elem.parentNode.removeChild(elem);
          return;
        }

        document.title = route.title;

        updateMeta('description', route.description);
        // Update necessary tags in <head> at runtime here, ie:
        // updateMeta('keywords', route.keywords);
        // updateCustomMeta('og:url', route.canonicalUrl);
        // updateCustomMeta('og:image', route.imageUrl);
        // updateLink('canonical', route.canonicalUrl);
        // etc.

        let scrollX = 0;
        let scrollY = 0;
        const pos = scrollPositionsHistory[location.key];
        if (pos) {
          scrollX = pos.scrollX;
          scrollY = pos.scrollY;
        } else {
          const targetHash = location.hash.substr(1);
          if (targetHash) {
            const target = document.getElementById(targetHash);
            if (target) {
              scrollY = window.pageYOffset + target.getBoundingClientRect().top;
            }
          }
        }

        // Restore the scroll position if it was saved into the state
        // or scroll to the given #hash anchor
        // or scroll to top of the page
        window.scrollTo(scrollX, scrollY);

        // Google Analytics tracking. Don't send 'pageview' event after
        // the initial rendering, as it was already sent
        if (window.ga) {
          window.ga('send', 'pageview', createPath(location));
        }
      },
    );
  } catch (error) {
    if (__DEV__) {
      throw error;
    }

    console.error(error);

    // Do a full page reload if error occurs during client-side navigation
    if (!isInitialRender && currentLocation.key === location.key) {
      window.location.reload();
    }
  }
}

// Handle client-side navigation by using HTML5 History API
// For more information visit https://github.com/mjackson/history#readme
history.listen(onLocationChange);
onLocationChange(currentLocation);

// Enable Hot Module Replacement (HMR)
if (module.hot) {
  module.hot.accept('./router', () => {
    if (appInstance && appInstance.updater.isMounted(appInstance)) {
      // Force-update the whole tree, including components that refuse to update
      deepForceUpdate(appInstance);
    }

    onLocationChange(currentLocation);
  });
}
<|endoftext|>


<commit_before>// Software developed by Reginald Pierce for the RIT Honors Program
// Redistribution or use in any form without express permission
// of the RIT Honors Program and Reginald Pierce is strictly
// prohibited.

// All code is contained in the housing namespace
var housing = {

    /**
     * Starts the housing selection app
     *
     * Currently this function sets up tooltips for the data
     * and creates a navigation element.
     *
     * @param d3svg An SVG element in which the data will be drawn.
     */
    init: function(d3svg,nav,data) {
        // Set up fancy tooltips for the rooms
        housing.tooltip = d3.tip()
            .attr("class","tooltip")
            .html(housing.style.tooltip);
        d3svg.call(housing.tooltip);
        // Create navigation
        if( nav && data && data.length ) {
            var li = nav.selectAll("li")
                .data(data);
            li.exit().remove()
            li.enter().append("li")
                .append("a")
                    .attr("href","#")
                    .text(function(d){ return "Floor "+d.number; })
                    .on("click", function(d){
                        housing.load(data,d.number,d3svg);
                        return false;
                    });
        }
    },

    /**
     * Loads data for a floor
     *
     * The load function takes JSON data representing a floor
     * plan (with registration information) and displays the
     * data on the provided d3.js SVG element.
     *
     * @param data The JSON.  The format should follow from
     * 	/tests/sample.json
     * @param floor The floor to render
     * @param d3svg An SVG element in which to draw the data.
     */
    load: function(data,floor,d3svg) {
        // Sets up all the images so that the correct floor will always be visible
        housing.currentFloor = floor;
        var floorimgs = d3svg.selectAll("image");
        floorimgs.remove();
        var floorimgs = d3svg.selectAll("image")
                .data(data);
        var enterfloorimgs = floorimgs.enter();
        enterfloorimgs.append("image")
                .attr("x",0)
                .attr("y",0)
                .attr("width",1024)
                .attr("height",1325)
                .attr("xlink:href",housing.style.imghref)
                .attr("visibility",housing.style.imgvisibility);

        // Loop through the floors
        for( var i = 0; i < data.length; i += 1 ){
            // Find the floor we are looking for
            if( data[i].number == floor ){
                // Select all circles in the SVG canvas
                // and bind them to the rooms on the floor
                d3svg.selectAll(".circle").remove();
                var rooms = d3svg.selectAll(".circle")
                    .data(data[i].rooms)
                // Remove excess circles if needed
                rooms.exit().remove();
                // Add more circles if needed

                var group = rooms.enter().append("g")
                    .attr("transform",housing.style.transform)
                    .attr("class","circle");
                if(housing.tooltip){
                    group.on("mouseover",housing.tooltip.show)
                        .on("mouseout",housing.tooltip.hide);
                }
                group.append("circle")
                    .attr("r",housing.style.r)
                    .attr("fill",housing.style.color.empty)
                    .attr("stroke","black");
                var arc1 = d3.svg.arc()
                    .innerRadius(0)
                    .outerRadius(housing.style.r)
                    .startAngle(0)
                    .endAngle(housing.style.endAngle);
                group.append("path")
                    .attr("d", arc1)
                    .attr("fill",housing.style.color.partial);
                group.append("text")
                    .text(housing.style.title)
                    .attr("text-anchor","middle")
                    .attr("dy",housing.style.titleOffset);
            }
        }
    },

    /**
     * The style namespace contains functions to style d3 elements
     */
    style: {
        transform: function(d){ return "translate("+d.x+" "+d.y+")"; },
        x: function(d){ return d.x; },
        y: function(d){ return d.y; },
        color: function(d){
            if(typeof d === "undefined") {
                return housing.style.color;
            }
            // find color based on number of occupants
            if(0 == d.occupants){
                return housing.style.color.empty;
            }else if(d.occupants == d.capacity){
                return housing.style.color.full;
            }else{
                return housing.style.color.partial;
            }
        },
        r: function(d){ return 4*Math.sqrt(Math.abs(10.5*d.capacity - 1)); },
        endAngle: function(d){ return 6.28319 * d.occupants / d.capacity; },
        tooltip: function(d) {
            return ""+d.number+"";
        },
        title: function(d) {
            return d.number;
        },
        titleOffset: function(d) {
            return housing.style.r(d) + 10;
        },
        imghref: function(d) {
            return "/img/049-"+d.number+".png";
        },
        imgvisibility: function(d){
            if( housing.currentFloor == d.number ) {
                return "visible";
            } else {
                return "hidden";
            }
        }
    }
};
// Allow for statements of the form housing.style.color.empty
housing.style.color.empty = "#00ff00";
housing.style.color.partial = "#0000ff";
housing.style.color.full = "#ff0000";

<commit_msg>Fixed possible similar bug<commit_after>// Software developed by Reginald Pierce for the RIT Honors Program
// Redistribution or use in any form without express permission
// of the RIT Honors Program and Reginald Pierce is strictly
// prohibited.

// All code is contained in the housing namespace
var housing = {

    /**
     * Starts the housing selection app
     *
     * Currently this function sets up tooltips for the data
     * and creates a navigation element.
     *
     * @param d3svg An SVG element in which the data will be drawn.
     */
    init: function(d3svg,nav,data) {
        // Set up fancy tooltips for the rooms
        housing.tooltip = d3.tip()
            .attr("class","tooltip")
            .html(housing.style.tooltip);
        d3svg.call(housing.tooltip);
        // Create navigation
        if( nav && data && data.length ) {
            // In case it has been initialized before,
            // get rid of old buttons
            nav.selectAll("li").remove();
            // Add new buttons based on the data
            nav.selectAll("li")
                .data(data)
                    .append("li")
                    .append("a") // Each button is an  inside a

                        .attr("href","#")
                        .text(function(d){ return "Floor "+d.number; })
                        .on("click", function(d){
                            housing.load(data,d.number,d3svg);
                            return false;
                        });
        }
    },

    /**
     * Loads data for a floor
     *
     * The load function takes JSON data representing a floor
     * plan (with registration information) and displays the
     * data on the provided d3.js SVG element.
     *
     * @param data The JSON.  The format should follow from
     * 	/tests/sample.json
     * @param floor The floor to render
     * @param d3svg An SVG element in which to draw the data.
     */
    load: function(data,floor,d3svg) {
        // Sets up all the images so that the correct floor will always be visible
        housing.currentFloor = floor;
        var floorimgs = d3svg.selectAll("image");
        floorimgs.remove();
        var floorimgs = d3svg.selectAll("image")
                .data(data);
        var enterfloorimgs = floorimgs.enter();
        enterfloorimgs.append("image")
                .attr("x",0)
                .attr("y",0)
                .attr("width",1024)
                .attr("height",1325)
                .attr("xlink:href",housing.style.imghref)
                .attr("visibility",housing.style.imgvisibility);

        // Loop through the floors
        for( var i = 0; i < data.length; i += 1 ){
            // Find the floor we are looking for
            if( data[i].number == floor ){
                // Select all circles in the SVG canvas
                // and bind them to the rooms on the floor
                d3svg.selectAll(".circle").remove();
                var rooms = d3svg.selectAll(".circle")
                    .data(data[i].rooms)
                // Remove excess circles if needed
                rooms.exit().remove();
                // Add more circles if needed

                var group = rooms.enter().append("g")
                    .attr("transform",housing.style.transform)
                    .attr("class","circle");
                if(housing.tooltip){
                    group.on("mouseover",housing.tooltip.show)
                        .on("mouseout",housing.tooltip.hide);
                }
                group.append("circle")
                    .attr("r",housing.style.r)
                    .attr("fill",housing.style.color.empty)
                    .attr("stroke","black");
                var arc1 = d3.svg.arc()
                    .innerRadius(0)
                    .outerRadius(housing.style.r)
                    .startAngle(0)
                    .endAngle(housing.style.endAngle);
                group.append("path")
                    .attr("d", arc1)
                    .attr("fill",housing.style.color.partial);
                group.append("text")
                    .text(housing.style.title)
                    .attr("text-anchor","middle")
                    .attr("dy",housing.style.titleOffset);
            }
        }
    },

    /**
     * The style namespace contains functions to style d3 elements
     */
    style: {
        transform: function(d){ return "translate("+d.x+" "+d.y+")"; },
        x: function(d){ return d.x; },
        y: function(d){ return d.y; },
        color: function(d){
            if(typeof d === "undefined") {
                return housing.style.color;
            }
            // find color based on number of occupants
            if(0 == d.occupants){
                return housing.style.color.empty;
            }else if(d.occupants == d.capacity){
                return housing.style.color.full;
            }else{
                return housing.style.color.partial;
            }
        },
        r: function(d){ return 4*Math.sqrt(Math.abs(10.5*d.capacity - 1)); },
        endAngle: function(d){ return 6.28319 * d.occupants / d.capacity; },
        tooltip: function(d) {
            return ""+d.number+"";
        },
        title: function(d) {
            return d.number;
        },
        titleOffset: function(d) {
            return housing.style.r(d) + 10;
        },
        imghref: function(d) {
            return "/img/049-"+d.number+".png";
        },
        imgvisibility: function(d){
            if( housing.currentFloor == d.number ) {
                return "visible";
            } else {
                return "hidden";
            }
        }
    }
};
// Allow for statements of the form housing.style.color.empty
housing.style.color.empty = "#00ff00";
housing.style.color.partial = "#0000ff";
housing.style.color.full = "#ff0000";

<|endoftext|>


<commit_before>/**
 * impress.js
 *
 * impress.js is a presentation tool based on the power of CSS3 transforms and transitions
 * in modern browsers and inspired by the idea behind prezi.com.
 *
 * MIT Licensed.
 *
 * Copyright 2011 Bartek Szopka (@bartaz)
 */

(function ( document, window ) {
    'use strict';

    // HELPER FUNCTIONS

    var pfx = (function () {

        var style = document.createElement('dummy').style,
            prefixes = 'Webkit Moz O ms Khtml'.split(' '),
            memory = {};

        return function ( prop ) {
            if ( typeof memory[ prop ] === "undefined" ) {

                var ucProp  = prop.charAt(0).toUpperCase() + prop.substr(1),
                    props   = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');

                memory[ prop ] = null;
                for ( var i in props ) {
                    if ( style[ props[i] ] !== undefined ) {
                        memory[ prop ] = props[i];
                        break;
                    }
                }

            }

            return memory[ prop ];
        }

    })();

    var arrayify = function ( a ) {
        return [].slice.call( a );
    };

    var css = function ( el, props ) {
        var key, pkey;
        for ( key in props ) {
            if ( props.hasOwnProperty(key) ) {
                pkey = pfx(key);
                if ( pkey != null ) {
                    el.style[pkey] = props[key];
                }
            }
        }
        return el;
    }

    var byId = function ( id ) {
        return document.getElementById(id);
    }

    var $ = function ( selector, context ) {
        context = context || document;
        return context.querySelector(selector);
    };

    var $$ = function ( selector, context ) {
        context = context || document;
        return arrayify( context.querySelectorAll(selector) );
    };

    var translate = function ( t ) {
        return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
    };

    var rotate = function ( r, revert ) {
        var rX = " rotateX(" + r.x + "deg) ",
            rY = " rotateY(" + r.y + "deg) ",
            rZ = " rotateZ(" + r.z + "deg) ";

        return revert ? rZ+rY+rX : rX+rY+rZ;
    };

    var scale = function ( s ) {
        return " scale(" + s + ") ";
    };

    var getElementFromUrl = function () {
        // get id from url # by removing `#` or `#/` from the beginning,
        // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
        return byId( window.location.hash.replace(/^#\/?/,"") );
    };

    // CHECK SUPPORT

    var ua = navigator.userAgent.toLowerCase();
    var impressSupported = ( pfx("perspective") != null ) &&
                           ( ua.search(/(iphone)|(ipod)|(ipad)|(android)/) == -1 );

    var roots = {};

    var impress = window.impress = function ( rootId ) {

        rootId = rootId || "impress";

        // if already initialized just return the API
        if (roots["impress-root-" + rootId]) {
            return roots["impress-root-" + rootId];
        }

        // DOM ELEMENTS

        var root = byId( rootId );

        if (!impressSupported) {
            root.className = "impress-not-supported";
            return;
        } else {
            root.className = "";
        }

        var canvas = document.createElement("div");
        canvas.className = "canvas";

        arrayify( root.childNodes ).forEach(function ( el ) {
            canvas.appendChild( el );
        });
        root.appendChild(canvas);

        var steps = $$(".step", root);

        // SETUP
        // set initial values and defaults

        document.documentElement.style.height = "100%";

        css(document.body, {
            height: "100%",
            overflow: "hidden"
        });

        var props = {
            position: "absolute",
            transformOrigin: "top left",
            transition: "all 0s ease-in-out",
            transformStyle: "preserve-3d"
        }

        css(root, props);
        css(root, {
            top: "50%",
            left: "50%",
            perspective: "1000px"
        });
        css(canvas, props);

        var current = {
            translate: { x: 0, y: 0, z: 0 },
            rotate:    { x: 0, y: 0, z: 0 },
            scale:     1
        };

        var stepData = {};

        var isStep = function ( el ) {
            return !!(el && el.id && stepData["impress-" + el.id]);
        }

        steps.forEach(function ( el, idx ) {
            var data = el.dataset,
                step = {
                    translate: {
                        x: data.x || 0,
                        y: data.y || 0,
                        z: data.z || 0
                    },
                    rotate: {
                        x: data.rotateX || 0,
                        y: data.rotateY || 0,
                        z: data.rotateZ || data.rotate || 0
                    },
                    scale: data.scale || 1,
                    el: el
                };

            if ( !el.id ) {
                el.id = "step-" + (idx + 1);
            }

            stepData["impress-" + el.id] = step;

            css(el, {
                position: "absolute",
                transform: "translate(-50%,-50%)" +
                           translate(step.translate) +
                           rotate(step.rotate) +
                           scale(step.scale),
                transformStyle: "preserve-3d"
            });

        });

        // making given step active

        var active = null;
        var hashTimeout = null;

        var goto = function ( el ) {
            if ( !isStep(el) || el == active) {
                // selected element is not defined as step or is already active
                return false;
            }

            // Sometimes it's possible to trigger focus on first link with some keyboard action.
            // Browser in such a case tries to scroll the page to make this element visible
            // (even that body overflow is set to hidden) and it breaks our careful positioning.
            //
            // So, as a lousy (and lazy) workaround we will make the page scroll back to the top
            // whenever slide is selected
            //
            // If you are reading this and know any better way to handle it, I'll be glad to hear about it!
            window.scrollTo(0, 0);

            var step = stepData["impress-" + el.id];

            if ( active ) {
                active.classList.remove("active");
            }
            el.classList.add("active");

            root.className = "step-" + el.id;

            // `#/step-id` is used instead of `#step-id` to prevent default browser
            // scrolling to element in hash
            //
            // and it has to be set after animation finishes, because in chrome it
            // causes transtion being laggy
            window.clearTimeout( hashTimeout );
            hashTimeout = window.setTimeout(function () {
                window.location.hash = "#/" + el.id;
            }, 1000);

            var target = {
                rotate: {
                    x: -parseInt(step.rotate.x, 10),
                    y: -parseInt(step.rotate.y, 10),
                    z: -parseInt(step.rotate.z, 10)
                },
                translate: {
                    x: -step.translate.x,
                    y: -step.translate.y,
                    z: -step.translate.z
                },
                scale: 1 / parseFloat(step.scale)
            };

            // check if the transition is zooming in or not
            var zoomin = target.scale >= current.scale;

            // if presentation starts (nothing is active yet)
            // don't animate (set duration to 0)
            var duration = (active) ? "1s" : "0";

            css(root, {
                // to keep the perspective look similar for different scales
                // we need to 'scale' the perspective, too
                perspective: step.scale * 1000 + "px",
                transform: scale(target.scale),
                transitionDuration: duration,
                transitionDelay: (zoomin ? "500ms" : "0ms")
            });

            css(canvas, {
                transform: rotate(target.rotate, true) + translate(target.translate),
                transitionDuration: duration,
                transitionDelay: (zoomin ? "0ms" : "500ms")
            });

            current = target;
            active = el;

            return el;
        };

        var prev = function () {
            var prev = steps.indexOf( active ) - 1;
            prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ];

            return goto(prev);
        };

        var next = function () {
            var next = steps.indexOf( active ) + 1;
            next = next < steps.length ? steps[ next ] : steps[ 0 ];

            return goto(next);
        };

        window.addEventListener("hashchange", function () {
            goto( getElementFromUrl() );
        }, false);

        // START
        // by selecting step defined in url or first step of the presentation
        goto(getElementFromUrl() || steps[0]);

        return (roots[ "impress-root-" + rootId ] = {
            goto: goto,
            next: next,
            prev: prev
        });

    }
})(document, window);

// EVENTS

(function ( document, window ) {
    'use strict';

    // keyboard navigation handler
    document.addEventListener("keydown", function ( event ) {
        if ( event.keyCode == 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) {
            switch( event.keyCode ) {
                case 33: ; // pg up
                case 37: ; // left
                case 38:   // up
                         impress().prev();
                         break;
                case 9:  ; // tab
                case 32: ; // space
                case 34: ; // pg down
                case 39: ; // right
                case 40:   // down
                         impress().next();
                         break;
            }

            event.preventDefault();
        }
    }, false);

    // delegated handler for clicking on the links to presentation steps
    document.addEventListener("click", function ( event ) {
        // event delegation with "bubbling"
        // check if event target (or any of its parents is a link)
        var target = event.target;
        while ( (target.tagName != "A") &&
                (target != document.body) ) {
            target = target.parentNode;
        }

        if ( target.tagName == "A" ) {
            var href = target.getAttribute("href");

            // if it's a link to presentation step, target this step
            if ( href && href[0] == '#' ) {
                target = document.getElementById( href.slice(1) );
            }
        }

        if ( impress().goto(target) ) {
            event.stopImmediatePropagation();
            event.preventDefault();
        }
    }, false);

    // delegated handler for clicking on step elements
    document.addEventListener("click", function ( event ) {
        var target = event.target;
        // find closest step element
        while ( !target.classList.contains("step") &&
                (target != document.body) ) {
            target = target.parentNode;
        }

        if ( impress().goto(target) ) {
            event.preventDefault();
        }
    }, false);

})(document, window);

<commit_msg>impressive touch support<commit_after>/**
 * impress.js
 *
 * impress.js is a presentation tool based on the power of CSS3 transforms and transitions
 * in modern browsers and inspired by the idea behind prezi.com.
 *
 * MIT Licensed.
 *
 * Copyright 2011 Bartek Szopka (@bartaz)
 */

(function ( document, window ) {
    'use strict';

    // HELPER FUNCTIONS

    var pfx = (function () {

        var style = document.createElement('dummy').style,
            prefixes = 'Webkit Moz O ms Khtml'.split(' '),
            memory = {};

        return function ( prop ) {
            if ( typeof memory[ prop ] === "undefined" ) {

                var ucProp  = prop.charAt(0).toUpperCase() + prop.substr(1),
                    props   = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');

                memory[ prop ] = null;
                for ( var i in props ) {
                    if ( style[ props[i] ] !== undefined ) {
                        memory[ prop ] = props[i];
                        break;
                    }
                }

            }

            return memory[ prop ];
        }

    })();

    var arrayify = function ( a ) {
        return [].slice.call( a );
    };

    var css = function ( el, props ) {
        var key, pkey;
        for ( key in props ) {
            if ( props.hasOwnProperty(key) ) {
                pkey = pfx(key);
                if ( pkey != null ) {
                    el.style[pkey] = props[key];
                }
            }
        }
        return el;
    }

    var byId = function ( id ) {
        return document.getElementById(id);
    }

    var $ = function ( selector, context ) {
        context = context || document;
        return context.querySelector(selector);
    };

    var $$ = function ( selector, context ) {
        context = context || document;
        return arrayify( context.querySelectorAll(selector) );
    };

    var translate = function ( t ) {
        return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
    };

    var rotate = function ( r, revert ) {
        var rX = " rotateX(" + r.x + "deg) ",
            rY = " rotateY(" + r.y + "deg) ",
            rZ = " rotateZ(" + r.z + "deg) ";

        return revert ? rZ+rY+rX : rX+rY+rZ;
    };

    var scale = function ( s ) {
        return " scale(" + s + ") ";
    };

    var getElementFromUrl = function () {
        // get id from url # by removing `#` or `#/` from the beginning,
        // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
        return byId( window.location.hash.replace(/^#\/?/,"") );
    };

    // CHECK SUPPORT

    var ua = navigator.userAgent.toLowerCase();
    var impressSupported = ( pfx("perspective") != null ) &&
                           ( ua.search(/(iphone)|(ipod)|(ipad)|(android)/) == -1 );

    var roots = {};

    var impress = window.impress = function ( rootId ) {

        rootId = rootId || "impress";

        // if already initialized just return the API
        if (roots["impress-root-" + rootId]) {
            return roots["impress-root-" + rootId];
        }

        // DOM ELEMENTS

        var root = byId( rootId );

        if (!impressSupported) {
            root.className = "impress-not-supported";
            return;
        } else {
            root.className = "";
        }

        var canvas = document.createElement("div");
        canvas.className = "canvas";

        arrayify( root.childNodes ).forEach(function ( el ) {
            canvas.appendChild( el );
        });
        root.appendChild(canvas);

        var steps = $$(".step", root);

        // SETUP
        // set initial values and defaults

        document.documentElement.style.height = "100%";

        css(document.body, {
            height: "100%",
            overflow: "hidden"
        });

        var props = {
            position: "absolute",
            transformOrigin: "top left",
            transition: "all 0s ease-in-out",
            transformStyle: "preserve-3d"
        }

        css(root, props);
        css(root, {
            top: "50%",
            left: "50%",
            perspective: "1000px"
        });
        css(canvas, props);

        var current = {
            translate: { x: 0, y: 0, z: 0 },
            rotate:    { x: 0, y: 0, z: 0 },
            scale:     1
        };

        var stepData = {};

        var isStep = function ( el ) {
            return !!(el && el.id && stepData["impress-" + el.id]);
        }

        steps.forEach(function ( el, idx ) {
            var data = el.dataset,
                step = {
                    translate: {
                        x: data.x || 0,
                        y: data.y || 0,
                        z: data.z || 0
                    },
                    rotate: {
                        x: data.rotateX || 0,
                        y: data.rotateY || 0,
                        z: data.rotateZ || data.rotate || 0
                    },
                    scale: data.scale || 1,
                    el: el
                };

            if ( !el.id ) {
                el.id = "step-" + (idx + 1);
            }

            stepData["impress-" + el.id] = step;

            css(el, {
                position: "absolute",
                transform: "translate(-50%,-50%)" +
                           translate(step.translate) +
                           rotate(step.rotate) +
                           scale(step.scale),
                transformStyle: "preserve-3d"
            });

        });

        // making given step active

        var active = null;
        var hashTimeout = null;

        var goto = function ( el ) {
            if ( !isStep(el) || el == active) {
                // selected element is not defined as step or is already active
                return false;
            }

            // Sometimes it's possible to trigger focus on first link with some keyboard action.
            // Browser in such a case tries to scroll the page to make this element visible
            // (even that body overflow is set to hidden) and it breaks our careful positioning.
            //
            // So, as a lousy (and lazy) workaround we will make the page scroll back to the top
            // whenever slide is selected
            //
            // If you are reading this and know any better way to handle it, I'll be glad to hear about it!
            window.scrollTo(0, 0);

            var step = stepData["impress-" + el.id];

            if ( active ) {
                active.classList.remove("active");
            }
            el.classList.add("active");

            root.className = "step-" + el.id;

            // `#/step-id` is used instead of `#step-id` to prevent default browser
            // scrolling to element in hash
            //
            // and it has to be set after animation finishes, because in chrome it
            // causes transtion being laggy
            window.clearTimeout( hashTimeout );
            hashTimeout = window.setTimeout(function () {
                window.location.hash = "#/" + el.id;
            }, 1000);

            var target = {
                rotate: {
                    x: -parseInt(step.rotate.x, 10),
                    y: -parseInt(step.rotate.y, 10),
                    z: -parseInt(step.rotate.z, 10)
                },
                translate: {
                    x: -step.translate.x,
                    y: -step.translate.y,
                    z: -step.translate.z
                },
                scale: 1 / parseFloat(step.scale)
            };

            // check if the transition is zooming in or not
            var zoomin = target.scale >= current.scale;

            // if presentation starts (nothing is active yet)
            // don't animate (set duration to 0)
            var duration = (active) ? "1s" : "0";

            css(root, {
                // to keep the perspective look similar for different scales
                // we need to 'scale' the perspective, too
                perspective: step.scale * 1000 + "px",
                transform: scale(target.scale),
                transitionDuration: duration,
                transitionDelay: (zoomin ? "500ms" : "0ms")
            });

            css(canvas, {
                transform: rotate(target.rotate, true) + translate(target.translate),
                transitionDuration: duration,
                transitionDelay: (zoomin ? "0ms" : "500ms")
            });

            current = target;
            active = el;

            return el;
        };

        var prev = function () {
            var prev = steps.indexOf( active ) - 1;
            prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ];

            return goto(prev);
        };

        var next = function () {
            var next = steps.indexOf( active ) + 1;
            next = next < steps.length ? steps[ next ] : steps[ 0 ];

            return goto(next);
        };

        window.addEventListener("hashchange", function () {
            goto( getElementFromUrl() );
        }, false);

        // START
        // by selecting step defined in url or first step of the presentation
        goto(getElementFromUrl() || steps[0]);

        return (roots[ "impress-root-" + rootId ] = {
            goto: goto,
            next: next,
            prev: prev
        });

    }
})(document, window);

// EVENTS

(function ( document, window ) {
    'use strict';

    // keyboard navigation handler
    document.addEventListener("keydown", function ( event ) {
        if ( event.keyCode == 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) {
            switch( event.keyCode ) {
                case 33: ; // pg up
                case 37: ; // left
                case 38:   // up
                         impress().prev();
                         break;
                case 9:  ; // tab
                case 32: ; // space
                case 34: ; // pg down
                case 39: ; // right
                case 40:   // down
                         impress().next();
                         break;
            }

            event.preventDefault();
        }
    }, false);

    // delegated handler for clicking on the links to presentation steps
    document.addEventListener("click", function ( event ) {
        // event delegation with "bubbling"
        // check if event target (or any of its parents is a link)
        var target = event.target;
        while ( (target.tagName != "A") &&
                (target != document.body) ) {
            target = target.parentNode;
        }

        if ( target.tagName == "A" ) {
            var href = target.getAttribute("href");

            // if it's a link to presentation step, target this step
            if ( href && href[0] == '#' ) {
                target = document.getElementById( href.slice(1) );
            }
        }

        if ( impress().goto(target) ) {
            event.stopImmediatePropagation();
            event.preventDefault();
        }
    }, false);

    // delegated handler for clicking on step elements
    document.addEventListener("click", function ( event ) {
        var target = event.target;
        // find closest step element
        while ( !target.classList.contains("step") &&
                (target != document.body) ) {
            target = target.parentNode;
        }

        if ( impress().goto(target) ) {
            event.preventDefault();
        }
    }, false);

    // touch handler to detect taps on the left and right side of the screen
    document.addEventListener("touchstart", function ( event ) {
        if (event.touches.length === 1) {
            var x = event.touches[0].clientX,
                width = window.innerWidth * 0.3,
                result = null;

            if ( x < width ) {
                result = impress().prev();
            } else if ( x > window.innerWidth - width ) {
                result = impress().next();
            }

            if (result) {
                event.preventDefault();
            }
        }
    }, false);
})(document, window);

<|endoftext|>