<commit_before>'use strict';

const now = Date.now; // TODO: figure out how to utilize performance.now () properly – it's not as easy as it does not return a unix timestamp...
const microseconds = () => now () * 1000; // TODO: utilize performance.now for that purpose
const milliseconds = now;
const seconds = () => Math.floor (now () / 1000);
const uuidv1 = () => {
    const biasSeconds = 12219292800; // seconds from 15th Oct 1572 to Jan 1st 1970
    const bias = biasSeconds * 10000000;  // in hundreds of nanoseconds
    const time = microseconds () * 10 + bias;
    const timeHex = time.toString (16);
    const arranged = timeHex.slice (7, 15) + timeHex.slice (3, 7) + '1' + timeHex.slice (0, 3);
    // these should be random, but we're not making more than 10 requests per microsecond so who cares
    const clockId = '9696'; // a 14 bit number
    const macAddress = 'ff'.repeat (6);
    return arranged + clockId + macAddress;
};

const setTimeout_original = setTimeout;
const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = now () + ms) => {
    // avoid MAX_INT issue https://stackoverflow.com/questions/60474110
    if (ms >= 2147483647) {
        throw new Exception ('setTimeout() function was called with unrealistic value of ' + ms.toString ());
    }
    // The built-in setTimeout function can fire its callback earlier than specified, so we
    // need to ensure that it does not happen: sleep recursively until `targetTime` is reached...
    let clearInnerTimeout = () => {};
    let active = true;
    const id = setTimeout (() => {
        active = true;
        const rest = targetTime - now ();
        if (rest > 0) {
            clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime); // try sleep more
        } else {
            done ();
        }
    }, ms);
    return function clear () {
        if (active) {
            active = false; // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion)
            clearTimeout (id);
        }
        clearInnerTimeout ();
    };
};

class TimedOut extends Error {
    constructor () {
        const message = 'timed out';
        super (message);
        this.constructor = TimedOut;
        this.__proto__  = TimedOut.prototype;
        this.message = message;
    };
}

const iso8601 = (timestamp) => {
    let _timestampNumber = undefined;
    if (typeof timestamp === 'number') {
        _timestampNumber = Math.floor (timestamp);
    } else {
        _timestampNumber = parseInt (timestamp, 10);
    }
    // undefined, null and lots of nasty non-numeric values yield NaN
    if (Number.isNaN (_timestampNumber) || _timestampNumber < 0) {
        return undefined;
    }
    // last line of defence
    try {
        return new Date (_timestampNumber).toISOString ();
    } catch (e) {
        return undefined;
    }
}

const parse8601 = (x) => {
    if (typeof x !== 'string' || !x) {
        return undefined;
    }
    if (x.match (/^[0-9]+$/)) {
        // a valid number in a string, not a date.
        return undefined;
    }
    if (x.indexOf ('-') < 0 || x.indexOf (':') < 0) { // no date can be without a dash and a colon
        return undefined;
    }
    // last line of defence
    try {
        const candidate = Date.parse (((x.indexOf ('+') >= 0) || (x.slice (-1) === 'Z')) ? x : (x + 'Z').replace (/\s(\d\d):/, 'T$1:'));
        if (Number.isNaN (candidate)) {
            return undefined;
        }
        return candidate;
    } catch (e) {
        return undefined;
    }
}

const parseDate = (x) => {
    if (typeof x !== 'string' || !x) {
        return undefined;
    }
    if (x.indexOf ('GMT') >= 0) {
        try {
            return Date.parse (x);
        } catch (e) {
            return undefined;
        }
    }
    return parse8601 (x);
}

const rfc2616 = (timestamp = undefined) => new Date (timestamp).toUTCString ();

const mdy = (timestamp, infix = '-') => {
    infix = infix || ''
    const date = new Date (timestamp)
    const Y = date.getUTCFullYear ().toString ()
    let m = date.getUTCMonth () + 1
    let d = date.getUTCDate ()
    m = m < 10 ? ('0' + m) : m.toString ()
    d = d < 10 ? ('0' + d) : d.toString ()
    return m + infix + d + infix + Y
}

const ymd = (timestamp, infix, fullYear = true) => {
    infix = infix || ''
    const date = new Date (timestamp)
    const intYear = date.getUTCFullYear ()
    const year = fullYear ? intYear : (intYear - 2000)
    const Y = year.toString ()
    let m = date.getUTCMonth () + 1
    let d = date.getUTCDate ()
    m = m < 10 ? ('0' + m) : m.toString ()
    d = d < 10 ? ('0' + d) : d.toString ()
    return Y + infix + m + infix + d
}

const yymmdd = (timestamp, infix = '') => ymd (timestamp, infix, false)
const yyyymmdd = (timestamp, infix = '-') => ymd (timestamp, infix, true)

const ymdhms = (timestamp, infix = ' ') => {
    const date = new Date (timestamp)
    const Y = date.getUTCFullYear ()
    let m = date.getUTCMonth () + 1
    let d = date.getUTCDate ()
    let H = date.getUTCHours ()
    let M = date.getUTCMinutes ()
    let S = date.getUTCSeconds ()
    m = m < 10 ? ('0' + m) : m
    d = d < 10 ? ('0' + d) : d
    H = H < 10 ? ('0' + H) : H
    M = M < 10 ? ('0' + M) : M
    S = S < 10 ? ('0' + S) : S
    return Y + '-' + m + '-' + d + infix + H + ':' + M + ':' + S
}

module.exports = {
    now
    , microseconds
    , milliseconds
    , seconds
    , iso8601
    , parse8601
    , rfc2616
    , uuidv1
    , parseDate
    , mdy
    , ymd
    , yymmdd
    , yyyymmdd
    , ymdhms
    , setTimeout_safe
    , sleep: (ms) => new Promise ((resolve) => setTimeout_safe (resolve, ms))
    , TimedOut
    , timeout: async (ms, promise) => {
        let clear = () => {}
        const expires = new Promise ((resolve) => (clear = setTimeout_safe (resolve, ms)))

        try {
            return await Promise.race ([promise, expires.then (() => { throw new TimedOut () })])
        } finally {
            clear () // fixes https://github.com/ccxt/ccxt/issues/749
        }
    }
}
<commit_msg>timeout fix<commit_after>'use strict';

const now = Date.now; // TODO: figure out how to utilize performance.now () properly – it's not as easy as it does not return a unix timestamp...
const microseconds = () => now () * 1000; // TODO: utilize performance.now for that purpose
const milliseconds = now;
const seconds = () => Math.floor (now () / 1000);
const uuidv1 = () => {
    const biasSeconds = 12219292800; // seconds from 15th Oct 1572 to Jan 1st 1970
    const bias = biasSeconds * 10000000;  // in hundreds of nanoseconds
    const time = microseconds () * 10 + bias;
    const timeHex = time.toString (16);
    const arranged = timeHex.slice (7, 15) + timeHex.slice (3, 7) + '1' + timeHex.slice (0, 3);
    // these should be random, but we're not making more than 10 requests per microsecond so who cares
    const clockId = '9696'; // a 14 bit number
    const macAddress = 'ff'.repeat (6);
    return arranged + clockId + macAddress;
};

const setTimeout_original = setTimeout;
const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = now () + ms) => {
    // avoid MAX_INT issue https://github.com/ccxt/ccxt/issues/10761
    if (ms >= 2147483647) {
        throw new Error ('setTimeout() function was called with unrealistic value of ' + ms.toString ());
    }
    // The built-in setTimeout function can fire its callback earlier than specified, so we
    // need to ensure that it does not happen: sleep recursively until `targetTime` is reached...
    let clearInnerTimeout = () => {};
    let active = true;
    const id = setTimeout (() => {
        active = true;
        const rest = targetTime - now ();
        if (rest > 0) {
            clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime); // try sleep more
        } else {
            done ();
        }
    }, ms);
    return function clear () {
        if (active) {
            active = false; // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion)
            clearTimeout (id);
        }
        clearInnerTimeout ();
    };
};

class TimedOut extends Error {
    constructor () {
        const message = 'timed out';
        super (message);
        this.constructor = TimedOut;
        this.__proto__  = TimedOut.prototype;
        this.message = message;
    };
}

const iso8601 = (timestamp) => {
    let _timestampNumber = undefined;
    if (typeof timestamp === 'number') {
        _timestampNumber = Math.floor (timestamp);
    } else {
        _timestampNumber = parseInt (timestamp, 10);
    }
    // undefined, null and lots of nasty non-numeric values yield NaN
    if (Number.isNaN (_timestampNumber) || _timestampNumber < 0) {
        return undefined;
    }
    // last line of defence
    try {
        return new Date (_timestampNumber).toISOString ();
    } catch (e) {
        return undefined;
    }
}

const parse8601 = (x) => {
    if (typeof x !== 'string' || !x) {
        return undefined;
    }
    if (x.match (/^[0-9]+$/)) {
        // a valid number in a string, not a date.
        return undefined;
    }
    if (x.indexOf ('-') < 0 || x.indexOf (':') < 0) { // no date can be without a dash and a colon
        return undefined;
    }
    // last line of defence
    try {
        const candidate = Date.parse (((x.indexOf ('+') >= 0) || (x.slice (-1) === 'Z')) ? x : (x + 'Z').replace (/\s(\d\d):/, 'T$1:'));
        if (Number.isNaN (candidate)) {
            return undefined;
        }
        return candidate;
    } catch (e) {
        return undefined;
    }
}

const parseDate = (x) => {
    if (typeof x !== 'string' || !x) {
        return undefined;
    }
    if (x.indexOf ('GMT') >= 0) {
        try {
            return Date.parse (x);
        } catch (e) {
            return undefined;
        }
    }
    return parse8601 (x);
}

const rfc2616 = (timestamp = undefined) => new Date (timestamp).toUTCString ();

const mdy = (timestamp, infix = '-') => {
    infix = infix || ''
    const date = new Date (timestamp)
    const Y = date.getUTCFullYear ().toString ()
    let m = date.getUTCMonth () + 1
    let d = date.getUTCDate ()
    m = m < 10 ? ('0' + m) : m.toString ()
    d = d < 10 ? ('0' + d) : d.toString ()
    return m + infix + d + infix + Y
}

const ymd = (timestamp, infix, fullYear = true) => {
    infix = infix || ''
    const date = new Date (timestamp)
    const intYear = date.getUTCFullYear ()
    const year = fullYear ? intYear : (intYear - 2000)
    const Y = year.toString ()
    let m = date.getUTCMonth () + 1
    let d = date.getUTCDate ()
    m = m < 10 ? ('0' + m) : m.toString ()
    d = d < 10 ? ('0' + d) : d.toString ()
    return Y + infix + m + infix + d
}

