PageSourceSearch

https://gnec.org/wp-content/plugins/repayment-calculator/repayment-calculator.js?ver=7.1.2

js gnec.org collected 2026-10-09 22:12:20 UTC 20,925 bytes, 603 lines download raw bytes

1jQuery(document).ready(function($) {
2    (function(factory) {
3        if (typeof define === 'function' && define.amd) {
4            // AMD. Register as an anonymous module.
5            define(['jquery'], factory);
6        } else {
7            // Browser globals
8            factory(jQuery);
9        }
10    } (function($) {
11		
12        /**
13        * Range feature detection
14        * @return {Boolean}
15        */
16        function supportsRange() {
17            var input = document.createElement('input');
18            input.setAttribute('type', 'range');
19            return input.type !== 'text';
20        }
21
22        var pluginName = 'lrc',
23            pluginInstances = [],
24            inputrange = supportsRange(),
25            defaults = {
26                polyfill: true,
27                rangeClass: 'lrc',
28                disabledClass: 'lrc--disabled',
29                fillClass: 'lrc__fill',
30                handleClass: 'lrc__handle',
31                startEvent: ['mousedown', 'touchstart', 'pointerdown'],
32                moveEvent: ['mousemove', 'touchmove', 'pointermove'],
33                endEvent: ['mouseup', 'touchend', 'pointerup']
34            };
35
36        /**
37        * Delays a function for the given number of milliseconds, and then calls
38        * it with the arguments supplied.
39        * @param  {Function} fn   [description]
40        * @param  {Number}   wait [description]
41        * @return {Function}
42        */
43        function delay(fn, wait) {
44            var args = Array.prototype.slice.call(arguments, 2);
45            return setTimeout(function(){ return fn.apply(null, args); }, wait);
46        }
47
48        /**
49        * Returns a debounced function that will make sure the given
50        * function is not triggered too much.
51        * @param  {Function} fn Function to debounce.
52        * @param  {Number}   debounceDuration OPTIONAL. The amount of time in milliseconds for which we will debounce the function. (defaults to 100ms)
53        * @return {Function}
54        */
55        function debounce(fn, debounceDuration) {
56            debounceDuration = debounceDuration || 100;
57            return function() {
58                if (!fn.debouncing) {
59                    var args = Array.prototype.slice.apply(arguments);
60                    fn.lastReturnVal = fn.apply(window, args);
61                    fn.debouncing = true;
62                }
63                clearTimeout(fn.debounceTimeout);
64                fn.debounceTimeout = setTimeout(function(){
65                    fn.debouncing = false;
66                }, debounceDuration);
67                return fn.lastReturnVal;
68            };
69        }
70
71        /**
72        * Plugin
73        * @param {String} element
74        * @param {Object} options
75        */
76        function Plugin(element, options) {
77            this.$window    = $(window);
78            this.$document  = $(document);
79            this.$element   = $(element);
80            this.options    = $.extend( {}, defaults, options );
81            this._defaults  = defaults;
82            this._name      = pluginName;
83            this.startEvent = this.options.startEvent.join('.' + pluginName + ' ') + '.' + pluginName;
84            this.moveEvent  = this.options.moveEvent.join('.' + pluginName + ' ') + '.' + pluginName;
85            this.endEvent   = this.options.endEvent.join('.' + pluginName + ' ') + '.' + pluginName;
86            this.polyfill   = this.options.polyfill;
87            this.onInit     = this.options.onInit;
88            this.onSlide    = this.options.onSlide;
89            this.onSlideEnd = this.options.onSlideEnd;
90
91            // Plugin should only be used as a polyfill
92            if (this.polyfill) {
93                // Input range support?
94                if (inputrange) { return false; }
95            }
96
97            this.identifier = 'js-' + pluginName + '-' +(+new Date());
98            this.min        = parseFloat(this.$element[0].getAttribute('min') || 0);
99            this.max        = parseFloat(this.$element[0].getAttribute('max') || 100);
100            this.value      = parseFloat(this.$element[0].value || this.min + (this.max-this.min)/2);
101            this.step       = parseFloat(this.$element[0].getAttribute('step') || 1);
102			this.noChange	= false;
103            this.$fill      = $('<div class="' + this.options.fillClass + '" />');
104            this.$handle    = $('<div class="' + this.options.handleClass + '" />');
105            this.$range     = $('<div class="' + this.options.rangeClass + '" id="' + this.identifier + '" />').insertAfter(this.$element).prepend(this.$fill, this.$handle);
106            this.lockvalue	= -1;
107
108            // visually hide the input
109            this.$element.css({
110                'position': 'absolute',
111                'width': '1px',
112                'height': '1px',
113                'overflow': 'hidden',
114                'opacity': '0'
115            });
116
117            // Store context
118            this.handleDown = $.proxy(this.handleDown, this);
119            this.handleMove = $.proxy(this.handleMove, this);
120            this.handleEnd  = $.proxy(this.handleEnd, this);
121            this.init();
122
123            // Attach Events
124            var _this = this;
125            this.$window.on('resize' + '.' + pluginName, debounce(function() {
126                // Simulate resizeEnd event.
127                delay(function() { _this.update(); }, 300);
128            }, 20));
129
130            this.$document.on(this.startEvent, '#' + this.identifier + ':not(.' + this.options.disabledClass + ')', this.handleDown);
131
132            // Listen to programmatic value changes
133            this.$element.on('change' + '.' + pluginName, function(e, data) {
134                if (data && data.origin === pluginName) {
135                    return;
136                }
137				
138                var value = e.target.value,
139                    pos = _this.getPositionFromValue(value);
140                _this.setPosition(pos);
141            });
142        }
143
144        Plugin.prototype.init = function() {
145            if (this.onInit && typeof this.onInit === 'function') {
146                this.onInit();
147            }
148            this.update();
149        };
150		
151		Plugin.prototype.lock	= function(args) {
152			this.lockvalue		= args[0];
153		}
154		Plugin.prototype.unlock	= function() {
155			this.lockvalue		= -1;
156		}
157        Plugin.prototype.update = function() {
158
159			var hideagain		= 0;
160			if (!this.$element.is(':visible')) { hideagain = 1; }
161			
162			if (hideagain) this.$element.closest('.hidethis').show();
163            this.handleWidth    = this.$handle[0].offsetWidth;
164            this.rangeWidth     = this.$range[0].offsetWidth;
165            this.maxHandleX     = this.rangeWidth - this.handleWidth;
166            this.grabX          = this.handleWidth / 2;
167            this.position       = this.getPositionFromValue(this.value);
168			if (hideagain) this.$element.closest('.hidethis').hide();
169
170            // Consider disabled state
171            if (this.$element[0].disabled) {
172                this.$range.addClass(this.options.disabledClass);
173            } else {
174                this.$range.removeClass(this.options.disabledClass);
175            }
176
177			this.setPosition(this.position);
178
179        };
180
181        Plugin.prototype.handleDown = function(e) {
182			
183            e.preventDefault();
184            this.$document.on(this.moveEvent, this.handleMove);
185            this.$document.on(this.endEvent, this.handleEnd);
186
187            // If we click on the handle don't set the new position
188            if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) {
189                return;
190            }
191
192            var posX = this.getRelativePosition(this.$range[0], e),
193                handleX = this.getPositionFromNode(this.$handle[0]) - this.getPositionFromNode(this.$range[0]);
194
195            this.setPosition(posX - this.grabX);
196
197            if (posX >
197= handleX && posX < handleX + this.handleWidth) {
198                this.grabX = posX - handleX;
199            }
200        };
201
202        Plugin.prototype.handleMove = function(e) {
203            e.preventDefault();
204            var posX = this.getRelativePosition(this.$range[0], e);
205			
206            this.setPosition(posX - this.grabX);
207        };
208
209        Plugin.prototype.handleEnd = function(e) {
210            e.preventDefault();
211            this.$document.off(this.moveEvent, this.handleMove);
212            this.$document.off(this.endEvent, this.handleEnd);
213
214			var value, left, ppp;
215			
216			ppp = this.getPositionFromValue(this.min + this.step);
217			value = this.getValueFromPosition(Math.round(this.position / ppp) * ppp);
218            left = this.getPositionFromValue(value);
219			
220			// Update ui
221			this.$fill[0].style.width = (left + this.grabX)  + 'px';
222			this.$handle[0].style.left = left + 'px';
223			
224			this.position = left;
225            this.value = value;
226			
227            if (this.onSlideEnd && typeof this.onSlideEnd === 'function') {
228                this.onSlideEnd(this.position, this.value);
229            }
230        };
231
232        Plugin.prototype.cap = function(pos, min, max) {
233            if (pos < min) { return min; }
234            if (pos > max) { return max; }
235            return pos;
236        };
237
238        Plugin.prototype.setPosition = function(pos) {
239			
240			var value, left, ppp;
241			
242			var tobe = ((this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step);
243			
244			if (tobe >= this.lockvalue && this.lockvalue > -1) {
245				value	= this.lockvalue;
246				left	= this.getPositionFromValue(this.lockvalue);
247			} else {
248				// Moving steps
249				ppp		= this.getPositionFromValue(this.min + this.step);
250				value	= this.getValueFromPosition(Math.round(pos / ppp) * ppp);
251				left	= this.cap(pos, 0, this.maxHandleX);
252			}
253			
254            //Snapping steps
255            // value = (this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step;
256            // left = this.getPositionFromValue(value);
257
258            // Update ui
259            this.$fill[0].style.width = (left + this.grabX)  + 'px';
260            this.$handle[0].style.left = left + 'px';
261            this.setValue(value);
262
263            // Update globals
264            this.position = left;
265            this.value = value;
266
267            if (this.onSlide && typeof this.onSlide === 'function') {
268                this.onSlide(left, value);
269            }
270			
271        };
272
273        Plugin.prototype.getPositionFromNode = function(node) {
274            var i = 0;
275            while (node !== null) {
276                i += node.offsetLeft;
277                node = node.offsetParent;
278            }
279            return i;
280        };
281
282        Plugin.prototype.getRelativePosition = function(node, e) {
283            return (e.pageX || e.originalEvent.clientX || e.originalEvent.touches[0].clientX || e.currentPoint.x) - this.getPositionFromNode(node);
284        };
285
286        Plugin.prototype.getPositionFromValue = function(value) {
287            var percentage, pos;
288            percentage = (value - this.min)/(this.max - this.min);
289            pos = percentage * this.maxHandleX;
290            return pos;
291        };
292
293        Plugin.prototype.getValueFromPosition = function(pos) {
294            var percentage, value;
295            percentage = ((pos) / (this.maxHandleX || 1));
296            value = this.step * Math.round((((percentage) * (this.max - this.min)) + this.min) / this.step);
297            return Number((value).toFixed(2));
298        };
299
300        Plugin.prototype.setValue = function(value) {
301            if (value !== this.value) {
302                this.$element.val(value).trigger('change', {origin: pluginName});
303            }
304        };
305
306        Plugin.prototype.destroy = function() {
307            this.$document.off(this.startEvent, '#' + this.identifier, this.handleDown);
308            this.$element
309                .off('.' + pluginName)
310                .removeAttr('style')
311                .removeData('plugin_' + pluginName);
312
313            // Remove the generated markup
314            if (this.$range && this.$range.length) {
315                this.$range[0].parentNode.removeChild(this.$range[0]);
316            }
317
318            // Remove global events if there isn't any instance anymore.
319            pluginInstances.splice(pluginInstances.indexOf(this.$element[0]),1);
320            if (!pluginInstances.length) {
321                this.$window.off('.' + pluginName);
322            }
323        };
324
325        // A really lightweight plugin wrapper around the constructor,
326        // preventing against multiple instantiations
327        $.fn[pluginName] = function(options) {
328			
329			
330			var args = [].slice.call(arguments);
331			
332
333			return this.each(function() {
334                var $this = $(this),
335                    data  = $this.data('plugin_' + pluginName);
336
337                // Create a new instance.
338                if (!data) {
339                    $this.data('plugin_' + pluginName, (data = new Plugin(this, options)));
340                    pluginInstances.push(this);
341                }
342
343                // Make it possible to access methods from public.
344                // e.g `$element.lrc('method');`
345                if (typeof options === 'string') {
346                    data[args.shift()](args);
347                }
348            });
349        };
350    }));
351});
352
353
354var lrc_loan_selector = 'form.lrc_form';
355var lrc_slider_selector = 'div.range';
356
357function lrcCalculate(e) {
358	
359    /* Change relevent element's output value */
360	var $ 			= jQuery,
361		form 		= $(this).closest(lrc_loan_selector),
362		rates 		= lrc__rates[form.attr('id')],
363		sliders 	= form.find(lrc_slider_selector),
364		p 			= form.find(lrc_slider_selector).filter('.lrc-slider-principal'),
365		t			= form.find(lrc_slider_selector).filter('.lrc-slider-term'),
366		principal 	= parseFloat(p.find('input[type=range]').val()) || 0,
367		term 		= parseFloat(t.find('input[type=range]').val()) || ((!rates.periodslider)? 1:0);
368	
369	if ($(this).hasClass("output")) return;
370
371	/* Output Principal */
372    
373	p.find('output').text(rates.cb+principal.toString().lrc_rounding(rates)+rates.ca);
374
375	/* Output term */
376    
377	periodlabel = term == 1 ? rates.singleperiodlabel : rates.periodlabel;
378    
379	t.find('output').text(term+' '+periodlabel);
380	
381	form.find('.lrc-outputs').show();
382
383	/* Everything below this point should happen no matter WHICH slider is moved */
384    
385	var outputs = [];
386    
387    var currencyspace = rates.currencyspace ? ' ' : '';
388    rates.cb = rates.ca = '';
389    
390	if (rates.currency == false) rates.currency = '';
391    if (rates.ba == 'before') rates.cb = rates.currency+currencyspace;
392    if (rates.ba == 'after') rates.ca = currencyspace+rates.currency;
393    interest = principal > rates.trigger ? rates.secondary : rates.primary;
394
395    var creditscore = $("input[name='creditselector']:checked").val();
396    var rating = rates.usecreditselector ? rates.triggers[creditscore].name : 'N/A';
397    var low = rates.usecreditselector ? rates.triggers[creditscore].low : interest;
398    
399    outputs.push(lrc_amortization(term, principal, rates, low));
400	
401	if (rates.adminfeewhen == 'afterinterest') {
402		lrc_adminfee_after(rates,outputs,term);
403	}
404    
405	/* Display the Outputs */
406    form.find('.rate').text(low+'%');
407	form.find('.interest').text(rates.cb+lrc_doubledigit(outputs[0].interest,rates).lrc_rounding(rates)+rates.ca);
408	form.find('.grandtotal').text(rates.cb+lrc_doubledigit(outputs[0].total,rates).lrc_rounding(rates)+rates.ca);
409	form.find('.repayment').text(rates.cb+lrc_doubledigit(outputs[0].repayment,rates).lrc_rounding(rates)+rates.ca);
410    form.find('.rating').text(rating);
411	form.find('.principal').text(rates.cb+lrc_doubledigit(principal,rates).lrc_rounding(rates)+rates.ca);
412    form.find('.term').text(term+' '+periodlabel);
413	form.find('.processing').text(rates.cb+lrc_doubledigit(outputs[0].processing,rates).lrc_rounding(rates)+rates.ca);
414	
415	/* Fill the data into the hidden fields */
416	form.find('input[name=repayment]').val(lrc_doubledigit(outputs[0].repayment,rates)
416)
417	form.find('input[name=totalamount]').val(lrc_doubledigit(outputs[0].total,rates))
418}
419
420function lrc_doubledigit(num,rates) {
421	
422	if (rates.decimals == 'none') return Math.round(num).toString();
423	var n = num.toFixed(2);
424	if (rates.decimals == 'float') return n.replace('.00','');
425	return n;
426	
427}
428
429function lrc_adminfee(rates,P,T) {
430	
431    var termfee = 0, adminfee = 0;
432	if (rates.adminfee && rates.adminfeewhen == 'beforeinterest') {
433		adminfee = P * (rates.adminfeevalue * .01);
434		if (rates.adminfeetype != 'percent') {
435			adminfee = rates.adminfeevalue;
436		}
437	}
438    if (rates.termfee && rates.adminfeewhen == 'beforeinterest') {
439		termfee = T * (rates.termfeevalue);
440		adminfee = adminfee + termfee;
441	}
442
443    if (adminfee && adminfee < rates.adminfeemin && rates.adminfeemin != false) adminfee = rates.adminfeemin;
444    if (adminfee && adminfee > rates.adminfeemax && rates.adminfeemax != false) adminfee = rates.adminfeemax;
445    
446	return {'total':P+adminfee,'processing':adminfee};
447}
448
449function lrc_adminfee_after(rates,outputs,T) {
450
451	var adminfee = 0, termfee = 0;
452    
453    for (i in outputs) {
454		P = outputs[i].total;
455
456		if (rates.adminfee && rates.adminfeewhen == 'afterinterest') {
457			adminfee = P * (rates.adminfeevalue * .01);
458			if (rates.adminfeetype != 'percent') {
459				adminfee = rates.adminfeevalue;
460			}
461		}
462		if (rates.termfee && rates.adminfeewhen == 'afterinterest') {
463			termfee = T * (rates.termfeevalue);
464			adminfee = adminfee + termfee;
465		}
466		
467		if (adminfee < rates.adminfeemin && rates.adminfeemin != false) adminfee = rates.adminfeemin;
468		if (adminfee > rates.adminfeemax && rates.adminfeemax != false) adminfee = rates.adminfeemax;
469
470		outputs[i].total = P + adminfee;
471		outputs[i].processing = adminfee;
472	}
473}
474
475function lrc_amortization(term, principal, rates, low) {
476
477	var preP= lrc_adminfee(rates,principal,term);
478	var P	= preP.total;        // Principal
479	var T	= term;              // Term
480    var I   = low || 0;
481    var C   = lrc_rterm (rates); // Adjust number of repayment periods
482	var V 	= lrc_term (rates);  // Adjust number of periods
483    var Q   = T * C/V;           // 
484    var A   = rates.nominalapr ? low * .01 / C : Math.pow(1 + I * .01,1/C) -1;
485	if (A == 0) {
486		var M = P/Q;
487	} else {
488		var M 	= (A * P) / (1 - Math.pow(1 + A,-Q)) || 0;
489	}
490	var R	= M * Q;
491	
492	return {'repayment':M,'total':R,'interest':R-principal,'processing':preP.processing,'actual':A};
493}
494
495function lrc_term (rates) {
496    
497	var P = rates.period.toLowerCase();
498    if (P == 'years') var C = 1;
499    if (P == 'months') var C = 12;
500    if (P == 'weeks') var C = 52;
501    if (P == 'days') var C = 365;
502    return C;
503}
504
505function lrc_rterm (rates) {
506    
507	var P = rates.repaymentperiod.toLowerCase();
508    if (P == 'ryears') var C = 1;
509    if (P == 'rmonths') var C = 12;
510    if (P == 'rweeks') var C = 52;
511    if (P == 'rdays') var C = 365;
512    return C;
513}
514
515function lrc_apply_all() {
516	
517	var $ = jQuery;
518    
519    /* Select all relevant loan slider forms */
520	$(lrc_loan_selector).each(function() {
521		/* Initialize sliders */
522		var sliders = $(this).find('[data-lrc]'), x = $(this);
523		
524        sliders.change(lrcCalculate);
525        sliders.lrc({polyfill:false});
526        
527		var form	= $(this),
528			rates	= lrc__rates[form.attr('id')],
529			buttons	= form.find('.circle-control');
530		
531		buttons.filter('.lrc-down').click(function() {
532			
533			var range = $(this).closest('.range').find('input[type=range]');
534			var v = parseFloat(range.val());
535			var s = parseFloat(range.attr('step')) || 1;
536			var m = parseFloat(range.attr('min'));
537			var n = v - s;
538			if (n < m) range.val(m); 
539			else range.val(n);
540			
541			range.change();
542			
543		});
544		
545		buttons.filter('.lrc-up').click(function() {
546			
547            var range = $(this).closest('.range').find('input[type=range]');
548			var v = parseFloat(range.val());
549			var s = parseFloat(range.attr('step')) || 1;
550			var m = parseFloat(range.attr('max'));
551			var n = v + s;
552			if (n > m) range.val(m); 
553			else range.val(n);			
554			
555			range.change();
556			
557		});
558        
559        form.find('input[name=creditselector]').change(function() {
560			$(sliders[0]).change();
561		});
562		
563		form.find('select').change(function() {
564			$(sliders[0]).change();
565		});
566		
567		$(sliders[0]).change();
568		
569	});
570	
571}
572jQuery(document).ready(function($) {
573    
574	lrc_apply_all();
575
576});
577
578String.prototype.lrc_rounding = function(rates) {
579    
580    var rr = rates.rounding;
581    var r = 1;
582
583    if (rr == 'tenround')  var r = 10;
584    if (rr == 'hundredround') var r = 100;
585    if (rr == 'thousandround') var r = 1000;
586    if (rr == 'noround') var num = this;
587    else var num = Math.round(this / r) * r;
588    
589    var rs = rates.separator;
590    
591    if (rs == 'none') return num;
592    if (rs == 'apostrophe')  var s = "'";
593    else if (rs == 'dot')  var s = ".";
594    else if (rs == 'comma')  var s = ",";
595	else var s = ' ';
596    var str = num.toString().split('.');
597    if (str[0].length >= 4) {
598        str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1'+s);
599    }
600    if (rs == 'dot' || rates.decimalcomma) var decimalsdevider = ',';
601    else var decimalsdevider = '.';
602    return str.join(decimalsdevider);
603}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.