<commit_before>var xml = require('xml');
var url = require('url');
var generatorName = 'Feed for Node.js';

// ============================================================================
// Feed Main Module
// ============================================================================
function Feed(options) {
    this.id          = options.id;
    this.title       = options.title;
    this.description = options.description;
    this.link        = options.link;
    this.image       = options.image;
    this.copyright   = options.copyright;
    this.feed        = options.feed;
    this.hub         = options.hub;
    this.updated     = options.updated;

    this.author      = options.author;          // Atom only (at channel level)

    this.items       = [];
    this.categories  = [];
    this.contributors = [];     				// Atom only

    this.addItem = function (options) {
        options = options || {};
        var item = {
            title:          options.title,
            link:           options.link,
            description:    options.description,
            date:           options.date,
            image:          options.image,
            author:         options.author,
            contributor:    options.contributor,	// Atom only
            guid:           options.guid,
            id:             options.id,
            content:        options.content,
            copyright:		options.copyright	// Atom only
        };

        this.items.push(item);
        return this;
    }

    this.addCategory = function (category) {
        this.categories.push(category);
        return this;
    }

    this.addContributor = function (contributor) {
        this.contributors.push(contributor);
        return this;
    }

    this.render = function (format) {
        switch(format) {
            case 'rss-2.0':
                return rss_2_0(this);
            break;

            case 'atom-1.0':
                return atom_1_0(this);
            break;

            default:
                return rss_2_0(this);
        }
    };

    /**
     * DEPRECATED METHODS
     */
    this.item = function (options)  {
        console.warn('DEPRECATED: use addItem() instead of item()');
        return this.addItem(options);
    }

    this.category = function (category) {
        console.warn('DEPRECATED: use addCategory() instead of category()');
        return this.addCategory(category);
    }
};

// ============================================================================
// Atom 1.0 [application/atom+xml]
//
// - Reference: http://www.atomenabled.org/developers/syndication/
// ============================================================================

function atom_1_0(options) {
    var items       = [],
        container   = [],
        feed        = [],

        entries     = options.items,
        categories  = options.categories,
        author      = options.author,
        contributors = options.contributors,
        d           = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ feed: feed });
    feed.push({ _attr: { xmlns: 'http://www.w3.org/2005/Atom' }});

    /**************************************************************************
     * "feed" node: REQUIRED elements
     *************************************************************************/
    if(options.id == undefined)
        throw new Error('feed id is mandatory');
    else
        feed.push({ id: options.id });

    if(options.title == undefined)
        throw new Error('feed title is mandatory');
    else
        feed.push({ title: options.title });

    if(options.link == undefined)
        throw new Error('feed link is mandatory');
    else
        if(!validateURL(options.link))
            throw new Error('invalid feed link');

    if(Object.prototype.toString.call(options.updated) === '[object Date]')
        feed.push({ updated: RFC3339(options.updated) });
    else
       feed.push({ updated: RFC3339(d) });

    /**************************************************************************
     * "feed" node: recommended elements
     *************************************************************************/

    if(author) {
        var authorDetails = [];

        if(author.name)
            authorDetails.push({ name:     author.name });

        if(author.email)
            authorDetails.push({ email:     author.email });

        if(author.link)
            authorDetails.push({ uri:     author.link });

        feed.push({ author: authorDetails });
    }

    // link (rel="alternate")
    if(options.link)
        feed.push({ link: { _attr: { rel: 'alternate', href: options.link }}});

    // link (rel="self")
    if(options.feed)
        feed.push({ link: { _attr: { rel: 'self', href: options.feed }}});

    // link (rel="hub")
    if(options.hub)
        feed.push({ link: { _attr: { rel:'hub', href: options.hub }}});

    /**************************************************************************
     * "feed" node: optional elements
     *************************************************************************/

    if(options.description)
        feed.push({ subtitle:   options.description });

    if(options.image)
        feed.push({ logo:       options.image });

    if(options.copyright)
        feed.push({ rights:     options.copyright });

    feed.push({ generator:      generatorName });

    // Looping through categories (if any set!)
    for(var i=0; i<categories.length; i++)
        feed.push({ category: [{ _attr: { term: categories[i] } }] });

    if(contributors) {
    	for(var y = 0; y < contributors.length; y++) {
	        var contributorDetails = [];

	        if(contributors[y].name)
	        	contributorDetails.push({ name:     contributors[y].name });

	        if(contributors[y].email)
	        	contributorDetails.push({ email:     contributors[y].email });

	        if(contributors[y].link)
	            authorDetails.push({ uri:     contributors[y].link });

	        feed.push({ contributor: contributorDetails });
    	}
    }

    // icon

    /**************************************************************************
     * "entry" nodes
     *************************************************************************/
    for(var i = 0; i < entries.length; i++) {
        //
        // entry: required elements
        //
        if(entries[i].title == undefined)
            throw new Error('entry need a title');

        if(entries[i].link == undefined)
            throw new Error('entry need a link');
        else
            if(!validateURL(entries[i].link))
                throw new Error('invalid entry link');

        if(entries[i].date == undefined || !(entries[i].date instanceof Date))
            throw new Error('entry need a date');

        var entry = [
            { title:        { _attr: { type: 'html' }, _cdata: entries[i].title }},
            { id:           entries[i].id || entries[i].link },
            { link:         [{ _attr: { href: entries[i].link } }]},
            { updated:      RFC3339(entries[i].date) }
        ];

        //
        // entry: recommended elements
        //
        if(entries[i].description)
            entry.push({ summary: { _attr: { type: 'html' }, _cdata: entries[i].description }});

        if(entries[i].content)
            entry.push({ content: { _attr: { type: 'html' }, _cdata: entries[i].content }});

        // entry author(s)
        if(entries[i].author) {
            for(var y = 0; y < entries[i].author.length; y++) {
                var entryAuthor = [];

                if(entries[i].author[y].name)
                    entryAuthor.push({ name: entries[i].author[y].name });

                if(entries[i].author[y].email)
                    entryAuthor.push({ email: entries[i].author[y].email });

                if(entries[i].author[y].link)
                    entryAuthor.push({ uri: entries[i].author[y].link });

                entry.push({ author: entryAuthor });
            }
        }

        // content

        // link - relative link to article

        //
        // entry: optional elements
        //

        // category

        // contributor
        if(entries[i].contributor) {
            for(var y = 0; y < entries[i].contributor.length; y++) {
                var entryContributor = [];

                if(entries[i].contributor[y].name)
                    entryContributor.push({ name: entries[i].contributor[y].name });

                if(entries[i].contributor[y].email)
                    entryContributor.push({ email: entries[i].contributor[y].email });

                if(entries[i].contributor[y].link)
                    entryContributor.push({ uri: entries[i].contributor[y].link });

                entry.push({ contributor: entryContributor });
            }
        }

        // published

        // source

        // rights
        if(entries[i].copyright)
        	entry.push({ rights: entries[i].copyright });

        feed.push({ entry: entry });
    }

    return doctype + xml(container, true);
}

// ============================================================================
// Real Simple Syndication 2.0 (RSS 2.0)
//
// - Reference: http://feed2.w3.org/docs/rss2.html
// ============================================================================
function rss_2_0(options) {
    var items       = [],
        channel     = [],
        container   = [];

    var items = options.items;
    var categories = options.categories;
    var d = new Date();
    var bContent = false;
    var bAtom = false;

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ rss: [] });
    container[0].rss.push({ _attr: { version: '2.0' } });
    container[0].rss.push({ channel: channel });

    /**
     * Required Channel Elements
     * http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements
     */
    if(options.title == undefined)
        throw new Error('channel title is mandatory');
    else
        channel.push({ title: options.title });

    if(options.description == undefined)
        throw new Error('channel description is mandatory');
    else
        channel.push({ description: options.description });

    if(options.link == undefined)
        throw new Error('channel link is mandatory');
    else
        if(!validateURL(options.link))
            throw new Error('invalid channel link');
        else
            channel.push({ link: options.link });

    if(Object.prototype.toString.call(options.updated) === '[object Date]')
        channel.push({ lastBuildDate: options.updated.toUTCString() });
    else
       channel.push({ lastBuildDate: d.toUTCString() });

    channel.push({ docs: 'http://blogs.law.harvard.edu/tech/rss'});

    /**
     * Channel Image
     * http://cyber.law.harvard.edu/rss/rss.html#ltimagegtSubelementOfLtchannelgt
     */
    if(options.image)
        channel.push({
            image: [
                { title: options.title },
                { url: options.image },
                { link: options.link }
            ]
        });

    /**
     * Channel Copyright
     * http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
     */
    if(options.copyright)
        channel.push({ copyright: options.copyright });

    channel.push({ generator: generatorName });

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#comments
     */
    for(var i = 0; i < categories.length; i++)
        channel.push({ category: categories[i] });

    /**
     * Feed URL
     * http://validator.w3.org/feed/docs/warning/MissingAtomSelfLink.html
     */
    if(options.feed) {
        bAtom = true;
        channel.push({ "atom:link": { _attr: { href: options.feed, rel: 'self', type: 'application/rss+xml' }}});
    }

    /**
     * Hub for PubSubHubbub
     * https://code.google.com/p/pubsubhubbub/
     */
    if(options.hub) {
        bAtom = true;
        channel.push({ "atom:link": { _attr: { href: options.hub, rel: 'hub' }}});
    }

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
     */
    for(var i = 0; i < items.length; i++) {

        // You need to have at least a title OR a description
        if(!items[i].title && !items[i].description)
            throw new Error('item must have at least a title or a description');

        var item = [];

        if(items[i].title)
            item.push({ title: { _cdata: items[i].title }});

        if(items[i].link)
            item.push({ link: items[i].link });

        if(items[i].guid)
            item.push({ guid: items[i].guid });
        else if (items[i].link)
            item.push({ guid: items[i].link });

        if(items[i].date)
            item.push({ pubDate: items[i].date.toUTCString() });

        if(items[i].description)
            item.push({ description: { _cdata: items[i].description }});

        if(items[i].content) {
            bContent = true;
            item.push({ 'content:encoded': { _cdata: items[i].content }});
        }
        /**
         * Item Author
         * http://cyber.law.harvard.edu/rss/rss.html#ltauthorgtSubelementOfLtitemgt
         */
        if(items[i].author) {
            for(var y = 0; y < items[i].author.length; y++) {
                var itemAuthor = [];

                if(items[i].author[y].name)
                    itemAuthor.push({ name: items[i].author[y].name });

                if(items[i].author[y].email)
                    itemAuthor.push({ email: items[i].author[y].email });

                if(items[i].author[y].link)
                    itemAuthor.push({ link: items[i].author[y].link });

                item.push({ author: itemAuthor });
            }
        }

        if(items[i].image)
            item.push({ enclosure: [{ _attr: { url: items[i].image } }] });

        channel.push({ item: item });
    }

    if(bContent)
        container[0].rss[0]._attr['xmlns:content'] = 'http://purl.org/rss/1.0/modules/content/';

    if(bAtom)
        container[0].rss[0]._attr['xmlns:atom'] = 'http://www.w3.org/2005/Atom';

    return doctype + xml(container, true);
}

