diff --git a/nodejs/conf/production.js b/nodejs/conf/production.js index 88dac39..7606a56 100644 --- a/nodejs/conf/production.js +++ b/nodejs/conf/production.js @@ -24,6 +24,6 @@ module.exports = { runnerTemplate: 'crunner0', memTarget: 15, minAvailableRunners: 10, - domian: 'prod.718it.codeland.us', + domian: 'cl-worker-3.718it.codeland.us', }, }; diff --git a/nodejs/controller/codeland.js b/nodejs/controller/codeland.js index 91e90b6..8df9cb1 100644 --- a/nodejs/controller/codeland.js +++ b/nodejs/controller/codeland.js @@ -42,7 +42,7 @@ const clworker = new CodelandController({ssh, ...conf.clworker}); clworker.__log('memory', await clworker.ssh.memory()) }catch{} }, 2000, clworker) - await clworker.deleteUntracedRunners(); + await clworker.deleteUntrackedRunners(); await clworker.runnerOven(10*1000); clworker.__log('df', (await clworker.ssh.df())['/']) diff --git a/nodejs/lib/codeland.js b/nodejs/lib/codeland.js index e2d6cbf..5fc2342 100644 --- a/nodejs/lib/codeland.js +++ b/nodejs/lib/codeland.js @@ -110,8 +110,6 @@ class CodeLandWorker{ instances. */ async init(){ - - this.runnerTemplate = await LXC.get({ name: this.runnerTemplate, execInstance: this.ssh @@ -129,7 +127,6 @@ class CodeLandWorker{ environment: conf.environment, }), 1000) - return this; } @@ -168,12 +165,13 @@ class CodeLandWorker{ } /* - getCurrentCopies and deleteUntracedRunners clean up zombie runners from + getCurrentCopies and deleteUntrackedRunners clean up zombie runners from old instances of Codeland */ async getCurrentCopies(){ let containers = await this.runnerTemplate.list(); let runners = {}; + for(let container of containers){ if(container.name.startsWith(this.runnerPrefix ) ){ if(container.name === this.runnerTemplate) continue; @@ -187,7 +185,7 @@ class CodeLandWorker{ return runners; } - async deleteUntracedRunners(){ + async deleteUntrackedRunners(){ for(let [name, runner] of Object.entries(await this.getCurrentCopies())){ if(!this.__runners[name]){ (async ()=>{ @@ -455,7 +453,7 @@ if (require.main === module){(async function(){try{ // await clworker.ssh.exec('bash ~/clean_crunners.sh'); - // await clworker.deleteUntracedRunners(); + // await clworker.deleteUntrackedRunners(); await clworker.runnerOven(); console.log('mem info:', await clworker.memory()) diff --git a/nodejs/public/lib/js/jq-repeat_new.js b/nodejs/public/lib/js/jq-repeat_new.js index 97f5871..25c41f2 100644 --- a/nodejs/public/lib/js/jq-repeat_new.js +++ b/nodejs/public/lib/js/jq-repeat_new.js @@ -4,309 +4,979 @@ https://github.com/wmantly/jq-repeat MIT license */ -(function($, Mustache){ -'use strict'; - if (!$.scope) { - $.scope = {}; +class CallbackQueue { + constructor(callbacks) { + this.__callbacks = []; + + for (let callback of Array.isArray(callbacks) ? callbacks : [callbacks]) { + this.push(callback); + } } - - var make = function(element){ - var result = []; - result.splice = function(inputValue, ...args){ - //splice does all the heavy lifting by interacting with the DOM elements. + push(callback) { + if (callback instanceof Function) { + this.__callbacks.push(callback); + } + } - var toProto = [...args] + call() { + let args = arguments; + this.__callbacks.forEach(callback => { + callback(...args); + }); + } +} + +// Encapsulate the library within an IIFE to prevent global scope pollution +(function($, Mustache) { + 'use strict'; + + const throttleMap = {}; + + function throttleCheck(key) { + if (throttleMap[key] && throttleMap[key].args !== null) { + // Call the callback with the stored arguments + throttleMap[key].callBack(...throttleMap[key].args); + throttleMap[key].args = null; + + // Schedule next check + setTimeout(throttleCheck, throttleMap[key].minDelay, key); + } else { + // Clean up when no more pending calls + delete throttleMap[key]; + } + } - var index; - //if a string is submitted as the index, try to match it to index number - if(typeof arguments[0] === 'string'){ - index = this.indexOf( arguments[0] );//set where to start - if (index === -1) { - return []; + function throttle(key, minDelay, callBack, ...args) { + if (throttleMap[key]) { + // Update arguments for pending call + throttleMap[key].args = args; + } else { + // First call - execute immediately and set up throttling + throttleMap[key] = { + args: null, + minDelay, + callBack, + }; + + // Execute immediately + callBack(...args); + + // Schedule throttle check + setTimeout(throttleCheck, minDelay, key); + } + } + + // Internal scope object for managing repeat lists + const _scope = {}; + + Object.defineProperty(_scope, 'onNew', { + value: new CallbackQueue(), + writable: true, + enumerable: false, + configurable: true + }); + + $.scope = new Proxy(_scope, { + get(obj, prop) { + if (!obj[prop]) { + obj[prop] = []; + } + return Reflect.get(...arguments); + }, + set(obj, prop, value) { + return Reflect.set(...arguments); + }, + }); + + // --- Helper function to clean up nested jq-repeat elements --- + function cleanupNestedRepeats($element) { + if (!$element || !$element.length) return; + + // Find all nested jq-repeat elements within this element + const $nestedRepeats = $element.find('[jq-repeat-scope]'); + + $nestedRepeats.each(function() { + const $nested = $(this); + const nestedScopeId = $nested.attr('jq-repeat-scope'); + + if (nestedScopeId && $.scope[nestedScopeId]) { + // Clean up the nested scope instance + const nestedInstance = $.scope[nestedScopeId]; + + // Call take on all items to clean up their DOM elements + for (let i = 0; i < nestedInstance.length; i++) { + if (nestedInstance[i] && nestedInstance[i].__jq_$el) { + nestedInstance.__take(nestedInstance[i].__jq_$el, nestedInstance[i], nestedInstance); + } + } + + // Clear the array + nestedInstance.length = 0; + + // Remove from global scope + delete $.scope[nestedScopeId]; + } + }); + } + + // --- ES6 Class for jq-repeat lists --- + class RepeatList extends Array { + // This is the fix: tells built-in Array methods (like slice, map, filter) + // to return plain Array instances instead of RepeatList instances. + static get [Symbol.species]() { + return Array; + } + + constructor(element, scopeId, options, parentData, parentId, parentIndex, templateHTML, nestedTemplates) { + super(); + + this.__jqRepeatId = scopeId; + this.options = options; + + // Store the actual indexKey property name for quick access + if (options && options.indexKey !== undefined) { + this.__jqIndexKey = options.indexKey; + } + + // Store sorting options + if (options && options.orderBy !== undefined) { + this.__jqOrderBy = options.orderBy; + this.__jqOrderReverse = options.orderReverse === 'true'; + } else { + this.__jqOrderBy = null; + this.__jqOrderReverse = false; + } + + this._parentData = parentData; + this.__jqParent = parentId; + this.__jqParentIndex = parentIndex; + this.__jqTemplate = templateHTML; + this.nestedTemplates = nestedTemplates; + + this.$this = $(`