const yymmdd = (timestamp, infix = '') => ymd (timestamp, infix, false)
const yyyymmdd = (timestamp, infix = '-') => ymd (timestamp, infix, true)

const ymdhms = (timestamp, infix = ' ') => {
    const date = new Date (timestamp)
    const Y = date.getUTCFullYear ()
    let m = date.getUTCMonth () + 1
    let d = date.getUTCDate ()
    let H = date.getUTCHours ()
    let M = date.getUTCMinutes ()
    let S = date.getUTCSeconds ()
    m = m < 10 ? ('0' + m) : m
    d = d < 10 ? ('0' + d) : d
    H = H < 10 ? ('0' + H) : H
    M = M < 10 ? ('0' + M) : M
    S = S < 10 ? ('0' + S) : S
    return Y + '-' + m + '-' + d + infix + H + ':' + M + ':' + S
}

module.exports = {
    now
    , microseconds
    , milliseconds
    , seconds
    , iso8601
    , parse8601
    , rfc2616
    , uuidv1
    , parseDate
    , mdy
    , ymd
    , yymmdd
    , yyyymmdd
    , ymdhms
    , setTimeout_safe
    , sleep: (ms) => new Promise ((resolve) => setTimeout_safe (resolve, ms))
    , TimedOut
    , timeout: async (ms, promise) => {
        let clear = () => {}
        const expires = new Promise ((resolve) => (clear = setTimeout_safe (resolve, ms)))

        try {
            return await Promise.race ([promise, expires.then (() => { throw new TimedOut () })])
        } finally {
            clear () // fixes https://github.com/ccxt/ccxt/issues/749
        }
    }
}
<|endoftext|>


<commit_before>var http = require('http');
var url = require('url');
var fs = require('fs');

var winston = require('winston');
var connect = require('connect');

var DocumentHandler = require('./lib/document_handler');

// Load the configuration and set some defaults
var config = JSON.parse(fs.readFileSync('config.js', 'utf8'));
config.port = config.port || 7777;
config.host = config.host || 'localhost';

// Set up the logger
if (config.logging) {
  try {
    winston.remove(winston.transports.Console);
  } catch(er) { }
  var detail, type;
  for (var i = 0; i < config.logging.length; i++) {
    detail = config.logging[i];
    type = detail.type;
    delete detail.type;
    winston.add(winston.transports[type], detail);
  }
}

// build the store from the config on-demand - so that we don't load it
// for statics
if (!config.storage) {
  config.storage = { type: 'file' };
}
if (!config.storage.type) {
  config.storage.type = 'file';
}
var Store = require('./lib/' + config.storage.type + '_document_store');
var preferredStore = new Store(config.storage);

// Send the static documents into the preferred store, skipping expirations
for (var name in config.documents) {
  var path = config.documents[name];
  fs.readFile(path, 'utf8', function(err, data) {
    if (data && !err) {
      preferredStore.set(name, data, function(cb) {
        winston.info('loaded static document', { name: name, path: path });
      }, true);
    }
    else {
      winston.warn('failed to load static document', { name: name, path: path });
    }
  });
}

// Configure the document handler
var documentHandler = new DocumentHandler({
  store: preferredStore,
  maxLength: config.maxLength,
  keyLength: config.keyLength
});

// Set the server up with a static cache
connect.createServer(
  connect.router(function(app) {
    // add documents
    app.post('/documents', function(request, response, next) {
      return documentHandler.handlePost(request, response);
    });
    // get documents
    app.get('/documents/:id', function(request, response, next) {
      return documentHandler.handleGet(req.params.id, response);
    });
  }),
  // Otherwise, static
  connect.staticCache(),
  connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }),
  // Then we can loop back - and change these into '/' TODO
  connect.router(function(app) {
    app.get('/:id', function(request, response, next) {
      request.url = request.originalUrl = '/';
      next();
    });
  }),
  // Static
  connect.staticCache(),
  connect.static(__dirname + '/static', { maxAge: config.staticMaxAge })
).listen(config.port, config.host);

winston.info('listening on ' + config.host + ':' + config.port);
<commit_msg>Fixed bug that was preventing document lookup<commit_after>var http = require('http');
var url = require('url');
var fs = require('fs');

var winston = require('winston');
var connect = require('connect');

var DocumentHandler = require('./lib/document_handler');

// Load the configuration and set some defaults
var config = JSON.parse(fs.readFileSync('config.js', 'utf8'));
config.port = config.port || 7777;
config.host = config.host || 'localhost';

// Set up the logger
if (config.logging) {
  try {
    winston.remove(winston.transports.Console);
  } catch(er) { }
  var detail, type;
  for (var i = 0; i < config.logging.length; i++) {
    detail = config.logging[i];
    type = detail.type;
    delete detail.type;
    winston.add(winston.transports[type], detail);
  }
}

// build the store from the config on-demand - so that we don't load it
// for statics
if (!config.storage) {
  config.storage = { type: 'file' };
}
if (!config.storage.type) {
  config.storage.type = 'file';
}
var Store = require('./lib/' + config.storage.type + '_document_store');
var preferredStore = new Store(config.storage);

// Send the static documents into the preferred store, skipping expirations
for (var name in config.documents) {
  var path = config.documents[name];
  fs.readFile(path, 'utf8', function(err, data) {
    if (data && !err) {
      preferredStore.set(name, data, function(cb) {
        winston.info('loaded static document', { name: name, path: path });
      }, true);
    }
    else {
      winston.warn('failed to load static document', { name: name, path: path });
    }
  });
}

// Configure the document handler
var documentHandler = new DocumentHandler({
  store: preferredStore,
  maxLength: config.maxLength,
  keyLength: config.keyLength
});

// Set the server up with a static cache
connect.createServer(
  connect.router(function(app) {
    // add documents
    app.post('/documents', function(request, response, next) {
      return documentHandler.handlePost(request, response);
    });
    // get documents
    app.get('/documents/:id', function(request, response, next) {
      return documentHandler.handleGet(request.params.id, response);
    });
  }),
  // Otherwise, static
  connect.staticCache(),
  connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }),
  // Then we can loop back - and change these into '/' TODO
  connect.router(function(app) {
    app.get('/:id', function(request, response, next) {
      request.url = request.originalUrl = '/';
      next();
    });
  }),
  // Static
  connect.staticCache(),
  connect.static(__dirname + '/static', { maxAge: config.staticMaxAge })
).listen(config.port, config.host);

winston.info('listening on ' + config.host + ':' + config.port);
<|endoftext|>


<commit_before>/* =============================================================
 * bootstrap-typeahead.js v2.0.1
 * http://twitter.github.com/bootstrap/javascript.html#typeahead
 * =============================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */

!function( $ ){

  "use strict"

  var Typeahead = function ( element, options ) {
    this.$element = $(element)
    this.options = $.extend({}, $.fn.typeahead.defaults, options)
    this.matcher = this.options.matcher || this.matcher
    this.sorter = this.options.sorter || this.sorter
    this.highlighter = this.options.highlighter || this.highlighter
    this.$menu = $(this.options.menu).appendTo('body')
    this.source = this.options.source
    this.shown = false
    this.listen()
  }

  Typeahead.prototype = {

    constructor: Typeahead

  , select: function () {
      var val = this.$menu.find('.active').attr('data-value')
      this.$element.val(val)
      return this.hide()
    }

  , show: function () {
      var pos = $.extend({}, this.$element.offset(), {
        height: this.$element[0].offsetHeight
      })

      this.$menu.css({
        top: pos.top + pos.height
      , left: pos.left
      })

      this.$menu.show()
      this.shown = true
      return this
    }

  , hide: function () {
      this.$menu.hide()
      this.shown = false
      return this
    }

  , lookup: function (event) {
      var that = this
        , items
        , q

      this.query = this.$element.val()

      if (!this.query) {
        return this.shown ? this.hide() : this
      }

      items = $.grep(this.source, function (item) {
        if (that.matcher(item)) return item
      })

      items = this.sorter(items)

      if (!items.length) {
        return this.shown ? this.hide() : this
      }

      return this.render(items.slice(0, this.options.items)).show()
    }

  , matcher: function (item) {
      return ~item.toLowerCase().indexOf(this.query.toLowerCase())
    }

  , sorter: function (items) {
      var beginswith = []
        , caseSensitive = []
        , caseInsensitive = []
        , item

      while (item = items.shift()) {
        if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
        else if (~item.indexOf(this.query)) caseSensitive.push(item)
        else caseInsensitive.push(item)
      }

      return beginswith.concat(caseSensitive, caseInsensitive)
    }

  , highlighter: function (item) {
      return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
        return '' + match + ''
      })
    }

  , render: function (items) {
      var that = this

      items = $(items).map(function (i, item) {
        i = $(that.options.item).attr('data-value', item)
        i.find('a').html(that.highlighter(item))
        return i[0]
      })

      items.first().addClass('active')
      this.$menu.html(items)
      return this
    }

  , next: function (event) {
      var active = this.$menu.find('.active').removeClass('active')
        , next = active.next()

      if (!next.length) {
        next = $(this.$menu.find('li')[0])
      }

      next.addClass('active')
    }

  , prev: function (event) {
      var active = this.$menu.find('.active').removeClass('active')
        , prev = active.prev()

      if (!prev.length) {
        prev = this.$menu.find('li').last()
      }

      prev.addClass('active')
    }

  , listen: function () {
      this.$element
        .on('blur',     $.proxy(this.blur, this))
        .on('keypress', $.proxy(this.keypress, this))
        .on('keyup',    $.proxy(this.keyup, this))

      if ($.browser.webkit || $.browser.msie) {
        this.$element.on('keydown', $.proxy(this.keypress, this))
      }

      this.$menu
        .on('click', $.proxy(this.click, this))
        .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
    }

  , keyup: function (e) {
      e.stopPropagation()
      e.preventDefault()

      switch(e.keyCode) {
        case 40: // down arrow
        case 38: // up arrow
          break

        case 9: // tab
        case 13: // enter
          if (!this.shown) return
          this.select()
          break

        case 27: // escape
          this.hide()
          break

        default:
          this.lookup()
      }

  }

  , keypress: function (e) {
      e.stopPropagation()
      if (!this.shown) return

      switch(e.keyCode) {
        case 9: // tab
        case 13: // enter
        case 27: // escape
          e.preventDefault()
          break

        case 38: // up arrow
          e.preventDefault()
          this.prev()
          break

        case 40: // down arrow
          e.preventDefault()
          this.next()
          break
      }
    }

  , blur: function (e) {
      var that = this
      e.stopPropagation()
      e.preventDefault()
      setTimeout(function () { that.hide() }, 150)
    }

  , click: function (e) {
      e.stopPropagation()
      e.preventDefault()
      this.select()
    }

  , mouseenter: function (e) {
      this.$menu.find('.active').removeClass('active')
      $(e.currentTarget).addClass('active')
    }

  }

  /* TYPEAHEAD PLUGIN DEFINITION
   * =========================== */

  $.fn.typeahead = function ( option ) {
    return this.each(function () {
      var $this = $(this)
        , data = $this.data('typeahead')
        , options = typeof option == 'object' && option
      if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.typeahead.defaults = {
    source: []
  , items: 8
  , menu: '

'
  , item: '

'
  }

  $.fn.typeahead.Constructor = Typeahead

 /* TYPEAHEAD DATA-API
  * ================== */

  $(function () {
    $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
      var $this = $(this)
      if ($this.data('typeahead')) return
      e.preventDefault()
      $this.typeahead($this.data())
    })
  })

}( window.jQuery );<commit_msg>Allow events to propagate / default if not used.  - All key events when the menu is not shown now propagate.  - Blur is also allowed to propagate.<commit_after>/* =============================================================
 * bootstrap-typeahead.js v2.0.1
 * http://twitter.github.com/bootstrap/javascript.html#typeahead
 * =============================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */

!function( $ ){

  "use strict"

  var Typeahead = function ( element, options ) {
    this.$element = $(element)
    this.options = $.extend({}, $.fn.typeahead.defaults, options)
    this.matcher = this.options.matcher || this.matcher
    this.sorter = this.options.sorter || this.sorter
    this.highlighter = this.options.highlighter || this.highlighter
    this.$menu = $(this.options.menu).appendTo('body')
    this.source = this.options.source
    this.shown = false
    this.listen()
  }

  Typeahead.prototype = {

    constructor: Typeahead

  , select: function () {
      var val = this.$menu.find('.active').attr('data-value')
      this.$element.val(val)
      return this.hide()
    }

  , show: function () {
      var pos = $.extend({}, this.$element.offset(), {
        height: this.$element[0].offsetHeight
      })

      this.$menu.css({
        top: pos.top + pos.height
      , left: pos.left
      })

      this.$menu.show()
      this.shown = true
      return this
    }

  , hide: function () {
      this.$menu.hide()
      this.shown = false
      return this
    }

  , lookup: function (event) {
      var that = this
        , items
        , q

      this.query = this.$element.val()

      if (!this.query) {
        return this.shown ? this.hide() : this
      }

      items = $.grep(this.source, function (item) {
        if (that.matcher(item)) return item
      })

      items = this.sorter(items)

      if (!items.length) {
        return this.shown ? this.hide() : this
      }

      return this.render(items.slice(0, this.options.items)).show()
    }

  , matcher: function (item) {
      return ~item.toLowerCase().indexOf(this.query.toLowerCase())
    }

  , sorter: function (items) {
      var beginswith = []
        , caseSensitive = []
        , caseInsensitive = []
        , item

      while (item = items.shift()) {
        if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
        else if (~item.indexOf(this.query)) caseSensitive.push(item)
        else caseInsensitive.push(item)
      }

      return beginswith.concat(caseSensitive, caseInsensitive)
    }

  , highlighter: function (item) {
      return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
        return '' + match + ''
      })
    }

  , render: function (items) {
      var that = this

      items = $(items).map(function (i, item) {
        i = $(that.options.item).attr('data-value', item)
        i.find('a').html(that.highlighter(item))
        return i[0]
      })

      items.first().addClass('active')
      this.$menu.html(items)
      return this
    }

  , next: function (event) {
      var active = this.$menu.find('.active').removeClass('active')
        , next = active.next()

      if (!next.length) {
        next = $(this.$menu.find('li')[0])
      }

      next.addClass('active')
    }

  , prev: function (event) {
      var active = this.$menu.find('.active').removeClass('active')
        , prev = active.prev()

      if (!prev.length) {
        prev = this.$menu.find('li').last()
      }

      prev.addClass('active')
    }

  , listen: function () {
      this.$element
        .on('blur',     $.proxy(this.blur, this))
        .on('keypress', $.proxy(this.keypress, this))
        .on('keyup',    $.proxy(this.keyup, this))

      if ($.browser.webkit || $.browser.msie) {
        this.$element.on('keydown', $.proxy(this.keypress, this))
      }

      this.$menu
        .on('click', $.proxy(this.click, this))
        .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
    }

  , keyup: function (e) {
      switch(e.keyCode) {
        case 40: // down arrow
        case 38: // up arrow
          break

        case 9: // tab
        case 13: // enter
          if (!this.shown) return
          this.select()
          break

        case 27: // escape
          this.hide()
          break

        default:
          this.lookup()
      }

      e.stopPropagation()
      e.preventDefault()
  }

  , keypress: function (e) {
      if (!this.shown) return

      switch(e.keyCode) {
        case 9: // tab
        case 13: // enter
        case 27: // escape
          e.preventDefault()
          break

        case 38: // up arrow
          e.preventDefault()
          this.prev()
          break

        case 40: // down arrow
          e.preventDefault()
          this.next()
          break
      }

      e.stopPropagation()
    }

  , blur: function (e) {
      var that = this
      setTimeout(function () { that.hide() }, 150)
    }

  , click: function (e) {
      e.stopPropagation()
      e.preventDefault()
      this.select()
    }

  , mouseenter: function (e) {
      this.$menu.find('.active').removeClass('active')
      $(e.currentTarget).addClass('active')
    }

  }

  /* TYPEAHEAD PLUGIN DEFINITION
   * =========================== */

  $.fn.typeahead = function ( option ) {
    return this.each(function () {
      var $this = $(this)
        , data = $this.data('typeahead')
        , options = typeof option == 'object' && option
      if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.typeahead.defaults = {
    source: []
  , items: 8
  , menu: '

'
  , item: '

'
  }

  $.fn.typeahead.Constructor = Typeahead

 /* TYPEAHEAD DATA-API
  * ================== */

  $(function () {
    $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
      var $this = $(this)
      if ($this.data('typeahead')) return
      e.preventDefault()
      $this.typeahead($this.data())
    })
  })

}( window.jQuery );<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var util = require('util');
var mongoose = require('mongoose');
var fs = require('fs');
var multer = require('multer');
var path = require('path');
var morgan = require('morgan');
var bodyParser = require('body-parser');
var connect = require('connect');
var qs = require('query-string');
var url = require('url');
var base64url = require('base64url');

var upload = multer({ dest: 'uploads/' });
var bcrypt = require('bcryptjs');
var expressvalidator = require('express-validator');

//Create EJS Engine view
app.set('view engine', 'ejs');

//body-parser and cokie-parser middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
app.use(cookieParser());
app.use(morgan('dev'));

app.set('port', (process.env.PORT || 8080));
app.listen(app.get('port'), function()
{
	console.log('Hurray am running on port ' + app.get('port'))
	});
<commit_msg>add static files<commit_after>var express = require('express');
var app = express();
var util = require('util');
var mongoose = require('mongoose');
var fs = require('fs');
var multer = require('multer');
var path = require('path');
var morgan = require('morgan');
var bodyParser = require('body-parser');
var connect = require('connect');
var qs = require('query-string');
var url = require('url');
var base64url = require('base64url');

var upload = multer({ dest: 'uploads/' });
var bcrypt = require('bcryptjs');
var expressvalidator = require('express-validator');

// set static folder
app.use(express.static(__dirname + '/assets'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'views')));
app.use('/uploads', express.static('uploads'));

//Create EJS Engine view
app.set('view engine', 'ejs');

//body-parser and cokie-parser middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
app.use(cookieParser());
app.use(morgan('dev'));

app.set('port', (process.env.PORT || 8080));
app.listen(app.get('port'), function()
{
	console.log('Hurray am running on port ' + app.get('port'))
	});
<|endoftext|>


<commit_before>/*
* server.js
*
* Sample App
*
* gets all profile data about a user and displays it
*
* Copyright (C) Province of British Columbia, 2013
*/

var express = require('express')
  , app = express()
  , fs = require('fs')
  , request = require('request')
  , a2p3 = require('a2p3')
// make sure you have a config.json and vault.json per a2p3 documentation
  , config = require('./config.json')
  , vault = require('./vault.json')

// REMOVE!!! here for testing

var jwt = require('./node_modules/a2p3/lib/jwt')

var LISTEN_PORT = 8080  // change if you want listen on a different port

if (process.env.DOTCLOUD_WWW_HTTP_URL) {
  // looks like we are running on DotCloud, adjust our world
  var HOST_URL = 'https://' + process.env.DOTCLOUD_WWW_HTTP_HOST
  LISTEN_PORT = 8080
}

// HACK UNTIL WE FIGURE OUT HOW TO DETECT WE ARE ON AZURE

LISTEN_PORT = process.env.PORT
var HOST_URL = 'http://a2p3-sample.azurewebsites.net'

// returnURL and callbackURL are constructed from the host that we are loaded from
function makeHostUrl (req) {
  if (HOST_URL) return HOST_URL
  // make URL from URL we are running on
  var hostURL = req.protocol + '://' + req.host
  if (LISTEN_PORT) hostURL += ':' + LISTEN_PORT
  return hostURL
}

var RESOURCES =
    [ 'http://email.a2p3.net/scope/default'
    , 'http://people.a2p3.net/scope/details'
    , 'http://si.a2p3.net/scope/number'
    , 'http://health.a2p3.net/scope/prov_number'
    ]

var APIS =
  { 'http://email.a2p3.net/email/default': null
  , 'http://people.a2p3.net/details': null
  , 'http://si.a2p3.net/number': null
  , 'http://health.a2p3.net/prov_number': null
  }

// HTML for meta refresh and Agent Install Page
// we could read this in once, but reading it in each
// time makes it easy to edit and reload for development
var META_REFRESH_HTML_FILE = __dirname + '/html/meta_refresh.html'