function RFC3339(d) {
    function pad(n) { return n < 10 ? '0' + n : n }

    return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
}

function validateURL(str) {
    var parsed = url.parse(str);
    return parsed.protocol && parsed.host;
}

module.exports = Feed;
<commit_msg>Support for the entry#published attribute<commit_after>var xml = require('xml');
var url = require('url');
var generatorName = 'Feed for Node.js';

// ============================================================================
// Feed Main Module
// ============================================================================
function Feed(options) {
    this.id          = options.id;
    this.title       = options.title;
    this.description = options.description;
    this.link        = options.link;
    this.image       = options.image;
    this.copyright   = options.copyright;
    this.feed        = options.feed;
    this.hub         = options.hub;
    this.updated     = options.updated;

    this.author      = options.author;          // Atom only (at channel level)

    this.items       = [];
    this.categories  = [];
    this.contributors = [];     				// Atom only

    this.addItem = function (options) {
        options = options || {};
        var item = {
            title:          options.title,
            link:           options.link,
            description:    options.description,
            date:           options.date,
            published:      options.published,
            image:          options.image,
            author:         options.author,
            contributor:    options.contributor,	// Atom only
            guid:           options.guid,
            id:             options.id,
            content:        options.content,
            copyright:		options.copyright	// Atom only
        };

        this.items.push(item);
        return this;
    }

    this.addCategory = function (category) {
        this.categories.push(category);
        return this;
    }

    this.addContributor = function (contributor) {
        this.contributors.push(contributor);
        return this;
    }

    this.render = function (format) {
        switch(format) {
            case 'rss-2.0':
                return rss_2_0(this);
            break;

            case 'atom-1.0':
                return atom_1_0(this);
            break;

            default:
                return rss_2_0(this);
        }
    };

    /**
     * DEPRECATED METHODS
     */
    this.item = function (options)  {
        console.warn('DEPRECATED: use addItem() instead of item()');
        return this.addItem(options);
    }

    this.category = function (category) {
        console.warn('DEPRECATED: use addCategory() instead of category()');
        return this.addCategory(category);
    }
};

// ============================================================================
// Atom 1.0 [application/atom+xml]
//
// - Reference: http://www.atomenabled.org/developers/syndication/
// ============================================================================

function atom_1_0(options) {
    var items       = [],
        container   = [],
        feed        = [],

        entries     = options.items,
        categories  = options.categories,
        author      = options.author,
        contributors = options.contributors,
        d           = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ feed: feed });
    feed.push({ _attr: { xmlns: 'http://www.w3.org/2005/Atom' }});

    /**************************************************************************
     * "feed" node: REQUIRED elements
     *************************************************************************/
    if(options.id == undefined)
        throw new Error('feed id is mandatory');
    else
        feed.push({ id: options.id });

    if(options.title == undefined)
        throw new Error('feed title is mandatory');
    else
        feed.push({ title: options.title });

    if(options.link == undefined)
        throw new Error('feed link is mandatory');
    else
        if(!validateURL(options.link))
            throw new Error('invalid feed link');

    if(Object.prototype.toString.call(options.updated) === '[object Date]')
        feed.push({ updated: RFC3339(options.updated) });
    else
       feed.push({ updated: RFC3339(d) });

    /**************************************************************************
     * "feed" node: recommended elements
     *************************************************************************/

    if(author) {
        var authorDetails = [];

        if(author.name)
            authorDetails.push({ name:     author.name });

        if(author.email)
            authorDetails.push({ email:     author.email });

        if(author.link)
            authorDetails.push({ uri:     author.link });

        feed.push({ author: authorDetails });
    }

    // link (rel="alternate")
    if(options.link)
        feed.push({ link: { _attr: { rel: 'alternate', href: options.link }}});

    // link (rel="self")
    if(options.feed)
        feed.push({ link: { _attr: { rel: 'self', href: options.feed }}});

    // link (rel="hub")
    if(options.hub)
        feed.push({ link: { _attr: { rel:'hub', href: options.hub }}});

    /**************************************************************************
     * "feed" node: optional elements
     *************************************************************************/

    if(options.description)
        feed.push({ subtitle:   options.description });

    if(options.image)
        feed.push({ logo:       options.image });

    if(options.copyright)
        feed.push({ rights:     options.copyright });

    feed.push({ generator:      generatorName });

    // Looping through categories (if any set!)
    for(var i=0; i<categories.length; i++)
        feed.push({ category: [{ _attr: { term: categories[i] } }] });

    if(contributors) {
    	for(var y = 0; y < contributors.length; y++) {
	        var contributorDetails = [];

	        if(contributors[y].name)
	        	contributorDetails.push({ name:     contributors[y].name });

	        if(contributors[y].email)
	        	contributorDetails.push({ email:     contributors[y].email });

	        if(contributors[y].link)
	            authorDetails.push({ uri:     contributors[y].link });

	        feed.push({ contributor: contributorDetails });
    	}
    }

    // icon

    /**************************************************************************
     * "entry" nodes
     *************************************************************************/
    for(var i = 0; i < entries.length; i++) {
        //
        // entry: required elements
        //
        if(entries[i].title == undefined)
            throw new Error('entry need a title');

        if(entries[i].link == undefined)
            throw new Error('entry need a link');
        else
            if(!validateURL(entries[i].link))
                throw new Error('invalid entry link');

        if(entries[i].date == undefined || !(entries[i].date instanceof Date))
            throw new Error('entry need a date');

        var entry = [
            { title:        { _attr: { type: 'html' }, _cdata: entries[i].title }},
            { id:           entries[i].id || entries[i].link },
            { link:         [{ _attr: { href: entries[i].link } }]},
            { updated:      RFC3339(entries[i].date) }
        ];

        //
        // entry: recommended elements
        //
        if(entries[i].description)
            entry.push({ summary: { _attr: { type: 'html' }, _cdata: entries[i].description }});

        if(entries[i].content)
            entry.push({ content: { _attr: { type: 'html' }, _cdata: entries[i].content }});

        // entry author(s)
        if(entries[i].author) {
            for(var y = 0; y < entries[i].author.length; y++) {
                var entryAuthor = [];

                if(entries[i].author[y].name)
                    entryAuthor.push({ name: entries[i].author[y].name });

                if(entries[i].author[y].email)
                    entryAuthor.push({ email: entries[i].author[y].email });

                if(entries[i].author[y].link)
                    entryAuthor.push({ uri: entries[i].author[y].link });

                entry.push({ author: entryAuthor });
            }
        }

        // content

        // link - relative link to article

        //
        // entry: optional elements
        //

        // category

        // contributor
        if(entries[i].contributor) {
            for(var y = 0; y < entries[i].contributor.length; y++) {
                var entryContributor = [];

                if(entries[i].contributor[y].name)
                    entryContributor.push({ name: entries[i].contributor[y].name });

                if(entries[i].contributor[y].email)
                    entryContributor.push({ email: entries[i].contributor[y].email });

                if(entries[i].contributor[y].link)
                    entryContributor.push({ uri: entries[i].contributor[y].link });

                entry.push({ contributor: entryContributor });
            }
        }

        // published
        if(entries[i].published)
            entry.push({ published: RFC3339(entries[i].published) });

        // source

        // rights
        if(entries[i].copyright)
        	entry.push({ rights: entries[i].copyright });

        feed.push({ entry: entry });
    }

    return doctype + xml(container, true);
}

// ============================================================================
// Real Simple Syndication 2.0 (RSS 2.0)
//
// - Reference: http://feed2.w3.org/docs/rss2.html
// ============================================================================
function rss_2_0(options) {
    var items       = [],
        channel     = [],
        container   = [];

    var items = options.items;
    var categories = options.categories;
    var d = new Date();
    var bContent = false;
    var bAtom = false;

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ rss: [] });
    container[0].rss.push({ _attr: { version: '2.0' } });
    container[0].rss.push({ channel: channel });

    /**
     * Required Channel Elements
     * http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements
     */
    if(options.title == undefined)
        throw new Error('channel title is mandatory');
    else
        channel.push({ title: options.title });

    if(options.description == undefined)
        throw new Error('channel description is mandatory');
    else
        channel.push({ description: options.description });

    if(options.link == undefined)
        throw new Error('channel link is mandatory');
    else
        if(!validateURL(options.link))
            throw new Error('invalid channel link');
        else
            channel.push({ link: options.link });

    if(Object.prototype.toString.call(options.updated) === '[object Date]')
        channel.push({ lastBuildDate: options.updated.toUTCString() });
    else
       channel.push({ lastBuildDate: d.toUTCString() });

    channel.push({ docs: 'http://blogs.law.harvard.edu/tech/rss'});

    /**
     * Channel Image
     * http://cyber.law.harvard.edu/rss/rss.html#ltimagegtSubelementOfLtchannelgt
     */
    if(options.image)
        channel.push({
            image: [
                { title: options.title },
                { url: options.image },
                { link: options.link }
            ]
        });

    /**
     * Channel Copyright
     * http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
     */
    if(options.copyright)
        channel.push({ copyright: options.copyright });

    channel.push({ generator: generatorName });

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#comments
     */
    for(var i = 0; i < categories.length; i++)
        channel.push({ category: categories[i] });

    /**
     * Feed URL
     * http://validator.w3.org/feed/docs/warning/MissingAtomSelfLink.html
     */
    if(options.feed) {
        bAtom = true;
        channel.push({ "atom:link": { _attr: { href: options.feed, rel: 'self', type: 'application/rss+xml' }}});
    }

    /**
     * Hub for PubSubHubbub
     * https://code.google.com/p/pubsubhubbub/
     */
    if(options.hub) {
        bAtom = true;
        channel.push({ "atom:link": { _attr: { href: options.hub, rel: 'hub' }}});
    }

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
     */
    for(var i = 0; i < items.length; i++) {

        // You need to have at least a title OR a description
        if(!items[i].title && !items[i].description)
            throw new Error('item must have at least a title or a description');

        var item = [];

        if(items[i].title)
            item.push({ title: { _cdata: items[i].title }});

        if(items[i].link)
            item.push({ link: items[i].link });

        if(items[i].guid)
            item.push({ guid: items[i].guid });
        else if (items[i].link)
            item.push({ guid: items[i].link });

        if(items[i].date)
            item.push({ pubDate: items[i].date.toUTCString() });

        if(items[i].description)
            item.push({ description: { _cdata: items[i].description }});

        if(items[i].content) {
            bContent = true;
            item.push({ 'content:encoded': { _cdata: items[i].content }});
        }
        /**
         * Item Author
         * http://cyber.law.harvard.edu/rss/rss.html#ltauthorgtSubelementOfLtitemgt
         */
        if(items[i].author) {
            for(var y = 0; y < items[i].author.length; y++) {
                var itemAuthor = [];

                if(items[i].author[y].name)
                    itemAuthor.push({ name: items[i].author[y].name });

                if(items[i].author[y].email)
                    itemAuthor.push({ email: items[i].author[y].email });

                if(items[i].author[y].link)
                    itemAuthor.push({ link: items[i].author[y].link });

                item.push({ author: itemAuthor });
            }
        }

        if(items[i].image)
            item.push({ enclosure: [{ _attr: { url: items[i].image } }] });

        channel.push({ item: item });
    }

    if(bContent)
        container[0].rss[0]._attr['xmlns:content'] = 'http://purl.org/rss/1.0/modules/content/';

    if(bAtom)
        container[0].rss[0]._attr['xmlns:atom'] = 'http://www.w3.org/2005/Atom';

    return doctype + xml(container, true);
}

