<commit_before>var mongoose = require('mongoose');
var feedparser = require('feedparser')
var request = require('request');

var feedSchema = mongoose.Schema({
    _id:              { type: String, required: true, unique: true },
    title:            String,
    description:      String,
    link:             String,
    xmlUrl:           String,
    date:             { type: Date },
    pubDate:          { type: Date },
    author:           String,
    language:         String,
    image:            { title: String, url: String },
    favicon:          String,
    copyright:        String,
    generator:        String,
    categories:       [String]
});

feedSchema.virtual('url').get(function () {
  return this._id;
});

feedSchema.virtual('url').set(function (url) {
  this._id = url;
});

var Feed = mongoose.model('Feed', feedSchema);

var articleSchema = mongoose.Schema({
    title:            String,
    description:      String,
    link:             String,
    origLink:         String,
    date:             { type: Date },
    pubDate:          { type: Date },
    author:           String,
    guid:             String,
    comments:         String,
    image:            { title: String, url: String },
    categories:       [String],
    source:           { title: String, url: String },
    enclosures:       [{ url: String, type: String, length: String }],
    feed:             { type: String, ref: 'Feed' },
}, { capped: capForArticlesInBytes });

var Article = mongoose.model('Article', articleSchema);

var timeoutInMs = 10000;
var defaultMongoDbPort = 27017;
var updateIntervalHandle = null;
var capForArticlesInBytes = 100000;

function connect(uri, database, port) {
    port = typeof port !== 'undefined' ? port : defaultMongoDbPort;
    mongoose.connect(uri, database, port);

    mongoose.connection.on('error', function(e){
        console.error("Could not connect to MongoDB: "+e.message);
        process.exit(1);
    });

    mongoose.connection.once('open', function callback () {
        console.log("Connection to MongoDB was successful.");
    });
}

function addFeed(url) {
    Feed.count({ _id: url }, function(error, count) {
        if(error != null) {
            console.error('Failed to check if feed exists in MongoDB: '+error);
        }
        else {
            if(count == 0) {
               requestAndParseFeed(url);
            }
            else if(count == 1) {
                console.warn('Feed '+url+' already exists in the MongoDB. '
                + 'Skipping database write.');
            }
            else {
                console.error('Multiple documents with same url exist in '
                + 'MongoDB. This should not happen!');
            }
        }
    });
}

function requestAndParseFeed(url) {
    request(createRequest(url), function(error, response, body)
        { handleResponse(url, error, response, body) });
}

function createRequest(url) {
    var request = { uri: url, timeout: timeoutInMs };
    return request;
}

function handleResponse(url, error, response, body) {
    if(error != null) {
        console.error('Failed to handle HTTP response: '+error);
    }
    else if (response.statusCode == 200) {
        feedparser.parseString(body)
        .on('meta', function(meta) { saveOrUpdateFeedMeta(meta, url) })
        .on('article', function(article) { saveOrUpdateArticle(article, url) })
        .on('error', function(error) { handleParseError(error, url) });
    }
    else if (response.statusCode == 304) {
        console.log('Feed '+url+' not modified (304). Skipping parsing.' );
    }
    else {
        console.warn('HTTP status code received that can not be handled by '
            + 'this module: '+response.statusCode);
    }
}

function handleParseError(error, url) {
    console.error('Could not parse feed at '+url+'. '+error);
}

function saveOrUpdateFeedMeta(meta, url) {
    Feed.findOne({ _id: url }, function(error, feedDocument) {
        if(error != null) {
            console.error('Failed to get feed from MongoDB: '+error);
        }
        else {
            if(feedDocument == null) {
                saveFeedMeta(meta, url);
            }
            else {
                updateFeedMeta(feedDocument, meta, url);
            }
        }
    });
}

function saveFeedMeta(meta, url) {
    var feedDocument = createFeedDocument(meta, url);

    feedDocument.save(function (error, feedDocument) {
        if(error != null) {
            console.error('Failed to save feed meta to MongoDB: '+error);
        }
        else {
            console.log('Added new feed "'+feedDocument.url+'" to MongoDB.');
        }
    });
}

function updateFeedMeta(feedDocument, meta) {
    if(feedRequiresUpdate(feedDocument, meta)) {
        feedDocument.title = meta.title;
        feedDocument.description = meta.description;
        feedDocument.link = meta.link;
        feedDocument.xmlUrl = meta.xmlUrl;
        feedDocument.date = meta.date;
        feedDocument.pubDate = meta.pubDate;
        feedDocument.author = meta.author;
        feedDocument.language = meta.language;
        feedDocument.image = meta.image;
        feedDocument.favicon = meta.favicon;
        feedDocument.copyright = meta.copyright;
        feedDocument.generator = meta.generator;
        feedDocument.categories = meta.categories;

        feedDocument.save(function (error, feedDocument) {
            if(error != null) {
                console.error('Failed to update feed meta to MongoDB: '+error);
            }
            else {
                console.log('Updated feed "'+feedDocument.url+'" in MongoDB.');
            }
        });
    }
}

function feedRequiresUpdate(feedDocument, meta) {
    var requiresUpdate = false;

    if(feedDocument.title != meta.title &&
        feedDocument.description != meta.description &&
        feedDocument.link != meta.link &&
        feedDocument.xmlUrl != meta.xmlUrl &&
        feedDocument.author != meta.author &&
        feedDocument.language != meta.language &&
        feedDocument.image.title != meta.image.title &&
        feedDocument.image.url != meta.image.url &&
        feedDocument.favicon != meta.favicon &&
        feedDocument.copyright != meta.copyright &&
        feedDocument.generator != meta.generator &&
        JSON.stringify(feedDocument.categories) !=
            JSON.stringify(meta.categories)) {
        requiresUpdate = true;
    }

    return requiresUpdate;
}

function createFeedDocument(meta, url) {
    return new Feed({ url: url, title: meta.title,
        description: meta.description, link: meta.link, xmlUrl: meta.xmlUrl,
        date: meta.date, pubDate: meta.pubDate, author: meta.author,
        language: meta.language, image: meta.image, favicon: meta.favicon,
        copyright: meta.copyright, generator: meta.generator,
        categories: meta.categories });
}

function saveOrUpdateArticle(article, url) {
    Article.findOne({ guid: article.guid, feed: url }, function(error, articleDocument) {
        if(error != null) {
            console.error('Failed to get article from MongoDB: '+error);
        }
        else {
            if(articleDocument == null) {
                saveArticle(article, url);
            }
            else {
                updateArticle(articleDocument, article);
            }
        }
    });
}

function saveArticle(article, url) {
    var articleDocument = createArticleDocument(article, url);

    articleDocument.save(function (error, articleDocument) {
        if(error != null) {
            console.error('Failed to save article to MongoDB: '+error);
        }
        else {
            console.log('Added new article "'+articleDocument.guid
            + '" to MongoDB.');
        }
    });
}

function updateArticle(articleDocument, article) {
    if(articleRequiresUpdate(articleDocument, article)) {
        articleDocument.title = article.title;
        articleDocument.description = article.description;
        articleDocument.link = article.link;
        articleDocument.xmlUrl = article.xmlUrl;
        articleDocument.date = article.date;
        articleDocument.pubDate = article.pubDate;
        articleDocument.author = article.author;
        articleDocument.language = article.language;
        articleDocument.image = article.image;
        articleDocument.favicon = article.favicon;
        articleDocument.copyright = article.copyright;
        articleDocument.generator = article.generator;
        articleDocument.categories = article.categories;

        articleDocument.save(function (error, feedDocument) {
            if(error != null) {
                console.error('Failed to update article to MongoDB: '+error);
            }
            else {
                console.log('Updated article "'+articleDocument.guid+'" in MongoDB.');
            }
        });
    }
}

function articleRequiresUpdate(articleDocument, article) {
    var requiresUpdate = false;

    if(articleDocument.title != article.title &&
        articleDocument.description != article.description &&
        articleDocument.link != article.link &&
        articleDocument.origLink != article.origLink &&
        articleDocument.author != article.author &&
        articleDocument.guid != article.guid &&
        articleDocument.comments != article.comments &&
        articleDocument.image.title != article.image.title &&
        articleDocument.image.url != article.image.url &&
        JSON.stringify(articleDocument.categories) !=
            JSON.stringify(article.categories) &&
        articleDocument.source.title != article.source.title &&
        articleDocument.source.url != article.source.url &&
        articleDocument.enclosures.url != article.enclosures.url &&
        articleDocument.enclosures.type != article.enclosures.type &&
        articleDocument.enclosures.length != article.enclosures.length) {
        requiresUpdate = true;
    }

    return requiresUpdate;

}

function createArticleDocument(article, url) {
    return new Article({ title: article.title,
        description: article.description, link: article.link,
        origLink: article.origLink, author: article.author, guid: article.guid,
        comments: article.comments, image: article.image,
        categories: article.categories, source: article.source,
        enclosures: article.enclosures, feed: url });
}

function updateDatabase() {
    Feed.find(function(error, feedDocuments) {
        if(error != null) {
            console.error('Failed to get feeds from MongoDB: '+error);
        }
        else {
            feedDocuments.forEach(function(feedDocument) {
                console.log('Updating feed and articles from '
                + feedDocument.url);
                requestAndParseFeed(feedDocument.url);
            });
        }
    });
}

function updateDatabaseAtInterval(seconds) {
    if(updateIntervalHandle == null) {
        updateIntervalHandle = setInterval(updateDatabase, seconds);
    }
    else {
        console.warn('Database update interval already set. Skipping.');
    }
}

function stopUpdateDataBaseAtInterval() {
    if(updateIntervalHandle != null) {
        clearInterval(updateIntervalHandle);
    }
}

exports.connect = connect;
exports.addFeed = addFeed;
exports.updateDatabase = updateDatabase;
exports.updateDatabaseAtInterval = updateDatabaseAtInterval;
exports.stopUpdateDataBaseAtInterval = stopUpdateDataBaseAtInterval;
<commit_msg>Changed uri key of request to url.<commit_after>var mongoose = require('mongoose');
var feedparser = require('feedparser')
var request = require('request');

var feedSchema = mongoose.Schema({
    _id:              { type: String, required: true, unique: true },
    title:            String,
    description:      String,
    link:             String,
    xmlUrl:           String,
    date:             { type: Date },
    pubDate:          { type: Date },
    author:           String,
    language:         String,
    image:            { title: String, url: String },
    favicon:          String,
    copyright:        String,
    generator:        String,
    categories:       [String]
});

feedSchema.virtual('url').get(function () {
  return this._id;
});