/*
*  Users can use the Agent running on their mobile phone to log into a web site
*  The server sends a qrURL down to the web page which draws a QR code
*  When the WR reader on the Agent reads the QR code, it appends &json=true
*  and the server then responds with a JSON response
*  If the QR code is scanned with a standard reader, then the server returns
*  the agent_install.html page which then tries to redirect the user to
*  Agent scheme. If it is not successful, the User learns how to get an Agent
*  for their phone
*
*  When an Agent is scanning the QR code, the User is running the App on a different
*  device. We use the sessions object to pass the Agent Request and IX Token that
*  we get from the Agent to the session where the App is running
*
*/

// calculate this once
var QR_SESSION_LENGTH = a2p3.random16bytes().length

// Global for holding QR sessions, need to put in DB if running mulitple instances
// NOTE: DOES NOT SCALE AS CODED
// checkForTokenRequest and storeTokenRequest are coded with callbacks so that
// they can easily be implemented to store data in a DB
var sessions = {}

// checks if we are have received the IX Token and Agent Request from the Agent
function checkForTokenRequest ( qrSession, callback ) {
  if ( !sessions[ qrSession ] || !sessions[ qrSession ].ixToken ) return callback( null, null )
  var data = JSON.parse( JSON.stringify( sessions[ qrSession ] ) )
  delete sessions[ qrSession ]
  callback( data )
}

// stores IX Token and Agent Request we received back channel from the Agent
function storeTokenRequest ( qrSession, agentRequest, ixToken, notificationURL, callback ) {
  sessions[ qrSession ] = sessions[ qrSession ] || {} // we might have a remember property stored
  sessions[ qrSession ].ixToken = ixToken
  sessions[ qrSession ].agentRequest = agentRequest
  sessions[ qrSession ].notificationURL = notificationURL
  callback( null )
}

function storeRememberMe ( qrSession, remember, callback ) {
  sessions[ qrSession ] = sessions[ qrSession ] || {}
  sessions[ qrSession ].remember = remember
  callback( null )
}

function checkRememberMe ( qrSession, callback ) {
  var remember = sessions[ qrSession ] && sessions[ qrSession ].remember
  callback( null, remember )
}

// metaRedirectInfoPage() returns a meta-refresh page with the supplied URL
function metaRedirectInfoPage ( redirectURL ) {
  var html = fs.readFileSync( META_REFRESH_HTML_FILE, 'utf8' )
  return html.replace( '$REDIRECT_URL', redirectURL )
}

function fetchProfile( agentRequest, ixToken, callback ) {
  var resource = new a2p3.Resource( config, vault )
  resource.exchange( agentRequest, ixToken, function ( error, di ) {
    if ( error ) return callback ( error )
    var userDI = di // App's directed identifier for User
    resource.callMultiple( APIS, function ( error, results ) {
      if (results)
        results['ix.a2p3.net'] = { di: userDI }
      callback( error, results )
    })
  })
}

/*
*   request handlers
*/

// loginQR() - called by web app when it wants a QR code link
// creates an agentRequest and state
function loginQR ( req, res )  {
  var qrSession = a2p3.random16bytes()
  req.session.qrSession = qrSession
  var qrCodeURL = makeHostUrl( req ) + '/QR/' + qrSession
  res.send( { result: { qrURL: qrCodeURL, qrSession: qrSession } } )
}

// loginDirect -- loaded when web app thinks it is running on a mobile device that
// can support the agent
// we send a meta-refresh so that we show a info page in case there is no agent to
// handle the a2p3.net: protcol scheme
function loginDirect ( req, res ) {
  var params =
    { returnURL: makeHostUrl( req ) + '/response/redirect'
    , resources: RESOURCES
    }
    , agentRequest = a2p3.createAgentRequest( config, vault, params )
    , redirectURL = 'a2p3.net://token?request=' + agentRequest
    , html = metaRedirectInfoPage( redirectURL )
  res.send( html )
}

// loginBackdoor -- development login that uses a development version of setup.a2p3.net
function loginBackdoor ( req, res )  {
  var params =
    { returnURL: makeHostUrl( req ) + '/response/redirect'
    , resources: RESOURCES
    }
    , agentRequest = a2p3.createAgentRequest( config, vault, params )
  var redirectURL = 'http://setup.a2p3.net/backdoor/login?request=' + agentRequest
  res.redirect( redirectURL )
}

// clear session, logout user
function logout ( req, res )  {
  req.session = null
  res.redirect('/')
}

// QR Code was scanned
// if scanned by Agent, then 'json=true' has been set and we return the Agent Request in JSON
// if scanned by a general QR reader, then return a meta refresh page with Agent Reqeuest and
// and state parameter of qrSession so we can link the response from the Agent
// back to this web app session in checkQR
function qrCode ( req, res ) {
  var qrSession = req.params.qrSession
  // make sure we got something that looks like a qrSession
  if ( !qrSession || qrSession.length != QR_SESSION_LENGTH || qrSession.match(/[^\w-]/g) ) {
    return res.redirect('/error')
  }
  checkRememberMe( qrSession, function ( e, remember ) {
    // ignore error since we can't do anything about it

    var params =
      { callbackURL: makeHostUrl( req ) + '/response/callback'
      , resources: RESOURCES
      }
    var agentRequest = a2p3.createAgentRequest( config, vault, params )
    var json = req.query.json
    if ( json ) {
      var response = { result: { agentRequest: agentRequest, state: qrSession } }
      if (remember) response.result.notificationURL = true
      return res.send( response )
    } else {
      var redirectURL = 'a2p3://token?request=' + agentRequest + '&state=' + qrSession
      // below never happens as remember me can only be set if scanning a QR code
      // if (remember) redirectURL += '&notificationURL=true'
      var html =  metaRedirectInfoPage( redirectURL )
      return res.send( html )
    }
  })

}

/*
* We are getting called back through the redirect which means we are running on the
* same device as the Agent is
*/
function loginResponseRedirect ( req, res )  {
  var ixToken = req.query.token
  var agentRequest = req.query.request

  if (!ixToken || !agentRequest) {
    return res.redirect( '/error' )
  }
  fetchProfile( agentRequest, ixToken, function ( error, results ) {
    if ( error ) return res.redirect( '/error' )
    req.session.profile = results
    return res.redirect('/')
  })
}

/*
* Agent is calling us back with the IX Token and Agent Request, but
* Agent is running on a different device
*/
function loginResponseCallback ( req, res )  {
  var ixToken = req.body.token
  var agentRequest = req.body.request
  var qrSession = req.body.state
  var notificationURL = req.body.notificationURL
  if (!ixToken || !agentRequest || !qrSession) {
    var code = 'MISSING_STATE'
    if (!agentRequest) code = 'MISSING_REQUEST'
    if (!ixToken) code = 'MISSING_TOKEN'
    return res.send( { error: { code: code, message: 'token, request and state are required' } } )
  }
  storeTokenRequest( qrSession, agentRequest, ixToken, notificationURL, function ( error ) {
    if ( error ) return res.send( { error: error } )
    return res.send( { result: { success: true } } )
  })
}

function checkQR ( req, res ) {
  if (!req.body.qrSession)
    return res.send( { error: 'No QR Session provided' } )
  checkForTokenRequest( req.body.qrSession, function ( tokenResponse ) {

    if (!tokenResponse) {
      return res.send( { status: 'waiting'} )
    }
    fetchProfile( tokenResponse.agentRequest, tokenResponse.ixToken, function ( error, results ) {
      var response = {}
      if ( error ) response.error = error
      if ( results ) {
        response.result = results
        req.session.profile = results
      }
      var name = results['people.a2p3.net'] &&
        results['people.a2p3.net'].redirects &&
        results['people.a2p3.net'].redirects[0] &&
        results[ results['people.a2p3.net'].redirects[0] ] &&
        results[ results['people.a2p3.net'].redirects[0] ].name
      if ( tokenResponse.notificationURL && name ) {
        var cookieValue = { name: name, url: tokenResponse.notificationURL }
        res.cookie( 'notificationURL', cookieValue, { maxAge: 900000, signed: true } )
      }
      return res.send( response )
    })
  })
}

function rememberMe ( req, res ) {
  var remember = req.body.remember
  var qrSession = req.session.qrSession
  if (!remember || !qrSession) return res.send({ error: 'no remember or QR session'})
  storeRememberMe( qrSession, remember, function ( e ) {
    if (e) return res.send( { error: e  } )
    return res.send( { result: { success: true } } )
  })
}

function profile ( req, res )  {
  if ( req.session.profile ) {
    return res.send( { result: req.session.profile } )
  } else { //
    return res.send( { errror: 'NOT_LOGGED_IN'} )
  }
}

// checks if a NotificationURL has been set
function notificationCheck ( req, res ) {
  var notification = req.signedCookies.notificationURL
  var response = { result: { name: null } }
  if ( notification && notification.name )
    response.result.name = notification.name
  return res.send( response )
}

// invokes Notification URL
function notificationInvoke ( req, res, next ) {
  var notification = req.signedCookies.notificationURL
  if ( !notification || !notification.url ) return next( new Error('No notification URL was set') )

  // we simulate creating a QR code short URl and send that to the
  // notification URL to invoke, handling from there on is the same
  // as if we have put up a QR code
  var qrSession = a2p3.random16bytes()
  req.session.qrSession = qrSession
  var options =
    { url: notification.url
    , method: 'POST'
    , json:
      { url: makeHostUrl( req ) + '/QR/' + qrSession
      , alert: 'Sample App (' + req.host + ') login request'
      }
    }
  // call Notification URL which will send the QR Code Short URL to the Personal Agent
  request.post( options, function ( e, r, body ) {
    if (e) return res.send( { result: { error: e  } } )
    if (r.statusCode && r.statusCode != 200) return res.send( { result: { error:  r.statusCode } } )
    if (body && body.result && body.result.error) return res.send( { result: { error: body.result.error  } } )
    if (body && body.result && body.result.success)
      return res.send( { result: { qrSession: qrSession } } )
    return res.send( { result: { error: 'UNKNOWN'  } } )
  })
}

// check we have valid keys
// TBD -- check that our keys are valid with the Registrar

// set up middleware

app.use( express.static( __dirname + '/html/assets' ) )   // put static assets here
app.use( express.logger( 'dev' ) )                        // so that we only log page requests
app.use( express.limit('10kb') )                          // protect against large POST attack
app.use( express.bodyParser() )

app.use( express.cookieParser('a secret string') )
var cookieOptions =
  { 'secret': 'helloworldsecret'
  , 'cookie': { path: '/' } }
app.use( express.cookieSession( cookieOptions ))