function RFC3339(d) {
    function pad(n) { return n < 10 ? '0' + n : n }

    return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
}

function validateURL(str) {
    var parsed = url.parse(str);
    return parsed.protocol && parsed.host;
}

module.exports = Feed;
<|endoftext|>


<commit_before>/*
 * This binds the node-validator library to the req object so that
 * the validation / sanitization methods can be called on parameter
 * names rather than the actual strings.
 *
 *
 * 1. To validate parameters, use `req.check(param_name, [err_message])`
 *        e.g. req.check('param1').len(1, 6).isInt();
 *        e.g. req.checkHeader('referer').contains('mydomain.com');
 *
 *    Each call to `check()` will throw an exception by default. To
 *    specify a custom err handler, use `req.onValidationError(errback)`
 *    where errback receives a parameter containing the error message
 *
 * 2. To sanitize parameters, use `req.sanitize(param_name)`
 *        e.g. req.sanitize('param1').toBoolean();
 *        e.g. req.sanitize('param2').toInt();
 *
 * 3. Done! Access your validated and sanitized paramaters through the
 *    `req.params` object
 */

var validator = require('validator');

var expressValidator = function(options) {
  options = options || {};

  var _options = {};

  _options.errorFormatter = options.errorFormatter || function(param, msg, value) {
    return {
      param : param,
      msg   : msg,
      value : value
    };
  };

  var sanitizers = ['trim', 'ltrim', 'rtrim', 'escape', 'whitelist',
  'blacklist'];

  var sanitize = function(request, param, value) {
    var methods = {};

    Object.keys(validator).forEach(function(methodName) {
      if (methodName.match(/^to/) || sanitizers.indexOf(methodName) !== -1) {
        methods[methodName] = function() {
          var args = [value].concat(Array.prototype.slice.call(arguments));
          var result = validator[methodName].apply(validator, args);
          request.updateParam(param, result);
          return result;
        }
      }
    });

    return methods;
  }

  function checkParam(req, getter) {
    return function(param, failMsg) {

      var value;

      // If param is not an array, then split by dot notation
      // returning an array. It will return an array even if
      // param doesn't have the dot notation.
      //      'blogpost' = ['blogpost']
      //      'login.username' = ['login', 'username']
      // For regex matches you can access the parameters using numbers.
      if (!Array.isArray(param)) {
        param = typeof param === 'number' ?
                [param] :
                param.split('.').filter(function(e){
                  return e !== '';
                });
      }

      // Extract value from params
      param.map(function(item) {
          if (value === undefined) {
            value = getter(item)
          } else {
            value = value[item];
          }
      });
      param = param.join('.');

      var errorHandler = function(msg) {
        var error = _options.errorFormatter(param, msg, value);

        if (req._validationErrors === undefined) {
          req._validationErrors = [];
        }
        req._validationErrors.push(error);

        if (req.onErrorCallback) {
          req.onErrorCallback(msg);
        }
        return this;
      }

      var methods = [];

      Object.keys(validator).forEach(function(methodName) {
        if (!methodName.match(/^to/) && sanitizers.indexOf(methodName) === -1) {
          methods[methodName] = function() {
            var args = [value].concat(Array.prototype.slice.call(arguments));
            var isCorrect = validator[methodName].apply(validator, args);

            if (!isCorrect) {
              errorHandler(failMsg || 'Invalid value');
            }

            return methods;
          }
        }
      });

      methods['notEmpty'] = function() {
        return methods.isLength(1);
      }

      methods['len'] = function() {
        return methods.isLength.apply(methods.isLength, Array.prototype.slice.call(arguments));
      }

      methods['optional'] = function() {
        if (value !== undefined)
          return methods;

        var dummyMethods = [];
        for (var methodName in methods) {
          dummyMethods[methodName] = function() { return dummyMethods; };
        }
        return dummyMethods;
      };

      return methods;
    }
  }
  return function(req, res, next) {

    req.updateParam = function(name, value) {
      // route params like /user/:id
      if (this.params && this.params.hasOwnProperty(name) &&
          undefined !== this.params[name]) {
        return this.params[name] = value;
      }
      // query string params
      if (undefined !== this.query[name]) {
        return this.query[name] = value;
      }
      // request body params via connect.bodyParser
      if (this.body && undefined !== this.body[name]) {
        return this.body[name] = value;
      }
      return false;
    };

    req.check = checkParam(req, function(item) {
      return req.param(item);
    });

    req.checkFiles = checkParam(req, function(item) {
      return req.files && req.files[item];
    });

    req.checkBody = checkParam(req, function(item) {
      return req.body && req.body[item];
    });

    req.checkParams = checkParam(req, function(item) {
      return req.params && req.params[item];
    });

    req.checkQuery = checkParam(req, function(item) {
      return req.query && req.query[item];
    });

    req.checkHeader = checkParam(req, function(header) {
        var toCheck;

        if (header === 'referrer' || header === 'referer') {
          toCheck = req.headers.referer;
        } else {
          toCheck = req.headers[header];
        }
        return toCheck || '';
    });

    req.onValidationError = function(errback) {
      req.onErrorCallback = errback;
    };

    req.validationErrors = function(mapped) {
      if (req._validationErrors === undefined) {
        return null;
      }
      if (mapped) {
        var errors = {};
        req._validationErrors.forEach(function(err) {
          errors[err.param] = err;
        });
        return errors;
      }
      return req._validationErrors;
    }

    req.filter = function(param) {
      return sanitize(this, param, this.param(param));
    };

    // Create some aliases - might help with code readability
    req.sanitize = req.filter;
    req.assert = req.check;
    req.validate = req.check;

    return next();
  };
}
module.exports = expressValidator;
module.exports.validator = validator;
<commit_msg>add curly braces to respect the coding style<commit_after>/*
 * This binds the node-validator library to the req object so that
 * the validation / sanitization methods can be called on parameter
 * names rather than the actual strings.
 *
 *
 * 1. To validate parameters, use `req.check(param_name, [err_message])`
 *        e.g. req.check('param1').len(1, 6).isInt();
 *        e.g. req.checkHeader('referer').contains('mydomain.com');
 *
 *    Each call to `check()` will throw an exception by default. To
 *    specify a custom err handler, use `req.onValidationError(errback)`
 *    where errback receives a parameter containing the error message
 *
 * 2. To sanitize parameters, use `req.sanitize(param_name)`
 *        e.g. req.sanitize('param1').toBoolean();
 *        e.g. req.sanitize('param2').toInt();
 *
 * 3. Done! Access your validated and sanitized paramaters through the
 *    `req.params` object
 */

var validator = require('validator');

