
/*!
 *
 * tumblrStream v0.7.5
 * created by Jonathan Armstrong (http://jonathanarmstrong.com)
 *
 * requires jQuery v1.4.2, ja.base and ja.tumblrPost
 */


// tumblr stream
JA.TumblrStream = function(settings) {

	var context = this;

	// settings - extend/override default settings
	this.settings = $.extend({
			'containingElement':'',
			'dataUrl':'',
			'postsPerFetch':30,
			'imageWidth':JA.TumblrStream.imageWidths.small,
			'postMargin':10,
			'intervalLength':300,
			'init':null
		}, settings);

	// bind post events
	this.settings.containingElement.bind('JA.tumblrPost.finalized', function(e, index) {
		context.setPostPosition(index);
		context.addPost();
	});

	//
	this.init();
};

	// init stream
	JA.TumblrStream.prototype.init = function() {

		var context = this;

		// init properties
		this.keys = [];
		this.data = {};
		this.posts = {};
		this.tags = {};
		this.totalPosts = 0;
		this.columnHeights = [];
		this.columnWidth = this.settings.imageWidth + this.settings.postMargin;
		this.columnCount = this.getColumnCount();
		this.isDisabled = false;
		this.isLoading = false;
		this.deferredActions = {};

		//
		if (typeof(this.settings.init) == 'function') {
			this.settings.init();
		}

		//
		this.getData();
	};

    // image width dictionary
    JA.TumblrStream.imageWidths = {
        'thumb':75,
        'xsmall':100,
        'small':250,
        'medium':400,
        'large':500,
        'xlarge':1280
    };

	// get current index
	JA.TumblrStream.prototype.getCurrentIndex = function() {
		return JA.utils.isNullorEmpty(this.posts) ? 0 : Object.size(this.posts)-1;
	};

	// get column count
	JA.TumblrStream.prototype.getColumnCount = function() {
		return parseInt(this.settings.containingElement.width() / this.columnWidth);
	};

	// get data
	JA.TumblrStream.prototype.getData = function() {

		var index = this.getCurrentIndex();

		// if data is already being loaded or there are no more posts to get, then stop here
		if (this.isDisabled || this.isLoading || (index > 0 && index >= this.totalPosts)) {
			return false;
		}

		if (this.settings.dataUrl.length > 0) {
		    var context = this;

			this.isLoading = true;
			// trigger fetch event
			this.settings.containingElement.trigger('JA.TumblrStream.fetch');
			// load data
			$.ajax({
				'url':[this.settings.dataUrl, '?start=', index, '&num=', this.settings.postsPerFetch, '&type=photo&callback=?'].join(''),
				'dataType':'json',
				'success': function (data) {
                    // parse results
                    context.parseResults(data);
                }
			});
		}
	};

    // parse results
    JA.TumblrStream.prototype.parseResults = function(data) {

        //
        if (JA.utils.isNullorEmpty(data.posts)) {
            this.finalize();
        } else {
            var context = this;

            // only set the totalPosts property once to maintain the integrity of the index in case a post is added
            // between fetches (I'm ignoring the converse scenario as a severe edge case)
            this.totalPosts = this.totalPosts || data['posts-total'];
            // map result data to posts object
            $.map(data.posts, function(post) {
                context.mapResults(post);
            });
            // add first post -subsequent posts are loaded sequentially once the previous post has been finalized
            this.addPost();
        }
    };

    // map results
    JA.TumblrStream.prototype.mapResults = function(post) {

        // don't map posts that already exist -this would only manifest when a post is added between fetches
        if (this.data[post.id] !== undefined) {
            return;
        }

        this.keys.push(post.id);
        this.data[post.id] = post;
        if (!JA.utils.isNullorEmpty(post.tags)) {
            for (var i = 0; i < post.tags.length; i++) {
                if (JA.utils.isNullorEmpty(this.tags[post.tags[i]])) {
                    this.tags[post.tags[i]] = 1;
                } else {
                    this.tags[post.tags[i]]++;
                }
            }
        }
    };
	// add post
	JA.TumblrStream.prototype.addPost = function() {

		var index = this.getCurrentIndex();

		if (index < this.keys.length) {
			this.posts[this.keys[index]] = new JA.TumblrPost(
				{
					'containingElement':this.settings.containingElement,
					'imageWidth':this.settings.imageWidth,
					'intervalLength':this.settings.intervalLength
				},
				index,
				this.data[this.keys[index]]
			);
		} else {
			this.finalize();
		}
	};

	// set post position
	JA.TumblrStream.prototype.setPostPosition = function(index) {

		var shortestColumn = 0;

		if (index < this.columnCount) {
			this.columnHeights[index] = 0;
			shortestColumn = index;
		} else {
			for (var i = 0; i < this.columnCount; i++) {
				if (this.columnHeights[i] < this.columnHeights[shortestColumn]) {
					shortestColumn = i;
				}
			}
		}

		var post = this.posts[this.keys[index]].element
			.css({
				'top':this.columnHeights[shortestColumn],
				'left':this.columnWidth * shortestColumn
			})
			.fadeIn(this.settings.intervalLength);

		this.columnHeights[shortestColumn] += post.height() + this.settings.postMargin;
	};

	// finalize build
	JA.TumblrStream.prototype.finalize = function() {

		this.isLoading = false;
		// trigger complete event
		this.settings.containingElement.trigger('JA.TumblrStream.complete', [!this.isDisabled]);
		// loop through any deferred actions, and call them if applicable
		for (var action in this.deferredActions) {
			if (typeof(this.deferredActions[action]) == 'function') {
				this.deferredActions[action].call(this);
			}
			this.deferredActions[action] = null;
		}
		// if the page isn't full or the user is at the bottom of the page, then get more data
		if ($.document.height() <= $.window.height() || $.document.scrollTop() + $.window.height() >= $.document.height()) {
			this.getData();
		}
	};

	// reset stream
	JA.TumblrStream.prototype.reset = function() {

		if (!this.isDisabled && !this.isLoading) {
			// remove existing posts
			this.settings.containingElement
				.fadeOut(this.settings.intervalLength, function() {
					$(this)
						.children()
							.remove()
						.end()
						.show();
				});

			//
			this.init();
		}
	};

	// reflow posts
	JA.TumblrStream.prototype.reflow = function() {

		// if data is being loaded, then create a deferredAction and stop here
		if(this.isLoading) {
			// create a deferred action to be performed when loading is complete
			if (this.deferredActions.reflow == undefined) {
				this.deferredActions.reflow = this.reflow;
			}
			return false;
		}

		var newColumnCount = this.getColumnCount();

		if (this.keys.length > 0 && newColumnCount != this.columnCount) {
			this.columnHeights = [];
			this.columnCount = newColumnCount;

			for (var i = 0; i < this.keys.length; i++) {
				this.setPostPosition(i);
			}
		}
	};