//setup request routes

// these end points are all AJAX calls from the web app and return a JSON response
app.post('/login/QR', loginQR )
app.post('/profile', profile )
app.post('/check/QR', checkQR )
app.post('/remember/me', rememberMe )
app.post('/notification/check', notificationCheck )
app.post('/notification/invoke', notificationInvoke )

// this page is called by either the Agent or a QR Code reader
// returns either the Agent Request in JSON if called by Agent
// or sends a redirect to the a2p3.net://token URL
// also called by the Agent via the notification URL mechanism
app.get('/QR/:qrSession', qrCode )

// these pages return a redirect
app.get('/logout', logout)
app.get('/login/backdoor', loginBackdoor)
app.get('/login/direct', loginDirect)
// called if App and Agent are on same device
app.get('/response/redirect', loginResponseRedirect )
// called if App and Agent are on different devices
app.post('/response/callback', loginResponseCallback )

// these endpoints serve static HTML pages
app.get('/', function( req, res ) { res.sendfile( __dirname + '/html/index.html' ) } )
app.get('/error', function( req, res ) { res.sendfile( __dirname + '/html/login_error.html' ) } )
app.get('/agent/install', function( req, res ) { res.sendfile( __dirname + '/html/agent_install.html' ) } )

app.get('/test', function( req, res ) { res.sendfile( __dirname + '/html/test.html' ) } )

app.listen( LISTEN_PORT )

console.log('\nSample App available on this machine on port:', LISTEN_PORT )

console.log('\nprocess.env dump\n',process.env)
<commit_msg>more environment fiddling<commit_after>/*
* server.js
*
* Sample App
*
* gets all profile data about a user and displays it
*
* Copyright (C) Province of British Columbia, 2013
*/

var express = require('express')
  , app = express()
  , fs = require('fs')
  , request = require('request')
  , a2p3 = require('a2p3')
// make sure you have a config.json and vault.json per a2p3 documentation
  , config = require('./config.json')
  , vault = require('./vault.json')

// REMOVE!!! here for testing

var jwt = require('./node_modules/a2p3/lib/jwt')

var LISTEN_PORT = 8080  // change if you want listen on a different port

if (process.env.DOTCLOUD_WWW_HTTP_URL) {
  // looks like we are running on DotCloud, adjust our world
  var HOST_URL = 'https://' + process.env.DOTCLOUD_WWW_HTTP_HOST
  LISTEN_PORT = 8080
}

// HACK UNTIL WE FIGURE OUT HOW TO DETECT WE ARE ON AZURE

LISTEN_PORT = process.env.PORT
var HOST_URL = 'http://a2p3-sample.azurewebsites.net'

// returnURL and callbackURL are constructed from the host that we are loaded from
function makeHostUrl (req) {

console.log('\nreq\n', require('util').inspect( req, {depth: 1} ) )

  if (HOST_URL) return HOST_URL
  // make URL from URL we are running on
  var hostURL = req.protocol + '://' + req.host
  if (LISTEN_PORT) hostURL += ':' + LISTEN_PORT
  return hostURL
}

var RESOURCES =
    [ 'http://email.a2p3.net/scope/default'
    , 'http://people.a2p3.net/scope/details'
    , 'http://si.a2p3.net/scope/number'
    , 'http://health.a2p3.net/scope/prov_number'
    ]

var APIS =
  { 'http://email.a2p3.net/email/default': null
  , 'http://people.a2p3.net/details': null
  , 'http://si.a2p3.net/number': null
  , 'http://health.a2p3.net/prov_number': null
  }

// HTML for meta refresh and Agent Install Page
// we could read this in once, but reading it in each
// time makes it easy to edit and reload for development
var META_REFRESH_HTML_FILE = __dirname + '/html/meta_refresh.html'

/*
*  Users can use the Agent running on their mobile phone to log into a web site
*  The server sends a qrURL down to the web page which draws a QR code
*  When the WR reader on the Agent reads the QR code, it appends &json=true
*  and the server then responds with a JSON response
*  If the QR code is scanned with a standard reader, then the server returns
*  the agent_install.html page which then tries to redirect the user to
*  Agent scheme. If it is not successful, the User learns how to get an Agent
*  for their phone
*
*  When an Agent is scanning the QR code, the User is running the App on a different
*  device. We use the sessions object to pass the Agent Request and IX Token that
*  we get from the Agent to the session where the App is running
*
*/

// calculate this once
var QR_SESSION_LENGTH = a2p3.random16bytes().length

// Global for holding QR sessions, need to put in DB if running mulitple instances
// NOTE: DOES NOT SCALE AS CODED
// checkForTokenRequest and storeTokenRequest are coded with callbacks so that
// they can easily be implemented to store data in a DB
var sessions = {}

// checks if we are have received the IX Token and Agent Request from the Agent
function checkForTokenRequest ( qrSession, callback ) {
  if ( !sessions[ qrSession ] || !sessions[ qrSession ].ixToken ) return callback( null, null )
  var data = JSON.parse( JSON.stringify( sessions[ qrSession ] ) )
  delete sessions[ qrSession ]
  callback( data )
}

// stores IX Token and Agent Request we received back channel from the Agent
function storeTokenRequest ( qrSession, agentRequest, ixToken, notificationURL, callback ) {
  sessions[ qrSession ] = sessions[ qrSession ] || {} // we might have a remember property stored
  sessions[ qrSession ].ixToken = ixToken
  sessions[ qrSession ].agentRequest = agentRequest
  sessions[ qrSession ].notificationURL = notificationURL
  callback( null )
}

function storeRememberMe ( qrSession, remember, callback ) {
  sessions[ qrSession ] = sessions[ qrSession ] || {}
  sessions[ qrSession ].remember = remember
  callback( null )
}

function checkRememberMe ( qrSession, callback ) {
  var remember = sessions[ qrSession ] && sessions[ qrSession ].remember
  callback( null, remember )
}

// metaRedirectInfoPage() returns a meta-refresh page with the supplied URL
function metaRedirectInfoPage ( redirectURL ) {
  var html = fs.readFileSync( META_REFRESH_HTML_FILE, 'utf8' )
  return html.replace( '$REDIRECT_URL', redirectURL )
}

function fetchProfile( agentRequest, ixToken, callback ) {
  var resource = new a2p3.Resource( config, vault )
  resource.exchange( agentRequest, ixToken, function ( error, di ) {
    if ( error ) return callback ( error )
    var userDI = di // App's directed identifier for User
    resource.callMultiple( APIS, function ( error, results ) {
      if (results)
        results['ix.a2p3.net'] = { di: userDI }
      callback( error, results )
    })
  })
}

/*
*   request handlers
*/

// loginQR() - called by web app when it wants a QR code link
// creates an agentRequest and state
function loginQR ( req, res )  {
  var qrSession = a2p3.random16bytes()
  req.session.qrSession = qrSession
  var qrCodeURL = makeHostUrl( req ) + '/QR/' + qrSession
  res.send( { result: { qrURL: qrCodeURL, qrSession: qrSession } } )
}

// loginDirect -- loaded when web app thinks it is running on a mobile device that
// can support the agent
// we send a meta-refresh so that we show a info page in case there is no agent to
// handle the a2p3.net: protcol scheme
function loginDirect ( req, res ) {
  var params =
    { returnURL: makeHostUrl( req ) + '/response/redirect'
    , resources: RESOURCES
    }
    , agentRequest = a2p3.createAgentRequest( config, vault, params )
    , redirectURL = 'a2p3.net://token?request=' + agentRequest
    , html = metaRedirectInfoPage( redirectURL )
  res.send( html )
}

// loginBackdoor -- development login that uses a development version of setup.a2p3.net
function loginBackdoor ( req, res )  {
  var params =
    { returnURL: makeHostUrl( req ) + '/response/redirect'
    , resources: RESOURCES
    }
    , agentRequest = a2p3.createAgentRequest( config, vault, params )
  var redirectURL = 'http://setup.a2p3.net/backdoor/login?request=' + agentRequest
  res.redirect( redirectURL )
}

// clear session, logout user
function logout ( req, res )  {
  req.session = null
  res.redirect('/')
}

// QR Code was scanned
// if scanned by Agent, then 'json=true' has been set and we return the Agent Request in JSON
// if scanned by a general QR reader, then return a meta refresh page with Agent Reqeuest and
// and state parameter of qrSession so we can link the response from the Agent
// back to this web app session in checkQR
function qrCode ( req, res ) {
  var qrSession = req.params.qrSession
  // make sure we got something that looks like a qrSession
  if ( !qrSession || qrSession.length != QR_SESSION_LENGTH || qrSession.match(/[^\w-]/g) ) {
    return res.redirect('/error')
  }
  checkRememberMe( qrSession, function ( e, remember ) {
    // ignore error since we can't do anything about it

    var params =
      { callbackURL: makeHostUrl( req ) + '/response/callback'
      , resources: RESOURCES
      }
    var agentRequest = a2p3.createAgentRequest( config, vault, params )
    var json = req.query.json
    if ( json ) {
      var response = { result: { agentRequest: agentRequest, state: qrSession } }
      if (remember) response.result.notificationURL = true
      return res.send( response )
    } else {
      var redirectURL = 'a2p3://token?request=' + agentRequest + '&state=' + qrSession
      // below never happens as remember me can only be set if scanning a QR code
      // if (remember) redirectURL += '&notificationURL=true'
      var html =  metaRedirectInfoPage( redirectURL )
      return res.send( html )
    }
  })

}

/*
* We are getting called back through the redirect which means we are running on the
* same device as the Agent is
*/
function loginResponseRedirect ( req, res )  {
  var ixToken = req.query.token
  var agentRequest = req.query.request

  if (!ixToken || !agentRequest) {
    return res.redirect( '/error' )
  }
  fetchProfile( agentRequest, ixToken, function ( error, results ) {
    if ( error ) return res.redirect( '/error' )
    req.session.profile = results
    return res.redirect('/')
  })
}

/*
* Agent is calling us back with the IX Token and Agent Request, but
* Agent is running on a different device
*/
function loginResponseCallback ( req, res )  {
  var ixToken = req.body.token
  var agentRequest = req.body.request
  var qrSession = req.body.state
  var notificationURL = req.body.notificationURL
  if (!ixToken || !agentRequest || !qrSession) {
    var code = 'MISSING_STATE'
    if (!agentRequest) code = 'MISSING_REQUEST'
    if (!ixToken) code = 'MISSING_TOKEN'
    return res.send( { error: { code: code, message: 'token, request and state are required' } } )
  }
  storeTokenRequest( qrSession, agentRequest, ixToken, notificationURL, function ( error ) {
    if ( error ) return res.send( { error: error } )
    return res.send( { result: { success: true } } )
  })
}