var expressValidator = function(options) {
  options = options || {};

  var _options = {};

  _options.errorFormatter = options.errorFormatter || function(param, msg, value) {
    return {
      param : param,
      msg   : msg,
      value : value
    };
  };

  var sanitizers = ['trim', 'ltrim', 'rtrim', 'escape', 'whitelist',
  'blacklist'];

  var sanitize = function(request, param, value) {
    var methods = {};

    Object.keys(validator).forEach(function(methodName) {
      if (methodName.match(/^to/) || sanitizers.indexOf(methodName) !== -1) {
        methods[methodName] = function() {
          var args = [value].concat(Array.prototype.slice.call(arguments));
          var result = validator[methodName].apply(validator, args);
          request.updateParam(param, result);
          return result;
        }
      }
    });

    return methods;
  }

  function checkParam(req, getter) {
    return function(param, failMsg) {

      var value;

      // If param is not an array, then split by dot notation
      // returning an array. It will return an array even if
      // param doesn't have the dot notation.
      //      'blogpost' = ['blogpost']
      //      'login.username' = ['login', 'username']
      // For regex matches you can access the parameters using numbers.
      if (!Array.isArray(param)) {
        param = typeof param === 'number' ?
                [param] :
                param.split('.').filter(function(e){
                  return e !== '';
                });
      }

      // Extract value from params
      param.map(function(item) {
          if (value === undefined) {
            value = getter(item)
          } else {
            value = value[item];
          }
      });
      param = param.join('.');

      var errorHandler = function(msg) {
        var error = _options.errorFormatter(param, msg, value);

        if (req._validationErrors === undefined) {
          req._validationErrors = [];
        }
        req._validationErrors.push(error);

        if (req.onErrorCallback) {
          req.onErrorCallback(msg);
        }
        return this;
      }

      var methods = [];

      Object.keys(validator).forEach(function(methodName) {
        if (!methodName.match(/^to/) && sanitizers.indexOf(methodName) === -1) {
          methods[methodName] = function() {
            var args = [value].concat(Array.prototype.slice.call(arguments));
            var isCorrect = validator[methodName].apply(validator, args);

            if (!isCorrect) {
              errorHandler(failMsg || 'Invalid value');
            }

            return methods;
          }
        }
      });

      methods['notEmpty'] = function() {
        return methods.isLength(1);
      }

      methods['len'] = function() {
        return methods.isLength.apply(methods.isLength, Array.prototype.slice.call(arguments));
      }

      methods['optional'] = function() {
        if (value !== undefined) {
          return methods;
        }

        var dummyMethods = [];
        for (var methodName in methods) {
          dummyMethods[methodName] = function() { return dummyMethods; };
        }
        return dummyMethods;
      };

      return methods;
    }
  }
  return function(req, res, next) {

    req.updateParam = function(name, value) {
      // route params like /user/:id
      if (this.params && this.params.hasOwnProperty(name) &&
          undefined !== this.params[name]) {
        return this.params[name] = value;
      }
      // query string params
      if (undefined !== this.query[name]) {
        return this.query[name] = value;
      }
      // request body params via connect.bodyParser
      if (this.body && undefined !== this.body[name]) {
        return this.body[name] = value;
      }
      return false;
    };

    req.check = checkParam(req, function(item) {
      return req.param(item);
    });

    req.checkFiles = checkParam(req, function(item) {
      return req.files && req.files[item];
    });

    req.checkBody = checkParam(req, function(item) {
      return req.body && req.body[item];
    });

    req.checkParams = checkParam(req, function(item) {
      return req.params && req.params[item];
    });

    req.checkQuery = checkParam(req, function(item) {
      return req.query && req.query[item];
    });

    req.checkHeader = checkParam(req, function(header) {
        var toCheck;

        if (header === 'referrer' || header === 'referer') {
          toCheck = req.headers.referer;
        } else {
          toCheck = req.headers[header];
        }
        return toCheck || '';
    });

    req.onValidationError = function(errback) {
      req.onErrorCallback = errback;
    };

    req.validationErrors = function(mapped) {
      if (req._validationErrors === undefined) {
        return null;
      }
      if (mapped) {
        var errors = {};
        req._validationErrors.forEach(function(err) {
          errors[err.param] = err;
        });
        return errors;
      }
      return req._validationErrors;
    }

    req.filter = function(param) {
      return sanitize(this, param, this.param(param));
    };

    // Create some aliases - might help with code readability
    req.sanitize = req.filter;
    req.assert = req.check;
    req.validate = req.check;

    return next();
  };
}
module.exports = expressValidator;
module.exports.validator = validator;
<|endoftext|>