feedSchema.virtual('url').set(function (url) {
  this._id = url;
});

var Feed = mongoose.model('Feed', feedSchema);

var articleSchema = mongoose.Schema({
    title:            String,
    description:      String,
    link:             String,
    origLink:         String,
    date:             { type: Date },
    pubDate:          { type: Date },
    author:           String,
    guid:             String,
    comments:         String,
    image:            { title: String, url: String },
    categories:       [String],
    source:           { title: String, url: String },
    enclosures:       [{ url: String, type: String, length: String }],
    feed:             { type: String, ref: 'Feed' },
}, { capped: capForArticlesInBytes });

var Article = mongoose.model('Article', articleSchema);

var timeoutInMs = 10000;
var defaultMongoDbPort = 27017;
var updateIntervalHandle = null;
var capForArticlesInBytes = 100000;

function connect(uri, database, port) {
    port = typeof port !== 'undefined' ? port : defaultMongoDbPort;
    mongoose.connect(uri, database, port);

    mongoose.connection.on('error', function(e){
        console.error("Could not connect to MongoDB: "+e.message);
        process.exit(1);
    });

    mongoose.connection.once('open', function callback () {
        console.log("Connection to MongoDB was successful.");
    });
}

function addFeed(url) {
    Feed.count({ _id: url }, function(error, count) {
        if(error != null) {
            console.error('Failed to check if feed exists in MongoDB: '+error);
        }
        else {
            if(count == 0) {
               requestAndParseFeed(url);
            }
            else if(count == 1) {
                console.warn('Feed '+url+' already exists in the MongoDB. '
                + 'Skipping database write.');
            }
            else {
                console.error('Multiple documents with same url exist in '
                + 'MongoDB. This should not happen!');
            }
        }
    });
}

function requestAndParseFeed(url) {
    request(createRequest(url), function(error, response, body)
        { handleResponse(url, error, response, body) });
}

function createRequest(url) {
    var request = { url: url, timeout: timeoutInMs };
    return request;
}

function handleResponse(url, error, response, body) {
    if(error != null) {
        console.error('Failed to handle HTTP response: '+error);
    }
    else if (response.statusCode == 200) {
        feedparser.parseString(body)
        .on('meta', function(meta) { saveOrUpdateFeedMeta(meta, url) })
        .on('article', function(article) { saveOrUpdateArticle(article, url) })
        .on('error', function(error) { handleParseError(error, url) });
    }
    else if (response.statusCode == 304) {
        console.log('Feed '+url+' not modified (304). Skipping parsing.' );
    }
    else {
        console.warn('HTTP status code received that can not be handled by '
            + 'this module: '+response.statusCode);
    }
}

function handleParseError(error, url) {
    console.error('Could not parse feed at '+url+'. '+error);
}

function saveOrUpdateFeedMeta(meta, url) {
    Feed.findOne({ _id: url }, function(error, feedDocument) {
        if(error != null) {
            console.error('Failed to get feed from MongoDB: '+error);
        }
        else {
            if(feedDocument == null) {
                saveFeedMeta(meta, url);
            }
            else {
                updateFeedMeta(feedDocument, meta, url);
            }
        }
    });
}

function saveFeedMeta(meta, url) {
    var feedDocument = createFeedDocument(meta, url);

    feedDocument.save(function (error, feedDocument) {
        if(error != null) {
            console.error('Failed to save feed meta to MongoDB: '+error);
        }
        else {
            console.log('Added new feed "'+feedDocument.url+'" to MongoDB.');
        }
    });
}

function updateFeedMeta(feedDocument, meta) {
    if(feedRequiresUpdate(feedDocument, meta)) {
        feedDocument.title = meta.title;
        feedDocument.description = meta.description;
        feedDocument.link = meta.link;
        feedDocument.xmlUrl = meta.xmlUrl;
        feedDocument.date = meta.date;
        feedDocument.pubDate = meta.pubDate;
        feedDocument.author = meta.author;
        feedDocument.language = meta.language;
        feedDocument.image = meta.image;
        feedDocument.favicon = meta.favicon;
        feedDocument.copyright = meta.copyright;
        feedDocument.generator = meta.generator;
        feedDocument.categories = meta.categories;

        feedDocument.save(function (error, feedDocument) {
            if(error != null) {
                console.error('Failed to update feed meta to MongoDB: '+error);
            }
            else {
                console.log('Updated feed "'+feedDocument.url+'" in MongoDB.');
            }
        });
    }
}

function feedRequiresUpdate(feedDocument, meta) {
    var requiresUpdate = false;

    if(feedDocument.title != meta.title &&
        feedDocument.description != meta.description &&
        feedDocument.link != meta.link &&
        feedDocument.xmlUrl != meta.xmlUrl &&
        feedDocument.author != meta.author &&
        feedDocument.language != meta.language &&
        feedDocument.image.title != meta.image.title &&
        feedDocument.image.url != meta.image.url &&
        feedDocument.favicon != meta.favicon &&
        feedDocument.copyright != meta.copyright &&
        feedDocument.generator != meta.generator &&
        JSON.stringify(feedDocument.categories) !=
            JSON.stringify(meta.categories)) {
        requiresUpdate = true;
    }

    return requiresUpdate;
}

function createFeedDocument(meta, url) {
    return new Feed({ url: url, title: meta.title,
        description: meta.description, link: meta.link, xmlUrl: meta.xmlUrl,
        date: meta.date, pubDate: meta.pubDate, author: meta.author,
        language: meta.language, image: meta.image, favicon: meta.favicon,
        copyright: meta.copyright, generator: meta.generator,
        categories: meta.categories });
}

function saveOrUpdateArticle(article, url) {
    Article.findOne({ guid: article.guid, feed: url }, function(error, articleDocument) {
        if(error != null) {
            console.error('Failed to get article from MongoDB: '+error);
        }
        else {
            if(articleDocument == null) {
                saveArticle(article, url);
            }
            else {
                updateArticle(articleDocument, article);
            }
        }
    });
}

function saveArticle(article, url) {
    var articleDocument = createArticleDocument(article, url);

    articleDocument.save(function (error, articleDocument) {
        if(error != null) {
            console.error('Failed to save article to MongoDB: '+error);
        }
        else {
            console.log('Added new article "'+articleDocument.guid
            + '" to MongoDB.');
        }
    });
}

function updateArticle(articleDocument, article) {
    if(articleRequiresUpdate(articleDocument, article)) {
        articleDocument.title = article.title;
        articleDocument.description = article.description;
        articleDocument.link = article.link;
        articleDocument.xmlUrl = article.xmlUrl;
        articleDocument.date = article.date;
        articleDocument.pubDate = article.pubDate;
        articleDocument.author = article.author;
        articleDocument.language = article.language;
        articleDocument.image = article.image;
        articleDocument.favicon = article.favicon;
        articleDocument.copyright = article.copyright;
        articleDocument.generator = article.generator;
        articleDocument.categories = article.categories;

        articleDocument.save(function (error, feedDocument) {
            if(error != null) {
                console.error('Failed to update article to MongoDB: '+error);
            }
            else {
                console.log('Updated article "'+articleDocument.guid+'" in MongoDB.');
            }
        });
    }
}

function articleRequiresUpdate(articleDocument, article) {
    var requiresUpdate = false;

    if(articleDocument.title != article.title &&
        articleDocument.description != article.description &&
        articleDocument.link != article.link &&
        articleDocument.origLink != article.origLink &&
        articleDocument.author != article.author &&
        articleDocument.guid != article.guid &&
        articleDocument.comments != article.comments &&
        articleDocument.image.title != article.image.title &&
        articleDocument.image.url != article.image.url &&
        JSON.stringify(articleDocument.categories) !=
            JSON.stringify(article.categories) &&
        articleDocument.source.title != article.source.title &&
        articleDocument.source.url != article.source.url &&
        articleDocument.enclosures.url != article.enclosures.url &&
        articleDocument.enclosures.type != article.enclosures.type &&
        articleDocument.enclosures.length != article.enclosures.length) {
        requiresUpdate = true;
    }

    return requiresUpdate;

}

function createArticleDocument(article, url) {
    return new Article({ title: article.title,
        description: article.description, link: article.link,
        origLink: article.origLink, author: article.author, guid: article.guid,
        comments: article.comments, image: article.image,
        categories: article.categories, source: article.source,
        enclosures: article.enclosures, feed: url });
}

function updateDatabase() {
    Feed.find(function(error, feedDocuments) {
        if(error != null) {
            console.error('Failed to get feeds from MongoDB: '+error);
        }
        else {
            feedDocuments.forEach(function(feedDocument) {
                console.log('Updating feed and articles from '
                + feedDocument.url);
                requestAndParseFeed(feedDocument.url);
            });
        }
    });
}

function updateDatabaseAtInterval(seconds) {
    if(updateIntervalHandle == null) {
        updateIntervalHandle = setInterval(updateDatabase, seconds);
    }
    else {
        console.warn('Database update interval already set. Skipping.');
    }
}

function stopUpdateDataBaseAtInterval() {
    if(updateIntervalHandle != null) {
        clearInterval(updateIntervalHandle);
    }
}

exports.connect = connect;
exports.addFeed = addFeed;
exports.updateDatabase = updateDatabase;
exports.updateDatabaseAtInterval = updateDatabaseAtInterval;
exports.stopUpdateDataBaseAtInterval = stopUpdateDataBaseAtInterval;
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import './style/about.css';

class AboutMe extends Component {

  render() {
    return (

About Me

            I'm a senior software engineer in Fort Worth, TX currently working on automotive telematics
            systems (ATS) for Spireon, Inc.

LinkedIn

GitHub

GMail

@Twitter

    );
  }
}

export default AboutMe;<commit_msg>Fixing about me link to linked in<commit_after>import React, { Component } from 'react';

import './style/about.css';

class AboutMe extends Component {

  render() {
    return (

About Me

            I'm a senior software engineer in Fort Worth, TX currently working on automotive telematics
            systems (ATS) for Spireon, Inc.

LinkedIn

GitHub

GMail

@Twitter

    );
  }
}

export default AboutMe;<|endoftext|>


<commit_before>"use strict";
/* jshint undef: true, unused: true */
/* global Hogan, promise  */

var Applet = function () {
  var i = this; // === this instance

  i.funcs = [];

  i.data_cache = {};

  i.new_func(_.flatten(_.toArray(arguments)));

  i.run('constructor');
  i.run('dom');
}; // === Applet constructor ===========================

