jQuery fnQueue plugin - now with promises

by amindunited

HTML

<!-- html elements to assign fnQueues to -->
<div id="secondfndiv"></div>
<div id="thirdfndiv"></div>

<!-- attach the first function to the document -->
<script>
    $(document).fnQueue( 
        function () { console.log('First function');} , {promise:false}
    )
</script>

<!-- attach the second function to the window -->
<script>
    $(window).fnQueue(
        function(){
             console.log('Second function');   
        }
    );
</script>
<!-- attach TWO functions to a div -->
<script>
$('#thirdfndiv').fnQueue('set', {'onDocumentReady':false});
    $('#thirdfndiv').fnQueue(
        function(){
            console.log('third function');   
        }
    );
    $('#thirdfndiv').fnQueue(
        function(){
             console.log('third function a second time');   
        }
    );
</script>

<!-- Create buttons to run each of the attached function sets -->
<button id="runfirst">RUN the functions attached to the 'document'</button><br/>
<button id="runsecond">RUN the functions attached to the 'window'</button><br/>
<button id="runthird">RUN the functions attached to the '#thirdfndiv'</button><br/>

JavaScript

////////////////////////////////////////////////////////////////////////////////////
(function ($) {
	//An object to hold the functions of the plugin
	var methods = {
		//Init adds a given function to a stored array for each given element
		init: function (fn, options) {


			//If the onDocumentReady property is still true when the document it ready
			// run this fnQueue
			var self = this;

			$(document).ready(function () {
				var data = $(this).data('fnQueue');
				if (data.onDocumentReady) {
					self.fnQueue('run');
				}
			});

			//for each element that was passed to the plugin:
			return this.each(function (i, obj) {
				//Check if it already has fnQueue data added
				// and create data for it if it doesn't
				if (!$(this).data('fnQueue')) {
                    $(this).data('fnQueue', {fnArray: [], qArray: [], afterArray: [], onDocumentReady: true});
				}

				var data = $(this).data('fnQueue');//get a referance to the stored data
				//Push this function into the stored array
				data.fnArray.push(fn);
			});
		},
		//Run executes each stored function for each given element
		run: function () {
			//for each element that was passed to the plugin:
			return this.each(function () {
				//Get the element's stored data
				var data = $(this).data('fnQueue');
				if (!data || (!data.fnArray && !data.qArray)) {
					return;
				}
				//then for each function stored...call it
				for (var i = 0; i < data.fnArray.length; i++) {
					data.fnArray[i].call();
				}

				//then for each promise function stored...call it
				if (data.qArray.length) {
					var firstFn = data.qArray.shift();
					var fnChain = firstFn.call();
					var prevFn = fnChain;
                    // less than or equal so that it runs one more loop to fire the afterArray:
					for (var i = 0; i <= data.qArray.length; i++){
                        //If this is the last loop ... run the afterArray
                        if (!data.qArray[i]) {
                           ...