function checkQR ( req, res ) {
  if (!req.body.qrSession)
    return res.send( { error: 'No QR Session provided' } )
  checkForTokenRequest( req.body.qrSession, function ( tokenResponse ) {

    if (!tokenResponse) {
      return res.send( { status: 'waiting'} )
    }
    fetchProfile( tokenResponse.agentRequest, tokenResponse.ixToken, function ( error, results ) {
      var response = {}
      if ( error ) response.error = error
      if ( results ) {
        response.result = results
        req.session.profile = results
      }
      var name = results['people.a2p3.net'] &&
        results['people.a2p3.net'].redirects &&
        results['people.a2p3.net'].redirects[0] &&
        results[ results['people.a2p3.net'].redirects[0] ] &&
        results[ results['people.a2p3.net'].redirects[0] ].name
      if ( tokenResponse.notificationURL && name ) {
        var cookieValue = { name: name, url: tokenResponse.notificationURL }
        res.cookie( 'notificationURL', cookieValue, { maxAge: 900000, signed: true } )
      }
      return res.send( response )
    })
  })
}

function rememberMe ( req, res ) {
  var remember = req.body.remember
  var qrSession = req.session.qrSession
  if (!remember || !qrSession) return res.send({ error: 'no remember or QR session'})
  storeRememberMe( qrSession, remember, function ( e ) {
    if (e) return res.send( { error: e  } )
    return res.send( { result: { success: true } } )
  })
}

function profile ( req, res )  {
  if ( req.session.profile ) {
    return res.send( { result: req.session.profile } )
  } else { //
    return res.send( { errror: 'NOT_LOGGED_IN'} )
  }
}

// checks if a NotificationURL has been set
function notificationCheck ( req, res ) {
  var notification = req.signedCookies.notificationURL
  var response = { result: { name: null } }
  if ( notification && notification.name )
    response.result.name = notification.name
  return res.send( response )
}

// invokes Notification URL
function notificationInvoke ( req, res, next ) {
  var notification = req.signedCookies.notificationURL
  if ( !notification || !notification.url ) return next( new Error('No notification URL was set') )

  // we simulate creating a QR code short URl and send that to the
  // notification URL to invoke, handling from there on is the same
  // as if we have put up a QR code
  var qrSession = a2p3.random16bytes()
  req.session.qrSession = qrSession
  var options =
    { url: notification.url
    , method: 'POST'
    , json:
      { url: makeHostUrl( req ) + '/QR/' + qrSession
      , alert: 'Sample App (' + req.host + ') login request'
      }
    }
  // call Notification URL which will send the QR Code Short URL to the Personal Agent
  request.post( options, function ( e, r, body ) {
    if (e) return res.send( { result: { error: e  } } )
    if (r.statusCode && r.statusCode != 200) return res.send( { result: { error:  r.statusCode } } )
    if (body && body.result && body.result.error) return res.send( { result: { error: body.result.error  } } )
    if (body && body.result && body.result.success)
      return res.send( { result: { qrSession: qrSession } } )
    return res.send( { result: { error: 'UNKNOWN'  } } )
  })
}

// check we have valid keys
// TBD -- check that our keys are valid with the Registrar

// set up middleware

app.use( express.static( __dirname + '/html/assets' ) )   // put static assets here
app.use( express.logger( 'dev' ) )                        // so that we only log page requests
app.use( express.limit('10kb') )                          // protect against large POST attack
app.use( express.bodyParser() )

app.use( express.cookieParser('a secret string') )
var cookieOptions =
  { 'secret': 'helloworldsecret'
  , 'cookie': { path: '/' } }
app.use( express.cookieSession( cookieOptions ))

//setup request routes

// these end points are all AJAX calls from the web app and return a JSON response
app.post('/login/QR', loginQR )
app.post('/profile', profile )
app.post('/check/QR', checkQR )
app.post('/remember/me', rememberMe )
app.post('/notification/check', notificationCheck )
app.post('/notification/invoke', notificationInvoke )

// this page is called by either the Agent or a QR Code reader
// returns either the Agent Request in JSON if called by Agent
// or sends a redirect to the a2p3.net://token URL
// also called by the Agent via the notification URL mechanism
app.get('/QR/:qrSession', qrCode )

// these pages return a redirect
app.get('/logout', logout)
app.get('/login/backdoor', loginBackdoor)
app.get('/login/direct', loginDirect)
// called if App and Agent are on same device
app.get('/response/redirect', loginResponseRedirect )
// called if App and Agent are on different devices
app.post('/response/callback', loginResponseCallback )

// these endpoints serve static HTML pages
app.get('/', function( req, res ) { res.sendfile( __dirname + '/html/index.html' ) } )
app.get('/error', function( req, res ) { res.sendfile( __dirname + '/html/login_error.html' ) } )
app.get('/agent/install', function( req, res ) { res.sendfile( __dirname + '/html/agent_install.html' ) } )

app.get('/test', function( req, res ) { res.sendfile( __dirname + '/html/test.html' ) } )

app.listen( LISTEN_PORT )

console.log('\nSample App available on this machine on port:', LISTEN_PORT )

console.log('\nprocess.env dump\n',process.env)
<|endoftext|>


<commit_before>var http = require('http')
var port = process.env.PORT || 1337;
http.createServer(function(req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World\n');
}).listen(port);
<commit_msg>Delete server.js<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 Andr Mekkawi <contact@andremekkawi.com>
 *
 * LICENSE
 *
 * This source file is subject to the MIT license in the file LICENSE.txt.
 * The license is also available at http://diskusagereport.sf.net/license.html
 */