(function () { // === scope ==============================

  // =====================================================
  // === HELPERS
  // =====================================================

  var log;

  log = Applet.log = function () {
    if (window.console)
      return console.log.apply(console, arguments);
    return this;
  };

  // Examples:
  //
  //   .new_id()           ->  Integer
  //   .new_id('prefix_')  ->  String
  //
  Applet.new_id = function (prefix) {
    if (!Applet.hasOwnProperty('_id'))
      Applet._id = -1;
    Applet._id = Applet._id + 1;
    return (prefix) ? prefix + Applet._id : Applet._id;
  }; // === func

  // Returns id.
  // Sets id of element if no id is set.
  //
  // .dom_id(raw_or_jquery)
  // .dom_id('prefix', raw_or_jquer)
  //
  Applet.dom_id = function () {
    var args = _.toArray(arguments);
    var o      = _.find(args, _.negate(_.isString));
    var prefix = _.find(args, _.isString);
    var old    = o.attr('id');

    if (old && !_.isEmpty(old))
      return old;

    var new_id = Applet.new_id(prefix || 'id_for_applet_') ;
    o.attr('id', new_id);
    return new_id;
  }; // === id

  Applet.insert_after = function (script) {
    $($(script).contents()).insertAfter($(script));
  }; // === insert_after

  Applet.insert_before = function (script) {
    $($(script).contents()).insertBefore($(script));
  }; // === insert_before

  Applet.is_true = function (data, raw_key) {
    if (!Applet.FRONT_BANGS)
      Applet.FRONT_BANGS = /^\!+/;

    var key        = _.trim(raw_key);
    var bang_match = key.match(Applet.FRONT_BANGS);
    var dots       = ( bang_match ? key.replace(bang_match[0], '') : key ).split('.');
    var keys       = _.map( dots, _.trim );

    var current = data;
    var ans  = false;

    _.detect(keys, function (key) {
      if (_.has(current, key)) {
        current = data[key];
        ans = !!current;
      } else {
        ans = undefined;
      }

      return !ans;
    });

    if (ans === undefined)
      return ans;

    if (bang_match) {
      _.times(bang_match[0].length, function () {
        ans = !ans;
      });
    }

    return ans;
  }; // === func

  Applet.attrs = function (dom) {
    return _.reduce(
      dom.attributes,
      function (kv, o) {
        kv[o.name] = o.value;
        return kv;
      },
      {}
    );
  }; // === attrs

  Applet.standard_name = function (str) {
    return _.trim(str).replace(/\ +/g, ' ').toLowerCase();
  };

  Applet.remove_attr = function (node, name) {
    var val = $(node).attr(name);
    $(node).removeAttr(name);
    return val;
  };

  Applet.node_array = function (unknown) {
    var arr = [];
    _.each($(unknown), function (dom) {
      if (dom.nodeType !== 1)
        return arr.push(dom);

      arr.push({
        tag    : dom.nodeName,
        attrs  : Applet.attrs(dom),
        custom : {},
        childs : Applet.node_array($(dom).contents())
      });
    });

    return arr;
  };

  Applet.top_descendents = function (dom, selector) {
    var arr = [];
    _.each($(dom), function (node) {
      var o = $(node);
      if (o.is(selector))
        return arr.push(o);
      arr = arr.concat(Applet.top_descendents(o.children(), selector));
    }); // === func

    return arr;
  }; // === func

  // =====================================================
  // === PROTOTYPE
  // =====================================================

  Applet.prototype.config_for_func = function (f) {
    var i = this;

    if (!i.func_ids) {
      i.func_id_to_config = {}; // === used by callbacks to store info.
      i.func_ids          = {}; // give each callback an id to be used in .configs
    }

    var id = _.findKey(i.func_ids, function (v) { return v === f; } );

    if (!id) {
      id                      = Applet.new_id('config_id_');
      i.func_id_to_config[id] = {};
      i.func_ids[id]          = f;
    }

    return i.func_id_to_config[id];
  }; // === config_id

  // === Examples:
  //
  // .run('str')               => {name: 'str'}
  // .run('str', {...})        => {name: 'str', data: {...}}
  // .run({name: 'str', ... }) => {name: 'str', ... }
  //
  Applet.prototype.run = function (msg, data) {

    var o = null;
    var instance = this;

    if (_.isPlainObject(msg)) {
      o = msg;
    } else { // === is String
      o = {
        name : Applet.standard_name(msg),
        data : data
      };
    }

    o.applet = instance;

    o.name = Applet.standard_name(o.name);

    if (o.name === 'data' && o.data)
      instance.data_cache = _.extend(instance.data_cache, o.data);

    var i = 0, f;

    // if (o.name.indexOf('ajax') > -1)
    // log(instance)
    while (instance.funcs[i]) {
      f             = instance.funcs[i];
      o.this_config = instance.config_for_func(f);
      o.this_func   = f;
      o.data_cache  = instance.data_cache;

      f(o);
      ++i;
    }

    return instance;
  }; // === func

  Applet.prototype.new_func = function () {

    var arr = _.flatten(_.toArray(arguments));
    var i   = this;

    _.each(arr, function (f) {
      if (!_.isFunction(f))
        throw new Error('Error: Not a function');
      if (f({name : 'this position', applet: i}) === 'top')
        i.funcs.unshift(f);
      else
        i.funcs.push(f);
    });

    return this;
  };

  // =====================================================
  // === FUNCTIONS
  // =====================================================

  Applet.funcs = {};

  // === template ====================
  Applet.funcs.template = function (o) {
    if (o.name === 'this position')
      return 'top';

    if (o.name !== 'dom')
      return;

    var selector    = 'script[type^="text/mustache"]:not(script.compiled)';
    var scripts     = (o.target || $('body')).find(selector).addBack(selector);
    if (scripts.length < 1)
      return;

    if (!o.this_func.render) {
      o.this_func.render = function (o) {
        var meta = this;
        if (o.name !== 'data' || !_.isPlainObject(o.data[meta.key]))
          return;

        // === Remove old nodes:
        if (meta.elements && meta.pos === 'replace') {
          meta.elements.remove();
        }

        var html = $(meta.mustache.render(o.data));
        if (meta.pos === 'replace' || meta.pos === 'bottom')
          html.insertBefore($('#' + meta.placeholder_id));
        else
          html.insertAfter($('#' + meta.placeholder_id));

        meta.elements = html;
        o.applet.run({
          name   : 'dom',
          target : html
        });
      };
    } // === if render

    var i = 0, t, html, data_key, placeholder_id, id, pos, types;
    while (scripts[i]) {
      t    = $(scripts[i]);
      types = t.attr('type').split('/');
      html = t.html();
      t.addClass('compiled');
      ++i;

      placeholder_id = Applet.dom_id(t);
      data_key       = types[2];
      id             = Applet.dom_id(t, 'mustache_templates_' + (data_key || ''));

      switch (_.trim(types[1])) {

        case 'mustache-top':
          pos = 'top';
          break;

        case 'mustache-bottom':
          pos = 'bottom';
          break;

        default:
          pos = 'replace';

      } // === switch type[1]

      o.applet.new_func(o.this_func.render.bind({
        id             : id,
        key            : data_key,
        html           : html,
        mustache       : Hogan.compile(html),
        placeholder_id : placeholder_id,
        elements       : null,
        pos            : pos
      }));

    } // === while

  }; // ==== funcs: template ==========

  // === show_if ====================
  Applet.funcs.show_if = function (o) {
    if (o.name !== 'dom') return;

    var selector   = '*[data-show_if]';
    var targets    = $(o.target || $('body')).find(selector).addBack(selector);

    _.each(targets, function (raw_node) {
      var node    = $(raw_node);
      var the_key = Applet.remove_attr(raw_node, 'data-show_if');

      if (!Applet.is_true(o.data_cache, the_key))
        node.hide();

      var the_id  = Applet.dom_id(node);

      o.applet.new_func(
        function (o) {
          if (o.name !== 'data') return;

          switch (Applet.is_true(o.data, the_key)) {
            case true:
              $('#' + the_id).show();
            return;

            case false:
              $('#' + the_id).hide();
            return;
          } // === switch value

        } // === func
      );

    }); // === each

  }; // === funcs: show_if ========

  // === hide_if ====================
  Applet.funcs.hide_if = function (o) {
    if (o.name !== 'dom') return;

    var selector   = '*[data-hide_if]';
    var targets    = $(o.target || $('body')).find(selector).addBack(selector);

    _.each(targets, function (raw_node) {
      var node = $(raw_node);
      var key  = Applet.remove_attr(node, 'data-hide_if');

      if (Applet.is_true(o.data_cache, key) === true)
        node.hide();

      var id  = Applet.dom_id(node);

      o.applet.new_func(
        function (o) {
          if (o.name !== 'data') return;

          switch (Applet.is_true(o.data, key)) {
            case true:
              $('#' + id).hide();
            return;
            case false:
              $('#' + id).show();
            return;
          } // === switch value
        }
      ); // === new_func

    }); // === each target

  }; // === funcs: hide_if ========

  // === ajax =====================
  Applet.funcs.ajax = function (o) {

    switch (o.name) {

      case 'ajax':
        if (o['send?']) {
          o.promise = promise.post(o.url, o.data, o.headers);
          o.promise.then(function (err, text, xhr) {
            o.applet.run({
              name: 'ajax response',
              err: err,
              text: text,
              xhr: xhr,
              promise: o.promise
            });
          });
        }
      break;

    } // === switch o.name

  }; // === funcs: ajax

  // === form =====================
  Applet.funcs.form = function (o) {

    if (o.name !== 'dom')
      return;

    var selector = $('form:not(form.compiled) button.submit');
    var target   = $(o.target || $('body')).find(selector).addBack(selector);

    if (!o.this_func.submit) {
      o.this_func.submit = function (a) {
        return function (e) {
          var form = $(this).parent('form');
          e.preventDefault();
          e.stopPropagation();
          a.run({
            name    : 'ajax',
            'send?' : true,
            form_id : form.attr('id'),
            url     : form.attr('action'),
            headers : {"Accept": "application/json"},
            data    : form.serializeJSON()
          });
        };
      };
    } // === if this_func

    var i = 0;
    while (target[i]) {
      $(target[i]).on('click', o.this_func.submit(o.applet));
      ++i;
    } // === while
  }; // === funcs: form

})(); // === end scope =================================

<commit_msg>Refactored: template<commit_after>"use strict";
/* jshint undef: true, unused: true */
/* global Hogan, promise  */

var Applet = function () {
  var i = this; // === this instance

  i.funcs = [];

  i.data_cache = {};

  i.new_func(_.flatten(_.toArray(arguments)));

  i.run('constructor');
  i.run('dom');
}; // === Applet constructor ===========================