<commit_before>/*
 * Utilities: A classic collection of JavaScript utilities
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var fs = require('fs')
  , path = require('path')
  , JS_PAT = /\.js$/
  , logger;

var logger = new (function () {
  var out;
  try {
    out = require('./logger');
  }
  catch (e) {
    out = console;
  }

  this.log = function (o) {
    out.log(o);
  };
})();

var fileUtils = new (function () {
  var _copyFile = function(fromPath, toPath, opts) {
        var from = path.normalize(fromPath)
          , to = path.normalize(toPath)
          , options = opts || {}
          , fromStat
          , toStat
          , destExists
          , destDoesNotExistErr
          , content
          , filename
          , dirContents
          , targetDir;

        fromStat = fs.statSync(from);

        try {
          //console.dir(to + ' destExists');
          toStat = fs.statSync(to);
          destExists = true;
        }
        catch(e) {
          //console.dir(to + ' does not exist');
          destDoesNotExistErr = e;
          destExists = false;
        }
        // Destination dir or file exists, copy into (directory)
        // or overwrite (file)
        if (destExists) {

          // If there's a rename-via-copy file/dir name passed, use it.
          // Otherwise use the actual file/dir name
          filename = options.rename || path.basename(from);

          // Copying a directory
          if (fromStat.isDirectory()) {
            dirContents = fs.readdirSync(from);
            targetDir = path.join(to, filename);
            // We don't care if the target dir already exists
            try {
              fs.mkdirSync(targetDir, options.mode || 0755);
            }
            catch(e) {
              if (e.code != 'EEXIST') {
                throw e;
              }
            }
            for (var i = 0, ii = dirContents.length; i < ii; i++) {
              //console.log(dirContents[i]);
              _copyFile(path.join(from, dirContents[i]), targetDir);
            }
          }
          // Copying a file
          else {
            content = fs.readFileSync(from);
            // Copy into dir
            if (toStat.isDirectory()) {
              //console.log('copy into dir ' + to);
              fs.writeFileSync(path.join(to, filename), content);
            }
            // Overwrite file
            else {
              //console.log('overwriting ' + to);
              fs.writeFileSync(to, content);
            }
          }
        }
        // Dest doesn't exist, can't create it
        else {
          throw destDoesNotExistErr;
        }
      }

    , _copyDir = function (from, to, opts) {
        var createDir = opts.createDir;
      }

    , _readDir = function (dirPath) {
        var dir = path.normalize(dirPath)
          , paths = []
          , ret = [dir];

        try {
          paths = fs.readdirSync(dir);
        }
        catch (e) {
          throw new Error('Could not read path ' + dir);
        }

        paths.forEach(function (p) {
          var curr = path.join(dir, p);
          var stat = fs.statSync(curr);
          if (stat.isDirectory()) {
            ret = ret.concat(_readDir(curr));
          }
          else {
            ret.push(curr);
          }
        });

        return ret;
      }

    , _rmDir = function (dirPath) {
        var dir = path.normalize(dirPath)
          , paths = [];
        paths = fs.readdirSync(dir);
        paths.forEach(function (p) {
          var curr = path.join(dir, p);
          var stat = fs.statSync(curr);
          if (stat.isDirectory()) {
            _rmDir(curr);
          }
          else {
            fs.unlinkSync(curr);
          }
        });
        fs.rmdirSync(dir);
      }

    // Recursively watch files with a callback
    , _watch = function (path, callback) {
        fs.stat(path, function (err, stats) {
          if (err) {
            return false;
          }
          if (stats.isFile() && JS_PAT.test(path)) {
            fs.watchFile(path, callback);
          }
          else if (stats.isDirectory()) {
            fs.readdir(path, function (err, files) {
              if (err) {
                return log.fatal(err);
              }
              for (var f in files) {
                _watch(path + '/' + files[f], callback);
              }
            });
          }
        });
      };

  this.cpR = function (fromPath, toPath, options) {
    var from = path.normalize(fromPath)
      , to = path.normalize(toPath)
      , toStat
      , doesNotExistErr
      , paths
      , filename
      , opts = options || {};

    if (!opts.silent) {
      logger.log('cp -r ' + fromPath + ' ' + toPath);
    }

    opts = {}; // Reset

    if (from == to) {
      throw new Error('Cannot copy ' + from + ' to itself.');
    }

    // Handle rename-via-copy
    try {
      toStat = fs.statSync(to);
    }
    catch(e) {
      doesNotExistErr = e;

      // Get abs path so it's possible to check parent dir
      if (!this.isAbsolute(to)) {
        to = path.join(process.cwd() , to);
      }

      // Save the file/dir name
      filename = path.basename(to);
      // See if a parent dir exists, so there's a place to put the
      /// renamed file/dir (resets the destination for the copy)
      to = path.dirname(to);
      try {
        toStat = fs.statSync(to);
      }
      catch(e) {}
      if (toStat && toStat.isDirectory()) {
        // Set the rename opt to pass to the copy func, will be used
        // as the new file/dir name
        opts.rename = filename;
        //console.log('filename ' + filename);
      }
      else {
        throw doesNotExistErr;
      }
    }

    _copyFile(from, to, opts);
  };

  this.mkdirP = function (dir, mode) {
    var dirPath = path.normalize(dir)
      , paths = dirPath.split(/\/|\\/)
      , currPath
      , next;

    if (paths[0] == '' || /^[A-Za-z]+:/.test(paths[0])) {
      currPath = paths.shift() || '/';
      currPath = path.join(currPath, paths.shift());
      //console.log('basedir');
    }
    while ((next = paths.shift())) {
      if (next == '..') {
        currPath = path.join(currPath, next);
        continue;
      }
      currPath = path.join(currPath, next);
      try {
        //console.log('making ' + currPath);
        fs.mkdirSync(currPath, mode || 0755);
      }
      catch(e) {
        if (e.code != 'EEXIST') {
          throw e;
        }
      }
    }
  };

  this.readdirR = function (dir, opts) {
    var options = opts || {}
      , format = options.format || 'array'
      , ret;
    ret = _readDir(dir);
    return format == 'string' ? ret.join('\n') : ret;
  };

  this.rmRf = function (p, options) {
    var stat
      , opts = options || {};
    if (!opts.silent) {
      logger.log('rm -rf ' + p);
    }
    try {
      stat = fs.statSync(p);
      if (stat.isDirectory()) {
        _rmDir(p);
      }
      else {
        fs.unlinkSync(p);
      }
    }
    catch (e) {}
  };

  this.isAbsolute = function (p) {
    var match = /^[A-Za-z]+:\\|^\//.exec(p);
    if (match && match.length) {
      return match[0];
    }
    return false;
  };

  this.absolutize = function (p) {
    if (this.isAbsolute(p)) {
      return p;
    }
    else {
      return path.join(process.cwd(), p);
    }
  };

  this.basedir = function (p) {
    var str = p || ''
      , abs = this.isAbsolute(p);
    if (abs) {
      return abs;
    }
    // Split into segments
    str = str.split(/\\|\//)[0];
    // If the path has a leading asterisk, basedir is the current dir
    if (str.indexOf('*') > -1) {
      return '.';
    }
    // Otherwise it's the first segment in the path
    else {
      return str;
    }
  };

  // Search for a directory in parent directories if it can't be found in cwd
  this.searchParentPath = function(location, callback) {
    var cwd = process.cwd();

    if(!location) {
      // Return if no path is given
      return;
    }
    var relPath = ''
      , i = 5 // Only search up to 5 directories
      , pathLoc
      , pathExists;

    while(--i >= 0) {
      pathLoc = path.join(cwd, relPath, location);
      pathExists = this.existsSync(pathLoc);

      if(pathExists) {
        callback && callback(undefined, pathLoc);
        break;
      } else {
        // Dir could not be found
        if(i === 0) {
          callback && callback(new Error("Path \"" + pathLoc + "\" not found"), undefined);
          break;
        }

        // Add a relative parent directory
        relPath += '../';
        // Switch to relative parent directory
        process.chdir(path.join(cwd, relPath));
      }
    }
  };

  this.watch = function () {
    _watch.apply(this, arguments);
  };

  // Compatibility for fs.exists(0.8) and path.exists(0.6)
  this.exists = (typeof fs.exists === 'function') ? fs.exists : path.exists;

  // Compatibility for fs.existsSync(0.8) and path.existsSync(0.6)
  this.existsSync = (typeof fs.existsSync === 'function') ? fs.existsSync : path.existsSync;

  // Require a module locally, i.e., in the node_modules directory
  // in the current directory
  this.requireLocal = function(module, message) {
    // Try to require in the application directory
    try {
      dep = require(path.join(process.cwd(), 'node_modules', module));
    }
    catch(err) {
      if(message) {
        throw new Error(message);
      }
      throw new Error('Module "' + module + '" could not be found as a ' +
          'local module.\n Please make sure there is a node_modules directory in the ' +
          'current directory,\n and install it by doing "npm install ' +
          module + '"');
    }
    return dep;
  };

})();

module.exports = fileUtils;

<commit_msg>Allow CoffeeScript files to be watched as well<commit_after>/*
 * Utilities: A classic collection of JavaScript utilities
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var fs = require('fs')
  , path = require('path')
  , JS_PAT = /\.(js|coffee)$/
  , logger;

var logger = new (function () {
  var out;
  try {
    out = require('./logger');
  }
  catch (e) {
    out = console;
  }

  this.log = function (o) {
    out.log(o);
  };
})();

var fileUtils = new (function () {
  var _copyFile = function(fromPath, toPath, opts) {
        var from = path.normalize(fromPath)
          , to = path.normalize(toPath)
          , options = opts || {}
          , fromStat
          , toStat
          , destExists
          , destDoesNotExistErr
          , content
          , filename
          , dirContents
          , targetDir;

        fromStat = fs.statSync(from);

        try {
          //console.dir(to + ' destExists');
          toStat = fs.statSync(to);
          destExists = true;
        }
        catch(e) {
          //console.dir(to + ' does not exist');
          destDoesNotExistErr = e;
          destExists = false;
        }
        // Destination dir or file exists, copy into (directory)
        // or overwrite (file)
        if (destExists) {

          // If there's a rename-via-copy file/dir name passed, use it.
          // Otherwise use the actual file/dir name
          filename = options.rename || path.basename(from);

          // Copying a directory
          if (fromStat.isDirectory()) {
            dirContents = fs.readdirSync(from);
            targetDir = path.join(to, filename);
            // We don't care if the target dir already exists
            try {
              fs.mkdirSync(targetDir, options.mode || 0755);
            }
            catch(e) {
              if (e.code != 'EEXIST') {
                throw e;
              }
            }
            for (var i = 0, ii = dirContents.length; i < ii; i++) {
              //console.log(dirContents[i]);
              _copyFile(path.join(from, dirContents[i]), targetDir);
            }
          }
          // Copying a file
          else {
            content = fs.readFileSync(from);
            // Copy into dir
            if (toStat.isDirectory()) {
              //console.log('copy into dir ' + to);
              fs.writeFileSync(path.join(to, filename), content);
            }
            // Overwrite file
            else {
              //console.log('overwriting ' + to);
              fs.writeFileSync(to, content);
            }
          }
        }
        // Dest doesn't exist, can't create it
        else {
          throw destDoesNotExistErr;
        }
      }

    , _copyDir = function (from, to, opts) {
        var createDir = opts.createDir;
      }

    , _readDir = function (dirPath) {
        var dir = path.normalize(dirPath)
          , paths = []
          , ret = [dir];

        try {
          paths = fs.readdirSync(dir);
        }
        catch (e) {
          throw new Error('Could not read path ' + dir);
        }

        paths.forEach(function (p) {
          var curr = path.join(dir, p);
          var stat = fs.statSync(curr);
          if (stat.isDirectory()) {
            ret = ret.concat(_readDir(curr));
          }
          else {
            ret.push(curr);
          }
        });

        return ret;
      }

    , _rmDir = function (dirPath) {
        var dir = path.normalize(dirPath)
          , paths = [];
        paths = fs.readdirSync(dir);
        paths.forEach(function (p) {
          var curr = path.join(dir, p);
          var stat = fs.statSync(curr);
          if (stat.isDirectory()) {
            _rmDir(curr);
          }
          else {
            fs.unlinkSync(curr);
          }
        });
        fs.rmdirSync(dir);
      }

    // Recursively watch files with a callback
    , _watch = function (path, callback) {
        fs.stat(path, function (err, stats) {
          if (err) {
            return false;
          }
          if (stats.isFile() && JS_PAT.test(path)) {
            fs.watchFile(path, callback);
          }
          else if (stats.isDirectory()) {
            fs.readdir(path, function (err, files) {
              if (err) {
                return log.fatal(err);
              }
              for (var f in files) {
                _watch(path + '/' + files[f], callback);
              }
            });
          }
        });
      };

  this.cpR = function (fromPath, toPath, options) {
    var from = path.normalize(fromPath)
      , to = path.normalize(toPath)
      , toStat
      , doesNotExistErr
      , paths
      , filename
      , opts = options || {};

    if (!opts.silent) {
      logger.log('cp -r ' + fromPath + ' ' + toPath);
    }

    opts = {}; // Reset

    if (from == to) {
      throw new Error('Cannot copy ' + from + ' to itself.');
    }

    // Handle rename-via-copy
    try {
      toStat = fs.statSync(to);
    }
    catch(e) {
      doesNotExistErr = e;

      // Get abs path so it's possible to check parent dir
      if (!this.isAbsolute(to)) {
        to = path.join(process.cwd() , to);
      }

      // Save the file/dir name
      filename = path.basename(to);
      // See if a parent dir exists, so there's a place to put the
      /// renamed file/dir (resets the destination for the copy)
      to = path.dirname(to);
      try {
        toStat = fs.statSync(to);
      }
      catch(e) {}
      if (toStat && toStat.isDirectory()) {
        // Set the rename opt to pass to the copy func, will be used
        // as the new file/dir name
        opts.rename = filename;
        //console.log('filename ' + filename);
      }
      else {
        throw doesNotExistErr;
      }
    }

    _copyFile(from, to, opts);
  };

  this.mkdirP = function (dir, mode) {
    var dirPath = path.normalize(dir)
      , paths = dirPath.split(/\/|\\/)
      , currPath
      , next;

    if (paths[0] == '' || /^[A-Za-z]+:/.test(paths[0])) {
      currPath = paths.shift() || '/';
      currPath = path.join(currPath, paths.shift());
      //console.log('basedir');
    }
    while ((next = paths.shift())) {
      if (next == '..') {
        currPath = path.join(currPath, next);
        continue;
      }
      currPath = path.join(currPath, next);
      try {
        //console.log('making ' + currPath);
        fs.mkdirSync(currPath, mode || 0755);
      }
      catch(e) {
        if (e.code != 'EEXIST') {
          throw e;
        }
      }
    }
  };

  this.readdirR = function (dir, opts) {
    var options = opts || {}
      , format = options.format || 'array'
      , ret;
    ret = _readDir(dir);
    return format == 'string' ? ret.join('\n') : ret;
  };

  this.rmRf = function (p, options) {
    var stat
      , opts = options || {};
    if (!opts.silent) {
      logger.log('rm -rf ' + p);
    }
    try {
      stat = fs.statSync(p);
      if (stat.isDirectory()) {
        _rmDir(p);
      }
      else {
        fs.unlinkSync(p);
      }
    }
    catch (e) {}
  };

  this.isAbsolute = function (p) {
    var match = /^[A-Za-z]+:\\|^\//.exec(p);
    if (match && match.length) {
      return match[0];
    }
    return false;
  };

  this.absolutize = function (p) {
    if (this.isAbsolute(p)) {
      return p;
    }
    else {
      return path.join(process.cwd(), p);
    }
  };

  this.basedir = function (p) {
    var str = p || ''
      , abs = this.isAbsolute(p);
    if (abs) {
      return abs;
    }
    // Split into segments
    str = str.split(/\\|\//)[0];
    // If the path has a leading asterisk, basedir is the current dir
    if (str.indexOf('*') > -1) {
      return '.';
    }
    // Otherwise it's the first segment in the path
    else {
      return str;
    }
  };

  // Search for a directory in parent directories if it can't be found in cwd
  this.searchParentPath = function(location, callback) {
    var cwd = process.cwd();

    if(!location) {
      // Return if no path is given
      return;
    }
    var relPath = ''
      , i = 5 // Only search up to 5 directories
      , pathLoc
      , pathExists;

    while(--i >= 0) {
      pathLoc = path.join(cwd, relPath, location);
      pathExists = this.existsSync(pathLoc);

      if(pathExists) {
        callback && callback(undefined, pathLoc);
        break;
      } else {
        // Dir could not be found
        if(i === 0) {
          callback && callback(new Error("Path \"" + pathLoc + "\" not found"), undefined);
          break;
        }

        // Add a relative parent directory
        relPath += '../';
        // Switch to relative parent directory
        process.chdir(path.join(cwd, relPath));
      }
    }
  };

  this.watch = function () {
    _watch.apply(this, arguments);
  };

  // Compatibility for fs.exists(0.8) and path.exists(0.6)
  this.exists = (typeof fs.exists === 'function') ? fs.exists : path.exists;

  // Compatibility for fs.existsSync(0.8) and path.existsSync(0.6)
  this.existsSync = (typeof fs.existsSync === 'function') ? fs.existsSync : path.existsSync;

  // Require a module locally, i.e., in the node_modules directory
  // in the current directory
  this.requireLocal = function(module, message) {
    // Try to require in the application directory
    try {
      dep = require(path.join(process.cwd(), 'node_modules', module));
    }
    catch(err) {
      if(message) {
        throw new Error(message);
      }
      throw new Error('Module "' + module + '" could not be found as a ' +
          'local module.\n Please make sure there is a node_modules directory in the ' +
          'current directory,\n and install it by doing "npm install ' +
          module + '"');
    }
    return dep;
  };

})();

module.exports = fileUtils;

<|endoftext|>


<commit_before>/*
 * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
 */