;(function($){

$.extend(Controller.prototype, {

	// The default language, if the requested language is not supported.
	defaultLanguage: 'en-us',

	// The current language.
	language: null,

	// The language data.
	_languages: { },

	translate: function() {
		arguments = $.makeArray(arguments);

		if (!this._languages[this.language]) {
			throw "The language file for '" + this.language + "' has not been loaded.";
		}

		var key = arguments.shift(),
			str = this._languages[this.language][key],
			parts = [], isTextOnly = true,
			argIndex, lastIndex = 0, match, re = new RegExp('{([0-9])}', 'g');

		// Throw an error because the key does not exist.
		if (!$.isString(str)) {
			throw "'" + key + "' does not exist in language file.";
		}
		else {
			// TODO: Remove toUpperCase() debug code.
			//str = str.toUpperCase();

			// Find all replacements in the string.
			while (match = re.exec(str)) {

				// Add any text between the last match and this one.
				if (lastIndex < match.index) {
					parts.push(document.createTextNode(str.substring(lastIndex, match.index)));
				}

				argIndex = parseInt(match[1]) - 1;

				if ($.isUndefined(arguments[argIndex]) || arguments[argIndex] == null) {
					throw 'Replacement not passed to Controller.translate() for ' + match[0] + ' in ' + key;
				}
				else if ($.isString(arguments[argIndex]) || $.isNumber(arguments[argIndex])) {
					parts.push(document.createTextNode(arguments[argIndex]+''));
				}
				else if (arguments[argIndex].toArray) {
					parts.push.apply(parts, arguments[argIndex].toArray());
					isTextOnly = false;
				}
				else {
					throw 'Invalid argument for Controller.translate(). Must be string or jQuery object.';
				}

				lastIndex = re.lastIndex;
			}

			if (lastIndex != str.length) {
				parts.push(document.createTextNode(str.substr(lastIndex)));
			}

			if (isTextOnly) {
				return $(parts).text();
			}
			else {
				return $(parts);
			}
		}
	},

	isSupportedLanguage: function(lang) {
		return $.isDefined(this._languages[lang.toLowerCase()]);
	},

	isLanguageLoaded: function(lang) {
		return this.isSupportedLanguage(lang) && this._languages[lang] != 'load';
	},

	addLanguage: function(lang) {
		if ($.isString(lang)) lang = [ lang ];

		for (var i = 0; i < lang.length; i++)
			if ($.isUndefined(this._languages[lang[i].toLowerCase()]))
				this._languages[lang[i].toLowerCase()] = 'load';
	},

	setLanguage: function(rules, returnFn) {
		var self = this,
			matches = this.matchSupportedLanguagesToRules(rules);

		// Set the lang to the first supported langauge match.
		// Otherwise set it to the language default.
		var lang = matches.length ? matches[0].lang : this.defaultLanguage;

		// Retrieve the language data if it has not been loaded.
		if (this._preLoad) {
			this.language = lang;
		}
		else {
			this.loadLanguage(lang, function(ret, message){
				if (ret) {
					self.language = lang;
					self._languageChangeStatic();
				}

				returnFn(ret, message);
			});
		}
	},

	loadLanguage: function(lang, returnFn) {
		var self = this;

		// Return that the language is unsupported.
		if (!this.isSupportedLanguage(lang)) {
			if ($.isFunction(returnFn))
				returnFn(false, 'Unsupported language: ' + lang);
		}

		// Retrieve the language data if it has not been loaded.
		else if (this._languages[lang] == 'load') {
			try {
				if (this._langXHR) this._langXHR.abort();
				this._langXHR = $.ajax({
					url: 'lang/' + lang + '.json',
					dataType: 'json',
					error: function(xhr, status, ex) {
						if (status == 'abort') return;

						if ($.isFunction(returnFn))
							returnFn(false, 'Failed to load language file (' + status + '): lang/' + lang + '.json');
					},
					success: function(data, status, xhr) {
						if (data) {
							if ($.isString(data['extends'])) {
								self.loadLanguage(data['extends'], function(ret, message){
									self._languages[lang] = $.extend({}, self._languages[data['extends']], data);
									returnFn(ret, message);
								});
							}
							else {
								if ($.isFunction(returnFn))
									self._languages[lang] = data;
									returnFn(true);
							}
						}
					}
				});
			}
			catch (e) {
				// TODO: Handle exception when data is viewed via 'file:///' protocol.
				if ($.isFunction(returnFn))
					returnFn(false, 'Failed to load language file (AJAX exception): lang/' + lang + '.json');
			}
		}

		// The data has already been loaded.
		else {
			if ($.isFunction(returnFn))
				returnFn(true);
		}
	},

	_languageChangeStatic: function(part) {
		if (!part || part == 'title') {
			if (this.settings && this.settings.name) {
				$('#Title').html(this.translate('title_with_name', $('').text(this.settings.name))).show();
				document.title = this.translate('title_with_name', this.settings.name);
			}
			else {
				$('#Title').html(this.translate('title')).show();
				document.title = this.translate('title');
			}
		}

		if (!part || part == 'errors') {
			$('#ErrorsTitle span').html(this.translate('errors_title'));
			$('#ErrorsTitle div').html(this.translate('errors_close'));
		}

		if (!part || part == 'tree') {
			$('#TreeSort .tree-sortby-label span').html(this.translate('tree_header_name'));
			$('#TreeSort .tree-sortby-byte span').html(this.translate('tree_header_size'));
			$('#TreeSort .tree-sortby-num span').html(this.translate('tree_header_count'));
			$('#DirectoryTree li.ui-tree-files span.ui-tree-label').html(this.translate('tree_files_in_this_directory'));
		}

		if (!part || part == 'tabs') {
			$('#Tab_SubDirs').html(this.translate('tab_contents'));
			$('#Tab_Files').html(this.translate('tab_file_list'));
			$('#Tab_Modified').html(this.translate('tab_last_modified'));
			$('#Tab_Sizes').html(this.translate('tab_file_sizes'));
			$('#Tab_Types').html(this.translate('tab_file_types'));
			$('#Tab_Top100').html(this.translate('tab_top_100'));
		}

		if (!part || part == 'pager') {
			$('#Sections .pager-prev').html(this.translate('prev_page_button'));
			$('#Sections .pager-next').html(this.translate('next_page_button'));
		}

		if (!part || part == 'sections') {
			$('#SubDirs > thead .totals-sortby-label span').html(this.translate('contents_header_name'));
			$('#SubDirs > thead .totals-sortby-byte span').html(this.translate('contents_header_total_size'));
			$('#SubDirs > thead .totals-sortby-num span').html(this.translate('contents_header_file_count'));
			$('#SubDirs > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Files-SortBy-name span').html(this.translate('file_list_header_name'));
			$('#Files-SortBy-type span').html(this.translate('file_list_header_type'));
			$('#Files-SortBy-size span').html(this.translate('file_list_header_size'));
			$('#Files-SortBy-modified span').html(this.translate('file_list_header_modified'));

			$('#Modified > thead .totals-sortby-label span').html(this.translate('last_modified_header_age'));
			$('#Modified > thead .totals-sortby-byte span').html(this.translate('last_modified_header_total_size'));
			$('#Modified > thead .totals-sortby-num span').html(this.translate('last_modified_header_file_count'));
			$('#Modified > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Sizes > thead .totals-sortby-label span').html(this.translate('file_sizes_header_range'));
			$('#Sizes > thead .totals-sortby-byte span').html(this.translate('file_sizes_header_total_size'));
			$('#Sizes > thead .totals-sortby-num span').html(this.translate('file_sizes_header_file_count'));
			$('#Sizes > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Types > thead .totals-sortby-label span').html(this.translate('file_types_header_extension'));
			$('#Types > thead .totals-sortby-byte span').html(this.translate('file_types_header_total_size'));
			$('#Types > thead .totals-sortby-num span').html(this.translate('file_types_header_file_count'));
			$('#Types > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Top100-SortBy-name span').html(this.translate('top_100_header_name'));
			$('#Top100-SortBy-type span').html(this.translate('top_100_header_type'));
			$('#Top100-SortBy-size span').html(this.translate('top_100_header_size'));
			$('#Top100-SortBy-modified span').html(this.translate('top_100_header_modified'));
			$('#Top100-SortBy-path span').html(this.translate('top_100_header_path'));
		}

		if (!part || part == 'footer') {
			$('#Footer').html(this.translate(this.settings ? 'footer_with_created' : 'footer',
				this.settings ? this.settings.created.htmlencode() : null,
				$('Disk Usage Reports'),
				this._languages[this.language].language_name
			));
		}

		// Resize window in case the header was previously hidden.
		this.resizeWindow();
	},

	getSupportedLanguages: function() {
		return $.getArrayKeys(this._languages);
	},

	parseAcceptLanguage: function(rules) {
		// Remove whitespace and split into rules.
		rules = rules.toLowerCase().replace(/\s/g, '').split(',');

		for (var i = rules.length - 1; i >= 0; i--) {
			var split = rules[i].match(/^(([a-z]{1,8})(-([a-z]{1,8}))?)(;q=((1(\.[0]{1,3})?)|(0(\.[0-9]{1,3})?)))?$/i);
			// 1 = full language tag
			// 2 = primary language tag
			// 4 = secondary language tag
			// 6 = quality

			if (split) {
				rules[i] = { order: i, full: split[1], primary: split[2], secondary: split[4] ? split[4] : null, quality: isNaN(split[6]) ? 1 : parseFloat(split[6]) };
			}
			else {
				rules.splice(i, 1);
			}
		}

		this.sortLanguageRules(rules);

		return rules;
	},

	sortLanguageRules: function(rules) {
		rules.sort(function(a,b){
			var acomp = a.lang ? a.lang == a.full ? 0 : 1 : 0;
			var bcomp = b.lang ? b.lang == b.full ? 0 : 1 : 0;
			if (a.order == b.order && acomp != bcomp) {
				return acomp - bcomp;
			}
			else if (a.quality == 0 || b.quality == 0 || a.quality == b.quality) {
				return a.order  - b.order;
			}
			else {
				return b.quality - a.quality;
			}
		});
	},

	parseLanguageTag: function(lang) {
		var match = lang.replace(/\s/g, '').match(/^(([a-z]{1,8})(-([a-z]{1,8}))?)$/i);
		if (match)
			return { full: match[1], primary: match[2], secondary: match[4] ? match[4] : null };
		else
			return false;
	},

	determineRuleForLanguage: function(rules, lang) {
		var langMatch = this.parseLanguageTag(lang),
			ret = false;

		if (langMatch !== false) {
			for (var i = 0; i < rules.length; i++) {
				// The rule is an exact match.
				if (rules[i].full == langMatch.full) {
					return rules[i];
				}

				// The primary part of the language tag matches.
				else if (rules[i].primary == langMatch.primary) {
					// Our first partial match.
					if (ret === false) {
						ret = rules[i];
					}
					// Only override the current partial match if it is more general.
					// Ex: For the lang 'en-xx', 'en' would override 'en-yy', but not vise vera.
					else if (ret.secondary != null && rules[i].secondary == null) {
						ret = rules[i];
					}
				}
			}
		}
		return ret;
	},

	matchSupportedLanguagesToRules: function(rules) {
		var supported = this.getSupportedLanguages();

		// Parse the rules as an Accept-Language header if it is a string.
		if ($.isString(rules)) rules = this.parseAcceptLanguage(rules);

		// Go through the list of supported languages and determine what matches the language rules.
		for (var i = supported.length - 1; i >= 0; i--) {
			var matchedRule = this.determineRuleForLanguage(rules, supported[i]);
			if (matchedRule === false || matchedRule.quality == 0) {
				supported.splice(i, 1);
			}
			else {
				supported[i] = $.extend({ lang: supported[i] }, matchedRule);
			}

		}

		this.sortLanguageRules(supported);

		return supported;
	}
});

})(jQuery);<commit_msg>* Added 'doctitle' and 'doctitle_with_name' to lang file.<commit_after>/*
 * Copyright (c) 2011 Andr Mekkawi <contact@andremekkawi.com>
 *
 * LICENSE
 *
 * This source file is subject to the MIT license in the file LICENSE.txt.
 * The license is also available at http://diskusagereport.sf.net/license.html
 */