(function () { // === scope ==============================

  // =====================================================
  // === HELPERS
  // =====================================================

  var log;

  log = Applet.log = function () {
    if (window.console)
      return console.log.apply(console, arguments);
    return this;
  };

  // Examples:
  //
  //   .new_id()           ->  Integer
  //   .new_id('prefix_')  ->  String
  //
  Applet.new_id = function (prefix) {
    if (!Applet.hasOwnProperty('_id'))
      Applet._id = -1;
    Applet._id = Applet._id + 1;
    return (prefix) ? prefix + Applet._id : Applet._id;
  }; // === func

  // Returns id.
  // Sets id of element if no id is set.
  //
  // .dom_id(raw_or_jquery)
  // .dom_id('prefix', raw_or_jquer)
  //
  Applet.dom_id = function () {
    var args = _.toArray(arguments);
    var o      = _.find(args, _.negate(_.isString));
    var prefix = _.find(args, _.isString);
    var old    = o.attr('id');

    if (old && !_.isEmpty(old))
      return old;

    var new_id = Applet.new_id(prefix || 'id_for_applet_') ;
    o.attr('id', new_id);
    return new_id;
  }; // === id

  Applet.insert_after = function (script) {
    $($(script).contents()).insertAfter($(script));
  }; // === insert_after

  Applet.insert_before = function (script) {
    $($(script).contents()).insertBefore($(script));
  }; // === insert_before

  Applet.is_true = function (data, raw_key) {
    if (!Applet.FRONT_BANGS)
      Applet.FRONT_BANGS = /^\!+/;

    var key        = _.trim(raw_key);
    var bang_match = key.match(Applet.FRONT_BANGS);
    var dots       = ( bang_match ? key.replace(bang_match[0], '') : key ).split('.');
    var keys       = _.map( dots, _.trim );

    var current = data;
    var ans  = false;

    _.detect(keys, function (key) {
      if (_.has(current, key)) {
        current = data[key];
        ans = !!current;
      } else {
        ans = undefined;
      }

      return !ans;
    });

    if (ans === undefined)
      return ans;

    if (bang_match) {
      _.times(bang_match[0].length, function () {
        ans = !ans;
      });
    }

    return ans;
  }; // === func

  Applet.attrs = function (dom) {
    return _.reduce(
      dom.attributes,
      function (kv, o) {
        kv[o.name] = o.value;
        return kv;
      },
      {}
    );
  }; // === attrs

  Applet.standard_name = function (str) {
    return _.trim(str).replace(/\ +/g, ' ').toLowerCase();
  };

  Applet.remove_attr = function (node, name) {
    var val = $(node).attr(name);
    $(node).removeAttr(name);
    return val;
  };

  Applet.node_array = function (unknown) {
    var arr = [];
    _.each($(unknown), function (dom) {
      if (dom.nodeType !== 1)
        return arr.push(dom);

      arr.push({
        tag    : dom.nodeName,
        attrs  : Applet.attrs(dom),
        custom : {},
        childs : Applet.node_array($(dom).contents())
      });
    });

    return arr;
  };

  Applet.top_descendents = function (dom, selector) {
    var arr = [];
    _.each($(dom), function (node) {
      var o = $(node);
      if (o.is(selector))
        return arr.push(o);
      arr = arr.concat(Applet.top_descendents(o.children(), selector));
    }); // === func

    return arr;
  }; // === func

  // =====================================================
  // === PROTOTYPE
  // =====================================================

  Applet.prototype.config_for_func = function (f) {
    var i = this;

    if (!i.func_ids) {
      i.func_id_to_config = {}; // === used by callbacks to store info.
      i.func_ids          = {}; // give each callback an id to be used in .configs
    }

    var id = _.findKey(i.func_ids, function (v) { return v === f; } );

    if (!id) {
      id                      = Applet.new_id('config_id_');
      i.func_id_to_config[id] = {};
      i.func_ids[id]          = f;
    }

    return i.func_id_to_config[id];
  }; // === config_id

  // === Examples:
  //
  // .run('str')               => {name: 'str'}
  // .run('str', {...})        => {name: 'str', data: {...}}
  // .run({name: 'str', ... }) => {name: 'str', ... }
  //
  Applet.prototype.run = function (msg, data) {

    var o = null;
    var instance = this;

    if (_.isPlainObject(msg)) {
      o = msg;
    } else { // === is String
      o = {
        name : Applet.standard_name(msg),
        data : data
      };
    }

    o.applet = instance;

    o.name = Applet.standard_name(o.name);

    if (o.name === 'data' && o.data)
      instance.data_cache = _.extend(instance.data_cache, o.data);

    var i = 0, f;

    // if (o.name.indexOf('ajax') > -1)
    // log(instance)
    while (instance.funcs[i]) {
      f             = instance.funcs[i];
      o.this_config = instance.config_for_func(f);
      o.this_func   = f;
      o.data_cache  = instance.data_cache;

      f(o);
      ++i;
    }

    return instance;
  }; // === func

  Applet.prototype.new_func = function () {

    var arr = _.flatten(_.toArray(arguments));
    var i   = this;

    _.each(arr, function (f) {
      if (!_.isFunction(f))
        throw new Error('Error: Not a function');
      if (f({name : 'this position', applet: i}) === 'top')
        i.funcs.unshift(f);
      else
        i.funcs.push(f);
    });

    return this;
  };

  // =====================================================
  // === FUNCTIONS
  // =====================================================

  Applet.funcs = {};

  // === template ====================
  Applet.funcs.template = function (o) {
    if (o.name === 'this position')
      return 'top';

    if (o.name !== 'dom')
      return;

    var selector    = 'script[type^="text/mustache"]:not(script.compiled)';
    var scripts     = (o.target || $('body')).find(selector).addBack(selector);
    if (scripts.length < 1)
      return;

    _.each(scripts, function (raw) {
      var t              = $(raw);
      var types          = t.attr('type').split('/');
      var html           = t.html();
      var placeholder_id = Applet.dom_id(t);
      var data_key       = types[2];
      var id             = Applet.dom_id(t, 'mustache_templates_' + (data_key || ''));
      var pos            = 'replace';

      t.addClass('compiled');

      switch (_.trim(types[1])) {
        case 'mustache-top':
          pos = 'top';
        break;

        case 'mustache-bottom':
          pos = 'bottom';
        break;
      } // === switch type[1]

      var meta = {
        id             : id,
        key            : data_key,
        html           : html,
        mustache       : Hogan.compile(html),
        placeholder_id : placeholder_id,
        elements       : null,
        pos            : pos
      };

      o.applet.new_func(
        function (o) {
          if (o.name !== 'data' || !_.isPlainObject(o.data[meta.key]))
            return;

          // === Remove old nodes:
          if (meta.elements && meta.pos === 'replace') {
            meta.elements.remove();
          }

          var html = $(meta.mustache.render(o.data));
          if (meta.pos === 'replace' || meta.pos === 'bottom')
            html.insertBefore($('#' + meta.placeholder_id));
          else
            html.insertAfter($('#' + meta.placeholder_id));

          meta.elements = html;
          o.applet.run({
            name   : 'dom',
            target : html
          });
        }
      ); // === new_func

    });

  }; // ==== funcs: template ==========

  // === show_if ====================
  Applet.funcs.show_if = function (o) {
    if (o.name !== 'dom') return;

    var selector   = '*[data-show_if]';
    var targets    = $(o.target || $('body')).find(selector).addBack(selector);

    _.each(targets, function (raw_node) {
      var node    = $(raw_node);
      var the_key = Applet.remove_attr(raw_node, 'data-show_if');

      if (!Applet.is_true(o.data_cache, the_key))
        node.hide();

      var the_id  = Applet.dom_id(node);

      o.applet.new_func(
        function (o) {
          if (o.name !== 'data') return;

          switch (Applet.is_true(o.data, the_key)) {
            case true:
              $('#' + the_id).show();
            return;

            case false:
              $('#' + the_id).hide();
            return;
          } // === switch value

        } // === func
      );

    }); // === each

  }; // === funcs: show_if ========

  // === hide_if ====================
  Applet.funcs.hide_if = function (o) {
    if (o.name !== 'dom') return;

    var selector   = '*[data-hide_if]';
    var targets    = $(o.target || $('body')).find(selector).addBack(selector);

    _.each(targets, function (raw_node) {
      var node = $(raw_node);
      var key  = Applet.remove_attr(node, 'data-hide_if');

      if (Applet.is_true(o.data_cache, key) === true)
        node.hide();

      var id  = Applet.dom_id(node);

      o.applet.new_func(
        function (o) {
          if (o.name !== 'data') return;

          switch (Applet.is_true(o.data, key)) {
            case true:
              $('#' + id).hide();
            return;
            case false:
              $('#' + id).show();
            return;
          } // === switch value
        }
      ); // === new_func

    }); // === each target

  }; // === funcs: hide_if ========

  // === ajax =====================
  Applet.funcs.ajax = function (o) {

    switch (o.name) {

      case 'ajax':
        if (o['send?']) {
          o.promise = promise.post(o.url, o.data, o.headers);
          o.promise.then(function (err, text, xhr) {
            o.applet.run({
              name: 'ajax response',
              err: err,
              text: text,
              xhr: xhr,
              promise: o.promise
            });
          });
        }
      break;

    } // === switch o.name

  }; // === funcs: ajax

  // === form =====================
  Applet.funcs.form = function (o) {

    if (o.name !== 'dom')
      return;

    var selector = $('form:not(form.compiled) button.submit');
    var target   = $(o.target || $('body')).find(selector).addBack(selector);

    if (!o.this_func.submit) {
      o.this_func.submit = function (a) {
        return function (e) {
          var form = $(this).parent('form');
          e.preventDefault();
          e.stopPropagation();
          a.run({
            name    : 'ajax',
            'send?' : true,
            form_id : form.attr('id'),
            url     : form.attr('action'),
            headers : {"Accept": "application/json"},
            data    : form.serializeJSON()
          });
        };
      };
    } // === if this_func

    var i = 0;
    while (target[i]) {
      $(target[i]).on('click', o.this_func.submit(o.applet));
      ++i;
    } // === while
  }; // === funcs: form

})(); // === end scope =================================

<|endoftext|>


<commit_before>import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'
import format from 'date-fns/format'
import { truncateString } from '../utils'
import Highlighter from 'react-highlight-words'