L.Draggable = L.Class.extend({
	includes: L.Mixin.Events,

	statics: {
		START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
		END: L.Browser.touch ? ['touchend', 'mouseup'] : ['mouseup'],
		MOVE: L.Browser.touch ? ['touchmove', 'mousemove'] : ['mousemove'],
		TAP_TOLERANCE: 15
	},

	initialize: function (element, dragStartTarget, longPress) {
		this._element = element;
		this._dragStartTarget = dragStartTarget || element;
		this._longPress = longPress && !L.Browser.msTouch;
	},

	enable: function () {
		if (this._enabled) { return; }

		for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
			L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
		}
		this._enabled = true;
	},

	disable: function () {
		if (!this._enabled) { return; }

		L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown);
		this._enabled = false;
		this._moved = false;
	},

	_onDown: function (e) {
		if ((!L.Browser.touch && e.shiftKey) ||
		    ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }

		L.DomEvent.preventDefault(e);
		L.DomEvent.stopPropagation(e);

		if (L.Draggable._disabled) { return; }

		this._simulateClick = true;

		if (e.touches && e.touches.length > 1) {
			this._simulateClick = false;
			clearTimeout(this._longPressTimeout);
			return;
		}

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
		    el = first.target;

		if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
			L.DomUtil.addClass(el, 'leaflet-active');
		}

		this._moved = false;
		if (this._moving) { return; }

		this._startPoint = new L.Point(first.clientX, first.clientY);
		this._startPos = this._newPos = L.DomUtil.getPosition(this._element);

		//Touch contextmenu event emulation
		if (e.touches && e.touches.length === 1 && L.Browser.touch && this._longPress) {
			this._longPressTimeout = setTimeout(L.bind(function () {
				var dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

				if (dist < L.Draggable.TAP_TOLERANCE) {
					this._simulateClick = false;
					this._onUp();
					this._simulateEvent('contextmenu', first);
				}
			}, this), 1000);
		}

		for (var i = L.Draggable.MOVE.length - 1; i >= 0; i--) {
			L.DomEvent.on(document, L.Draggable.MOVE[i], this._onMove, this);
			L.DomEvent.on(document, L.Draggable.END[i], this._onUp, this);
		}
	},

	_onMove: function (e) {
		if (e.touches && e.touches.length > 1) { return; }

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
		    newPoint = new L.Point(first.clientX, first.clientY),
		    diffVec = newPoint.subtract(this._startPoint);

		if (!diffVec.x && !diffVec.y) { return; }

		L.DomEvent.preventDefault(e);

		if (!this._moved) {
			this.fire('dragstart');
			this._moved = true;

			this._startPos = L.DomUtil.getPosition(this._element).subtract(diffVec);

			if (!L.Browser.touch) {
				L.DomUtil.disableTextSelection();
				this._setMovingCursor();
			}
		}

		this._newPos = this._startPos.add(diffVec);
		this._moving = true;

		L.Util.cancelAnimFrame(this._animRequest);
		this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
	},

	_updatePosition: function () {
		this.fire('predrag');
		L.DomUtil.setPosition(this._element, this._newPos);
		this.fire('drag');
	},

	_onUp: function (e) {
		var simulateClickTouch;
		clearTimeout(this._longPressTimeout);
		if (this._simulateClick && e.changedTouches) {
			var first = e.changedTouches[0],
			    el = first.target,
			    dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

			if (el.tagName.toLowerCase() === 'a') {
				L.DomUtil.removeClass(el, 'leaflet-active');
			}

			if (dist < L.Draggable.TAP_TOLERANCE) {
				simulateClickTouch = first;
			}
		}

		if (!L.Browser.touch) {
			L.DomUtil.enableTextSelection();
			this._restoreCursor();
		}

		for (var i = L.Draggable.MOVE.length - 1; i >= 0; i--) {
			L.DomEvent.off(document, L.Draggable.MOVE[i], this._onMove);
			L.DomEvent.off(document, L.Draggable.END[i], this._onUp);
		}

		if (this._moved) {
			// ensure drag is not fired after dragend
			L.Util.cancelAnimFrame(this._animRequest);

			this.fire('dragend');
		}
		this._moving = false;

		if (simulateClickTouch) {
			this._moved = false;
			this._simulateEvent('click', simulateClickTouch);
		}
	},

	_setMovingCursor: function () {
		L.DomUtil.addClass(document.body, 'leaflet-dragging');
	},

	_restoreCursor: function () {
		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
	},

	_simulateEvent: function (type, e) {
		var simulatedEvent = document.createEvent('MouseEvents');

		simulatedEvent.initMouseEvent(
		        type, true, true, window, 1,
		        e.screenX, e.screenY,
		        e.clientX, e.clientY,
		        false, false, false, false, 0, null);

		e.target.dispatchEvent(simulatedEvent);
	}
});
<commit_msg>Only wire move/end listeners for the thing we want to listen to.<commit_after>/*
 * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
 */

L.Draggable = L.Class.extend({
	includes: L.Mixin.Events,

	statics: {
		START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
		END: { 'mousedown': 'mouseup', 'touchstart' : 'touchend', 'MSPointerDown': 'touchend' },
		MOVE: { 'mousedown': 'mousemove', 'touchstart': 'touchmove', 'MSPointerDown': 'touchmove' },
		TAP_TOLERANCE: 15
	},

	initialize: function (element, dragStartTarget, longPress) {
		this._element = element;
		this._dragStartTarget = dragStartTarget || element;
		this._longPress = longPress && !L.Browser.msTouch;
	},

	enable: function () {
		if (this._enabled) { return; }

		for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
			L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
		}
		this._enabled = true;
	},

	disable: function () {
		if (!this._enabled) { return; }

		L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown);
		this._enabled = false;
		this._moved = false;
	},

	_onDown: function (e) {
		if ((!L.Browser.touch && e.shiftKey) ||
		    ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }

		L.DomEvent.preventDefault(e);
		L.DomEvent.stopPropagation(e);

		if (L.Draggable._disabled) { return; }

		this._simulateClick = true;

		if (e.touches && e.touches.length > 1) {
			this._simulateClick = false;
			clearTimeout(this._longPressTimeout);
			return;
		}

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
		    el = first.target;

		if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
			L.DomUtil.addClass(el, 'leaflet-active');
		}

		this._moved = false;
		if (this._moving) { return; }

		this._startPoint = new L.Point(first.clientX, first.clientY);
		this._startPos = this._newPos = L.DomUtil.getPosition(this._element);

		//Touch contextmenu event emulation
		if (e.touches && e.touches.length === 1 && L.Browser.touch && this._longPress) {
			this._longPressTimeout = setTimeout(L.bind(function () {
				var dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

				if (dist < L.Draggable.TAP_TOLERANCE) {
					this._simulateClick = false;
					this._onUp();
					this._simulateEvent('contextmenu', first);
				}
			}, this), 1000);
		}

		L.DomEvent.on(document, L.Draggable.MOVE[e.type], this._onMove, this);
		L.DomEvent.on(document, L.Draggable.END[e.type], this._onUp, this);
	},

	_onMove: function (e) {
		if (e.touches && e.touches.length > 1) { return; }

		var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
		    newPoint = new L.Point(first.clientX, first.clientY),
		    diffVec = newPoint.subtract(this._startPoint);

		if (!diffVec.x && !diffVec.y) { return; }

		L.DomEvent.preventDefault(e);

		if (!this._moved) {
			this.fire('dragstart');
			this._moved = true;

			this._startPos = L.DomUtil.getPosition(this._element).subtract(diffVec);

			if (!L.Browser.touch) {
				L.DomUtil.disableTextSelection();
				this._setMovingCursor();
			}
		}

		this._newPos = this._startPos.add(diffVec);
		this._moving = true;

		L.Util.cancelAnimFrame(this._animRequest);
		this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
	},

	_updatePosition: function () {
		this.fire('predrag');
		L.DomUtil.setPosition(this._element, this._newPos);
		this.fire('drag');
	},

	_onUp: function (e) {
		var simulateClickTouch;
		clearTimeout(this._longPressTimeout);
		if (this._simulateClick && e.changedTouches) {
			var first = e.changedTouches[0],
			    el = first.target,
			    dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;

			if (el.tagName.toLowerCase() === 'a') {
				L.DomUtil.removeClass(el, 'leaflet-active');
			}

			if (dist < L.Draggable.TAP_TOLERANCE) {
				simulateClickTouch = first;
			}
		}

		if (!L.Browser.touch) {
			L.DomUtil.enableTextSelection();
			this._restoreCursor();
		}

		for (var i in L.Draggable.MOVE) {
			if (L.Draggable.MOVE.hasOwnProperty(i))
			{
				L.DomEvent.off(document, L.Draggable.MOVE[i], this._onMove);
				L.DomEvent.off(document, L.Draggable.END[i], this._onUp);
			}
		}

		if (this._moved) {
			// ensure drag is not fired after dragend
			L.Util.cancelAnimFrame(this._animRequest);

			this.fire('dragend');
		}
		this._moving = false;

		if (simulateClickTouch) {
			this._moved = false;
			this._simulateEvent('click', simulateClickTouch);
		}
	},

	_setMovingCursor: function () {
		L.DomUtil.addClass(document.body, 'leaflet-dragging');
	},

	_restoreCursor: function () {
		L.DomUtil.removeClass(document.body, 'leaflet-dragging');
	},

	_simulateEvent: function (type, e) {
		var simulatedEvent = document.createEvent('MouseEvents');

		simulatedEvent.initMouseEvent(
		        type, true, true, window, 1,
		        e.screenX, e.screenY,
		        e.clientX, e.clientY,
		        false, false, false, false, 0, null);

		e.target.dispatchEvent(simulatedEvent);
	}
});
<|endoftext|>


<commit_before>/*

 Library to work with files and directories

 */

/* global Java */

var File = Java.type('java.io.File'),
    FileReader = Java.type('java.io.FileReader');

