<commit_before>/*
    InfinityScroller

    Create the Google Reader-type of endless scrolling on (almost) any page.

	(c) Copyright 2008 Gyula László

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

/**
    See README for more info or go to

        http://psinc.wordpress.com/cscroller_js

        or

        http://github.com/gyulalaszlo/csscroller

    to obtain your copy.

    Usage by example:

	new thepaw.ContinousScroller("recommended_programs",{ url:"/top10/$page_num$" }, {
		loading_placeholder:'
Blah-blah
',
		check_interval:0.5,
		failiure_message:'Something terrible happened.
'+
		    'We cannot help you get any more content. Check your internet connection.'
	})

*/

thepaw = {}

thepaw.ContinousScroller = Class.create()
thepaw.ContinousScroller.prototype = {

    /**
    new thepaw.ContinousScroller(content_div_id, url_generator, options)

    Create a continous scroller. Only one instance per page...

    Parameters:

        content_div_id:
            The div to serve the content into.
        url_generator:
            The url generator to use. See "The default url generator"
            bit bellow.

    */
    initialize: function(content_div_id, url_generator, options) {

        this.options = $H( thepaw.ContinousScroller.DefaultConfiguration ).merge(options)
        this.url_generator = (typeof url_generator.next == 'undefined' ?
            $H(thepaw.ContinousScroller.DefaultUrlGenerator ).merge(url_generator) : url_generator)

        this.div_id = content_div_id
        this.updateInProgress = false

        new Insertion.After( this.div_id,
            "
" +
            options.loading_placeholder + "
")

        this.updater = new PeriodicalExecuter( function(pe) {
            var t = pe.target_obj
            if (t == null) return
            if (!t.updateInProgress &&
                    (thepaw.third_party.getPageHeight() -
                    thepaw.third_party.getScrollHeight()) < t.options.trigger_height) {
            	    t.updateInProgress = true;
            	    t.getMoreContent();
            }
        }, this.options.check_interval );
        this.updater.target_obj = this

    },

    getMoreContent: function(){
        var t = this
		var placeholder_div = $( this.placeholderDivId )
		placeholder_div.show()

		// LoadStart Callback
		this.options.onSegmentLoadStart()

        new Ajax.Request(this.url_generator.next()  , {
            method: 'get',
            onSuccess: function(transport) {
				t.options.onSegmentLoadComplete( transport )

                // No more elements for us. Stop.
                if (transport.responseText.length < 300) t.updater.stop()

                // Update the content
                new Insertion.Bottom(t.div_id, transport.responseText)
                placeholder_div.hide()
                t.updateInProgress = false;
				t.options.onSegmentInsertComplete()

            },

            onFailure: function(transport) {
				t.options.onSegmentLoadFaliure( transport )
                new Insertion.bottom( t.div_id,
                    "
" + t.options.failiure_message
                    + "
"
                )
            }

        })
    },

    placeholderDivId:'continous_scroller_loading_placeholder'

}

/**
    The default options
    =========================

    Options:

        check_interval:
            The interval between checks (seconds). Default is 0.2

        trigger_height:
            The maximum distance from the bottom where the content
            update gets triggered (pixels). Default is 1000

        failiure_message:
            The message to show on the bottom when the scroller
            can't load the next page.

        loading_placeholder:
            The placeholder text to show when the loading is still
            in progress.

	Callbacks:

		onSegmentLoadStart:
			 function() { ... }
			Callback when starting to load a new segment.

		onSegmentLoadComplete:
			 function(transport) { ... }
			Callback when the AJAX request completed. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

		onSegmentInsertComplete:
			function() { }
			Callback when the new contents are inserted to the bottom.

		onSegmentLoadFaliure
			 function(transport) { ... }
			Callback when the AJAX request fails. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

*/

thepaw.ContinousScroller.DefaultConfiguration = {
    check_interval:0.2,
    trigger_height:1000,
    failiure_message: "Error while trying to fetch the new items." +
                        "Check your internet connection...",
    loading_placeholder: "Loading newer items...",
    uid:"continous_scroller_uid",

	onSegmentLoadStart:function() { },
	onSegmentLoadComplete:function(transport) { },
	onSegmentInsertComplete:function() { },
	onSegmentLoadFaliure:function(transport) { }
}

/**
    The default url generator
    =========================

    A generator is a hash with a next() function which returns
    the url of the next segment to load. The second parameter of
    the ContinousScroller accepts hash or a custom url generator:

    Examples:

    // Using the default url generator
    // we don't define next()

    {  url:"/pages/$page_num$.html"  }
        // => '/pages/1.html', '/pages/2.html', ....

    {  start_index:10, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/11', '/pages/12', ...

    {  start_index:10, default_spacing:20, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/20', '/pages/30', ...

    // Using a custom url generator
    // we simply define next()

    {  next:function() { return "?page_idx=" + Math.round(Math.random() * 100) } }
        // => '?page_idx=57', '?page_idx=19', '/pages/81', ...

    {
        idx = 0,
        next:function() {
            return "items?from=" + this.idx + "&until=" + (this.idx += 10)
        }
    }
        // => 'items?from=0&until=10', 'items?from=0&until=10', '/pages/30'

*/

thepaw.ContinousScroller.DefaultUrlGenerator = {
    url:null,
    start_index:1,
    default_spacing:1,

    next: function() {
        if (typeof this.current_index == "undefined") this.current_index = this.start_index
        this.current_index += this.default_spacing

        var tmp_url = ( this.url == null ? "?page=$page_num$" : this.url )
        return tmp_url.replace('$page_num$', this.current_index)
    }

}

// These two functions are based on code from
// http://www.quirksmode.org/viewport/compatibility.html
thepaw.third_party = {

    getPageHeight: function (){
        var y;
        var test1 = document.body.scrollHeight;
        var test2 = document.body.offsetHeight
        if (test1 > test2) // all but Explorer Mac
        {
            y = document.body.scrollHeight;
        }
        else // Explorer Mac;
             //would also work in Explorer 6 Strict, Mozilla and Safari
        {
            y = document.body.offsetHeight;
        }
        return parseInt(y);
    },

    _getWindowHeight: function (){
        if (self.innerWidth)
        {
            frameWidth = self.innerWidth;
            frameHeight = self.innerHeight;
        }
        else if (document.documentElement && document.documentElement.clientWidth)
        {
            frameWidth = document.documentElement.clientWidth;
            frameHeight = document.documentElement.clientHeight;
        }
        else if (document.body)
        {
            frameWidth = document.body.clientWidth;
            frameHeight = document.body.clientHeight;
        }
        return parseInt(frameHeight);
    },

    getScrollHeight: function (){
        var y;
        // all except Explorer
        if (self.pageYOffset)
        {
            y = self.pageYOffset;
        }
        else if (document.documentElement && document.documentElement.scrollTop)
        {
            y = document.documentElement.scrollTop;
        }
        else if (document.body) // all other Explorers
        {
            y = document.body.scrollTop;
        }
        return parseInt(y)+thepaw.third_party._getWindowHeight();
    }

}<commit_msg>Deleted the link to my blog from the source.<commit_after>/*
    InfinityScroller

    Create the Google Reader-type of endless scrolling on (almost) any page.

	(c) Copyright 2008 Gyula László

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

/**
    See README for more info or go to

        http://github.com/gyulalaszlo/csscroller

    to obtain your copy.

    Usage by example:

	new thepaw.ContinousScroller("recommended_programs",{ url:"/top10/$page_num$" }, {
		loading_placeholder:'
Blah-blah
',
		check_interval:0.5,
		failiure_message:'Something terrible happened.
'+
		    'We cannot help you get any more content. Check your internet connection.'
	})

*/

thepaw = {}

thepaw.ContinousScroller = Class.create()
thepaw.ContinousScroller.prototype = {

    /**
    new thepaw.ContinousScroller(content_div_id, url_generator, options)

    Create a continous scroller. Only one instance per page...

    Parameters:

        content_div_id:
            The div to serve the content into.
        url_generator:
            The url generator to use. See "The default url generator"
            bit bellow.

    */
    initialize: function(content_div_id, url_generator, options) {

        this.options = $H( thepaw.ContinousScroller.DefaultConfiguration ).merge(options)
        this.url_generator = (typeof url_generator.next == 'undefined' ?
            $H(thepaw.ContinousScroller.DefaultUrlGenerator ).merge(url_generator) : url_generator)

        this.div_id = content_div_id
        this.updateInProgress = false

        new Insertion.After( this.div_id,
            "
" +
            options.loading_placeholder + "
")

        this.updater = new PeriodicalExecuter( function(pe) {
            var t = pe.target_obj
            if (t == null) return
            if (!t.updateInProgress &&
                    (thepaw.third_party.getPageHeight() -
                    thepaw.third_party.getScrollHeight()) < t.options.trigger_height) {
            	    t.updateInProgress = true;
            	    t.getMoreContent();
            }
        }, this.options.check_interval );
        this.updater.target_obj = this

    },

    getMoreContent: function(){
        var t = this
		var placeholder_div = $( this.placeholderDivId )
		placeholder_div.show()

		// LoadStart Callback
		this.options.onSegmentLoadStart()

        new Ajax.Request(this.url_generator.next()  , {
            method: 'get',
            onSuccess: function(transport) {
				t.options.onSegmentLoadComplete( transport )

                // No more elements for us. Stop.
                if (transport.responseText.length < 300) t.updater.stop()

                // Update the content
                new Insertion.Bottom(t.div_id, transport.responseText)
                placeholder_div.hide()
                t.updateInProgress = false;
				t.options.onSegmentInsertComplete()

            },

            onFailure: function(transport) {
				t.options.onSegmentLoadFaliure( transport )
                new Insertion.bottom( t.div_id,
                    "
" + t.options.failiure_message
                    + "
"
                )
            }

        })
    },

    placeholderDivId:'continous_scroller_loading_placeholder'

}

/**
    The default options
    =========================

    Options:

        check_interval:
            The interval between checks (seconds). Default is 0.2

        trigger_height:
            The maximum distance from the bottom where the content
            update gets triggered (pixels). Default is 1000

        failiure_message:
            The message to show on the bottom when the scroller
            can't load the next page.

        loading_placeholder:
            The placeholder text to show when the loading is still
            in progress.

	Callbacks:

		onSegmentLoadStart:
			 function() { ... }
			Callback when starting to load a new segment.

		onSegmentLoadComplete:
			 function(transport) { ... }
			Callback when the AJAX request completed. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

		onSegmentInsertComplete:
			function() { }
			Callback when the new contents are inserted to the bottom.

		onSegmentLoadFaliure
			 function(transport) { ... }
			Callback when the AJAX request fails. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

*/

thepaw.ContinousScroller.DefaultConfiguration = {
    check_interval:0.2,
    trigger_height:1000,
    failiure_message: "Error while trying to fetch the new items." +
                        "Check your internet connection...",
    loading_placeholder: "Loading newer items...",
    uid:"continous_scroller_uid",

	onSegmentLoadStart:function() { },
	onSegmentLoadComplete:function(transport) { },
	onSegmentInsertComplete:function() { },
	onSegmentLoadFaliure:function(transport) { }
}

/**
    The default url generator
    =========================

    A generator is a hash with a next() function which returns
    the url of the next segment to load. The second parameter of
    the ContinousScroller accepts hash or a custom url generator:

    Examples:

    // Using the default url generator
    // we don't define next()

    {  url:"/pages/$page_num$.html"  }
        // => '/pages/1.html', '/pages/2.html', ....

    {  start_index:10, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/11', '/pages/12', ...

    {  start_index:10, default_spacing:20, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/20', '/pages/30', ...

    // Using a custom url generator
    // we simply define next()

    {  next:function() { return "?page_idx=" + Math.round(Math.random() * 100) } }
        // => '?page_idx=57', '?page_idx=19', '/pages/81', ...

    {
        idx = 0,
        next:function() {
            return "items?from=" + this.idx + "&until=" + (this.idx += 10)
        }
    }
        // => 'items?from=0&until=10', 'items?from=0&until=10', '/pages/30'

*/

thepaw.ContinousScroller.DefaultUrlGenerator = {
    url:null,
    start_index:1,
    default_spacing:1,

    next: function() {
        if (typeof this.current_index == "undefined") this.current_index = this.start_index
        this.current_index += this.default_spacing

        var tmp_url = ( this.url == null ? "?page=$page_num$" : this.url )
        return tmp_url.replace('$page_num$', this.current_index)
    }

}

/*
	The following code (c) Copyright 2007 [Aza Raskin](http://azarask.in/blog/):

	From the implementation of [Humanized reader](http://humanized.com/reader)
*/

thepaw.third_party = {

    getPageHeight: function (){
        var y;
        var test1 = document.body.scrollHeight;
        var test2 = document.body.offsetHeight
        if (test1 > test2) // all but Explorer Mac
        {
            y = document.body.scrollHeight;
        }
        else // Explorer Mac;
             //would also work in Explorer 6 Strict, Mozilla and Safari
        {
            y = document.body.offsetHeight;
        }
        return parseInt(y);
    },

    _getWindowHeight: function (){
        if (self.innerWidth)
        {
            frameWidth = self.innerWidth;
            frameHeight = self.innerHeight;
        }
        else if (document.documentElement && document.documentElement.clientWidth)
        {
            frameWidth = document.documentElement.clientWidth;
            frameHeight = document.documentElement.clientHeight;
        }
        else if (document.body)
        {
            frameWidth = document.body.clientWidth;
            frameHeight = document.body.clientHeight;
        }
        return parseInt(frameHeight);
    },

    getScrollHeight: function (){
        var y;
        // all except Explorer
        if (self.pageYOffset)
        {
            y = self.pageYOffset;
        }
        else if (document.documentElement && document.documentElement.scrollTop)
        {
            y = document.documentElement.scrollTop;
        }
        else if (document.body) // all other Explorers
        {
            y = document.body.scrollTop;
        }
        return parseInt(y)+thepaw.third_party._getWindowHeight();
    }

}<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var path = require('path');

/**
 * A module resource loader
 *
 * Environment variable:
 * RESOURCE_BASE_PATH is used to override the base lookup path
 */

var _Resource;
var _basePath = function() {
  return (!!process.env.RESOURCE_BASE_PATH) ? process.env.RESOURCE_BASE_PATH : process.cwd();
};

/**
 * Create a relative module loader
 *
 * @param {String} namespace A namespace to load from; defaults to null
 */
var _createLoader = function(namespace) {
  /**
   * Get a resolved path as a string
   *
   * @param  {String} src A source path
   */
  var _resolve = function(src) {
    var base = namespace || _basePath();
    var relative = path.sep + src;
    return base + relative;
  };

  /**
   * Load module according to resolved path
   *
   * @param  {String} src A source path
   */
  var instance = function(src) {
    return require(_resolve(src));
  };

  instance.resolve = _resolve;

  return instance;
};

/**
 * Load a module according to base path
 */
module.exports = _Resource = _createLoader(null);
module.exports.load = _Resource;

/**
 * Set/Get a mapping for a namespace to another path
 */
module.exports.namespace = function(name, base) {
  // Set
  if(arguments.length === 2) {
    if(name.match(/load|resolve/)) {
      throw new Error('Resource namespace conflict '+name);
    }

    return _Resource[name] = _createLoader(base);
  }

  // Get
  return _Resource[name];
};
<commit_msg>Updated path resolution to store resolved path to avoid relative conflicts<commit_after>'use strict';

/**
 * Module dependencies.
 */
var path = require('path');

/**
 * A module resource loader
 *
 * Environment variable:
 * RESOURCE_BASE_PATH is used to override the base lookup path
 */

var _Resource;
var _basePath = function() {
  return (!!process.env.RESOURCE_BASE_PATH) ? process.env.RESOURCE_BASE_PATH : process.cwd();
};

/**
 * Create a relative module loader
 *
 * @param {String} namespace A namespace to load from; defaults to null
 */
var _createLoader = function(namespace) {
  /**
   * Get a resolved path as a string
   *
   * @param  {String} src A source path
   */
  var _resolve = function(src) {
    var base = namespace ? path.resolve(namespace) : _basePath();
    var relative = path.sep + src;
    return base + relative;
  };

  /**
   * Load module according to resolved path
   *
   * @param  {String} src A source path
   */
  var instance = function(src) {
    return require(_resolve(src));
  };

  instance.resolve = _resolve;

  return instance;
};

/**
 * Load a module according to base path
 */
module.exports = _Resource = _createLoader(null);
module.exports.load = _Resource;

/**
 * Set/Get a mapping for a namespace to another path
 */
module.exports.namespace = function(name, base) {
  // Set
  if(arguments.length === 2) {
    if(name.match(/load|resolve/)) {
      throw new Error('Resource namespace conflict '+name);
    }

    return _Resource[name] = _createLoader(base);
  }

  // Get
  return _Resource[name];
};
<|endoftext|>


<commit_before>/*
    InfinityScroller

    Create the Google Reader-type of endless scrolling on (almost) any page.

    (c) Copyright 2008 Gyula László. All Rights Reserved.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/**
    See README for more info or go to

        http://psinc.wordpress.com/cscroller_js

        or

        http://github.com/gyulalaszlo/csscroller

    to obtain your copy.

    Usage by example:

	new thepaw.ContinousScroller("recommended_programs",{ url:"/top10/$page_num$" }, {
		loading_placeholder:'
Blah-blah
',
		check_interval:0.5,
		failiure_message:'Something terrible happened.
'+
		    'We cannot help you get any more content. Check your internet connection.'
	})

*/

thepaw = {}

thepaw.ContinousScroller = Class.create()
thepaw.ContinousScroller.prototype = {

    /**
    new thepaw.ContinousScroller(content_div_id, url_generator, options)

    Create a continous scroller. Only one instance per page...

    Parameters:

        content_div_id:
            The div to serve the content into.
        url_generator:
            The url generator to use. See "The default url generator"
            bit bellow.

    */
    initialize: function(content_div_id, url_generator, options) {

        this.options = $H( thepaw.ContinousScroller.DefaultConfiguration ).merge(options)
        this.url_generator = (typeof url_generator.next == 'undefined' ?
            $H(thepaw.ContinousScroller.DefaultUrlGenerator ).merge(url_generator) : url_generator)

        this.div_id = content_div_id
        this.updateInProgress = false

        new Insertion.After( this.div_id,
            "
" +
            options.loading_placeholder + "
")

        this.updater = new PeriodicalExecuter( function(pe) {
            var t = pe.target_obj
            if (t == null) return
            if (!t.updateInProgress &&
                    (thepaw.third_party.getPageHeight() -
                    thepaw.third_party.getScrollHeight()) < t.options.trigger_height) {
            	    t.updateInProgress = true;
            	    t.getMoreContent();
            }
        }, this.options.check_interval );
        this.updater.target_obj = this

    },

    getMoreContent: function(){
        var t = this
		var placeholder_div = $( this.placeholderDivId )
		placeholder_div.show()

		// LoadStart Callback
		this.options.onSegmentLoadStart()

        new Ajax.Request(this.url_generator.next()  , {
            method: 'get',
            onSuccess: function(transport) {
				t.options.onSegmentLoadComplete( transport )

                // No more elements for us. Stop.
                if (transport.responseText.length < 300) t.updater.stop()

                // Update the content
                new Insertion.Bottom(t.div_id, transport.responseText)
                placeholder_div.hide()
                t.updateInProgress = false;
				t.options.onSegmentInsertComplete()

            },

            onFailure: function(transport) {
				t.options.onSegmentLoadFaliure( transport )
                new Insertion.bottom( t.div_id,
                    "
" + t.options.failiure_message
                    + "
"
                )
            }

        })
    },

    placeholderDivId:'continous_scroller_loading_placeholder'

}

/**
    The default options
    =========================

    Options:

        check_interval:
            The interval between checks (seconds). Default is 0.2

        trigger_height:
            The maximum distance from the bottom where the content
            update gets triggered (pixels). Default is 1000

        failiure_message:
            The message to show on the bottom when the scroller
            can't load the next page.

        loading_placeholder:
            The placeholder text to show when the loading is still
            in progress.

	Callbacks:

		onSegmentLoadStart:
			 function() { ... }
			Callback when starting to load a new segment.

		onSegmentLoadComplete:
			 function(transport) { ... }
			Callback when the AJAX request completed. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

		onSegmentInsertComplete:
			function() { }
			Callback when the new contents are inserted to the bottom.

		onSegmentLoadFaliure
			 function(transport) { ... }
			Callback when the AJAX request fails. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

*/

thepaw.ContinousScroller.DefaultConfiguration = {
    check_interval:0.2,
    trigger_height:1000,
    failiure_message: "Error while trying to fetch the new items." +
                        "Check your internet connection...",
    loading_placeholder: "Loading newer items...",
    uid:"continous_scroller_uid",

	onSegmentLoadStart:function() { },
	onSegmentLoadComplete:function(transport) { },
	onSegmentInsertComplete:function() { },
	onSegmentLoadFaliure:function(transport) { }
}

/**
    The default url generator
    =========================

    A generator is a hash with a next() function which returns
    the url of the next segment to load. The second parameter of
    the ContinousScroller accepts hash or a custom url generator:

    Examples:

    // Using the default url generator
    // we don't define next()

    {  url:"/pages/$page_num$.html"  }
        // => '/pages/1.html', '/pages/2.html', ....

    {  start_index:10, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/11', '/pages/12', ...

    {  start_index:10, default_spacing:20, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/20', '/pages/30', ...

    // Using a custom url generator
    // we simply define next()

    {  next:function() { return "?page_idx=" + Math.round(Math.random() * 100) } }
        // => '?page_idx=57', '?page_idx=19', '/pages/81', ...

    {
        idx = 0,
        next:function() {
            return "items?from=" + this.idx + "&until=" + (this.idx += 10)
        }
    }
        // => 'items?from=0&until=10', 'items?from=0&until=10', '/pages/30'

*/

thepaw.ContinousScroller.DefaultUrlGenerator = {
    url:null,
    start_index:1,
    default_spacing:1,

    next: function() {
        if (typeof this.current_index == "undefined") this.current_index = this.start_index
        this.current_index += this.default_spacing

        var tmp_url = ( this.url == null ? "?page=$page_num$" : this.url )
        return tmp_url.replace('$page_num$', this.current_index)
    }

}

// These two functions are based on code from
// http://www.quirksmode.org/viewport/compatibility.html
thepaw.third_party = {

    getPageHeight: function (){
        var y;
        var test1 = document.body.scrollHeight;
        var test2 = document.body.offsetHeight
        if (test1 > test2) // all but Explorer Mac
        {
            y = document.body.scrollHeight;
        }
        else // Explorer Mac;
             //would also work in Explorer 6 Strict, Mozilla and Safari
        {
            y = document.body.offsetHeight;
        }
        return parseInt(y);
    },

    _getWindowHeight: function (){
        if (self.innerWidth)
        {
            frameWidth = self.innerWidth;
            frameHeight = self.innerHeight;
        }
        else if (document.documentElement && document.documentElement.clientWidth)
        {
            frameWidth = document.documentElement.clientWidth;
            frameHeight = document.documentElement.clientHeight;
        }
        else if (document.body)
        {
            frameWidth = document.body.clientWidth;
            frameHeight = document.body.clientHeight;
        }
        return parseInt(frameHeight);
    },

    getScrollHeight: function (){
        var y;
        // all except Explorer
        if (self.pageYOffset)
        {
            y = self.pageYOffset;
        }
        else if (document.documentElement && document.documentElement.scrollTop)
        {
            y = document.documentElement.scrollTop;
        }
        else if (document.body) // all other Explorers
        {
            y = document.body.scrollTop;
        }
        return parseInt(y)+thepaw.third_party._getWindowHeight();
    }

}<commit_msg>marked aza's code, updated licence to MIT<commit_after>/*
    InfinityScroller

    Create the Google Reader-type of endless scrolling on (almost) any page.

	(c) Copyright 2008 Gyula László

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

/**
    See README for more info or go to

        http://psinc.wordpress.com/cscroller_js

        or

        http://github.com/gyulalaszlo/csscroller

    to obtain your copy.

    Usage by example:

	new thepaw.ContinousScroller("recommended_programs",{ url:"/top10/$page_num$" }, {
		loading_placeholder:'
Blah-blah
',
		check_interval:0.5,
		failiure_message:'Something terrible happened.
'+
		    'We cannot help you get any more content. Check your internet connection.'
	})

*/

thepaw = {}

thepaw.ContinousScroller = Class.create()
thepaw.ContinousScroller.prototype = {

    /**
    new thepaw.ContinousScroller(content_div_id, url_generator, options)

    Create a continous scroller. Only one instance per page...

    Parameters:

        content_div_id:
            The div to serve the content into.
        url_generator:
            The url generator to use. See "The default url generator"
            bit bellow.

    */
    initialize: function(content_div_id, url_generator, options) {

        this.options = $H( thepaw.ContinousScroller.DefaultConfiguration ).merge(options)
        this.url_generator = (typeof url_generator.next == 'undefined' ?
            $H(thepaw.ContinousScroller.DefaultUrlGenerator ).merge(url_generator) : url_generator)

        this.div_id = content_div_id
        this.updateInProgress = false

        new Insertion.After( this.div_id,
            "
" +
            options.loading_placeholder + "
")

        this.updater = new PeriodicalExecuter( function(pe) {
            var t = pe.target_obj
            if (t == null) return
            if (!t.updateInProgress &&
                    (thepaw.third_party.getPageHeight() -
                    thepaw.third_party.getScrollHeight()) < t.options.trigger_height) {
            	    t.updateInProgress = true;
            	    t.getMoreContent();
            }
        }, this.options.check_interval );
        this.updater.target_obj = this

    },

    getMoreContent: function(){
        var t = this
		var placeholder_div = $( this.placeholderDivId )
		placeholder_div.show()

		// LoadStart Callback
		this.options.onSegmentLoadStart()

        new Ajax.Request(this.url_generator.next()  , {
            method: 'get',
            onSuccess: function(transport) {
				t.options.onSegmentLoadComplete( transport )

                // No more elements for us. Stop.
                if (transport.responseText.length < 300) t.updater.stop()

                // Update the content
                new Insertion.Bottom(t.div_id, transport.responseText)
                placeholder_div.hide()
                t.updateInProgress = false;
				t.options.onSegmentInsertComplete()

            },

            onFailure: function(transport) {
				t.options.onSegmentLoadFaliure( transport )
                new Insertion.bottom( t.div_id,
                    "
" + t.options.failiure_message
                    + "
"
                )
            }

        })
    },

    placeholderDivId:'continous_scroller_loading_placeholder'

}

/**
    The default options
    =========================

    Options:

        check_interval:
            The interval between checks (seconds). Default is 0.2

        trigger_height:
            The maximum distance from the bottom where the content
            update gets triggered (pixels). Default is 1000

        failiure_message:
            The message to show on the bottom when the scroller
            can't load the next page.

        loading_placeholder:
            The placeholder text to show when the loading is still
            in progress.

	Callbacks:

		onSegmentLoadStart:
			 function() { ... }
			Callback when starting to load a new segment.

		onSegmentLoadComplete:
			 function(transport) { ... }
			Callback when the AJAX request completed. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

		onSegmentInsertComplete:
			function() { }
			Callback when the new contents are inserted to the bottom.

		onSegmentLoadFaliure
			 function(transport) { ... }
			Callback when the AJAX request fails. The transport is the
			raw Protype transport. (use transport.responseText
			to get the raw response text)

*/

thepaw.ContinousScroller.DefaultConfiguration = {
    check_interval:0.2,
    trigger_height:1000,
    failiure_message: "Error while trying to fetch the new items." +
                        "Check your internet connection...",
    loading_placeholder: "Loading newer items...",
    uid:"continous_scroller_uid",

	onSegmentLoadStart:function() { },
	onSegmentLoadComplete:function(transport) { },
	onSegmentInsertComplete:function() { },
	onSegmentLoadFaliure:function(transport) { }
}

/**
    The default url generator
    =========================

    A generator is a hash with a next() function which returns
    the url of the next segment to load. The second parameter of
    the ContinousScroller accepts hash or a custom url generator:

    Examples:

    // Using the default url generator
    // we don't define next()

    {  url:"/pages/$page_num$.html"  }
        // => '/pages/1.html', '/pages/2.html', ....

    {  start_index:10, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/11', '/pages/12', ...

    {  start_index:10, default_spacing:20, url:"/pages/$page_num$"  }
        // => '/pages/10', '/pages/20', '/pages/30', ...

    // Using a custom url generator
    // we simply define next()

    {  next:function() { return "?page_idx=" + Math.round(Math.random() * 100) } }
        // => '?page_idx=57', '?page_idx=19', '/pages/81', ...

    {
        idx = 0,
        next:function() {
            return "items?from=" + this.idx + "&until=" + (this.idx += 10)
        }
    }
        // => 'items?from=0&until=10', 'items?from=0&until=10', '/pages/30'

*/

thepaw.ContinousScroller.DefaultUrlGenerator = {
    url:null,
    start_index:1,
    default_spacing:1,

    next: function() {
        if (typeof this.current_index == "undefined") this.current_index = this.start_index
        this.current_index += this.default_spacing

        var tmp_url = ( this.url == null ? "?page=$page_num$" : this.url )
        return tmp_url.replace('$page_num$', this.current_index)
    }

}

// These two functions are based on code from
// http://www.quirksmode.org/viewport/compatibility.html
thepaw.third_party = {

    getPageHeight: function (){
        var y;
        var test1 = document.body.scrollHeight;
        var test2 = document.body.offsetHeight
        if (test1 > test2) // all but Explorer Mac
        {
            y = document.body.scrollHeight;
        }
        else // Explorer Mac;
             //would also work in Explorer 6 Strict, Mozilla and Safari
        {
            y = document.body.offsetHeight;
        }
        return parseInt(y);
    },

    _getWindowHeight: function (){
        if (self.innerWidth)
        {
            frameWidth = self.innerWidth;
            frameHeight = self.innerHeight;
        }
        else if (document.documentElement && document.documentElement.clientWidth)
        {
            frameWidth = document.documentElement.clientWidth;
            frameHeight = document.documentElement.clientHeight;
        }
        else if (document.body)
        {
            frameWidth = document.body.clientWidth;
            frameHeight = document.body.clientHeight;
        }
        return parseInt(frameHeight);
    },

    getScrollHeight: function (){
        var y;
        // all except Explorer
        if (self.pageYOffset)
        {
            y = self.pageYOffset;
        }
        else if (document.documentElement && document.documentElement.scrollTop)
        {
            y = document.documentElement.scrollTop;
        }
        else if (document.body) // all other Explorers
        {
            y = document.body.scrollTop;
        }
        return parseInt(y)+thepaw.third_party._getWindowHeight();
    }

}<|endoftext|>


<commit_before>// Load modules

var Stream = require('stream');
var Events = require('events');
var Boom = require('boom');
var Hoek = require('hoek');
var Peekaboo = require('peekaboo');

// Declare internals

var internals = {};

exports = module.exports = internals.Response = function (source, request, options) {

    Events.EventEmitter.call(this);

    options = options || {};

    this.request = request;
    this.statusCode = null;
    this.headers = {};                          // Incomplete as some headers are stored in flags
    this.variety = null;
    this.app = {};
    this.plugins = {};
    this.send = null;                           // Set by reply()
    this.hold = null;                           // Set by reply()

    this.settings = {
        encoding: 'utf8',
        charset: 'utf-8',                       // '-' required by IANA
        ttl: null,
        stringify: null,                        // JSON.stringify options
        passThrough: true,
        varyEtag: false
    };

    this._payload = null;                       // Readable stream
    this._takeover = false;
    this._contentEncoding = null;               // Set during transmit
    this._error = null;                         // The boom object when created from an error

    this._processors = {
        marshal: options.marshal,
        prepare: options.prepare,
        close: options.close
    };

    this._setSource(source, options.variety);
};

Hoek.inherits(internals.Response, Events.EventEmitter);

internals.Response.wrap = function (result, request) {

    return (result instanceof Error ? Boom.wrap(result)
                                    : (result instanceof internals.Response ? result
                                                                            : new internals.Response(result, request)));
};

internals.Response.prototype._setSource = function (source, variety) {

    // Method must not set any headers or other properties as source can change later

    this.variety = variety || 'plain';

    if (source === null ||
        source === undefined ||
        source === '') {

        source = null;
    }
    else if (Buffer.isBuffer(source)) {
        this.variety = 'buffer';
    }
    else if (source instanceof Stream) {
        this.variety = 'stream';
    }
    else if (typeof source === 'object' &&
        typeof source.then === 'function') {                // Promise object

        this.variety = 'promise';
    }

    this.source = source;
};

internals.Response.prototype.code = function (statusCode) {

    Hoek.assert(Hoek.isInteger(statusCode), 'Status code must be an integer');

    this.statusCode = statusCode;
    return this;
};

internals.Response.prototype.header = function (key, value, options) {

    key = key.toLowerCase();
    if (key === 'vary') {
        return this.vary(value);
    }

    return this._header(key, value, options);
};

internals.Response.prototype._header = function (key, value, options) {

    options = options || {};
    var append = options.append || false;
    var separator = options.separator || ',';
    var override = options.override !== false;
    var duplicate = options.duplicate !== false;

    // Ensure key and values do not include non-ascii text

    if (value !== undefined &&
        value !== null) {

        var headerValues = [key].concat(value);
        for (var v = 0, vl = headerValues.length; v < vl; ++v) {
            var header = headerValues[v];
            var buffer = Buffer.isBuffer(header) ? header : new Buffer(header.toString());
            for (var b = 0, bl = buffer.length; b < bl; ++b) {
                Hoek.assert((buffer[b] & 0x7f) === buffer[b], 'Header value cannot contain or convert into non-ascii characters:', key);
            }
        };
    }

    if ((!append && override) ||
        !this.headers[key]) {

        this.headers[key] = value;
    }
    else if (override) {
        if (key === 'set-cookie') {
            this.headers[key] = [].concat(this.headers[key], value);
        }
        else {
            var existing = this.headers[key];
            if (!duplicate) {
                var values = existing.split(separator);
                for (var i = 0, il = values.length; i < il; ++i) {
                    if (values[i] === value) {
                        return this;
                    }
                }
            }

            this.headers[key] = existing + separator + value;
        }
    }

    return this;
};

internals.Response.prototype.vary = function (value) {

    if (value === '*') {
        this.headers.vary = '*';
    }
    else if (!this.headers.vary) {
        this.headers.vary = value;
    }
    else if (this.headers.vary !== '*') {
        this._header('vary', value, { append: true, duplicate: false });
    }

    return this;
};

internals.Response.prototype.etag = function (tag, options) {

    Hoek.assert(tag !== '*', 'ETag cannot be *');

    options = options || {};
    this._header('etag', (options.weak ? 'W/' : '') + '"' + tag + '"');
    this.settings.varyEtag = options.vary !== false && !options.weak;       // vary defaults to true
    return this;
};

internals.Response.prototype.type = function (type) {

    this._header('content-type', type);
    return this;
};

internals.Response.prototype.bytes = function (bytes) {

    this._header('content-length', bytes);
    return this;
};

internals.Response.prototype.location = function (uri) {

    this._header('location', uri);
    return this;
};

internals.Response.prototype.created = function (location) {

    Hoek.assert(this.request.method === 'post' || this.request.method === 'put', 'Cannot create resource on GET');

    this.statusCode = 201;
    this.location(location);
    return this;
};

internals.Response.prototype.replacer = function (method) {

    this.settings.stringify = this.settings.stringify || {};
    this.settings.stringify.replacer = method;
    return this;
};

internals.Response.prototype.spaces = function (count) {

    this.settings.stringify = this.settings.stringify || {};
    this.settings.stringify.space = count;
    return this;
};

internals.Response.prototype.suffix = function (suffix) {

    this.settings.stringify = this.settings.stringify || {};
    this.settings.stringify.suffix = suffix;
    return this;
};

internals.Response.prototype.passThrough = function (enabled) {

    this.settings.passThrough = (enabled !== false);    // Defaults to true
    return this;
};

internals.Response.prototype.redirect = function (location) {

    this.statusCode = 302;
    this.location(location);
    this.temporary = this._temporary;
    this.permanent = this._permanent;
    this.rewritable = this._rewritable;
    return this;
};

internals.Response.prototype._temporary = function (isTemporary) {

    this._setTemporary(isTemporary !== false);           // Defaults to true
    return this;
};

internals.Response.prototype._permanent = function (isPermanent) {

    this._setTemporary(isPermanent === false);           // Defaults to true
    return this;
};

internals.Response.prototype._rewritable = function (isRewritable) {

    this._setRewritable(isRewritable !== false);         // Defaults to true
    return this;
};

internals.Response.prototype._isTemporary = function () {

    return this.statusCode === 302 || this.statusCode === 307;
};

internals.Response.prototype._isRewritable = function () {

    return this.statusCode === 301 || this.statusCode === 302;
};

internals.Response.prototype._setTemporary = function (isTemporary) {

    if (isTemporary) {
        if (this._isRewritable()) {
            this.statusCode = 302;
        }
        else {
            this.statusCode = 307;
        }
    }
    else {
        if (this._isRewritable()) {
            this.statusCode = 301;
        }
        else {
            this.statusCode = 308;
        }
    }
};

internals.Response.prototype._setRewritable = function (isRewritable) {

    if (isRewritable) {
        if (this._isTemporary()) {
            this.statusCode = 302;
        }
        else {
            this.statusCode = 301;
        }
    }
    else {
        if (this._isTemporary()) {
            this.statusCode = 307;
        }
        else {
            this.statusCode = 308;
        }
    }
};

internals.Response.prototype.encoding = function (encoding) {

    this.settings.encoding = encoding;
    return this;
};

internals.Response.prototype.charset = function (charset) {

    this.settings.charset = charset || null;
    return this;
};

internals.Response.prototype.ttl = function (ttl) {

    this.settings.ttl = ttl;
    return this;
};

internals.Response.prototype.state = function (name, value, options) {          // options: see Defaults.state

    this.request._setState(name, value, options);
    return this;
};

internals.Response.prototype.unstate = function (name, options) {

    this.request._clearState(name, options);
    return this;
};

internals.Response.prototype.takeover = function () {

    this._takeover = true;
    return this;
};

internals.Response.prototype._prepare = function (data, next) {

    var self = this;

    this._passThrough();

    if (this.variety !== 'promise') {
        return this._processPrepare(data, next);
    }

    var onDone = function (source) {

        if (source instanceof Error) {
            return next(Boom.wrap(source), data);
        }

        if (source instanceof internals.Response) {
            return source._processPrepare(data, next);
        }

        self._setSource(source);
        self._passThrough();
        self._processPrepare(data, next);
    };

    this.source.then(onDone, onDone);
};

internals.Response.prototype._passThrough = function () {

    if (this.variety === 'stream' &&
        this.settings.passThrough) {

        if (this.source.statusCode &&
            !this.statusCode) {

            this.statusCode = this.source.statusCode;                        // Stream is an HTTP response
        }

        if (this.source.headers) {
            var headerKeys = Object.keys(this.source.headers);

            if (headerKeys.length) {
                var localHeaders = this.headers;
                this.headers = {};

                for (var i = 0, il = headerKeys.length; i < il; ++i) {
                    var key = headerKeys[i];
                    this.header(key.toLowerCase(), Hoek.clone(this.source.headers[key]));     // Clone arrays
                }

                headerKeys = Object.keys(localHeaders);
                for (i = 0, il = headerKeys.length; i < il; ++i) {
                    key = headerKeys[i];
                    this.header(key, localHeaders[key], { append: key === 'set-cookie' });
                }
            }
        }
    }

    this.statusCode = this.statusCode || 200;
};

internals.Response.prototype._processPrepare = function (data, next) {

    if (!this._processors.prepare) {
        return next(this, data);
    }

    this._processors.prepare(this, function (prepared) {

        return next(prepared, data);
    });
};

internals.Response.prototype._marshal = function (next) {

    var self = this;

    if (!this._processors.marshal) {
        return this._streamify(this.source, next);
    }

    this._processors.marshal(this, function (err, source) {

        if (err) {
            return next(err);
        }

        return self._streamify(source, next);
    });
};

internals.Response.prototype._streamify = function (source, next) {

    if (source instanceof Stream) {
        if (typeof source._read !== 'function' || typeof source._readableState !== 'object') {
            return next(Boom.badImplementation('Stream must have a streams2 readable interface'));
        }

        if (source._readableState.objectMode) {
            return next(Boom.badImplementation('Cannot reply with stream in object mode'));
        }

        this._payload = source;
        return next();
    }

    var payload = source;
    if (this.variety === 'plain' &&
        source !== null &&
        typeof source !== 'string') {

        var options = this.settings.stringify || {};
        var space = options.space || this.request.route.settings.json.space;
        var replacer = options.replacer || this.request.route.settings.json.replacer;
        var suffix = options.suffix || this.request.route.settings.json.suffix || '';
        try {
            payload = JSON.stringify(payload, replacer, space);
        }
        catch (err) {
            return next(err);
        }

        if (suffix) {
            payload += suffix;
        }
    }
    else if (this.settings.stringify) {
        return next(Boom.badImplementation('Cannot set formatting options on non object response'));
    }

    this._payload = new internals.Payload(payload, this.settings);
    return next();
};

internals.Response.prototype._tap = function () {

    return (this.listeners('finish').length || this.listeners('peek').length ? new Peekaboo(this) : null);
};

internals.Response.prototype._close = function () {

    if (this._processors.close) {
        this._processors.close(this);
    }

    var stream = this._payload || this.source;
    if (stream instanceof Stream) {
        if (stream.close) {
            stream.close();
        }
        else if (stream.destroy) {
            stream.destroy();
        }
        else {
            var read = function () {

                stream.read();
            };

            var end = function () {

                stream.removeListener('readable', read);
                stream.removeListener('error', end);
                stream.removeListener('end', end);
            };

            stream.on('readable', read);
            stream.once('error', end);
            stream.once('end', end);
        }
    }
};

internals.Response.prototype._isPayloadSupported = function () {

    return (this.request.method !== 'head' && this.statusCode !== 304 && this.statusCode !== 204);
};

internals.Response.Payload = internals.Payload = function (payload, options) {

    Stream.Readable.call(this);
    this._data = payload;
    this._prefix = null;
    this._suffix = null;
    this._sizeOffset = 0;
    this._encoding = options.encoding;
};

Hoek.inherits(internals.Payload, Stream.Readable);

internals.Payload.prototype._read = function (/* size */) {

    if (this._prefix) {
        this.push(this._prefix, this._encoding);
    }

    if (this._data) {
        this.push(this._data, this._encoding);
    }

    if (this._suffix) {
        this.push(this._suffix, this._encoding);
    }

    this.push(null);
};

internals.Payload.prototype.size = function () {

    if (!this._data) {
        return this._sizeOffset;
    }

    return (Buffer.isBuffer(this._data) ? this._data.length : Buffer.byteLength(this._data, this._encoding)) + this._sizeOffset;
};

internals.Payload.prototype.jsonp = function (variable) {

    this._sizeOffset += variable.length + 7;
    this._prefix = '/**/' + variable + '(';                 // '/**/' prefix prevents CVE-2014-4671 security exploit
    this._data = Buffer.isBuffer(this._data) ? this._data : this._data.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
    this._suffix = ');';
};
<commit_msg>Fix lab style warning<commit_after>// Load modules

var Stream = require('stream');
var Events = require('events');
var Boom = require('boom');
var Hoek = require('hoek');
var Peekaboo = require('peekaboo');

// Declare internals

var internals = {};

exports = module.exports = internals.Response = function (source, request, options) {

    Events.EventEmitter.call(this);

    options = options || {};

    this.request = request;
    this.statusCode = null;
    this.headers = {};                          // Incomplete as some headers are stored in flags
    this.variety = null;
    this.app = {};
    this.plugins = {};
    this.send = null;                           // Set by reply()
    this.hold = null;                           // Set by reply()

    this.settings = {
        encoding: 'utf8',
        charset: 'utf-8',                       // '-' required by IANA
        ttl: null,
        stringify: null,                        // JSON.stringify options
        passThrough: true,
        varyEtag: false
    };

    this._payload = null;                       // Readable stream
    this._takeover = false;
    this._contentEncoding = null;               // Set during transmit
    this._error = null;                         // The boom object when created from an error

    this._processors = {
        marshal: options.marshal,
        prepare: options.prepare,
        close: options.close
    };

    this._setSource(source, options.variety);
};

Hoek.inherits(internals.Response, Events.EventEmitter);

internals.Response.wrap = function (result, request) {

    return (result instanceof Error ? Boom.wrap(result)
                                    : (result instanceof internals.Response ? result
                                                                            : new internals.Response(result, request)));
};

internals.Response.prototype._setSource = function (source, variety) {

    // Method must not set any headers or other properties as source can change later

    this.variety = variety || 'plain';

    if (source === null ||
        source === undefined ||
        source === '') {

        source = null;
    }
    else if (Buffer.isBuffer(source)) {
        this.variety = 'buffer';
    }
    else if (source instanceof Stream) {
        this.variety = 'stream';
    }
    else if (typeof source === 'object' &&
        typeof source.then === 'function') {                // Promise object

        this.variety = 'promise';
    }

    this.source = source;
};

internals.Response.prototype.code = function (statusCode) {

    Hoek.assert(Hoek.isInteger(statusCode), 'Status code must be an integer');

    this.statusCode = statusCode;
    return this;
};

internals.Response.prototype.header = function (key, value, options) {

    key = key.toLowerCase();
    if (key === 'vary') {
        return this.vary(value);
    }

    return this._header(key, value, options);
};

internals.Response.prototype._header = function (key, value, options) {

    options = options || {};
    var append = options.append || false;
    var separator = options.separator || ',';
    var override = options.override !== false;
    var duplicate = options.duplicate !== false;

    // Ensure key and values do not include non-ascii text

    if (value !== undefined &&
        value !== null) {

        var headerValues = [key].concat(value);
        for (var v = 0, vl = headerValues.length; v < vl; ++v) {
            var header = headerValues[v];
            var buffer = Buffer.isBuffer(header) ? header : new Buffer(header.toString());
            for (var b = 0, bl = buffer.length; b < bl; ++b) {
                Hoek.assert((buffer[b] & 0x7f) === buffer[b], 'Header value cannot contain or convert into non-ascii characters:', key);
            }
        }
    }

    if ((!append && override) ||
        !this.headers[key]) {

        this.headers[key] = value;
    }
    else if (override) {
        if (key === 'set-cookie') {
            this.headers[key] = [].concat(this.headers[key], value);
        }
        else {
            var existing = this.headers[key];
            if (!duplicate) {
                var values = existing.split(separator);
                for (var i = 0, il = values.length; i < il; ++i) {
                    if (values[i] === value) {
                        return this;
                    }
                }
            }

            this.headers[key] = existing + separator + value;
        }
    }

    return this;
};

internals.Response.prototype.vary = function (value) {

    if (value === '*') {
        this.headers.vary = '*';
    }
    else if (!this.headers.vary) {
        this.headers.vary = value;
    }
    else if (this.headers.vary !== '*') {
        this._header('vary', value, { append: true, duplicate: false });
    }

    return this;
};

internals.Response.prototype.etag = function (tag, options) {

    Hoek.assert(tag !== '*', 'ETag cannot be *');

    options = options || {};
    this._header('etag', (options.weak ? 'W/' : '') + '"' + tag + '"');
    this.settings.varyEtag = options.vary !== false && !options.weak;       // vary defaults to true
    return this;
};

internals.Response.prototype.type = function (type) {

    this._header('content-type', type);
    return this;
};

internals.Response.prototype.bytes = function (bytes) {

    this._header('content-length', bytes);
    return this;
};

internals.Response.prototype.location = function (uri) {

    this._header('location', uri);
    return this;
};

internals.Response.prototype.created = function (location) {

    Hoek.assert(this.request.method === 'post' || this.request.method === 'put', 'Cannot create resource on GET');

    this.statusCode = 201;
    this.location(location);
    return this;
};

internals.Response.prototype.replacer = function (method) {

    this.settings.stringify = this.settings.stringify || {};
    this.settings.stringify.replacer = method;
    return this;
};

internals.Response.prototype.spaces = function (count) {

    this.settings.stringify = this.settings.stringify || {};
    this.settings.stringify.space = count;
    return this;
};

internals.Response.prototype.suffix = function (suffix) {

    this.settings.stringify = this.settings.stringify || {};
    this.settings.stringify.suffix = suffix;
    return this;
};

internals.Response.prototype.passThrough = function (enabled) {

    this.settings.passThrough = (enabled !== false);    // Defaults to true
    return this;
};

internals.Response.prototype.redirect = function (location) {

    this.statusCode = 302;
    this.location(location);
    this.temporary = this._temporary;
    this.permanent = this._permanent;
    this.rewritable = this._rewritable;
    return this;
};

internals.Response.prototype._temporary = function (isTemporary) {

    this._setTemporary(isTemporary !== false);           // Defaults to true
    return this;
};

internals.Response.prototype._permanent = function (isPermanent) {

    this._setTemporary(isPermanent === false);           // Defaults to true
    return this;
};

internals.Response.prototype._rewritable = function (isRewritable) {

    this._setRewritable(isRewritable !== false);         // Defaults to true
    return this;
};

internals.Response.prototype._isTemporary = function () {

    return this.statusCode === 302 || this.statusCode === 307;
};

internals.Response.prototype._isRewritable = function () {

    return this.statusCode === 301 || this.statusCode === 302;
};

internals.Response.prototype._setTemporary = function (isTemporary) {

    if (isTemporary) {
        if (this._isRewritable()) {
            this.statusCode = 302;
        }
        else {
            this.statusCode = 307;
        }
    }
    else {
        if (this._isRewritable()) {
            this.statusCode = 301;
        }
        else {
            this.statusCode = 308;
        }
    }
};

internals.Response.prototype._setRewritable = function (isRewritable) {

    if (isRewritable) {
        if (this._isTemporary()) {
            this.statusCode = 302;
        }
        else {
            this.statusCode = 301;
        }
    }
    else {
        if (this._isTemporary()) {
            this.statusCode = 307;
        }
        else {
            this.statusCode = 308;
        }
    }
};

internals.Response.prototype.encoding = function (encoding) {

    this.settings.encoding = encoding;
    return this;
};

internals.Response.prototype.charset = function (charset) {

    this.settings.charset = charset || null;
    return this;
};

internals.Response.prototype.ttl = function (ttl) {

    this.settings.ttl = ttl;
    return this;
};

internals.Response.prototype.state = function (name, value, options) {          // options: see Defaults.state

    this.request._setState(name, value, options);
    return this;
};

internals.Response.prototype.unstate = function (name, options) {

    this.request._clearState(name, options);
    return this;
};

internals.Response.prototype.takeover = function () {

    this._takeover = true;
    return this;
};

internals.Response.prototype._prepare = function (data, next) {

    var self = this;

    this._passThrough();

    if (this.variety !== 'promise') {
        return this._processPrepare(data, next);
    }

    var onDone = function (source) {

        if (source instanceof Error) {
            return next(Boom.wrap(source), data);
        }

        if (source instanceof internals.Response) {
            return source._processPrepare(data, next);
        }

        self._setSource(source);
        self._passThrough();
        self._processPrepare(data, next);
    };

    this.source.then(onDone, onDone);
};

internals.Response.prototype._passThrough = function () {

    if (this.variety === 'stream' &&
        this.settings.passThrough) {

        if (this.source.statusCode &&
            !this.statusCode) {

            this.statusCode = this.source.statusCode;                        // Stream is an HTTP response
        }

        if (this.source.headers) {
            var headerKeys = Object.keys(this.source.headers);

            if (headerKeys.length) {
                var localHeaders = this.headers;
                this.headers = {};

                for (var i = 0, il = headerKeys.length; i < il; ++i) {
                    var key = headerKeys[i];
                    this.header(key.toLowerCase(), Hoek.clone(this.source.headers[key]));     // Clone arrays
                }

                headerKeys = Object.keys(localHeaders);
                for (i = 0, il = headerKeys.length; i < il; ++i) {
                    key = headerKeys[i];
                    this.header(key, localHeaders[key], { append: key === 'set-cookie' });
                }
            }
        }
    }

    this.statusCode = this.statusCode || 200;
};

internals.Response.prototype._processPrepare = function (data, next) {

    if (!this._processors.prepare) {
        return next(this, data);
    }

    this._processors.prepare(this, function (prepared) {

        return next(prepared, data);
    });
};

internals.Response.prototype._marshal = function (next) {

    var self = this;

    if (!this._processors.marshal) {
        return this._streamify(this.source, next);
    }

    this._processors.marshal(this, function (err, source) {

        if (err) {
            return next(err);
        }

        return self._streamify(source, next);
    });
};

internals.Response.prototype._streamify = function (source, next) {

    if (source instanceof Stream) {
        if (typeof source._read !== 'function' || typeof source._readableState !== 'object') {
            return next(Boom.badImplementation('Stream must have a streams2 readable interface'));
        }

        if (source._readableState.objectMode) {
            return next(Boom.badImplementation('Cannot reply with stream in object mode'));
        }

        this._payload = source;
        return next();
    }

    var payload = source;
    if (this.variety === 'plain' &&
        source !== null &&
        typeof source !== 'string') {

        var options = this.settings.stringify || {};
        var space = options.space || this.request.route.settings.json.space;
        var replacer = options.replacer || this.request.route.settings.json.replacer;
        var suffix = options.suffix || this.request.route.settings.json.suffix || '';
        try {
            payload = JSON.stringify(payload, replacer, space);
        }
        catch (err) {
            return next(err);
        }

        if (suffix) {
            payload += suffix;
        }
    }
    else if (this.settings.stringify) {
        return next(Boom.badImplementation('Cannot set formatting options on non object response'));
    }

    this._payload = new internals.Payload(payload, this.settings);
    return next();
};

internals.Response.prototype._tap = function () {

    return (this.listeners('finish').length || this.listeners('peek').length ? new Peekaboo(this) : null);
};

internals.Response.prototype._close = function () {

    if (this._processors.close) {
        this._processors.close(this);
    }

    var stream = this._payload || this.source;
    if (stream instanceof Stream) {
        if (stream.close) {
            stream.close();
        }
        else if (stream.destroy) {
            stream.destroy();
        }
        else {
            var read = function () {

                stream.read();
            };

            var end = function () {

                stream.removeListener('readable', read);
                stream.removeListener('error', end);
                stream.removeListener('end', end);
            };

            stream.on('readable', read);
            stream.once('error', end);
            stream.once('end', end);
        }
    }
};

internals.Response.prototype._isPayloadSupported = function () {

    return (this.request.method !== 'head' && this.statusCode !== 304 && this.statusCode !== 204);
};

internals.Response.Payload = internals.Payload = function (payload, options) {

    Stream.Readable.call(this);
    this._data = payload;
    this._prefix = null;
    this._suffix = null;
    this._sizeOffset = 0;
    this._encoding = options.encoding;
};

Hoek.inherits(internals.Payload, Stream.Readable);

internals.Payload.prototype._read = function (/* size */) {

    if (this._prefix) {
        this.push(this._prefix, this._encoding);
    }

    if (this._data) {
        this.push(this._data, this._encoding);
    }

    if (this._suffix) {
        this.push(this._suffix, this._encoding);
    }

    this.push(null);
};

internals.Payload.prototype.size = function () {

    if (!this._data) {
        return this._sizeOffset;
    }

    return (Buffer.isBuffer(this._data) ? this._data.length : Buffer.byteLength(this._data, this._encoding)) + this._sizeOffset;
};

internals.Payload.prototype.jsonp = function (variable) {

    this._sizeOffset += variable.length + 7;
    this._prefix = '/**/' + variable + '(';                 // '/**/' prefix prevents CVE-2014-4671 security exploit
    this._data = Buffer.isBuffer(this._data) ? this._data : this._data.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
    this._suffix = ');';
};
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs')
const path = require('path')

const config = require('../config.json')
const validation = require('./validation')

module.exports.render = function (type, data, callback) {
  let response = buildResponse(500, 'An error has occurred.', data)
  switch (type) {
    case 'encrypt-bad-encrypt-email':
      response = buildResponse(422, config.MSG_BAD_ENCRYPT_EMAIL || 'The supplied email address is not valid.', data)
      break
    case 'encrypt-no-encrypt-email':
      response = buildResponse(422, config.MSG_NO_ENCRYPT_EMAIL || 'You need to provide an email address to encrypt.', data)
      break
    case 'receive-honeypot':
      response = buildResponse(422, config.MSG_HONEYPOT || 'You shall not pass.', data)
      break
    case 'receive-no-admin-email':
      response = buildResponse(422, config.MSG_NO_ADMIN_EMAIL || 'Form not sent, the admin has not set up a forwarding email address.', data)
      break
    case 'receive-bad-admin-email':
      response = buildResponse(422, config.MSG_BAD_ADMIN_EMAIL || 'Form not sent, the admin email address is not valid.', data)
      break
    case 'receive-error':
      response = buildResponse(500, config.MSG_ERROR || 'Form not sent, there was an error adding it to the database.', data)
      break
    case 'receive-success':
      let successStatusCode = validation.hasRedirect(data) ? 302 : 200
      response = buildResponse(successStatusCode, config.MSG_SUCCESS || 'Form submission successfully made.', data)
      break
  }
  callback(null, response)
}

function buildResponse (statusCode, message, data) {
  if (validation.isJsonResponse(data)) {
    return buildJsonResponse(statusCode, message, data)
  }
  return buildHtmlResponse(statusCode, message, data)
}

function buildJsonResponse (statusCode, message, data) {
  let response = {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      statusCode: statusCode,
      message: message
    })
  }
  return response
}

function buildHtmlResponse (statusCode, message, data) {
  let response = {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'text/html'
    },
    body: generateView(message)
  }
  if (statusCode === 302) {
    response.headers.Location = data['_redirect']
  }
  return response
}

function generateView (message) {
  let template = fs.readFileSync(path.resolve(__dirname, 'template.html')).toString()
  if (!template) {
    return message
  }
  return template.replace('{{ message }}', message)
}
<commit_msg>create encrypt success response route<commit_after>'use strict'

const fs = require('fs')
const path = require('path')

const config = require('../config.json')
const validation = require('./validation')

module.exports.render = function (type, data, callback) {
  let response = buildResponse(500, 'An error has occurred.', data)
  switch (type) {
    case 'encrypt-bad-encrypt-email':
      response = buildResponse(422, config.MSG_BAD_ENCRYPT_EMAIL || 'The supplied email address is not valid.', data)
      break
    case 'encrypt-no-encrypt-email':
      response = buildResponse(422, config.MSG_NO_ENCRYPT_EMAIL || 'You need to provide an email address to encrypt.', data)
      break
    case 'encrypt-success':
      let message = data['_encrypted']
      response = buildResponse(200, message, data)
      break
    case 'receive-honeypot':
      response = buildResponse(422, config.MSG_HONEYPOT || 'You shall not pass.', data)
      break
    case 'receive-no-admin-email':
      response = buildResponse(422, config.MSG_NO_ADMIN_EMAIL || 'Form not sent, the admin has not set up a forwarding email address.', data)
      break
    case 'receive-bad-admin-email':
      response = buildResponse(422, config.MSG_BAD_ADMIN_EMAIL || 'Form not sent, the admin email address is not valid.', data)
      break
    case 'receive-error':
      response = buildResponse(500, config.MSG_ERROR || 'Form not sent, there was an error adding it to the database.', data)
      break
    case 'receive-success':
      let successStatusCode = validation.hasRedirect(data) ? 302 : 200
      response = buildResponse(successStatusCode, config.MSG_SUCCESS || 'Form submission successfully made.', data)
      break
  }
  callback(null, response)
}

function buildResponse (statusCode, message, data) {
  if (validation.isJsonResponse(data)) {
    return buildJsonResponse(statusCode, message, data)
  }
  return buildHtmlResponse(statusCode, message, data)
}

function buildJsonResponse (statusCode, message, data) {
  let response = {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      statusCode: statusCode,
      message: message
    })
  }
  return response
}

function buildHtmlResponse (statusCode, message, data) {
  let response = {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'text/html'
    },
    body: generateView(message)
  }
  if (statusCode === 302) {
    response.headers.Location = data['_redirect']
  }
  return response
}

function generateView (message) {
  let template = fs.readFileSync(path.resolve(__dirname, 'template.html')).toString()
  if (!template) {
    return message
  }
  return template.replace('{{ message }}', message)
}
<|endoftext|>