export default ({ link, query: { search } = {} } = {}) => {
  return (

          <Highlighter
            highlightClassName='highlight'
            searchWords={[search]}
            textToHighlight={
              link.title ? truncateString(link.title, 90) : link.url
            }
          />

          {link.description
            ? <Highlighter
              highlightClassName='highlight'
              searchWords={[search]}
              textToHighlight={truncateString(link.description, 140)}
              />
            : 'No Description'}

          {distanceInWordsToNow(link.timestamp)}
          {' '}
          ago (
          {format(link.timestamp, 'MMM, Do YYYY')}
          )

              {link._creator
                ? <a
                  className='by-user'
                  rel='noopener'
                  href={`https://github.com/${link._creator.username}`}
                  target='_blank'
                  >

                    Added By
                    VinayPuppal

                :

                    Added From whatsapp

}

                Open
                <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
                  <path
                    d='M488.727 0H302.545c-12.853 0-23.273 10.42-23.273 23.273s10.42 23.273 23.273 23.273h129.997L192.999 286.09c-9.089 9.089-9.089 23.823 0 32.912 4.543 4.544 10.499 6.816 16.455 6.816 5.956 0 11.913-2.271 16.457-6.817L465.455 79.458v129.997c0 12.853 10.42 23.273 23.273 23.273s23.273-10.42 23.273-23.273V23.273C512 10.42 501.58 0 488.727 0z'
                  />
                  <path
                    d='M395.636 232.727c-12.853 0-23.273 10.42-23.273 23.273v209.455H46.545V139.636H256c12.853 0 23.273-10.42 23.273-23.273S268.853 93.091 256 93.091H23.273C10.42 93.091 0 103.511 0 116.364v372.364C0 501.58 10.42 512 23.273 512h372.364c12.853 0 23.273-10.42 23.273-23.273V256c-.001-12.853-10.421-23.273-23.274-23.273z'
                  />
                </svg>

  )
}
<commit_msg>fix addedby typo;<commit_after>import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'
import format from 'date-fns/format'
import { truncateString } from '../utils'
import Highlighter from 'react-highlight-words'