(function() {

    var realFiles = [];

    // returns the content as an array of lines
    function _getContent(file) {

        return function() {

            var fr = new FileReader(file);

            //carriage returns
            var crs = [10, 13];

            var char = fr.read();

            var content = [];

            var lineContent = '';
            var lines = 0;

            while (char !== -1) {

                if (crs.indexOf(char) > -1) {
                    content.push(lineContent);
                    lineContent = '';
                } else {
                    lineContent = lineContent + String.fromCharCode(char);
                }

                char = fr.read();
            }
            fr.close();
            return content;
        }

    };

    // returns a file as an object
    function _realFile(file) {

        return {
            path: file,
            name: '',
            getContent: _getContent(file)
        };

    };

    function getDir(dir) {
        return new File(dir);
    };

    // list all directories in a directory
    function listDirs(dir){

      var currDir = getDir(dir),
          fileList = Java.from(currDir.listFiles()),
          dirs = [];

      print(fileList)

        // Get directories
        fileList.forEach(function(file) {
            currFile = new File(file);
            if (currFile.isDirectory()) {
                dirs.push[currFile.getName()];
            };
        });

        return dirs;

    };

    function filesInDir(options) {

        // default settings
        var settings = {
            dir: "",
            subDirectories: false
        };

        // extend settings with options
        for (obj in settings) {
            if (options.hasOwnProperty(obj)) {
                settings[obj] = options[obj];
            }
        }

        // We want all real files from dir and subdirs
        var dir = getDir(options.dir),
            fileList = Java.from(dir.listFiles()),
            currFile;

        // Get files
        fileList.forEach(function(file) {
            currFile = new File(file);
            // Get files in subdirectories
            if (currFile.isDirectory() && options.subDirectories) {
                var filesInSubDir = filesInDir(currFile.getAbsolutePath());
            } else if (currFile.isFile()) {
                realFiles.push(_realFile(file));
            };
        });

        return realFiles;

    };

    return {
        getDir: getDir,
        listDirs: listDirs,
        filesInDir: filesInDir
    };

})();
<commit_msg>no message<commit_after>/*

 Library to work with files and directories

 */

/* global Java */

var File = Java.type('java.io.File'),
    FileReader = Java.type('java.io.FileReader');

(function() {

    var realFiles = [];

    // returns the content as an array of lines
    function _getContent(file) {

        return function() {

            var fr = new FileReader(file);

            //carriage returns
            var crs = [10, 13];

            var char = fr.read();

            var content = [];

            var lineContent = '';
            var lines = 0;

            while (char !== -1) {

                if (crs.indexOf(char) > -1) {
                    content.push(lineContent);
                    lineContent = '';
                } else {
                    lineContent = lineContent + String.fromCharCode(char);
                }

                char = fr.read();
            }
            fr.close();
            return content;
        }

    };

    // returns a file as an object
    function _realFile(file) {

        return {
            path: file,
            name: '',
            getContent: _getContent(file)
        };

    };

    function getDir(dir) {
        return new File(dir);
    };

    // list all directories in a directory
    function listDirs(dir) {

        var currDir = getDir(dir),
            fileList = Java.from(currDir.listFiles()),
            dirs = [];

        print(fileList)

        // Get directories
        fileList.forEach(function(file) {
            currFile = new File(file);
            if (currFile.isDirectory()) {
                dirs.push[currFile.getName()];
            };
        });

        return dirs;

    };

    function filesInDir(options) {

        // default settings
        var settings = {
            dir: "",
            subDirectories: false
        };

        // extend settings with options
        for (obj in settings) {
            if (options.hasOwnProperty(obj)) {
                settings[obj] = options[obj];
            }
        }

        // We want all real files from dir and subdirs
        var dir = getDir(options.dir),
            fileList = Java.from(dir.listFiles()),
            currFile;

        // Get files
        fileList.forEach(function(file) {
            currFile = new File(file);
            // Get files in subdirectories
            if (currFile.isDirectory() && options.subDirectories) {
                var filesInSubDir = filesInDir(currFile.getAbsolutePath());
            } else if (currFile.isFile()) {
                realFiles.push(_realFile(file));
            };
        });

        return realFiles;

    };

    return {
        getDir: getDir,
        listDirs: listDirs,
        filesInDir: filesInDir
    };

})();
<|endoftext|>


<commit_before>/*
 * fis
 * http://fis.baidu.com/
 */

'use strict';

function getReleaseExt(ext){
    if(ext){
        var rExt = fis.config.get('roadmap.ext' + ext);
        if(rExt){
            ext = normalizeExt(rExt);
        }
    }
    return ext;
}

function normalizeExt(ext){
    if(ext[0] !== '.'){
        ext = '.' + ext;
    }
    return ext;
}

