the-ikick / OIT WebCheckout Helper

// ==UserScript==
// @name         OIT WebCheckout Helper
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  This script is used by the University of Georgia to better help service our custermers.
//               By working with web checkout at COE's OIT and modifying it, we make a smoother experience.
// @author       Shawn Holman
// @include      /^https://webcheckout2.coe.uga.edu/webcheckout/wco*(\?.*|)/
// @license      MIT
// @grant        none
// ==/UserScript==
(function($) {
    'use strict';

    /////// BEIGIN EXTERNAL JS SECTION *******////////
    /**
	 * Featherlight - ultra slim jQuery lightbox
	 * Version 1.4.0 - http://noelboss.github.io/featherlight/
	 *
	 * Copyright 2016, Noël Raoul Bossart (http://www.noelboss.com)
	 * MIT Licensed.
	**/
    !function(e){"use strict";if(void 0!==e){var t=[],n=function(n){return t=e.grep(t,function(e){return e!==n&&e.$instance.closest("body").length>0})},r={keyup:"onKeyUp",resize:"onResize"},i=function(t){e.each(o.opened().reverse(),function(){if(!t.isDefaultPrevented()&&!1===this[r[t.type]](t))return t.preventDefault(),t.stopPropagation(),!1})},a=function(t){if(t!==o._globalHandlerInstalled){o._globalHandlerInstalled=t;var n=e.map(r,function(e,t){return t+"."+o.prototype.namespace}).join(" ");e(window)[t?"on":"off"](n,i)}};o.prototype={constructor:o,namespace:"featherlight",targetAttr:"data-featherlight",variant:null,resetCss:!1,background:null,openTrigger:"click",closeTrigger:"click",filter:null,root:"body",fadeInOnly:!1,openSpeed:150,closeSpeed:150,closeOnClick:"background",closeOnEsc:!0,closeIcon:"&#10005;",loading:"",persist:!1,otherClose:null,beforeOpen:e.noop,beforeContent:e.noop,beforeClose:e.noop,afterOpen:e.noop,afterContent:e.noop,afterClose:e.noop,onKeyUp:e.noop,onResize:e.noop,type:null,contentFilters:["jquery","image","html","ajax","iframe","text"],setup:function(t,n){"object"!=typeof t||t instanceof e!=!1||n||(n=t,t=void 0);var r=e.extend(this,n,{target:t}),i=r.resetCss?r.namespace+"-reset":r.namespace,a=e(r.background||['<div class="'+i+"-loading "+i+'">','<div class="'+i+'-content">','<span class="'+i+"-close-icon "+r.namespace+'-close" title="Press ESC to close">',r.closeIcon,"</span>",'<div class="'+r.namespace+'-inner">'+r.loading+"</div>","</div>","</div>"].join("")),o="."+r.namespace+"-close"+(r.otherClose?","+r.otherClose:"");return r.$instance=a.clone().addClass(r.variant),r.$instance.on(r.closeTrigger+"."+r.namespace,function(t){var n=e(t.target);("background"===r.closeOnClick&&n.is("."+r.namespace)||"anywhere"===r.closeOnClick||n.closest(o).length)&&(r.close(t),t.preventDefault())}),this},getContent:function(){if(this.$currentTarget&&this.$currentTarget[0].classList.contains("disabled"))return!1;if(!1!==this.persist&&this.$content)return this.$content;var t=this,n=this.constructor.contentFilters,r=function(e){return t.$currentTarget&&t.$currentTarget.attr(e)},i=r(t.targetAttr),a=t.target||i||"";t.$currentTarget&&t.$currentTarget[0].hasAttribute("data-unique")&&(e.featherlight.close(),t.$currentTarget[0].classList.add("disabled"));var o=n[t.type];if(!o&&a in n&&(o=n[a],a=t.target&&i),a=a||r("href")||"",!o)for(var s in n)t[s]&&(o=n[s],a=t[s]);if(!o){var c=a;if(a=null,e.each(t.contentFilters,function(){return(o=n[this]).test&&(a=o.test(c)),!a&&o.regex&&c.match&&c.match(o.regex)&&(a=c),!a}),!a)return"console"in window&&window.console.error("Featherlight: no content filter found "+(c?' for "'+c+'"':" (no target specified)")),!1}return o.process.call(t,a)},setContent:function(t){var n=this;return(t.is("iframe")||e("iframe",t).length>0)&&n.$instance.addClass(n.namespace+"-iframe"),n.$instance.removeClass(n.namespace+"-loading"),n.$instance.find("."+n.namespace+"-inner").not(t).slice(1).remove().end().replaceWith(e.contains(n.$instance[0],t[0])?"":t),n.$content=t.addClass(n.namespace+"-inner"),n},open:function(n){var r=this;if(r.$instance.hide().appendTo(r.root),!(n&&n.isDefaultPrevented()||!1===r.beforeOpen(n))){n&&n.preventDefault();var i=r.getContent();if(i){t.push(r),a(!0);var o=!1;return r.$currentTarget&&(o=r.$currentTarget.data("featherlight-fadeinonly")),r.beforeContent(n),e.when(i).always(function(e){r.setContent(e),(r.fadeInOnly||o)&&r.$instance.fadeIn(r.openSpeed),r.afterContent(n)}).then(r.$instance.promise()).done(function(){if(r.afterOpen(n),!r.fadeInOnly&&1!=o){var e=r.$instance.children(".featherlight-content");r.$instance.show();e[0].clientWidth,e[0].clientHeight;r.$instance.hide(),e.css("top",-700),r.$instance.fadeIn(r.openSpeed,function(){e.show().animate({top:0},r.openSpeed+500,function(){e.css("overflow","visible")})})}})}}return r.$instance.detach(),e.Deferred().reject().promise()},close:function(t){this.$currentTarget&&this.$currentTarget[0].classList.contains("disabled")&&e(".disabled").removeClass("disabled");var r=this,i=e.Deferred();return!1===r.beforeClose(t)?i.reject():(0===n(r).length&&a(!1),r.$instance.fadeOut(r.closeSpeed,function(){r.$instance.detach(),r.afterClose(t),i.resolve()})),i.promise()},resize:function(e,t){if(e&&t){this.$content.css("width","").css("height","");var n=Math.max(e/parseInt(this.$content.parent().css("width"),10),t/parseInt(this.$content.parent().css("height"),10));n>1&&this.$content.css("width",e/n+"px").css("height",t/n+"px")}},chainCallbacks:function(t){for(var n in t)this[n]=e.proxy(t[n],this,e.proxy(this[n],this))}},e.extend(o,{id:0,autoBind:"[data-featherlight]",defaults:o.prototype,contentFilters:{jquery:{regex:/^[#.]\w/,test:function(t){return t instanceof e&&t},process:function(t){return!1!==this.persist?e(t):e(t).clone(!0)}},image:{regex:/\.(png|jpg|jpeg|gif|tiff|bmp|svg)(\?\S*)?$/i,process:function(t){var n=e.Deferred(),r=new Image,i=e('<img src="'+t+'" alt="" class="'+this.namespace+'-image" />');return r.onload=function(){i.naturalWidth=r.width,i.naturalHeight=r.height,n.resolve(i)},r.onerror=function(){n.reject(i)},r.src=t,n.promise()}},html:{regex:/^\s*<[\w!][^<]*>/,process:function(t){return e(t)}},ajax:{regex:/./,process:function(t){var n=e.Deferred();e.get(t).then(function(t,r){n.resolve(e(t))},function(){n.fail()});return n.promise()}},iframe:{process:function(t){var n=new e.Deferred,r=e("<iframe/>").hide().attr("src",t).css(function(e,t){var n={},r=new RegExp("^"+t+"([A-Z])(.*)");for(var i in e){var a=i.match(r);a&&(n[(a[1]+a[2].replace(/([A-Z])/g,"-$1")).toLowerCase()]=e[i])}return n}(this,"iframe")).on("load",function(){n.resolve(r.show())}).appendTo(this.$instance.find("."+this.namespace+"-content"));return n.promise()}},text:{process:function(t){return e("<div>",{text:t})}}},functionAttributes:["beforeOpen","afterOpen","beforeContent","afterContent","beforeClose","afterClose"],readElementConfig:function(t,n){var r=this,i=new RegExp("^data-"+n+"-(.*)"),a={};return t&&t.attributes&&e.each(t.attributes,function(){var t=this.name.match(i);if(t){var n=this.value,o=e.camelCase(t[1]);if(e.inArray(o,r.functionAttributes)>=0)n=new Function(n);else try{n=e.parseJSON(n)}catch(e){}a[o]=n}}),a},extend:function(t,n){var r=function(){this.constructor=t};return r.prototype=this.prototype,t.prototype=new r,t.__super__=this.prototype,e.extend(t,this,n),t.defaults=t.prototype,t},attach:function(t,n,r){var i=this;"object"!=typeof n||n instanceof e!=!1||r||(r=n,n=void 0);var a,o=(r=e.extend({},r)).namespace||i.defaults.namespace,s=e.extend({},i.defaults,i.readElementConfig(t[0],o),r);return t.on(s.openTrigger+"."+s.namespace,s.filter,function(o){var c=e.extend({$source:t,$currentTarget:e(this)},i.readElementConfig(t[0],s.namespace),i.readElementConfig(this,s.namespace),r),l=a||e(this).data("featherlight-persisted")||new i(n,c);"shared"===l.persist?a=l:!1!==l.persist&&e(this).data("featherlight-persisted",l),c.$currentTarget.blur(),l.open(o)}),t},current:function(){var e=this.opened();return e[e.length-1]||{close:function(){}}},prev:function(){var e=this.opened();return e[e.length-2]||{close:function(){}}},opened:function(){var r=this;return n(),e.grep(t,function(e){return e instanceof r})},close:function(e){var t=this.current();if(t)return t.close(e)},closeAll:function(e){for(var t=this.opened(),n=0,r=t.length;n<r;n++)t[n]&&t[n].close(e)},_onReady:function(){var t=this;t.autoBind&&(e(t.autoBind).each(function(){t.attach(e(this))}),e(document).on("click",t.autoBind,function(n){n.isDefaultPrevented()||"featherlight"===n.namespace||(n.preventDefault(),t.attach(e(n.currentTarget)),e(n.target).trigger("click.featherlight"))}))},_callbackChain:{onKeyUp:function(t,n){return 27===n.keyCode?(this.closeOnEsc&&e.featherlight.close(n),!1):t(n)},onResize:function(e,t){return this.resize(this.$content.naturalWidth,this.$content.naturalHeight),e(t)},afterContent:function(e,t){var n=e(t);return this.onResize(t),n}}}),e.featherlight=o,e.fn.featherlight=function(e,t){return o.attach(this,e,t)},e(document).ready(function(){o._onReady()})}else"console"in window&&window.console.info("Too much lightness, Featherlight needs jQuery.");function o(e,t){if(!(this instanceof o)){var n=new o(e,t);return n.open(),n}this.id=o.id++,this.setup(e,t),this.chainCallbacks(o._callbackChain)}}(jQuery);
    /////// END EXTERNAL JS SECTION *******////////

    let styles = {
    	featherlight: "@media all{.featherlight{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:400;text-align:center;white-space:nowrap;cursor:pointer;background:rgba(0,0,0,.8)}.featherlight:before{content:'';display:inline-block;height:100%;vertical-align:middle;margin-right:-.25em}.featherlight .featherlight-content{position:relative;text-align:left;vertical-align:middle;display:inline-block;overflow:hidden;padding:25px 0 0 0;margin-left:5%;margin-right:5%;max-height:95%;background:#fff;cursor:auto;white-space:normal;}.featherlight .featherlight-inner{display:block;min-width:400px}.featherlight .featherlight-close-icon{position:absolute;z-index:9999;top:0;right:0;line-height:25px;width:25px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;background:#fff;background:rgba(255,255,255,.3);color:#000}li.nav-item.disabled,li.nav-item.disabled:hover{color:grey!important;background:#e4e4e4!important}.featherlight .featherlight-image{width:100%}.featherlight-iframe .featherlight-content{border-bottom:0;padding:0}.featherlight iframe{border:0}li.nav-item.disabled{cursor:not-allowed}}@media only screen and (max-width:1024px){.featherlight .featherlight-content{margin-left:10px;margin-right:10px;max-height:98%}}.lightbox-bottom-banner{background:#f2f2f2;margin:15px -25px -25px;padding:15px 25px;text-align:center;display:block;text-transform:capitalize}",
        main: `
        .featherlight-inner h1 {
        	font-size: 20px;
		    padding: 5px 10px;
		    margin: 0;
		    background: transparent;
		    color: #b93b40;
		    text-align: center;
		    font-weight: bold;
		    margin-top: -22px;
		    letter-spacing: -1px;
		}

		.featherlight-inner .content {
			padding: 10px 25px;
    		background: #f6f6f6;
		}

		.featherlight-inner .content textarea, div#progress-prompt {
			resize: none;
		    width: 100%;
		    height: 90px;
		    outline: none;
		    padding: 10px 15px 5px;
		    color: #4a4949;
		    border-radius: 4px;
		    border-color: #afafaf;
		    transition: 0.3s box-shadow, border-color;
		}

        #progress-prompt {
            background: white;
            border-color: white !important;
            border: 1px solid;
            overflow-y: auto;
            max-width: 310px;
        }

        #progress-prompt div {
        	padding: 5px;
            margin-bottom: 0;
        }

        #progress-prompt div.success {
        	color: green;
        }

		.featherlight-inner .content textarea:focus {
			box-shadow: inset 0 0px 1px grey;
		    border-color: #b7b7b7;
		}

		.featherlight-inner .content textarea.error {
			border-color: #e00000;
    		color: red;
		}

		.featherlight-inner input.form-control:focus {
		    box-shadow: inset 0 0px 1px grey;
		    border-color: #b7b7b7;
		}

		.featherlight-inner input.form-control.autocompleted {
			background: #e4e4e4;
    		color: #004802a8;
		}

		.featherlight-inner .footer-area {
			font-size: 0;
		}

        .autocomplete {
        	list-style: none;
		    margin: 5px 0 0 0;
		    padding: 0;
		    background: white;
		    position: absolute;
		    z-index: 233;
		    border: 1px solid #c7c7c7;
        }

        .autocomplete li:hover {
		    background: #eaeaea;
		    color: #b93b40;
		    cursor: pointer;
		}

        .autocomplete li {
            padding: 5px 10px;
            user-select: none;
        }

		.row {
			margin: 0;
		}

        .content .row {
            margin-bottom: 5px;
        }

        .content .row .remove-row {
            text-align: center;
            background: #c90000;
            color: #ffeaea;
            border-radius: 4px;
            border: 1px solid #750000;
            padding: 3px;
        }

        .content .row .remove-row:hover {
            background: red;
            cursor: pointer;
        }

		.featherlight-inner .footer-area .btn {
		    color: #fff;
		    border-radius: 0;
		    height: 40px;
		    outline:none;
		}

		.featherlight-inner .well.person-verification, .featherlight-inner .well.person-verification ul {
			margin:0;
		}

		.featherlight-inner .well.person-verification .list-group-item strong {
			float:right;
		}

		`
    }


    function objectContainsAll (obj, contains) {
    	for (let contain of contains) {
    		if (!(contain in obj)) return false;
    	}
    	return true;
    }

    /**
     * Parses the data string
     * @param {String:encoded in base64} data An encoded string which when decoded has the format: name:value,name2:value2 ....etc
     */
    function parseToDataString (data) {
        try {
            let unencode = window.atob(data); // parse string
            let dataPoints = unencode.split(/\s*,\s*/); //split into data points
            let parsedData = {};
            for (let dataPoint of dataPoints) { // run through each datapoint
                let dataComponents = dataPoint.split(/\s*:\s*/); // and split into its components
                let dataName = dataComponents[0];
                let dataValue = dataComponents[1];
                parsedData[dataName] = dataValue;
            }
            return parsedData;
        } catch (e) {
            return false;
        }
    }

    function average (numbers) {
        if (numbers == null || numbers.length == 0) return 0;
        let sum = 0;
        let len = numbers.length;
        for (let i = 0; i < len; i++) {
            sum += numbers[i];
        }
        return sum/len;
    }

    /**
     * Allows us to create a timeline of requests which contains many frames. For the case of webcheckout, this is nice way
     * of getting an action done since they require frames of requests
     * @param  {Array} frames The frames that will be executed through a post request
     * @return {Promise}
     */
    function makeFrameRequest(frames) {
    	let cancelled = false;
        let totalFrames = frames.length;
        let framesCompleted = 1;
        let progress = null;
        let globalTimes = []; // keep track of all of the times
        let avgTime = average(globalTimes); // keep track of the average execution time

        let startTime = performance.now();
    	let promise = new Promise(function(resolve, reject) {
            let frame = frames[0]; // get the first frame
	        if (frames.length == 0) { // resolve when all frames have been used
	            resolve();
	        } else {
				return $.post(frame.url, frame.hasOwnProperty('data') ? frame.data : null).done(() => { // run the request
	    			frames.shift(); // shift the frames

                    // if the user has cancelled and we aren't on the last frame
	    			if (cancelled && frames.length != 0) reject('cancelled');
	    			else { // else continue making frame requests
                        if (progress != null && typeof progress == "function") {
                            let progressData = { // set progress data
                            	completed: framesCompleted + 1,
                            	total: totalFrames,
                            	percent: (framesCompleted + 1) / totalFrames,
                                // calculate the time remaining by multiplying the frames left by the average execution time
                            	remaining: (totalFrames - (framesCompleted + 1)) * avgTime / 1000
                            };
                            progress.call(this, progressData, frames[0] != undefined ? frames[0] : null);
                        }

                        //push the execution time for the purpose of generating an average
                        globalTimes.push(performance.now() - startTime);
	    				return makeFrameRequest(frames).then(resolve, reject).progress(progress, totalFrames, ++framesCompleted, globalTimes) // we run the requests
	    			}
	    		}).error(function () { // if something goes wrong then we will default back to the checkout page
	    			$.post('?method=checkout-jump');
	    			reject('error');
	    		});
	        }
	    });
        Promise.prototype.progress = function (progressFunction, total, completed, times) { // keep track of progress
            totalFrames = total || frames.length;
            framesCompleted = completed || 0;
            progress = progressFunction || $.noop;
            globalTimes = times || [];
            avgTime = average(times);
        }
	    Promise.prototype.cancel = function () {
	    	cancelled = true;
            $.post('?method=checkout-jump');
	    }
        return promise;
    }

    /**
     * Adds all of the extra external css to the webpage
     */
    function addExternalStyling () {
    	let css = '';
    	for (let style in styles) {
    		css += styles[style];
    	}
    	$('head').append(`<style>${css}</style>`);
    }

    // holds different types of requests that we will be pulling
    let Requests = {
        /**
         * Adds a person the WebCheckout database
         * @param {Array} data A set of data of the person
         */
    	addPerson: function (data) {
    		// delete:
    		// 1111111111,
    		// delete resource: 135797,123,124,125,126,127,128
    		return makeFrameRequest([
		    	{ url: '?method=new-person-wizard' },
		    	{ url: '?method=new-userid-forward',
		    	  data: {
		    	  	"new-userid-form.userid": data.ugaid,
		    	  	"new-userid-form.first-name": data.firstname,
		    	  	"new-userid-form.other-name":  '',
					"new-userid-form.last-name": data.lastname,
					"new-userid-form.patron-class": data.class,
					"new-userid-form.department": data.department
		    	  }
		    	},
		    	{ url: '?method=new-person-contact-forward',
		    	  data: {
		    	  	"new-person-contact-form.street": '',
					"new-person-contact-form.street2": '',
					"new-person-contact-form.city": '',
					"new-person-contact-form.state": '',
					"new-person-contact-form.postal-code": '',
					"new-person-contact-form.country": '',
					"new-person-contact-form.telephone": data.phone,
					"new-person-contact-form.email": data.email
		    	  }
		    	},
		    	{ url: '?method=new-person-create-finish', finishing: true }
		    ]);
    	},

        /**
         * Find the autocomplete resources
         * @param  {String} string String to search for an autocompletion
         * @return {Promise}
         */
        autocomplete: function (string) {
            return new Promise (function (resolve, reject) {
                $.ajax({
                    url: '/webcheckout/rest/resourceType/autocomplete',
                    type: "POST",
                    dataType: "json",
                    data: `{"string": "${string}", "properties": ["name", "description"]}`,
                    contentType: "application/json",
                    headers: {
                        Accept: "application/json, text/plain, */*"
                    },
                    xhrFields: {
                        withCredentials: true
                    },
                    success: function (d) {
                        if (d == null || d.payload == null) resolve([]);
                        else {
                            let resources = [];
                            let results = d.payload;

                            for (let result of results) {
                                if (result.label == "OIT") {
                                    for (let value of result.values) {
                                        resources.push({
                                            oid: value.oid,
                                            name: value.name
                                        });
                                    }
                                }
                            }
                            resolve(resources);
                        }
                    }
                });
            });
        },

        /**
         * Adds a set of resources to WebCheckout
         * @param {Array} resources An array of resources with formate [{ id: 12, type: "oid|type"}, ...]
         */
        addResources: function (resources) {
            // remove 123456,
        	let masterFrame = [];
        	let ajaxSet = function (id, type) {
        		return [
			    	{ url: '?method=new-resource-wizard' },
			    	{ url: '?method=choose-resource-id-forward',
			    	  data: {
			    	  	"choose-resource-id-form.resource-id": id,
	      				"choose-resource-id-form.circulating": true
			    	  }
			    	},
			    	{ url: '?method=choose-resource-type-forward',
			    	  data: {
			    	  	"choose-resource-type-form.search-field": type
			    	  }
			    	},
			    	{ url: '?method=new-resource-wizard-finish', finishing: true }
			    ]
        	}
            for (let resource of resources) {
                 masterFrame = masterFrame.concat(ajaxSet(resource.id, resource.type));
            }
            return makeFrameRequest(masterFrame);
        }
    }

    function modifiedNewPerson () {
    	let createPersonWell = function (persondata) {
    		// skip a spot here since the values start at 1
    		let classes = [null, 'Other', 'Continuing education', 'Undergraduate freshman', 'Undergraduate sophomore', 'Undergraduate junior', 'Undergraduate senior', 'Graduate 1', 'Graduate 2', 'Employee', 'Faculty'];
    		let formatedNumber = persondata.phone.replace(/([0-9]{3})([0-9]{3})([0-9]{4})/, function (full, $1, $2, $3) {
                return "(" + $1 + ") " + $2 + "-" + $3;
            });
            return `<div id='person-ticket' class="well person-verification">
				<ul class="list-group list-group-flush">
			  		<li class="list-group-item">UGA ID: <strong>${persondata.ugaid}</strong></li>
				  	<li class="list-group-item">Name: <strong>${persondata.firstname} ${persondata.lastname}</strong></li>
			  		<li class="list-group-item">Class: <strong>${classes[persondata.class]}</strong></li>
			    	<li class="list-group-item">Department: <strong>${persondata.department}</strong></li>
					<li class="list-group-item">Email: <strong>${persondata.email}</strong></li>
			  		<li class="list-group-item">Phone: <strong>${formatedNumber}</strong></li>
				</ul>
				<div class="progress" style="margin-bottom: 0;margin-top: 10px;display:none">
				  <div class="progress-bar" role="progressbar" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100" style="width:10%"></div>
				</div>
			</div>`
    	}
    	$.featherlight(null, {
         	html: `
         		<div>
	         		<h1>Add Person</h1>
	         		<div class="content">
	    				<textarea id='persondata' placeholder="Enter unique user code.."></textarea>
					</div>
					<div class="footer-area row">
					    <input id='addperson' type="button" class="col-sm-6 btn btn-primary" value="Add Person" disabled>
					    <input id='cancel' type="button" class="col-sm-3 btn btn-danger" value="Cancel" disabled>
					    <input id='originalform' type="button" class="col-sm-3 btn btn-info" value="Original Form">
					</div>
				</div>`,
			afterOpen: function () {
                let person = null;

				$('#persondata').on('change paste keyup', function (e) {
                    setTimeout(() => { // add a timeout here so that we can get the proper value of the paste event
						person = parseToDataString($(this).val());

						$('.error-message').remove();
						// if the person contains all of the correct data
						if (person && objectContainsAll(person, ['ugaid', 'firstname', 'lastname', 'class', 'department', 'email', 'phone'])) {
							$(this).parent().html(createPersonWell(person));
							$('#addperson').attr('disabled', false);
						} else { // if not we will give an error
                            person = null;
							$(this).addClass('error').before('<div class="error-message" style="margin-bottom: 4px;text-align: center;color: #e00000;">Incorrect Code</div>')
						}
                    }, 5);
				});

				$('#addperson').on('click', function () { // when we add the person
					if (person != null) { // if we have a person then..
						$('#cancel').attr('disabled', false); // open up the cancel button
						$('#person-ticket .progress').show();
                        $(this).val("Adding...").add('#originalform').attr('disabled', true);

                        let req = Requests.addPerson(person).then(() => { // create the request
                            $(this).val("Added").removeClass('btn-primary').addClass('btn-success').parent().prev().prepend('<div class="alert alert-success"><strong>Success!</strong> User has been added!</div>');
                            $('#person-ticket .progress').hide();
                            setTimeout(() => {
                                $.featherlight.close();
                            }, 2000);
                        });
                        req.progress(function (prog, frame) {
                        	let cantCancel = (frame != null && frame.hasOwnProperty('finishing') && frame.finishing == true) || prog.total - prog.completed == 1;
                            $('#cancel').attr('disabled', cantCancel);
                            $('#person-ticket .progress-bar').attr('aria-valuenow', prog.percent*100).width(prog.percent*100 + '%');
                        });

                        $("#cancel").off('click').on('click', () => {
                            req.cancel();
                            $(this).val("Add Person").add('#originalform').attr('disabled', false);
                            $('#cancel').attr('disabled', true);
                            $('#person-ticket .progress').hide();
                        });
                    }
				});
			}
     	});
    }

    function modifiedResourceAdder () {
    	let inputRow = `<div class='row'>
	    					<div class='col-sm-5' style='padding: 0;'>
	    						<input type="text" class='numbers form-control' placeholder="11111, 22222, 33333,..." />
	    					</div>
	    					<div class='col-sm-5' style='padding: 0 0 0 5px;'>
	    						<input type="text" class='type form-control' placeholder='Resource Type' />
                                <ul class='autocomplete'></ul>
	    					</div>
	    					<div class='col-sm-2' style='padding: 0 0 0 5px;'>
	    						<div class='remove-row'>Remove</div>
	    					</div>
	    				</div>`;

    	$.featherlight(null, {
         	html: `
         		<div>
	         		<h1>Add Resources</h1>
	         		<div class="content">
                        <div class='well' style='margin-bottom:0'>
	    				    ${inputRow}
                        </div>
					</div>
					<div class="footer-area row">
                        <input id='finishadding' type="button" class="col-sm-3 btn btn-success" value="Finish" disabled>
					    <input id='addresource' type="button" class="col-sm-4 btn btn-primary" value="New Resource">
					    <input id='cancel' type="button" class="col-sm-2 btn btn-danger" value="Cancel" disabled>
					    <input id='originalform' type="button" class="col-sm-3 btn btn-info" value="Original Form">
					</div>
				</div>`,
			afterOpen: function () {
                function cons (mess, type) {
                     let classes = type || 'default'
                     document.getElementById('progress-prompt').innerHTML += `<div class='${classes}'>${mess}</div>`;
                     document.getElementById('progress-prompt').scrollTop = document.getElementById('progress-prompt').scrollHeight;
                }

                function checkFinishDisability () {
                    let numberOfInputs = $('.featherlight-inner .content .well .row .form-control').length;
                    let numberOfCompletedInputs = $('.featherlight-inner .content .well .row .form-control').filter(function () {
                        return ($(this).hasClass('type') && $(this).hasClass('autocompleted')) || ($(this).hasClass('numbers') && $(this).val().length > 0)
                    }).length;

                    $("#finishadding").attr("disabled", !(numberOfInputs == numberOfCompletedInputs));
                }

                $('#originalform').on('click', function () {
					document.location = '?method=new-resource-wizard'
				});

                $('.featherlight-inner').on('click', '.remove-row', function () { // remove a resource row
                	$(this).closest('.row').slideUp(600, function () {
                		$(this).remove();
                        checkFinishDisability ();
                	});
                }).on('click', function (e) { // clear the autocomplete when you click somewhere on the inner
                    $('.featherlight-inner .autocomplete').empty();
                }).on('click', '.autocomplete li', function () { // assign the autocompleted item to an input
                	let oid = $(this).data('oid');
                	let value = $(this).text();
                	$(this).parent().prev().data('oid', oid).val(value).addClass('autocompleted');
                	$(this).parent().empty();
                    checkFinishDisability ();
                }).on('keyup', '.type.form-control', function () { // look up auto complete values
                      let value = $(this).val();
                      $(this).removeClass('autocompleted');
                      Requests.autocomplete(value).then((results) => {
                          if ($(this).is(':focus')) { // only show the results if we are still focuses on the input
                              let list = '';
                              for (let result of results) {
                                  list += `<li data-oid="${result.oid}">${result.name}</li>`;
                              }
                              $(this).next().html(list); // add the list of items
                          }
                      });
                }).on('blur', '.type.form-control', function (){ // if we click off the input and have no auto completed
                	if (!$(this).hasClass('autocompleted')) {
	                    let value = $(this).val();
	                    Requests.autocomplete(value).then((results) => { // check the current value of the input and see if we can derivate an auto completed value anyways
                            if (results.length == 1) { // if the input only gives one autocorrected result then we can use that result to finish the completion
                                 let result = results[0];
                                 $(this).data('oid', result.oid).val(result.name).addClass('autocompleted');
                            } else {
                                // however, even if we get multiple results, if the input value matches one of the autocompleted ones exactly,
                                // we can choose that one as the autocorrect
                                for (let result of results) {
                                    if (result.name == value) {
                                        $(this).data('oid', result.oid).addClass('autocompleted');

                                        break;
                                    }
                                }
                            }
                            checkFinishDisability ();
	                    });
                 	}
                }).on('keyup change', '.form-control', checkFinishDisability);

                $("#addresource").on('click', function () {
                	$('.featherlight-inner .content .well').append(inputRow);
                    checkFinishDisability ();
                });

                $("#finishadding").on('click', function () { // Add Resources
                	// collect together all of the resources from the input
                    let allResources = [];
                    $('#finishadding, #addresource').attr('disabled', true);
                	$('.featherlight-inner .content .row').each(function () {
                		let type = $(this).find('.type').data('oid') + '|' + $(this).find('.type').val();
                		let resources = $(this).find('.numbers').val().split(/\s*,\s*/);
                        console.log(resources);
                		for (let resource of resources) {
                			allResources.push({"id": resource, type });
                		}
                        $(this).remove();
                	});
                    $('.featherlight-inner .content .well').html(`<div id='progress-prompt'><div>Opening resource creator</div></div><div style="
                    	margin-top: 2px;margin-bottom: -6px;display:none;">Time Remaining: <strong>0 seconds</strong></div><div class="progress" style="margin-bottom: 0;margin-top: 10px;">
                         <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0%"></div>
				    </div>`);

                	// add the resources
                    let req = Requests.addResources(allResources).then(function () {
                        $('#cancel, #finishadding, #addresource').attr('disabled', true);
                        cons('All Resources have been added.', 'success');
                    });
                    req.progress(function (prog, frame) { // progress as the resources are added
                        let cantCancel = (frame != null && frame.hasOwnProperty('finishing') && frame.finishing == true) || prog.total - prog.completed == 1;
                        let progressBar = $('.featherlight-inner .content .progress-bar');
                        $('#cancel').attr('disabled', cantCancel);


                        switch (prog.completed % 4) {
                        	case 0:
                        		cons('Resetting resource creator..');
                        	break;
                        	case 1:
                                let id = frame.data["choose-resource-id-form.resource-id"];
                        		cons(`Creating resource  ${id} ..`);
                        	break;
                        	case 2:
                                let type = frame.data["choose-resource-type-form.search-field"];
                        		cons(`Connecting type ${type} ..`);
                        	break
                        	case 3:
                        		cons('Finishing Resource Creation..');
                        	break;
                        }
                        if (prog.remaining == 0) {
                        	progressBar.parent().prev().hide()
                        } else {
                        	progressBar.parent().prev().show().find('strong').text(Math.round(prog.remaining) + ' seconds');
                        }
                        progressBar.attr('aria-valuenow', prog.percent*100).width(prog.percent*100 + '%');
                    });

                    $("#cancel").off('click').on('click', () => {
                        req.cancel();
                        $('#cancel, #finishadding, #addresource').attr('disabled', true);
                        $('.featherlight-inner .content .progress').hide().parent().prev().hide();
                        cons('Cancelled. Current Resource will be revoked and any pending resources will be ignored.', 'error');
                    });
                });
			}
     	});
    }

    (function main () {
    	window.onload = function () {
    		console.log(document.location);
    	}
    	addExternalStyling ();

    	$('#new-person-wizard').removeAttr('href').on('click', function () {
    		modifiedNewPerson();
    	});
    	$('#new-resource-wizard').removeAttr('href').on('click', function () {
    		modifiedResourceAdder ()
    	});
    })();
})(jQuery);