export default ({ link, query: { search } = {} } = {}) => {
  return (

          <Highlighter
            highlightClassName='highlight'
            searchWords={[search]}
            textToHighlight={
              link.title ? truncateString(link.title, 90) : link.url
            }
          />

          {link.description
            ? <Highlighter
              highlightClassName='highlight'
              searchWords={[search]}
              textToHighlight={truncateString(link.description, 140)}
              />
            : 'No Description'}

          {distanceInWordsToNow(link.timestamp)}
          {' '}
          ago (
          {format(link.timestamp, 'MMM, Do YYYY')}
          )

              {link._creator
                ? <a
                  className='by-user'
                  rel='noopener'
                  href={`https://github.com/${link._creator.username}`}
                  target='_blank'
                  >

                    Added By
                    {link._creator.username}

                :

                    Added From whatsapp

}

                Open
                <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
                  <path
                    d='M488.727 0H302.545c-12.853 0-23.273 10.42-23.273 23.273s10.42 23.273 23.273 23.273h129.997L192.999 286.09c-9.089 9.089-9.089 23.823 0 32.912 4.543 4.544 10.499 6.816 16.455 6.816 5.956 0 11.913-2.271 16.457-6.817L465.455 79.458v129.997c0 12.853 10.42 23.273 23.273 23.273s23.273-10.42 23.273-23.273V23.273C512 10.42 501.58 0 488.727 0z'
                  />
                  <path
                    d='M395.636 232.727c-12.853 0-23.273 10.42-23.273 23.273v209.455H46.545V139.636H256c12.853 0 23.273-10.42 23.273-23.273S268.853 93.091 256 93.091H23.273C10.42 93.091 0 103.511 0 116.364v372.364C0 501.58 10.42 512 23.273 512h372.364c12.853 0 23.273-10.42 23.273-23.273V256c-.001-12.853-10.421-23.273-23.274-23.273z'
                  />
                </svg>

  )
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

let transpilations = {};
let zwitterionJSON = {
    files: {}
};
let io;

const builder = createBuilder();

const chokidar = require('chokidar');
const program = require('commander');
const fs = require('fs');
const mkdirp = require('mkdirp');

program
    .version('0.0.8')
    .option('-s, --serve-dir [serveDir]', 'The directory to serve files from; the root directory of the server')
    .option('-h, --http', 'Use HTTP 1.x (the default is HTTP 2)')
    .option('-c, --cert-path [certPath]', 'Specify path to SSL certificate')
    .option('-k, --key-path [keyPath]', 'Specify path to SSL key')
    .option('-o, --output-dir [outputDir]', 'Specify the output directory for transpiled files (the default is in-memory transpilation only)')
    .option('-b, --build', 'Transpile all files specified in the files property in zwitterion.json to the corresponding location in the specified output directory (--output-dir)')
    .option('-t, --type-check-level [typeCheckLevel]', 'Specify the level of type checking (none, warn, error)')
    .parse(process.argv);

const serveDir = program.serveDir ? `${program.serveDir}/` : '';
const httpVersion = program.http ? 1 : 2;
const keyPath = program.keyPath;
const certPath = program.certPath;
const outputDir = program.outputDir;
const typeCheckLevel = program.typeCheckLevel;
const build = program.build;

try {
    zwitterionJSON = JSON.parse(fs.readFileSync('zwitterion.json'));
}
catch(error) {
    fs.writeFileSync('zwitterion.json', JSON.stringify(zwitterionJSON, null, 4));
}

if (build) {
    if (!outputDir) {
        throw new Error('You must specify an output directory from the command line, --output-dir [outputDir]');
    }

    mkdirp.sync(outputDir);
    const filePaths = Object.keys(zwitterionJSON.files);
    filePaths.forEach((filePath) => {
        try {
            const directoriesWithFile = filePath.split('/');
            if (directoriesWithFile.length > 1) {
                const directories = directoriesWithFile.slice(0, -1).join('/');
                mkdirp.sync(`${outputDir}/${directories}`);
            }

            if (filePath === `browser-config.js`) {
                fs.writeFileSync(`${outputDir}/browser-config.js`, getBrowserConfig());
            }
            else if (filePath === `system.js.map`) {
                fs.writeFileSync(`${outputDir}/system.js.map`, getSystemJSSourceMap());
            }
            else {
                const fileEnding = filePath.slice(filePath.lastIndexOf('.'));
                if (fileEnding === '.ts') {
                    const isChildImport = !zwitterionJSON.files[filePath].parentImport;
                    compile(isChildImport, serveDir, filePath).then((source) => {
                        fs.writeFileSync(`${outputDir}/${filePath}`, source);
                    });
                }
                else {
                    fs.writeFileSync(`${outputDir}/${filePath}`, fs.readFileSync(`${serveDir}${filePath}`));
                }
            }
        }
        catch(error) {
            console.log(error);
        }
    });

    return;
}

let watcher = configureFileWatching(serveDir);
createServer(builder, httpVersion, keyPath, certPath, outputDir, typeCheckLevel, serveDir);

function writeZwitterionJSON() {
    fs.writeFileSync('zwitterion.json', JSON.stringify(zwitterionJSON, null, 4));
}

function configureFileWatching(serveDir) {
    return chokidar.watch([]).on('change', (path) => {
        const fileEnding = path.slice(path.lastIndexOf('.'));

        if (fileEnding === '.ts') {
            const relativeFilePath = path.replace(`${serveDir}`, '');
            const isChildImport = !zwitterionJSON.files[relativeFilePath].parentImport;
            compile(isChildImport, serveDir, relativeFilePath).then((source) => {
                transpilations[relativeFilePath] = source;
                reloadBrowser();
            });
        }
        else {
            reloadBrowser();
        }
    });
}

function reloadBrowser() {
    io.emit('reload');
}

function createServer(builder, httpVersion, keyPath, certPath, outputDir, typeCheckLevel, serveDir) {
    const static = require('node-static');
    const fileServer = new static.Server(`${process.cwd()}/${serveDir}`);
    const httpServerPromise = httpVersion === 2 ? createHTTP2Server(builder, fileServer, keyPath, certPath) : createHTTPServer(builder, fileServer);
    httpServerPromise.then((httpServer) => {
        io = require('socket.io')(httpServer);
        httpServer.listen(8000, (error) => {
            if (error) {
                console.log(error);
            }
            else {
                console.log('zwitterion server listening on port 8000');
            }
        });
    }, (error) => {
        console.log(error);
    });
}

function createHTTPServer(builder, fileServer) {
    return new Promise((resolve, reject) => {
        resolve(require('http').createServer(handler(fileServer)));
    });
}

function createHTTP2Server(builder, fileServer, keyPath, certPath) {
    return new Promise((resolve, reject) => {
        getCertAndKey(keyPath, certPath).then((certAndKey) => {
            const options = {
                key: certAndKey.key,
                cert: certAndKey.cert
            };

            resolve(require('http2').createServer(options, handler(fileServer)));
        }, (error) => {
            console.log(error);
        });
    });
}

function getCertAndKey(keyPath, certPath) {
    return new Promise((resolve, reject) => {
        const fs = require('fs');
        const defaultKeyPath = `localhost.key`;
        const defaultCertPath = `localhost.cert`;

        try {
            const key = fs.readFileSync(keyPath || defaultKeyPath);
            const cert = fs.readFileSync(certPath || defaultCertPath);

            resolve({
                key,
                cert
            });
        }
        catch(error) {
            if (!keyPath && !certPath) {
                createCertAndKey(defaultKeyPath, defaultCertPath).then((certAndKey) => {
                    resolve(certAndKey);
                }, (error) => {
                    console.log(error);
                });
            }
            else {
                throw error;
            }
        }
    });
}

function handler(fileServer) {
    return (req, res) => {
        const absoluteFilePath = `${process.cwd()}${req.url}`;
        const relativeFilePath = req.url.slice(1);
        const fileExtension = relativeFilePath.slice(relativeFilePath.lastIndexOf('.'));

        const isChildImport = isSystemImportRequest(req);
        writeRelativeFilePathToZwitterionJSON(relativeFilePath || 'index.html', isChildImport);
        watcher.add(`${serveDir}${relativeFilePath}` || `${serveDir}index.html`);
        fileExtension === '.ts' ? buildAndServe(req, res, relativeFilePath) : serveWithoutBuild(fileServer, req, res);
    };
}

function writeRelativeFilePathToZwitterionJSON(relativeFilePath, isChildImport) {
    const newRelativeFilePath = relativeFilePath.indexOf(serveDir) === 0 ? relativeFilePath.replace(`${serveDir}`, '') : relativeFilePath;

    zwitterionJSON.files[newRelativeFilePath] = {
        parentImport: !isChildImport
    };
    writeZwitterionJSON();
}

function buildAndServe(req, res, relativeFilePath) {
    const transpilation = transpilations[relativeFilePath];
    if (transpilation) {
        res.end(transpilation);
    }
    else {
        const isChildImport = isSystemImportRequest(req);
        compile(isChildImport, serveDir, relativeFilePath).then((source) => {
            transpilations[relativeFilePath] = source;
            res.end(transpilations[relativeFilePath]);
        });
    }
}

function compile(isChildImport, serveDir, relativeFilePath) {
    return new Promise((resolve, reject) => {
        builder.compile(`${serveDir}${relativeFilePath}`, null, {
            minify: true
        }).then((output) => {
            const source = prepareSource(isChildImport, relativeFilePath, output.source);
            resolve(source);
        }, (error) => {
            console.log(error);
        });
    });
}

function prepareSource(isChildImport, relativeFilePath, rawSource) {
    if (isChildImport) {
        return rawSource;
    }
    else {
        const escapedSource = rawSource.replace(/\\/g, '\\\\');
        const preparedSource = `
            System.define(System.normalizeSync('${relativeFilePath}'), \`
                ${escapedSource}
            \`);
        `;
        return preparedSource;
    }
}

function isSystemImportRequest(req) {
    return req.headers.accept && req.headers.accept.includes('application/x-es-module');
}

function serveWithoutBuild(fileServer, req, res) {
    req.addListener('end', () => {
        if (req.url === '/browser-config.js') {
            res.end(getBrowserConfig());
        }
        else if (req.url === '/system.js.map') {
            res.end(getSystemJSSourceMap());
        }
        else {
            fileServer.serve(req, res, (error, result) => {
                if (error && error.status === 404) {
                    fileServer.serveFile('/index.html', 200, {}, req, res);
                }
            });
        }
    }).resume();
}

function getBrowserConfig() {
    const systemJS = fs.readFileSync('node_modules/systemjs/dist/system.js');
    const socketIO = fs.readFileSync('node_modules/socket.io-client/dist/socket.io.min.js');
    const tsImportsConfig = fs.readFileSync('node_modules/zwitterion/ts-imports-config.js');
    const socketIOConfig = fs.readFileSync('node_modules/zwitterion/socket-io-config.js');

    return `${systemJS}${socketIO}${tsImportsConfig}${socketIOConfig}`;
}

function getSystemJSSourceMap() {
    return fs.readFileSync('node_modules/systemjs/dist/system.js.map');
}

function createBuilder() {
    const Builder = require('systemjs-builder');

    const builder = new Builder();

    //TODO redo this config, get rid of everything that is unnecessary, becuase I believe there might be quite a bit of it
    builder.config({
        transpiler: 'ts',
        typescriptOptions: {
            target: 'es5',
            module: 'system'
        },
        meta: {
            '*.ts': {
                loader: 'ts'
            }
        },
        packages: {
            '/': {
                defaultExtension: 'ts'
            },
            ts: {
                main: 'plugin.js'
            },
            typescript: {
                main: 'typescript.js',
                meta: {
                    'typescript.js': {
                        exports: 'ts'
                    }
                }
            }
        },
        map: {
            ts: './node_modules/plugin-typescript/lib/',
            typescript: './node_modules/typescript/lib/'
        }
    });

    return builder;
}

function createCertAndKey(keyPath, certPath) {
    return new Promise((resolve, reject) => {
        const pem = require('pem');

        pem.createCertificate({
            selfSigned: true
        }, (error, keys) => {
            if (error) {
                console.log(error);
            }
            else {
                fs.writeFileSync(keyPath, keys.serviceKey);
                fs.writeFileSync(certPath, keys.certificate);

                resolve({
                    key: keys.serviceKey,
                    cert: keys.certificate
                });
            }
        });
    });
}
<commit_msg>allow ts or js to be transpiled<commit_after>#!/usr/bin/env node

let transpilations = {};
let zwitterionJSON = {
    files: {}
};
let io;

const builder = createBuilder();

const chokidar = require('chokidar');
const program = require('commander');
const fs = require('fs');
const mkdirp = require('mkdirp');

program
    .version('0.0.8')
    .option('-s, --serve-dir [serveDir]', 'The directory to serve files from; the root directory of the server')
    .option('-h, --http', 'Use HTTP 1.x (the default is HTTP 2)')
    .option('-c, --cert-path [certPath]', 'Specify path to SSL certificate')
    .option('-k, --key-path [keyPath]', 'Specify path to SSL key')
    .option('-o, --output-dir [outputDir]', 'Specify the output directory for transpiled files (the default is in-memory transpilation only)')
    .option('-b, --build', 'Transpile all files specified in the files property in zwitterion.json to the corresponding location in the specified output directory (--output-dir)')
    .option('-t, --type-check-level [typeCheckLevel]', 'Specify the level of type checking (none, warn, error)')
    .option('-d, --default-import-extension [defaultImportExtension]', 'Specify the default file extension for JavaScript imports (defaults to js, can be set to js or ts)')
    .parse(process.argv);

const serveDir = program.serveDir ? `${program.serveDir}/` : '';
const httpVersion = program.http ? 1 : 2;
const keyPath = program.keyPath;
const certPath = program.certPath;
const outputDir = program.outputDir;
const typeCheckLevel = program.typeCheckLevel;
const build = program.build;
const defaultImportExtension = program.defaultImportExtension || 'js';

try {
    zwitterionJSON = JSON.parse(fs.readFileSync('zwitterion.json'));
}
catch(error) {
    fs.writeFileSync('zwitterion.json', JSON.stringify(zwitterionJSON, null, 4));
}

if (build) {
    if (!outputDir) {
        throw new Error('You must specify an output directory from the command line, --output-dir [outputDir]');
    }

    mkdirp.sync(outputDir);
    const filePaths = Object.keys(zwitterionJSON.files);
    filePaths.forEach((filePath) => {
        try {
            const directoriesWithFile = filePath.split('/');
            if (directoriesWithFile.length > 1) {
                const directories = directoriesWithFile.slice(0, -1).join('/');
                mkdirp.sync(`${outputDir}/${directories}`);
            }

            if (filePath === `browser-config.js`) {
                fs.writeFileSync(`${outputDir}/browser-config.js`, getBrowserConfig());
            }
            else if (filePath === `system.js.map`) {
                fs.writeFileSync(`${outputDir}/system.js.map`, getSystemJSSourceMap());
            }
            else {
                const fileEnding = filePath.slice(filePath.lastIndexOf('.'));
                if (fileEnding === '.ts' || fileEnding === '.js') {
                    const isChildImport = !zwitterionJSON.files[filePath].parentImport;
                    compile(isChildImport, serveDir, filePath).then((source) => {
                        fs.writeFileSync(`${outputDir}/${filePath}`, source);
                    });
                }
                else {
                    fs.writeFileSync(`${outputDir}/${filePath}`, fs.readFileSync(`${serveDir}${filePath}`));
                }
            }
        }
        catch(error) {
            console.log(error);
        }
    });

    return;
}

let watcher = configureFileWatching(serveDir);
createServer(builder, httpVersion, keyPath, certPath, outputDir, typeCheckLevel, serveDir);

function writeZwitterionJSON() {
    fs.writeFileSync('zwitterion.json', JSON.stringify(zwitterionJSON, null, 4));
}

function configureFileWatching(serveDir) {
    return chokidar.watch([]).on('change', (path) => {
        const fileEnding = path.slice(path.lastIndexOf('.'));

        if (fileEnding === '.ts' || fileEnding === '.js') {
            const relativeFilePath = path.replace(`${serveDir}`, '');
            const isChildImport = !zwitterionJSON.files[relativeFilePath].parentImport;
            compile(isChildImport, serveDir, relativeFilePath).then((source) => {
                transpilations[relativeFilePath] = source;
                reloadBrowser();
            });
        }
        else {
            reloadBrowser();
        }
    });
}

function reloadBrowser() {
    io.emit('reload');
}

function createServer(builder, httpVersion, keyPath, certPath, outputDir, typeCheckLevel, serveDir) {
    const static = require('node-static');
    const fileServer = new static.Server(`${process.cwd()}/${serveDir}`);
    const httpServerPromise = httpVersion === 2 ? createHTTP2Server(builder, fileServer, keyPath, certPath) : createHTTPServer(builder, fileServer);
    httpServerPromise.then((httpServer) => {
        io = require('socket.io')(httpServer);
        httpServer.listen(8000, (error) => {
            if (error) {
                console.log(error);
            }
            else {
                console.log('zwitterion server listening on port 8000');
            }
        });
    }, (error) => {
        console.log(error);
    });
}

function createHTTPServer(builder, fileServer) {
    return new Promise((resolve, reject) => {
        resolve(require('http').createServer(handler(fileServer)));
    });
}

function createHTTP2Server(builder, fileServer, keyPath, certPath) {
    return new Promise((resolve, reject) => {
        getCertAndKey(keyPath, certPath).then((certAndKey) => {
            const options = {
                key: certAndKey.key,
                cert: certAndKey.cert
            };

            resolve(require('http2').createServer(options, handler(fileServer)));
        }, (error) => {
            console.log(error);
        });
    });
}

function getCertAndKey(keyPath, certPath) {
    return new Promise((resolve, reject) => {
        const fs = require('fs');
        const defaultKeyPath = `localhost.key`;
        const defaultCertPath = `localhost.cert`;

        try {
            const key = fs.readFileSync(keyPath || defaultKeyPath);
            const cert = fs.readFileSync(certPath || defaultCertPath);

            resolve({
                key,
                cert
            });
        }
        catch(error) {
            if (!keyPath && !certPath) {
                createCertAndKey(defaultKeyPath, defaultCertPath).then((certAndKey) => {
                    resolve(certAndKey);
                }, (error) => {
                    console.log(error);
                });
            }
            else {
                throw error;
            }
        }
    });
}

function handler(fileServer) {
    return (req, res) => {
        const absoluteFilePath = `${process.cwd()}${req.url}`;
        const relativeFilePath = req.url.slice(1);
        const fileExtension = relativeFilePath.slice(relativeFilePath.lastIndexOf('.'));

        const isChildImport = isSystemImportRequest(req);
        writeRelativeFilePathToZwitterionJSON(relativeFilePath || 'index.html', isChildImport);
        watcher.add(`${serveDir}${relativeFilePath}` || `${serveDir}index.html`);
        fileExtension === '.ts' || fileExtension === '.js' ? buildAndServe(req, res, relativeFilePath) : serveWithoutBuild(fileServer, req, res);
    };
}

function writeRelativeFilePathToZwitterionJSON(relativeFilePath, isChildImport) {
    const newRelativeFilePath = relativeFilePath.indexOf(serveDir) === 0 ? relativeFilePath.replace(`${serveDir}`, '') : relativeFilePath;

    zwitterionJSON.files[newRelativeFilePath] = {
        parentImport: !isChildImport
    };
    writeZwitterionJSON();
}

function buildAndServe(req, res, relativeFilePath) {
    const transpilation = transpilations[relativeFilePath];
    if (transpilation) {
        res.end(transpilation);
    }
    else {
        const isChildImport = isSystemImportRequest(req);
        compile(isChildImport, serveDir, relativeFilePath).then((source) => {
            transpilations[relativeFilePath] = source;
            res.end(transpilations[relativeFilePath]);
        });
    }
}

function compile(isChildImport, serveDir, relativeFilePath) {
    return new Promise((resolve, reject) => {
        builder.compile(`${serveDir}${relativeFilePath}`, null, {
            minify: true
        }).then((output) => {
            const source = prepareSource(isChildImport, relativeFilePath, output.source);
            resolve(source);
        }, (error) => {
            console.log(error);
        });
    });
}

function prepareSource(isChildImport, relativeFilePath, rawSource) {
    if (isChildImport) {
        return rawSource;
    }
    else {
        const escapedSource = rawSource.replace(/\\/g, '\\\\');
        const preparedSource = `
            System.define(System.normalizeSync('${relativeFilePath}'), \`
                ${escapedSource}
            \`);
        `;
        return preparedSource;
    }
}

function isSystemImportRequest(req) {
    return req.headers.accept && req.headers.accept.includes('application/x-es-module');
}

function serveWithoutBuild(fileServer, req, res) {
    req.addListener('end', () => {
        if (req.url === '/browser-config.js') {
            res.end(getBrowserConfig());
        }
        else if (req.url === '/system.js.map') {
            res.end(getSystemJSSourceMap());
        }
        else {
            fileServer.serve(req, res, (error, result) => {
                if (error && error.status === 404) {
                    fileServer.serveFile('/index.html', 200, {}, req, res);
                }
            });
        }
    }).resume();
}

function getBrowserConfig() {
    const systemJS = fs.readFileSync('node_modules/systemjs/dist/system.js');
    const socketIO = fs.readFileSync('node_modules/socket.io-client/dist/socket.io.min.js');
    const tsImportsConfig = fs.readFileSync('node_modules/zwitterion/ts-imports-config.js');
    const socketIOConfig = fs.readFileSync('node_modules/zwitterion/socket-io-config.js');

    return `${systemJS}${socketIO}${tsImportsConfig}${socketIOConfig}`;
}

function getSystemJSSourceMap() {
    return fs.readFileSync('node_modules/systemjs/dist/system.js.map');
}

function createBuilder() {
    const Builder = require('systemjs-builder');

    const builder = new Builder();

    //TODO redo this config, get rid of everything that is unnecessary, becuase I believe there might be quite a bit of it
    builder.config({
        transpiler: 'ts',
        typescriptOptions: {
            target: 'es5',
            module: 'system'
        },
        meta: {
            '*.ts': {
                loader: 'ts'
            }
        },
        packages: {
            '/': {
                defaultExtension: 'ts'
            },
            ts: {
                main: 'plugin.js'
            },
            typescript: {
                main: 'typescript.js',
                meta: {
                    'typescript.js': {
                        exports: 'ts'
                    }
                }
            }
        },
        map: {
            ts: './node_modules/plugin-typescript/lib/',
            typescript: './node_modules/typescript/lib/'
        }
    });

    return builder;
}

function createCertAndKey(keyPath, certPath) {
    return new Promise((resolve, reject) => {
        const pem = require('pem');

        pem.createCertificate({
            selfSigned: true
        }, (error, keys) => {
            if (error) {
                console.log(error);
            }
            else {
                fs.writeFileSync(keyPath, keys.serviceKey);
                fs.writeFileSync(certPath, keys.certificate);

                resolve({
                    key: keys.serviceKey,
                    cert: keys.certificate
                });
            }
        });
    });
}
<|endoftext|>


<commit_before>/**
  This is a jQuery plugin to support autocompleting values in our text fields.

  @module $.fn.autocomplete
**/
$.fn.autocomplete = function(options) {

  var autocompletePlugin = this;

  if (this.length === 0) return;

  if (options && options.cancel && this.data("closeAutocomplete")) {
    this.data("closeAutocomplete")();
    return this;
  }

  if (this.length !== 1) {
    alert("only supporting one matcher at the moment");
  }

  var disabled = options && options.disabled;
  var wrap = null;
  var autocompleteOptions = null;
  var selectedOption = null;
  var completeStart = null;
  var completeEnd = null;
  var me = this;
  var div = null;

  // input is handled differently
  var isInput = this[0].tagName === "INPUT";
  var inputSelectedItems = [];

  var closeAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    div = null;
    completeStart = null;
    autocompleteOptions = null;
  };

  var addInputSelectedItem = function(item) {
    var transformed;
    if (options.transformComplete) {
      transformed = options.transformComplete(item);
    }
    if (options.single){
      // dump what we have in single mode, just in case
      inputSelectedItems = [];
    }
    var d = $("
" + (transformed || item) + "
");
    var prev = me.parent().find('.item:last');
    if (prev.length === 0) {
      me.parent().prepend(d);
    } else {
      prev.after(d);
    }
    inputSelectedItems.push(item);
    if (options.onChangeItems) {
      options.onChangeItems(inputSelectedItems);
    }

    d.find('a').click(function() {
      closeAutocomplete();
      inputSelectedItems.splice($.inArray(item), 1);
      $(this).parent().parent().remove();
      if (options.single) {
        me.show();
      }
      if (options.onChangeItems) {
        options.onChangeItems(inputSelectedItems);
      }
    });
  };

  var completeTerm = function(term) {
    if (term) {
      if (isInput) {
        me.val("");
        if(options.single){
          me.hide();
        }
        addInputSelectedItem(term);
      } else {
        if (options.transformComplete) {
          term = options.transformComplete(term);
        }
        var text = me.val();
        text = text.substring(0, completeStart) + (options.key || "") + term + ' ' + text.substring(completeEnd + 1, text.length);
        me.val(text);
        Discourse.Utilities.setCaretPosition(me[0], completeStart + 1 + term.length);
      }
    }
    closeAutocomplete();
  };

  if (isInput) {
    var width = this.width();
    var height = this.height();
    wrap = this.wrap("
").parent();
    wrap.width(width);
    if(options.single) {
      this.css("width","100%");
    } else {
      this.width(150);
    }
    this.attr('name', this.attr('name') + "-renamed");
    var vals = this.val().split(",");
    _.each(vals,function(x) {
      if (x !== "") {
        if (options.reverseTransform) {
          x = options.reverseTransform(x);
        }
        addInputSelectedItem(x);
      }
    });
    this.val("");
    completeStart = 0;
    wrap.click(function() {
      autocompletePlugin.focus();
      return true;
    });
  }

  var markSelected = function() {
    var links = div.find('li a');
    links.removeClass('selected');
    return $(links[selectedOption]).addClass('selected');
  };

  var renderAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    if (autocompleteOptions.length === 0) return;

    div = $(options.template({ options: autocompleteOptions }));

    var ul = div.find('ul');
    selectedOption = 0;
    markSelected();
    ul.find('li').click(function() {
      selectedOption = ul.find('li').index(this);
      completeTerm(autocompleteOptions[selectedOption]);
      return false;
    });
    var pos = null;
    var vOffset = 0;
    var hOffset = 0;
    if (isInput) {
      pos = {
        left: 0,
        top: 0
      };
      vOffset = -32;
      hOffset = 0;
    } else {
      pos = me.caretPosition({
        pos: completeStart,
        key: options.key
      });
      hOffset = 27;
    }
    div.css({
      left: "-1000px"
    });

    me.parent().append(div);

    if(!isInput){
      vOffset = div.height();
    }

    var mePos = me.position();
    var borderTop = parseInt(me.css('border-top-width'), 10) || 0;
    div.css({
      position: 'absolute',
      top: (mePos.top + pos.top - vOffset + borderTop) + 'px',
      left: (mePos.left + pos.left + hOffset) + 'px'
    });
  };

  var updateAutoComplete = function(r) {
    if (completeStart === null) return;

    autocompleteOptions = r;
    if (!r || r.length === 0) {
      closeAutocomplete();
    } else {
      renderAutocomplete();
    }
  };

  // chain to allow multiples
  var oldClose = me.data("closeAutocomplete");
  me.data("closeAutocomplete", function() {
    if (oldClose) {
      oldClose();
    }
    closeAutocomplete();
  });

  $(this).keypress(function(e) {

    if (!options.key) return;

    // keep hunting backwards till you hit a
    if (e.which === options.key.charCodeAt(0)) {
      var caretPosition = Discourse.Utilities.caretPosition(me[0]);
      var prevChar = me.val().charAt(caretPosition - 1);
      if (!prevChar || /\s/.test(prevChar)) {
        completeStart = completeEnd = caretPosition;
        var term = "";
        options.dataSource(term).then(updateAutoComplete);
      }
    }
  });

  return $(this).keydown(function(e) {
    var c, caretPosition, i, initial, next, nextIsGood, prev, prevIsGood, stopFound, term, total, userToComplete;

    if(options.allowAny){
      // saves us wiring up a change event as well, keypress is while its pressed
      _.delay(function(){
        if(inputSelectedItems.length === 0) {
          inputSelectedItems.push("");
        }

        if(_.isString(inputSelectedItems[0])) {
          inputSelectedItems.pop();
          inputSelectedItems.push(me.val());
          if (options.onChangeItems) {
            options.onChangeItems(inputSelectedItems);
          }
        }

      },50);
    }

    if (!options.key) {
      completeStart = 0;
    }
    if (e.which === 16) return;
    if ((completeStart === null) && e.which === 8 && options.key) {
      c = Discourse.Utilities.caretPosition(me[0]);
      next = me[0].value[c];
      nextIsGood = next === void 0 || /\s/.test(next);
      c -= 1;
      initial = c;
      prevIsGood = true;
      while (prevIsGood && c >= 0) {
        c -= 1;
        prev = me[0].value[c];
        stopFound = prev === options.key;
        if (stopFound) {
          prev = me[0].value[c - 1];
          if (!prev || /\s/.test(prev)) {
            completeStart = c;
            caretPosition = completeEnd = initial;
            term = me[0].value.substring(c + 1, initial);
            options.dataSource(term).then(updateAutoComplete);
            return true;
          }
        }
        prevIsGood = /[a-zA-Z\.]/.test(prev);
      }
    }

    // ESC
    if (e.which === 27) {
      if (completeStart !== null) {
        closeAutocomplete();
        return false;
      }
      return true;
    }

    if (completeStart !== null) {
      caretPosition = Discourse.Utilities.caretPosition(me[0]);

      // If we've backspaced past the beginning, cancel unless no key
      if (caretPosition <= completeStart && options.key) {
        closeAutocomplete();
        return false;
      }

      // Keyboard codes! So 80's.
      switch (e.which) {
        case 13:
        case 39:
        case 9:
          if (!autocompleteOptions) return true;
          if (selectedOption >= 0 && (userToComplete = autocompleteOptions[selectedOption])) {
            completeTerm(userToComplete);
          } else {
            // We're cancelling it, really.
            return true;
          }
          e.stopImmediatePropagation();
          return false;
        case 38:
          selectedOption = selectedOption - 1;
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        case 40:
          total = autocompleteOptions.length;
          selectedOption = selectedOption + 1;
          if (selectedOption >= total) {
            selectedOption = total - 1;
          }
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        default:
          // otherwise they're typing - let's search for it!
          completeEnd = caretPosition;
          if (e.which === 8) {
            caretPosition--;
          }
          if (caretPosition < 0) {
            closeAutocomplete();
            if (isInput) {
              i = wrap.find('a:last');
              if (i) {
                i.click();
              }
            }
            return false;
          }
          term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition);
          if (e.which >= 48 && e.which <= 90) {
            term += String.fromCharCode(e.which);
          } else if (e.which === 187) {
            term += "+";
          } else if (e.which === 189) {
            term += (e.shiftKey) ? "_" : "-";
          } else {
            if (e.which !== 8) {
              term += ",";
            }
          }

          options.dataSource(term).then(updateAutoComplete);
          return true;
      }
    }
  });
};
<commit_msg>Fix for autocomplete delete action removing only the last item<commit_after>/**
  This is a jQuery plugin to support autocompleting values in our text fields.

  @module $.fn.autocomplete
**/
$.fn.autocomplete = function(options) {

  var autocompletePlugin = this;

  if (this.length === 0) return;

  if (options && options.cancel && this.data("closeAutocomplete")) {
    this.data("closeAutocomplete")();
    return this;
  }

  if (this.length !== 1) {
    alert("only supporting one matcher at the moment");
  }

  var disabled = options && options.disabled;
  var wrap = null;
  var autocompleteOptions = null;
  var selectedOption = null;
  var completeStart = null;
  var completeEnd = null;
  var me = this;
  var div = null;

  // input is handled differently
  var isInput = this[0].tagName === "INPUT";
  var inputSelectedItems = [];

  var closeAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    div = null;
    completeStart = null;
    autocompleteOptions = null;
  };

  var addInputSelectedItem = function(item) {
    var transformed;
    if (options.transformComplete) {
      transformed = options.transformComplete(item);
    }
    if (options.single){
      // dump what we have in single mode, just in case
      inputSelectedItems = [];
    }
    var d = $("
" + (transformed || item) + "
");
    var prev = me.parent().find('.item:last');
    if (prev.length === 0) {
      me.parent().prepend(d);
    } else {
      prev.after(d);
    }
    inputSelectedItems.push(item);
    if (options.onChangeItems) {
      options.onChangeItems(inputSelectedItems);
    }

    d.find('a').click(function() {
      closeAutocomplete();
      inputSelectedItems.splice($.inArray(item, inputSelectedItems), 1);
      $(this).parent().parent().remove();
      if (options.single) {
        me.show();
      }
      if (options.onChangeItems) {
        options.onChangeItems(inputSelectedItems);
      }
    });
  };

  var completeTerm = function(term) {
    if (term) {
      if (isInput) {
        me.val("");
        if(options.single){
          me.hide();
        }
        addInputSelectedItem(term);
      } else {
        if (options.transformComplete) {
          term = options.transformComplete(term);
        }
        var text = me.val();
        text = text.substring(0, completeStart) + (options.key || "") + term + ' ' + text.substring(completeEnd + 1, text.length);
        me.val(text);
        Discourse.Utilities.setCaretPosition(me[0], completeStart + 1 + term.length);
      }
    }
    closeAutocomplete();
  };

  if (isInput) {
    var width = this.width();
    var height = this.height();
    wrap = this.wrap("
").parent();
    wrap.width(width);
    if(options.single) {
      this.css("width","100%");
    } else {
      this.width(150);
    }
    this.attr('name', this.attr('name') + "-renamed");
    var vals = this.val().split(",");
    _.each(vals,function(x) {
      if (x !== "") {
        if (options.reverseTransform) {
          x = options.reverseTransform(x);
        }
        addInputSelectedItem(x);
      }
    });
    this.val("");
    completeStart = 0;
    wrap.click(function() {
      autocompletePlugin.focus();
      return true;
    });
  }

  var markSelected = function() {
    var links = div.find('li a');
    links.removeClass('selected');
    return $(links[selectedOption]).addClass('selected');
  };

  var renderAutocomplete = function() {
    if (div) {
      div.hide().remove();
    }
    if (autocompleteOptions.length === 0) return;

    div = $(options.template({ options: autocompleteOptions }));

    var ul = div.find('ul');
    selectedOption = 0;
    markSelected();
    ul.find('li').click(function() {
      selectedOption = ul.find('li').index(this);
      completeTerm(autocompleteOptions[selectedOption]);
      return false;
    });
    var pos = null;
    var vOffset = 0;
    var hOffset = 0;
    if (isInput) {
      pos = {
        left: 0,
        top: 0
      };
      vOffset = -32;
      hOffset = 0;
    } else {
      pos = me.caretPosition({
        pos: completeStart,
        key: options.key
      });
      hOffset = 27;
    }
    div.css({
      left: "-1000px"
    });

    me.parent().append(div);

    if(!isInput){
      vOffset = div.height();
    }

    var mePos = me.position();
    var borderTop = parseInt(me.css('border-top-width'), 10) || 0;
    div.css({
      position: 'absolute',
      top: (mePos.top + pos.top - vOffset + borderTop) + 'px',
      left: (mePos.left + pos.left + hOffset) + 'px'
    });
  };

  var updateAutoComplete = function(r) {
    if (completeStart === null) return;

    autocompleteOptions = r;
    if (!r || r.length === 0) {
      closeAutocomplete();
    } else {
      renderAutocomplete();
    }
  };

  // chain to allow multiples
  var oldClose = me.data("closeAutocomplete");
  me.data("closeAutocomplete", function() {
    if (oldClose) {
      oldClose();
    }
    closeAutocomplete();
  });

  $(this).keypress(function(e) {

    if (!options.key) return;

    // keep hunting backwards till you hit a
    if (e.which === options.key.charCodeAt(0)) {
      var caretPosition = Discourse.Utilities.caretPosition(me[0]);
      var prevChar = me.val().charAt(caretPosition - 1);
      if (!prevChar || /\s/.test(prevChar)) {
        completeStart = completeEnd = caretPosition;
        var term = "";
        options.dataSource(term).then(updateAutoComplete);
      }
    }
  });

  return $(this).keydown(function(e) {
    var c, caretPosition, i, initial, next, nextIsGood, prev, prevIsGood, stopFound, term, total, userToComplete;

    if(options.allowAny){
      // saves us wiring up a change event as well, keypress is while its pressed
      _.delay(function(){
        if(inputSelectedItems.length === 0) {
          inputSelectedItems.push("");
        }

        if(_.isString(inputSelectedItems[0])) {
          inputSelectedItems.pop();
          inputSelectedItems.push(me.val());
          if (options.onChangeItems) {
            options.onChangeItems(inputSelectedItems);
          }
        }

      },50);
    }

    if (!options.key) {
      completeStart = 0;
    }
    if (e.which === 16) return;
    if ((completeStart === null) && e.which === 8 && options.key) {
      c = Discourse.Utilities.caretPosition(me[0]);
      next = me[0].value[c];
      nextIsGood = next === void 0 || /\s/.test(next);
      c -= 1;
      initial = c;
      prevIsGood = true;
      while (prevIsGood && c >= 0) {
        c -= 1;
        prev = me[0].value[c];
        stopFound = prev === options.key;
        if (stopFound) {
          prev = me[0].value[c - 1];
          if (!prev || /\s/.test(prev)) {
            completeStart = c;
            caretPosition = completeEnd = initial;
            term = me[0].value.substring(c + 1, initial);
            options.dataSource(term).then(updateAutoComplete);
            return true;
          }
        }
        prevIsGood = /[a-zA-Z\.]/.test(prev);
      }
    }

    // ESC
    if (e.which === 27) {
      if (completeStart !== null) {
        closeAutocomplete();
        return false;
      }
      return true;
    }

    if (completeStart !== null) {
      caretPosition = Discourse.Utilities.caretPosition(me[0]);

      // If we've backspaced past the beginning, cancel unless no key
      if (caretPosition <= completeStart && options.key) {
        closeAutocomplete();
        return false;
      }

      // Keyboard codes! So 80's.
      switch (e.which) {
        case 13:
        case 39:
        case 9:
          if (!autocompleteOptions) return true;
          if (selectedOption >= 0 && (userToComplete = autocompleteOptions[selectedOption])) {
            completeTerm(userToComplete);
          } else {
            // We're cancelling it, really.
            return true;
          }
          e.stopImmediatePropagation();
          return false;
        case 38:
          selectedOption = selectedOption - 1;
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        case 40:
          total = autocompleteOptions.length;
          selectedOption = selectedOption + 1;
          if (selectedOption >= total) {
            selectedOption = total - 1;
          }
          if (selectedOption < 0) {
            selectedOption = 0;
          }
          markSelected();
          return false;
        default:
          // otherwise they're typing - let's search for it!
          completeEnd = caretPosition;
          if (e.which === 8) {
            caretPosition--;
          }
          if (caretPosition < 0) {
            closeAutocomplete();
            if (isInput) {
              i = wrap.find('a:last');
              if (i) {
                i.click();
              }
            }
            return false;
          }
          term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition);
          if (e.which >= 48 && e.which <= 90) {
            term += String.fromCharCode(e.which);
          } else if (e.which === 187) {
            term += "+";
          } else if (e.which === 189) {
            term += (e.shiftKey) ? "_" : "-";
          } else {
            if (e.which !== 8) {
              term += ",";
            }
          }

          options.dataSource(term).then(updateAutoComplete);
          return true;
      }
    }
  });
};
<|endoftext|>