function normalizePath(path, reg, rExt){
    return path
        .replace(reg, '')
        .replace(/[:*?"<>|]/g, '_') + rExt;
}

function addHash(path, file){
    var rExt  = file.rExt,
        qRExt = fis.util.escapeReg(rExt),
        qExt = fis.util.escapeReg(file.ext),
        hash = file.getHash(),
        reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
    return path.replace(reg, '') + '_' + hash + rExt;
}

function getDomainsByPath(path){
    var domain = fis.config.get('roadmap.domain', {}), value = [];
    if(typeof domain === 'string'){
        value = domain.split(/\s*,\s*/);
    } else if(fis.util.is(domain, 'Array')) {
        value = domain;
    } else {
        fis.util.map(domain, function(pattern, domain){
            if((pattern === 'image' && fis.util.isImageFile(path)) || fis.util.glob(pattern, path)){
                if(typeof domain === 'string'){
                    value = domain.split(/\s*,\s*/);
                } else if(fis.util.is(domain, 'Array')){
                    value = domain;
                } else {
                    fis.log.warning('invalid domain [' + domain + '] of [roadmap.domain.' + pattern + ']');
                }
                return true;
            }
        });
    }
    return value;
}

function getDomain(path){
    var hash = fis.util.md5(path),
        domains = getDomainsByPath(path),
        len = domains.length,
        domain = '';
    if(len){
        domain = domains[hash.charCodeAt(0) % len];
    }
    return domain;
}

var File = Object.derive(function(){
    var _ = fis.util,
        info = _.pathinfo(arguments);
    _.map(info, this, true);
    var ext = info.ext,
        rExt = this.rExt = getReleaseExt(ext),
        realpath = this.realpath = _.realpathSafe(info.fullname),
        realpathNoExt = this.realpathNoExt = info.rest,
        root = fis.project.getProjectPath();

    if(_.isTextFile(rExt)) {
        this._isImage = false;
        this._isText = true;
        this.charset = null;
        switch(rExt){
            case '.js':
                this.isJsLike = true;
                this.useDomain = true;
                this.useHash = true;
                break;
            case '.css':
                this.isCssLike = true;
                this.useDomain = true;
                this.useHash = true;
                break;
            case '.html':
            case '.xhtml':
            case '.htm':
            case '.tpl':
            case '.php':
            case '.jsp':
            case '.asp':
                this.isHtmlLike = true;
            break;
        }
    } else {
        this._isImage = true;
        this._isText = false;
        this.useDomain = true;
        this.useHash = rExt !== '.ico';
    }

    this.isMod = false;
    this.requires = [];
    this.extras = {};
    this.useCompile = true;

    if(realpath.indexOf(root) === 0){
        //subpath
        var len = root.length, subpath;
        this.subpath = subpath = realpath.substring(len);
        this.subdirname = info.dirname.substring(len);
        this.subpathNoExt = realpathNoExt.substring(len);

        //roadmap
        fis.uri.roadmap(subpath, 'path', this);

        //release
        if(this.release === false){
            var self = this;
            Object.defineProperty(this, 'url', {
                enumerable : true,
                get : function(){
                    fis.log.error('unreleasable file [' + self.realpath + ']');
                }
            });
        } else {
            //release & url
            var reg = new RegExp(_.escapeReg(ext) + '$|' + _.escapeReg(rExt) + '$', 'i');
            var release;
            if(this.release){
                release = this.release.replace(/[\/\\]+/, '');
                if(release[0] !== '/'){
                    release = '/' + release;
                }
            } else {
                release = this.subpath;
            }
            this.release = normalizePath(release, reg, rExt);
            this.url = this.url ? normalizePath(this.url, reg, rExt) : this.release;
        }

        //charset
        if(this._isText){
            this.charset = (
                this.charset || fis.config.get('project.charset', 'utf8')
            ).toLowerCase();
        }

        //file id
        var id = this.id || subpath.replace(/^\//, '');
        var ns = fis.config.get('namespace');
        if(ns) {
            id = ns + fis.config.get('namespaceConnector', ':') + id;
        }
        this.id = id;
    }
}, {
    exists : function(){
        return fis.util.exists(this.realpath);
    },
    isText : function(){
        return this._isText;
    },
    isImage : function(){
        return this._isImage;
    },
    toString : function(){
        return this.realpath;
    },
    getMtime : function(){
        return fis.util.mtime(this.realpath);
    },
    isFile : function(){
        return fis.util.isFile(this.realpath);
    },
    isDir : function(){
        return fis.util.isDir(this.realpath);
    },
    setContent : function(c){
        this._content = c;
        return this;
    },
    getContent : function(){
        if(typeof this._content === 'undefined'){
            this._content = fis.util.read(this.realpath, this.isText());
        }
        return this._content;
    },
    getHash : function(){
        if(typeof this._hash === 'undefined'){
            this._hash = fis.util.md5(this.getContent());
        }
        return this._hash;
    },
    getBase64 : function(prefix){
        prefix = typeof prefix === 'undefined' ? true : prefix;
        if(prefix){
            prefix = 'data:' + fis.util.getMimeType(this.rExt) + ';base64,';
        } else {
            prefix = '';
        }
        return prefix + fis.util.base64(this._content);
    },
    getId : function(){
        return this.id;
    },
    getUrl : function(withHash, withDomain){
        var url = this.url;
        if(withHash && this.useHash){
            url = addHash(url, this);
        }
        if(withDomain && this.useDomain){
            if(typeof this.domain === 'undefined'){
                this.domain = getDomain(this.subpath);
            }
            url = this.domain + url;
        }
        return url + this.query;
    },
    getHashRelease : function(release){
        release = release || this.release;
        if(release){
            if(this.useHash){
                return addHash(release, this);
            } else {
                return release;
            }
        } else {
            fis.log.error('unreleasable file [' + this.realpath + ']');
        }
    },
    addRequire : function(id){
        if(id && (id = id.trim())){
            if(this.requires.indexOf(id) < 0){
                this.requires.push(id);
            }
            return id;
        }
        return false;
    },
    addSameNameRequire : function(ext){
        var path;
        if(fis.util.isFile(this.realpathNoExt + ext)){
            path = './' + this.filename + ext;
        } else {
            var map = fis.config.get('roadmap.ext');
            for(var key in map){
                if(map.hasOwnProperty(key)){
                    var oExt = normalizeExt(key);
                    var rExt = normalizeExt(map[key]);
                    if(rExt === ext && fis.util.isFile(this.realpathNoExt + oExt)) {
                        path = './' + this.filename + oExt;
                        break;
                    }
                }
            }
        }
        if(path){
            var info = fis.uri.getId(path, this.dirname);
            this.addRequire(info.id);
        }
    },
    removeRequire : function(id){
        var pos = this.requires.indexOf(id);
        if(pos > -1){
            this.requires.splice(pos, 1);
        }
    }
});

module.exports = File.factory();
module.exports.wrap = function(file){
    if(typeof file === 'string'){
        return new File(file);
    } else if(file instanceof File){
        return file;
    } else {
        fis.log.error('unable to convert [' + (typeof file) + '] to [File] object.');
    }
};<commit_msg>修复路径中多"/"会被替换成空的bug<commit_after>/*
 * fis
 * http://fis.baidu.com/
 */

'use strict';

function getReleaseExt(ext){
    if(ext){
        var rExt = fis.config.get('roadmap.ext' + ext);
        if(rExt){
            ext = normalizeExt(rExt);
        }
    }
    return ext;
}

function normalizeExt(ext){
    if(ext[0] !== '.'){
        ext = '.' + ext;
    }
    return ext;
}

function normalizePath(path, reg, rExt){
    return path
        .replace(reg, '')
        .replace(/[:*?"<>|]/g, '_') + rExt;
}

function addHash(path, file){
    var rExt  = file.rExt,
        qRExt = fis.util.escapeReg(rExt),
        qExt = fis.util.escapeReg(file.ext),
        hash = file.getHash(),
        reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
    return path.replace(reg, '') + '_' + hash + rExt;
}

function getDomainsByPath(path){
    var domain = fis.config.get('roadmap.domain', {}), value = [];
    if(typeof domain === 'string'){
        value = domain.split(/\s*,\s*/);
    } else if(fis.util.is(domain, 'Array')) {
        value = domain;
    } else {
        fis.util.map(domain, function(pattern, domain){
            if((pattern === 'image' && fis.util.isImageFile(path)) || fis.util.glob(pattern, path)){
                if(typeof domain === 'string'){
                    value = domain.split(/\s*,\s*/);
                } else if(fis.util.is(domain, 'Array')){
                    value = domain;
                } else {
                    fis.log.warning('invalid domain [' + domain + '] of [roadmap.domain.' + pattern + ']');
                }
                return true;
            }
        });
    }
    return value;
}

function getDomain(path){
    var hash = fis.util.md5(path),
        domains = getDomainsByPath(path),
        len = domains.length,
        domain = '';
    if(len){
        domain = domains[hash.charCodeAt(0) % len];
    }
    return domain;
}

var File = Object.derive(function(){
    var _ = fis.util,
        info = _.pathinfo(arguments);
    _.map(info, this, true);
    var ext = info.ext,
        rExt = this.rExt = getReleaseExt(ext),
        realpath = this.realpath = _.realpathSafe(info.fullname),
        realpathNoExt = this.realpathNoExt = info.rest,
        root = fis.project.getProjectPath();

    if(_.isTextFile(rExt)) {
        this._isImage = false;
        this._isText = true;
        this.charset = null;
        switch(rExt){
            case '.js':
                this.isJsLike = true;
                this.useDomain = true;
                this.useHash = true;
                break;
            case '.css':
                this.isCssLike = true;
                this.useDomain = true;
                this.useHash = true;
                break;
            case '.html':
            case '.xhtml':
            case '.htm':
            case '.tpl':
            case '.php':
            case '.jsp':
            case '.asp':
                this.isHtmlLike = true;
            break;
        }
    } else {
        this._isImage = true;
        this._isText = false;
        this.useDomain = true;
        this.useHash = rExt !== '.ico';
    }

    this.isMod = false;
    this.requires = [];
    this.extras = {};
    this.useCompile = true;

    if(realpath.indexOf(root) === 0){
        //subpath
        var len = root.length, subpath;
        this.subpath = subpath = realpath.substring(len);
        this.subdirname = info.dirname.substring(len);
        this.subpathNoExt = realpathNoExt.substring(len);

        //roadmap
        fis.uri.roadmap(subpath, 'path', this);

        //release
        if(this.release === false){
            var self = this;
            Object.defineProperty(this, 'url', {
                enumerable : true,
                get : function(){
                    fis.log.error('unreleasable file [' + self.realpath + ']');
                }
            });
        } else {
            //release & url
            var reg = new RegExp(_.escapeReg(ext) + '$|' + _.escapeReg(rExt) + '$', 'i');
            var release;
            if(this.release){
                release = this.release.replace(/[\/\\]+/, '/');
                if(release[0] !== '/'){
                    release = '/' + release;
                }
            } else {
                release = this.subpath;
            }
            this.release = normalizePath(release, reg, rExt);
            this.url = this.url ? normalizePath(this.url, reg, rExt) : this.release;
        }

        //charset
        if(this._isText){
            this.charset = (
                this.charset || fis.config.get('project.charset', 'utf8')
            ).toLowerCase();
        }

        //file id
        var id = this.id || subpath.replace(/^\//, '');
        var ns = fis.config.get('namespace');
        if(ns) {
            id = ns + fis.config.get('namespaceConnector', ':') + id;
        }
        this.id = id;
    }
}, {
    exists : function(){
        return fis.util.exists(this.realpath);
    },
    isText : function(){
        return this._isText;
    },
    isImage : function(){
        return this._isImage;
    },
    toString : function(){
        return this.realpath;
    },
    getMtime : function(){
        return fis.util.mtime(this.realpath);
    },
    isFile : function(){
        return fis.util.isFile(this.realpath);
    },
    isDir : function(){
        return fis.util.isDir(this.realpath);
    },
    setContent : function(c){
        this._content = c;
        return this;
    },
    getContent : function(){
        if(typeof this._content === 'undefined'){
            this._content = fis.util.read(this.realpath, this.isText());
        }
        return this._content;
    },
    getHash : function(){
        if(typeof this._hash === 'undefined'){
            this._hash = fis.util.md5(this.getContent());
        }
        return this._hash;
    },
    getBase64 : function(prefix){
        prefix = typeof prefix === 'undefined' ? true : prefix;
        if(prefix){
            prefix = 'data:' + fis.util.getMimeType(this.rExt) + ';base64,';
        } else {
            prefix = '';
        }
        return prefix + fis.util.base64(this._content);
    },
    getId : function(){
        return this.id;
    },
    getUrl : function(withHash, withDomain){
        var url = this.url;
        if(withHash && this.useHash){
            url = addHash(url, this);
        }
        if(withDomain && this.useDomain){
            if(typeof this.domain === 'undefined'){
                this.domain = getDomain(this.subpath);
            }
            url = this.domain + url;
        }
        return url + this.query;
    },
    getHashRelease : function(release){
        release = release || this.release;
        if(release){
            if(this.useHash){
                return addHash(release, this);
            } else {
                return release;
            }
        } else {
            fis.log.error('unreleasable file [' + this.realpath + ']');
        }
    },
    addRequire : function(id){
        if(id && (id = id.trim())){
            if(this.requires.indexOf(id) < 0){
                this.requires.push(id);
            }
            return id;
        }
        return false;
    },
    addSameNameRequire : function(ext){
        var path;
        if(fis.util.isFile(this.realpathNoExt + ext)){
            path = './' + this.filename + ext;
        } else {
            var map = fis.config.get('roadmap.ext');
            for(var key in map){
                if(map.hasOwnProperty(key)){
                    var oExt = normalizeExt(key);
                    var rExt = normalizeExt(map[key]);
                    if(rExt === ext && fis.util.isFile(this.realpathNoExt + oExt)) {
                        path = './' + this.filename + oExt;
                        break;
                    }
                }
            }
        }
        if(path){
            var info = fis.uri.getId(path, this.dirname);
            this.addRequire(info.id);
        }
    },
    removeRequire : function(id){
        var pos = this.requires.indexOf(id);
        if(pos > -1){
            this.requires.splice(pos, 1);
        }
    }
});

module.exports = File.factory();
module.exports.wrap = function(file){
    if(typeof file === 'string'){
        return new File(file);
    } else if(file instanceof File){
        return file;
    } else {
        fis.log.error('unable to convert [' + (typeof file) + '] to [File] object.');
    }
};<|endoftext|>


<commit_before>import styled from 'styled-components'
import { Colors } from 'theme/variables'
import { media } from 'theme/utils/media.js'

export default styled.section`
  margin-bottom: 1.8em;
  .down {
    color: ${Colors.h};
  }
  button {
    :first-child {
      margin-left: 0;
    }
    :last-child {
      margin-right: 0;
    }
    display: inline-block;
    font-size: 1.8rem;
    display: inline-block;
    margin-top: 2em;
    color: ${Colors.code};
    line-height: 1.25;
    font-weight: 600;
    text-rendering: optimizeLegibility;
    margin-left: 1em;
    margin-right: 1em;
    ${media.phone`
      margin-left: .7em;
      margin-right: .7em;
    `}
  }
`
<commit_msg>hovering for sub nav<commit_after>import styled from 'styled-components'
import { Colors } from 'theme/variables'
import { media } from 'theme/utils/media.js'

export default styled.section`
  margin-bottom: 1.8em;
  .down {
    color: ${Colors.h};
  }
  button {
    :hover {
      text-decoration: underline;
    }
    :first-child {
      margin-left: 0;
    }
    :last-child {
      margin-right: 0;
    }
    display: inline-block;
    font-size: 1.8rem;
    display: inline-block;
    margin-top: 2em;
    color: ${Colors.code};
    line-height: 1.25;
    font-weight: 600;
    text-rendering: optimizeLegibility;
    margin-left: 1em;
    margin-right: 1em;
    ${media.phone`
      margin-left: .7em;
      margin-right: .7em;
    `}
  }
`
<|endoftext|>