;(function($){

$.extend(Controller.prototype, {

	// The default language, if the requested language is not supported.
	defaultLanguage: 'en-us',

	// The current language.
	language: null,

	// The language data.
	_languages: { },

	translate: function() {
		arguments = $.makeArray(arguments);

		if (!this._languages[this.language]) {
			throw "The language file for '" + this.language + "' has not been loaded.";
		}

		var key = arguments.shift(),
			str = this._languages[this.language][key],
			parts = [], isTextOnly = true,
			argIndex, lastIndex = 0, match, re = new RegExp('{([0-9])}', 'g');

		// Throw an error because the key does not exist.
		if (!$.isString(str)) {
			throw "'" + key + "' does not exist in language file.";
		}
		else {
			// TODO: Remove toUpperCase() debug code.
			//str = str.toUpperCase();

			// Find all replacements in the string.
			while (match = re.exec(str)) {

				// Add any text between the last match and this one.
				if (lastIndex < match.index) {
					parts.push(document.createTextNode(str.substring(lastIndex, match.index)));
				}

				argIndex = parseInt(match[1]) - 1;

				if ($.isUndefined(arguments[argIndex]) || arguments[argIndex] == null) {
					throw 'Replacement not passed to Controller.translate() for ' + match[0] + ' in ' + key;
				}
				else if ($.isString(arguments[argIndex]) || $.isNumber(arguments[argIndex])) {
					parts.push(document.createTextNode(arguments[argIndex]+''));
				}
				else if (arguments[argIndex].toArray) {
					parts.push.apply(parts, arguments[argIndex].toArray());
					isTextOnly = false;
				}
				else {
					throw 'Invalid argument for Controller.translate(). Must be string or jQuery object.';
				}

				lastIndex = re.lastIndex;
			}

			if (lastIndex != str.length) {
				parts.push(document.createTextNode(str.substr(lastIndex)));
			}

			if (isTextOnly) {
				return $(parts).text();
			}
			else {
				return $(parts);
			}
		}
	},

	isSupportedLanguage: function(lang) {
		return $.isDefined(this._languages[lang.toLowerCase()]);
	},

	isLanguageLoaded: function(lang) {
		return this.isSupportedLanguage(lang) && this._languages[lang] != 'load';
	},

	addLanguage: function(lang) {
		if ($.isString(lang)) lang = [ lang ];

		for (var i = 0; i < lang.length; i++)
			if ($.isUndefined(this._languages[lang[i].toLowerCase()]))
				this._languages[lang[i].toLowerCase()] = 'load';
	},

	setLanguage: function(rules, returnFn) {
		var self = this,
			matches = this.matchSupportedLanguagesToRules(rules);

		// Set the lang to the first supported langauge match.
		// Otherwise set it to the language default.
		var lang = matches.length ? matches[0].lang : this.defaultLanguage;

		// Retrieve the language data if it has not been loaded.
		if (this._preLoad) {
			this.language = lang;
		}
		else {
			this.loadLanguage(lang, function(ret, message){
				if (ret) {
					self.language = lang;
					self._languageChangeStatic();
				}

				returnFn(ret, message);
			});
		}
	},

	loadLanguage: function(lang, returnFn) {
		var self = this;

		// Return that the language is unsupported.
		if (!this.isSupportedLanguage(lang)) {
			if ($.isFunction(returnFn))
				returnFn(false, 'Unsupported language: ' + lang);
		}

		// Retrieve the language data if it has not been loaded.
		else if (this._languages[lang] == 'load') {
			try {
				if (this._langXHR) this._langXHR.abort();
				this._langXHR = $.ajax({
					url: 'lang/' + lang + '.json',
					dataType: 'json',
					error: function(xhr, status, ex) {
						if (status == 'abort') return;

						if ($.isFunction(returnFn))
							returnFn(false, 'Failed to load language file (' + status + '): lang/' + lang + '.json');
					},
					success: function(data, status, xhr) {
						if (data) {
							if ($.isString(data['extends'])) {
								self.loadLanguage(data['extends'], function(ret, message){
									self._languages[lang] = $.extend({}, self._languages[data['extends']], data);
									returnFn(ret, message);
								});
							}
							else {
								if ($.isFunction(returnFn))
									self._languages[lang] = data;
									returnFn(true);
							}
						}
					}
				});
			}
			catch (e) {
				// TODO: Handle exception when data is viewed via 'file:///' protocol.
				if ($.isFunction(returnFn))
					returnFn(false, 'Failed to load language file (AJAX exception): lang/' + lang + '.json');
			}
		}

		// The data has already been loaded.
		else {
			if ($.isFunction(returnFn))
				returnFn(true);
		}
	},

	_languageChangeStatic: function(part) {
		if (!part || part == 'title') {
			if (this.settings && this.settings.name) {
				$('#Title').html(this.translate('title_with_name', $('').text(this.settings.name))).show();
				document.title = this.translate('doctitle_with_name', this.settings.name);
			}
			else {
				$('#Title').html(this.translate('title')).show();
				document.title = this.translate('doctitle');
			}
		}

		if (!part || part == 'errors') {
			$('#ErrorsTitle span').html(this.translate('errors_title'));
			$('#ErrorsTitle div').html(this.translate('errors_close'));
		}

		if (!part || part == 'tree') {
			$('#TreeSort .tree-sortby-label span').html(this.translate('tree_header_name'));
			$('#TreeSort .tree-sortby-byte span').html(this.translate('tree_header_size'));
			$('#TreeSort .tree-sortby-num span').html(this.translate('tree_header_count'));
			$('#DirectoryTree li.ui-tree-files span.ui-tree-label').html(this.translate('tree_files_in_this_directory'));
		}

		if (!part || part == 'tabs') {
			$('#Tab_SubDirs').html(this.translate('tab_contents'));
			$('#Tab_Files').html(this.translate('tab_file_list'));
			$('#Tab_Modified').html(this.translate('tab_last_modified'));
			$('#Tab_Sizes').html(this.translate('tab_file_sizes'));
			$('#Tab_Types').html(this.translate('tab_file_types'));
			$('#Tab_Top100').html(this.translate('tab_top_100'));
		}

		if (!part || part == 'pager') {
			$('#Sections .pager-prev').html(this.translate('prev_page_button'));
			$('#Sections .pager-next').html(this.translate('next_page_button'));
		}

		if (!part || part == 'sections') {
			$('#SubDirs > thead .totals-sortby-label span').html(this.translate('contents_header_name'));
			$('#SubDirs > thead .totals-sortby-byte span').html(this.translate('contents_header_total_size'));
			$('#SubDirs > thead .totals-sortby-num span').html(this.translate('contents_header_file_count'));
			$('#SubDirs > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Files-SortBy-name span').html(this.translate('file_list_header_name'));
			$('#Files-SortBy-type span').html(this.translate('file_list_header_type'));
			$('#Files-SortBy-size span').html(this.translate('file_list_header_size'));
			$('#Files-SortBy-modified span').html(this.translate('file_list_header_modified'));

			$('#Modified > thead .totals-sortby-label span').html(this.translate('last_modified_header_age'));
			$('#Modified > thead .totals-sortby-byte span').html(this.translate('last_modified_header_total_size'));
			$('#Modified > thead .totals-sortby-num span').html(this.translate('last_modified_header_file_count'));
			$('#Modified > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Sizes > thead .totals-sortby-label span').html(this.translate('file_sizes_header_range'));
			$('#Sizes > thead .totals-sortby-byte span').html(this.translate('file_sizes_header_total_size'));
			$('#Sizes > thead .totals-sortby-num span').html(this.translate('file_sizes_header_file_count'));
			$('#Sizes > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Types > thead .totals-sortby-label span').html(this.translate('file_types_header_extension'));
			$('#Types > thead .totals-sortby-byte span').html(this.translate('file_types_header_total_size'));
			$('#Types > thead .totals-sortby-num span').html(this.translate('file_types_header_file_count'));
			$('#Types > tfoot td:eq(0)').html(this.translate('table_footer_total'));

			$('#Top100-SortBy-name span').html(this.translate('top_100_header_name'));
			$('#Top100-SortBy-type span').html(this.translate('top_100_header_type'));
			$('#Top100-SortBy-size span').html(this.translate('top_100_header_size'));
			$('#Top100-SortBy-modified span').html(this.translate('top_100_header_modified'));
			$('#Top100-SortBy-path span').html(this.translate('top_100_header_path'));
		}

		if (!part || part == 'footer') {
			$('#Footer').html(this.translate(this.settings ? 'footer_with_created' : 'footer',
				this.settings ? this.settings.created.htmlencode() : null,
				$('Disk Usage Reports'),
				$('').text(this._languages[this.language].language_name)
			));
		}

		// Resize window in case the header was previously hidden.
		this.resizeWindow();
	},

	getSupportedLanguages: function() {
		return $.getArrayKeys(this._languages);
	},

	parseAcceptLanguage: function(rules) {
		// Remove whitespace and split into rules.
		rules = rules.toLowerCase().replace(/\s/g, '').split(',');

		for (var i = rules.length - 1; i >= 0; i--) {
			var split = rules[i].match(/^(([a-z]{1,8})(-([a-z]{1,8}))?)(;q=((1(\.[0]{1,3})?)|(0(\.[0-9]{1,3})?)))?$/i);
			// 1 = full language tag
			// 2 = primary language tag
			// 4 = secondary language tag
			// 6 = quality

			if (split) {
				rules[i] = { order: i, full: split[1], primary: split[2], secondary: split[4] ? split[4] : null, quality: isNaN(split[6]) ? 1 : parseFloat(split[6]) };
			}
			else {
				rules.splice(i, 1);
			}
		}

		this.sortLanguageRules(rules);

		return rules;
	},

	sortLanguageRules: function(rules) {
		rules.sort(function(a,b){
			var acomp = a.lang ? a.lang == a.full ? 0 : 1 : 0;
			var bcomp = b.lang ? b.lang == b.full ? 0 : 1 : 0;
			if (a.order == b.order && acomp != bcomp) {
				return acomp - bcomp;
			}
			else if (a.quality == 0 || b.quality == 0 || a.quality == b.quality) {
				return a.order  - b.order;
			}
			else {
				return b.quality - a.quality;
			}
		});
	},

	parseLanguageTag: function(lang) {
		var match = lang.replace(/\s/g, '').match(/^(([a-z]{1,8})(-([a-z]{1,8}))?)$/i);
		if (match)
			return { full: match[1], primary: match[2], secondary: match[4] ? match[4] : null };
		else
			return false;
	},

	determineRuleForLanguage: function(rules, lang) {
		var langMatch = this.parseLanguageTag(lang),
			ret = false;

		if (langMatch !== false) {
			for (var i = 0; i < rules.length; i++) {
				// The rule is an exact match.
				if (rules[i].full == langMatch.full) {
					return rules[i];
				}

				// The primary part of the language tag matches.
				else if (rules[i].primary == langMatch.primary) {
					// Our first partial match.
					if (ret === false) {
						ret = rules[i];
					}
					// Only override the current partial match if it is more general.
					// Ex: For the lang 'en-xx', 'en' would override 'en-yy', but not vise vera.
					else if (ret.secondary != null && rules[i].secondary == null) {
						ret = rules[i];
					}
				}
			}
		}
		return ret;
	},

	matchSupportedLanguagesToRules: function(rules) {
		var supported = this.getSupportedLanguages();

		// Parse the rules as an Accept-Language header if it is a string.
		if ($.isString(rules)) rules = this.parseAcceptLanguage(rules);

		// Go through the list of supported languages and determine what matches the language rules.
		for (var i = supported.length - 1; i >= 0; i--) {
			var matchedRule = this.determineRuleForLanguage(rules, supported[i]);
			if (matchedRule === false || matchedRule.quality == 0) {
				supported.splice(i, 1);
			}
			else {
				supported[i] = $.extend({ lang: supported[i] }, matchedRule);
			}

		}

		this.sortLanguageRules(supported);

		return supported;
	}
});

})(jQuery);<|endoftext|>


<commit_before>// server.js

// // BASE SETUP
// // =============================================================================

var express    = require('express');        // call express
var app        = express();                 // define our app using express
var bodyParser = require('body-parser');

// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var port = process.env.PORT || 8080;        // set our port

// ROUTES FOR OUR API
// =============================================================================
var router = express.Router();              // get an instance of the express Router

// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
router.get('/', function(req, res) {
    res.json({ message: 'hooray! welcome to our api!' });
});

// more routes for our API will happen here

// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);

// START THE SERVER
// =============================================================================
app.listen(port);
console.log('Magic happens on port ' + port);
<commit_msg>Allow for database access<commit_after>// server.js

// // BASE SETUP
// // =============================================================================
//
var mongoose   = require('mongoose');
mongoose.connect('mongodb://localhost/assidu_node')

var express    = require('express');        // call express
var app        = express();                 // define our app using express
var bodyParser = require('body-parser');

// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var port = process.env.PORT || 8080;        // set our port

// ROUTES FOR OUR API
// =============================================================================
var router = express.Router();              // get an instance of the express Router

// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
router.get('/', function(req, res) {
    res.json({ message: 'hooray! welcome to our api!' });
});

// more routes for our API will happen here

// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);

// START THE SERVER
// =============================================================================
app.listen(port);
console.log('Magic happens on port ' + port);
<|endoftext|>