{ } };\n\n this.modelDebounceCallbacks.push(callbackRegister);\n\n // This is a normal \"timeout\" for a debounce function.\n var timeout;\n\n return e => {\n clearTimeout(timeout);\n\n timeout = setTimeout(() => {\n callback(e);\n timeout = undefined;\n\n // Because we just called the callback, let's return the\n // callback register to it's normal \"null\" state.\n callbackRegister.callback = () => { };\n }, time);\n\n // Register the current callback in the register as a kind-of \"escape-hatch\".\n callbackRegister.callback = () => {\n clearTimeout(timeout);\n callback(e);\n };\n };\n }\n}\n","export default class {\n constructor(el, skipWatcher = false) {\n this.el = el;\n this.skipWatcher = skipWatcher;\n this.updateQueue = [];\n }\n}\n","import Action from '.';\n\nexport default class extends Action {\n constructor(modelAttrVal, params, modelVal, isCustomEvent, el) {\n super(el);\n\n this.isCustomEvent = isCustomEvent;\n this.type = 'fireEvent';\n this.name = modelAttrVal;\n this.payload = {\n modelAttrVal,\n params,\n modelVal\n };\n }\n\n // Overriding toId() becuase some EventActions don't have an \"el\"\n toId() {\n return btoa(encodeURIComponent(this.type, this.payload.event, JSON.stringify(this.payload.params)));\n }\n}\n","export function dispatch(el, eventName, options, theHandler) {\n const event = new CustomEvent(eventName, options);\n el.addEventListener(eventName, theHandler);\n el.dispatchEvent(event);\n}","\nexport default class MessageBus {\n constructor() {\n this.listeners = {};\n }\n\n register(name, callback) {\n if (!this.listeners[name]) {\n this.listeners[name] = [];\n }\n\n this.listeners[name].push(callback);\n }\n\n call(name, ...params) {\n (this.listeners[name] || []).forEach(callback => {\n callback(...params);\n });\n }\n\n has(name) {\n return Object.keys(this.listeners).includes(name);\n }\n}\n","import MessageBus from './MessageBus';\n\nexport default {\n\tavailableHooks: [\n\t\t/**\n\t\t* Public Hooks\n\t\t*/\n\t\t'agent.initialized',\n\t\t'element.initialized',\n\t\t'element.updating',\n\t\t'element.updated',\n\t\t'element.removed',\n\t\t'message.sent',\n\t\t'message.failed',\n\t\t'message.received',\n\t\t'message.processed',\n\t\t'allMessages.processed',\n\t\t'request',\n\n\t\t/**\n\t\t* Private Hooks\n\t\t*/\n\t\t'interceptAsynModelSetValue',\n\t\t'interceptAsynModelAttachListener',\n\t\t'beforeReplaceState',\n\t\t'beforePushState'\n\t],\n\n bus: new MessageBus(),\n\n register(name, callback) {\n if (!this.availableHooks.includes(name)) {\n throw `ASYNergy: Referencing unknown hook: [${name}]`;\n }\n\n this.bus.register(name, callback);\n },\n\n call(name, ...params) {\n this.bus.call(name, ...params);\n }\n};\n","import EventAction from './action/event'; // ----------------------- temp test events\nimport { dispatch } from './util/dispatch'; // ------------------------------ temp test events\nimport HookManager from './HookManager';\nimport MessageBus from './MessageBus'; // -------- temp test events\nimport DirectiveManager from './DirectiveManager';\n// import Agent from './agent/index';\n\nconst store = {\n csrf: {},\n transmissionEls: [],\n mutables: [],\n directives: DirectiveManager,\n asynergyIsInBackground: false,\n asynergyIsOffline: false,\n hooks: HookManager,\n agents: [],\n listeners: new MessageBus(),\n nodesSetToDisabled: [],\n nodesSetToReadOnly: [],\n mutableIncludesModel: false,\n\n theAgents() {\n return Object.keys(this.agents).map(key => {\n return this.agents[key];\n });\n },\n\n registerHook(name, callback) {\n this.hooks.register(name, callback);\n },\n\n callHook(name, ...params) {\n this.hooks.call(name, ...params);\n },\n\n addAgent(agent) {\n this.agents.push(agent);\n return this.agents[this.agents.length - 1];\n },\n\n getAgentsByID(agentID) {\n return this.agents.filter(agent => {\n return agent.agentID === agentID;\n });\n },\n\n tearDownAgents() {\n let agentsLength = this.agents.length;\n for (let i = 0; i < agentsLength; i++) {\n let index = this.agents.length - 1;\n this.removeAgent(this.agents[index], index);\n }\n },\n\n emit(event, ...params) {\n this.listeners.call(event, ...params);\n this.agentsListeningForEvent(event).forEach(agent => {\n agent.addAction(new EventAction(event, params));\n });\n },\n\n agentsListeningForEvent(event) {\n return this.theAgents().filter(agent => {\n return agent.listeners.includes(event);\n });\n },\n \n addEmitEvent(el, directiveParams, handlerName, handler) {\n let options = {};\n const eventHandler = (...paramsA) => {\n \n const modelParams = [];\n modelParams[0] = [];\n let selectValues;\n \n if (paramsA !== undefined & paramsA.length !== 0) {\n if (paramsA[0] !== undefined & paramsA[0].length !== 0) {\n modelParams[0] = paramsA[0].split(',');\n }\n\n if (Array.isArray(paramsA) & (modelParams[0].toString() === directiveParams.toString())) {\n \n switch(el.type) { \n case 'text':\n if (paramsA[1] !== undefined) {\n el.value = paramsA[1];\n }\n\n break;\n \n case 'checkbox':\n if (paramsA[2] !== undefined) {\n el.checked = paramsA[2];\n }\n \n break;\n\t\t\t\t\t\t\n case 'radio':\n if (paramsA[2] !== undefined) {\n el.checked = paramsA[2];\n }\n \n break;\n\n case 'range':\n if (paramsA[1] !== undefined) {\n el.value = paramsA[1];\n }\n \n break;\n \n case 'select-multiple':\n if (paramsA[1] !== undefined) {\n selectValues = paramsA[1].split(',');\n }\n \n if (selectValues !== undefined) {\n options.detail = selectValues;\n }\n\n for (const option of el.options) {\n if (selectValues.indexOf(option.value) !== -1) {\n option.setAttribute('selected', 'selected');\n } else {\n option.removeAttribute('selected');\n }\n }\n \n break;\n\n case 'submit':\n if ( paramsA[1] !== undefined) {\n const modelValue = paramsA[1].split(',');\n \n if (modelValue !== undefined) {\n options.detail = modelValue;\n }\n }\n \n break;\n }\n\n dispatch(el, handlerName, options, handler);\n }\n } else { // if (paramsA !== undefined)\n dispatch(el, handlerName, options, handler);\n }\n };\n this.on(handlerName, eventHandler);\n },\n\n on(event, callback) {\n this.listeners.register(event, callback);\n },\n\n removeAgent(agent, index) {\n // Remove event listeners attached to the DOM.\n agent.tearDown();\n // Remove the component from the store.\n // delete this.agents[index];\n this.agents.splice(index, 1);\n },\n\n transmissionElsData(payload, listenerType, modelValueKey) {\n if (this.transmissionEls[0] !== undefined) {\n let index;\n for (index = 0; index < this.transmissionEls.length; index++) {\n if (this.transmissionEls[index].getAttribute('asyn:transmit') !== null) {\n\n const transmissionElIsCheckbox = this.transmissionEls[index].type === 'checkbox';\n\t\t\t\t\t\t\tconst transmissionElIsRadio = this.transmissionEls[index].type === 'radio';\n const transmissionElIsMutable = this.transmissionEls[index].mutable >= 0;\n\t\t\t\t\t\t\t\n if (transmissionElIsCheckbox) {\n const isChecked = this.transmissionEls[index].checked;\n payload.transmissionElsData[this.transmissionEls[index].getAttribute('asyn:transmit')] = isChecked ? this.transmissionEls[index].value : 'false';\n\t\t\t\t\t\t\t\n\t } else if (transmissionElIsRadio) {\n\t const isChecked = this.transmissionEls[index].checked;\n\t if (isChecked) {\n\t payload.transmissionElsData[this.transmissionEls[index].getAttribute('asyn:transmit')] = this.transmissionEls[index].value;\n\t }\n\t\t\t\t\t\t\t\n } else {\n payload.transmissionElsData[this.transmissionEls[index].getAttribute('asyn:transmit')] = (this.transmissionEls[index].tagName === 'INPUT') || (this.transmissionEls[index].tagName === 'TEXTAREA') || (this.transmissionEls[index].tagName === 'SELECT') ? this.transmissionEls[index].value : this.transmissionEls[index].innerHTML;\n\n // REPLACE CHECKBOX MODEL VALUE WITH MUTABLE ELEMENT DATA\n if (listenerType === 'checkbox' && transmissionElIsMutable) {\n payload.modelData[modelValueKey] = this.transmissionEls[index].innerHTML;\n }\n\n }\n }\n }\n }\n return payload;\n },\n\n addDisabledNode(theNode) {\n this.nodesSetToDisabled.push(theNode);\n },\n\n addReadOnlyNode(theNode) {\n this.nodesSetToReadOnly.push(theNode);\n },\n\n clearDisabledReadOnlyNodesArrays() {\n let index;\n\n for (index = 0; index < this.nodesSetToDisabled.length; index++) {\n this.nodesSetToDisabled[index].disabled = false;\n }\n this.nodesSetToDisabled.length = 0;\n\n for (index = 0; index < this.nodesSetToReadOnly.length; index++) {\n this.nodesSetToReadOnly[index].readOnly = false;\n }\n this.nodesSetToReadOnly.length = 0;\n },\n\n mutabelsData(payload) {\n if (this.mutables[0] !== undefined) {\n let index;\n for (index = 0; index < this.mutables.length; index++) {\n\n payload.mutablesData[index] = {};\n payload.mutablesData[index].mutableAttrVal = this.mutables[index].value;\n payload.mutablesData[index].el = this.mutables[index].el;\n payload.mutablesData[index].mutableInnerHTML = this.mutables[index].el.innerHTML;\n payload.mutablesData[index].lcFunc = this.mutables[index].lcFunc;\n payload.mutablesData[index].id = this.mutables[index].el.id;\n }\n } else {\n payload.mutablesData = null;\n }\n \n return payload;\n }\n};\n\nexport default store;\n","import MessageBus from './MessageBus';\n\nexport default {\n directives: new MessageBus(),\n\n register(name, callback) {\n if (this.has(name)) {\n throw `ASYNergy: Directive already registered: [${name}]`;\n }\n\n this.directives.register(name, callback);\n },\n\n call(name, el, directive, mutableElem, url) {\n this.directives.call(name, el, directive, mutableElem, url);\n },\n\n has(name) {\n return this.directives.has(name);\n }\n};\n","import Action from '.';\n\nexport default class extends Action {\n constructor(modelAttrVal, params, modelVal, isCustomEvent, el, skipWatcher = false) {\n super(el, skipWatcher);\n\n this.isCustomEvent = isCustomEvent;\n this.type = 'syncInput';\n this.name = modelAttrVal;\n this.payload = {\n modelAttrVal,\n params,\n modelVal\n };\n }\n}\n","import DeferredModelAction from './deferred-model';\n\nexport default {\n deferredActions: {},\n\n addAction(name, value, el) {\n if (!this.deferredActions[name]) {\n new DeferredModelAction(name, value, el);\n this.deferredActions[name] = [];\n }\n\n this.deferredActions[name].push(value);\n this.deferredActions[name].push(el);\n },\n\n get deferredActionsData() {\n let payloadDeferred = {};\n\n if (this.deferredActions.length !== 0) {\n\n // GET NAMES AND VALUES OF DEFERRED ACTIONS\n for (let action in this.deferredActions) {\n if (this.deferredActions.hasOwnProperty(action)) {\n let actionData = this.deferredActions[action];\n\n // actionData[0], IS THE INITIAL VALUE actionData[1] IS THE ELEMENT\n // this.asynPayload[action] = actionData[1].value;\n // USE THE ACTION SUFFIX AS PAYLOAD KEY\n\n // REGULAR EXPRESSION (?<=(\\.)).+$ DOES NOT WORK WITH WEBKIT\n // DUE TO LOOKBEHIND. WE USE (?:(\\.)).+$ AND STRIP THE FIRST\n // CHARACTER, A DOT IN THIS CASE\n // const actionSuffix = /(?<=(\\.)).+$/.exec(action);\n // this.asynPayload[actionSuffix[0]] = actionData[1].value;\n const actionExecResult = /(?:(\\.)).+$/.exec(action);\n const actionSuffix = actionExecResult[0].substr(1);\n payloadDeferred[actionSuffix] = actionData[1].value;\n }\n }\n }\n return payloadDeferred;\n }\n};\n","import store from '../Store';\nimport Deferred from '../action/Deferred';\n\nexport default class Connection {\n\tconstructor(URL, updateEl, modelAttrVal, modelEl, event) {\n\t\tthis.url = URL;\n\t\tthis.updateEl = updateEl;\n\t\t// this.postKey = postKey;\n\t\tthis.modelAttrVal = modelAttrVal;\n\t\tthis.event = event;\n\t\tthis.action = this.ajax;\n\t\tthis.callback = this.completed_callback;\n\t\tthis.headers = {};\n\t\tthis.options = {};\n\t\tthis.options.headers = this.headers;\n\t\tthis.asynPayload = {};\n\t\tthis.mutablesData = [];\n\t\tthis.modelEl = modelEl;\n\t\tthis.actionType = '';\n\t\tthis.modelSyncTimeout = 1000;\n\t\tthis.isCustomEvent = undefined;\n\t\tthis.reregisterEvLis = false;\n\t}\n\t\t\n\t\t\n onMessage(message, payload) {\n message.agent.receiveMessage(message, payload);\n }\n \n handleResponse(event, agent, responseObj, fetchedResponse) {\n if (fetchedResponse !== true) {\n this.event = event;\n this.updateEl = agent.connection.updateEl;\n };\n \n store.callHook('element.updating', this.updateEl, agent, event);\n\n Object.values(responseObj.asynergyResponse).forEach(respItem => {\n if (respItem.url !== undefined) {\n location = respItem.url;\n return;\n }\n\n if ((respItem.mutableVal === null) || (typeof(respItem.mutableVal) === \"object\") &&\n (Object.keys(respItem.mutableVal).length === 0)) {\n respItem.mutableVal = \"\";\n }\n\n if ((typeof respItem.mutableVal === \"string\") && (respItem.mutableVal.search(/asyn:/)) !== -1) {\n this.reregisterEvLis = true;\n }\n\n this.updateEl.updated = 0;\n\n this.updateMutablesByID(respItem);\n \n this.updateMutablesByAttrVal(respItem);\n \n if (this.updateEl.updated === 0) {\n this.updateEl.innerHTML = respItem.mutableVal;\n }\n \n store.callHook('element.updated', this.updateEl, agent, this.event);\n \n this.syncModels(respItem);\n\n store.callHook('message.processed', this.updateEl, agent, this.event);\n\n });\n\n if (this.reregisterEvLis === true) {\n ASYNergy.reregisterEventListeners();\n };\n\n }\n\t\t\n\n // FETCH COMPLETED ACTION //\n completed_callback(msg) {\n\t\t\tstore.clearDisabledReadOnlyNodesArrays();\n\t\t\tstore.callHook('allMessages.processed', msg);\n }\n \n\n ajax(message) {\n const payload = message.payload();\n const modelVal = payload.updates[0].payload.modelVal;\n const listenerType = message.agent.el.type;\n const modelParams = payload.updates[0].payload.params;\n\n this.asynPayload = {};\n\n this.asynPayload.modelData = {'modelAttrVal': this.modelAttrVal, 'modelVal': modelVal, 'modelParams': modelParams};\n\n // CSRF\n if (store.csrf.tokenName !== undefined && store.csrf.token !== '') {\n this.asynPayload[store.csrf.tokenName] = store.csrf.token;\n }\n \n // GET MUTABLE ELEMENTS DATA\n this.asynPayload.mutablesData = [];\n this.asynPayload = store.mutabelsData(this.asynPayload);\n this.mutablesData = this.asynPayload.mutablesData;\n \n if (this.mutablesData === null) {\n console.warn(\"Missing data of any mutable element!\");\n return;\n }\n \n if (this.updateEl === null) {\n this.updateEl = this.mutablesData[0].el;\n }\n let index;\n\n var mutableAttrVal = this.mutablesData[0].mutableAttrVal;\n\n // GET TRANSMISSION ELEMENTS DATA\n this.asynPayload.transmissionElsData = {};\n this.asynPayload = store.transmissionElsData(this.asynPayload, listenerType, 'modelVal');\n\n // GET DEFERRED MODEL ACTIONS\n this.asynPayload.deferredModelData = Deferred.deferredActionsData;\n\n this.asynPayload.actionType = message.updateQueue[0].type;\n this.actionType = this.asynPayload.actionType;\n \n this.asynPayload.isCustomEvent = message.updateQueue[0].isCustomEvent;\n this.isCustomEvent = this.asynPayload.isCustomEvent;\n\t\t\t\t\n\t\t\t store.callHook('request', this.options, this.mutablesData, this.asynPayload.modelData);\n\n this.asynPayload = JSON.stringify(this.asynPayload);\n\n\n fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'Accept': 'text/html, application/xhtml+xml',\n 'X-ASYNergy': true,\n\n // SET CUSTOM HEADERS\n ...(this.options.headers),\n\n // WE'LL SET THIS EXPLICITLY TO MITIGATE POTENTIAL\n // INTERFERENCE FROM AD-BLOCKERS/ETC.\n 'Referer': window.location.href\n },\n body: this.asynPayload\n }) \n .then(response => {\n if (response.ok) {\n\n response.text().then(response => {\n\n const responseObj = JSON.parse(response);\n\n this.onMessage(message, responseObj);\n \n const fetchedResponse = true;\n \n this.handleResponse(event, message.agent, responseObj, fetchedResponse);\n\n this.callback(message);\n\n });\n }\n })\n \n .catch((error) => {\n console.error(error);\n });\n }\n \n // IF THE RESPONSE INCLUDES IDs OF MUTABLE ELEMENTS\n // UPDATE ALL APPROPRIATE ELEMENTS\n updateMutablesByID (responseObj) {\n if ((responseObj.mutableID !== \"\") && \n (responseObj.mutableID !== null)) {\n const mutableID = responseObj.mutableID;\n \n if (typeof(mutableID) === \"object\") {\n Object.values(mutableID).forEach(theID => {\n this.updateEl = document.getElementById(`${theID}`);\n\n this.updateEl.nodeName === \"INPUT\" ? this.updateEl.value = responseObj.mutableVal : this.updateEl.innerHTML = responseObj.mutableVal;\n this.updateEl.updated = 1;\n });\n } else {\n this.updateEl = document.getElementById(`${mutableID}`);\n this.updateEl.nodeName === \"INPUT\" ? this.updateEl.value = responseObj.mutableVal : this.updateEl.innerHTML = responseObj.mutableVal;\n\n this.updateEl.updated = 1;\n }\n }\n }\n\n // IF MULTIPLE MUTABLE ELEMENTS ARE SPECIFIED BY MUTABLE\n // ATTRIBUTE VALUES IN THE RESPONSE, UPDATE ALL MUTABLE\n // ELEMENTS THAT HAVE THE APPROPRIATE MUTABLE ATTRIBUTE VALUE\n updateMutablesByAttrVal(responseObj) {\n if (responseObj.mutableAttrVal !== \"\") {\n if (this.mutablesData.length > 0) {\n let attrValFragments = [];\n let attrValPrefix = '';\n this.mutablesData.forEach(mutable => {\n const compoundAttrVal = mutable.mutableAttrVal.search(/[.]/);\n if (compoundAttrVal !== -1) {\n attrValFragments = /^.+(?=(\\.))/.exec(mutable.mutableAttrVal);\n attrValPrefix = attrValFragments[0];\n }\n\n if ((mutable.mutableAttrVal === responseObj.mutableAttrVal) || \n (mutable.mutableAttrVal === attrValPrefix) || \n (Object.values(responseObj.mutableAttrVal).indexOf(mutable.mutableAttrVal) > -1)) {\n this.updateEl = mutable.el;\n\n this.updateEl.nodeName === \"INPUT\" ? this.updateEl.value = responseObj.mutableVal : this.updateEl.innerHTML = responseObj.mutableVal;\n this.updateEl.updated = 1;\n }\n \n });\n }\n }\n }\n \n // SYNCHRONIZE ANY MODEL TO THE DATA OF THE MUTABLE ELEMENT\n syncModels(responseObj) {\n if ((responseObj.syncModelID !== \"\") && \n (responseObj.syncModelID !== null)) {\n let modelID = responseObj.syncModelID;\n\n if (this.actionType === \"syncInput\") {\n // SYNCHRONIZING NEEDS A DELAY, OTHERWISE SOME INPUT MAY BE LOST\n let timer;\n const model = document.getElementById(`${modelID}`);\n const mutableEl = this.updateEl;\n const timeOut = this.modelSyncTimeout;\n\n if (model.getAttribute('listener') !== 'true') {\n model.value = mutableEl.innerHTML;\n }\n\n model.addEventListener(\"input\", function (e) {\n model.setAttribute('listener', 'true');\n clearTimeout(timer);\n timer = setTimeout(() => {\n model.value = mutableEl.innerHTML;\n model.removeAttribute('listener');\n }, timeOut);\n });\n \n } else {\n \n const modelID = responseObj.syncModelID;\n \n // IF modelID IS AN OBJECT, THEN THERE ARE MULTIPLE OBJECTS TO BE SYNCED\n if (typeof(modelID) === \"object\") {\n Object.values(modelID).forEach(theID => {\n document.getElementById(`${theID}`).value = responseObj.mutableVal;\n });\n } else {\n // THERE IS ONLY ONE MODEL TO BE SYNCED\n document.getElementById(`${modelID}`).value = responseObj.mutableVal; // ?????????????\n }\n }\n }\n }\n}\n","import { asynDirectives } from '../util/asynDirectives';\n\n/**\n * This is intended to isolate all native DOM operations. The operations that happen\n * one specific element will be instance methods, the operations you would normally\n * perform on the \"document\" (like \"document.querySelector\") will be static methods.\n */\nexport default {\n allModelElementsInside(root) {\n return Array.from(root.querySelectorAll(`[asyn\\\\:model]`));\n },\n\n getByAttributeAndValue(attribute, value) {\n return document.querySelector(`[asyn\\\\:${attribute}=\"${value}\"]`);\n },\n\n hasAttribute(el, attribute) {\n return el.hasAttribute(`asyn:${attribute}`);\n },\n\n getAttribute(el, attribute) {\n return el.getAttribute(`asyn:${attribute}`);\n },\n\n removeAttribute(el, attribute) {\n return el.removeAttribute(`asyn:${attribute}`);\n },\n\n setAttribute(el, attribute, value) {\n return el.setAttribute(`asyn:${attribute}`, value);\n },\n\n hasFocus(el) {\n return el === document.activeElement;\n },\n\n isInput(el) {\n return ['INPUT', 'TEXTAREA', 'SELECT'].includes(\n el.tagName.toUpperCase()\n );\n },\n\n isTextInput(el) {\n return (\n ['INPUT', 'TEXTAREA'].includes(el.tagName.toUpperCase()) &&\n !['checkbox', 'radio'].includes(el.type)\n );\n },\n\n valueFromInput(el, agent) {\n if (el.type === 'checkbox') {\n let modelName = asynDirectives(el).get('model').value;\n // If there is an update from asyn:model.defer in the chamber,\n // we need to pretend that is the actual data from the server.\n let modelValue = agent.deferredActions[modelName]\n ? agent.deferredActions[modelName].asynPayload.value\n // : get(agent.data, modelName);\n : el.checked;\n\n if (Array.isArray(modelValue)) {\n return this.mergeCheckboxValueIntoArray(el, modelValue);\n }\n\n if (el.checked) {\n return el.getAttribute('value') || true;\n } else {\n return false;\n }\n } else if (el.tagName === 'SELECT' && el.multiple) {\n return this.getSelectValues(el);\n }\n\n return el.value;\n },\n\n mergeCheckboxValueIntoArray(el, arrayValue) {\n if (el.checked) {\n return arrayValue.includes(el.value)\n ? arrayValue\n : arrayValue.concat(el.value);\n }\n\n return arrayValue.filter(item => item != el.value);\n },\n \n setInputValueFromModel(el, agent) {\n const modelString = asynDirectives(el).get('model').value;\n const modelValue = get(agent.data, modelString);\n\n // Don't manually set file input's values.\n if (\n el.tagName.toLowerCase() === 'input' &&\n el.type === 'file'\n )\n return;\n\n this.setInputValue(el, modelValue);\n },\n\n setInputValue(el, value) {\n if (el.type === 'radio') {\n el.checked = el.value == value;\n } else if (el.type === 'checkbox') {\n if (Array.isArray(value)) {\n // I'm purposely not using Array.includes here because it's\n // strict, and because of Numeric/String mis-casting, I\n // want the \"includes\" to be \"fuzzy\".\n let valueFound = false;\n value.forEach(val => {\n if (val == el.value) {\n valueFound = true;\n }\n });\n\n el.checked = valueFound;\n } else {\n el.checked = !!value;\n }\n } else if (el.tagName === 'SELECT') {\n this.updateSelect(el, value);\n } else {\n value = value === undefined ? '' : value;\n\n el.value = value;\n }\n },\n\n getSelectValues(el) {\n return Array.from(el.options)\n .filter(option => option.selected)\n .map(option => option.value || option.text);\n },\n\n updateSelect(el, value) {\n const arrayWrappedValue = [].concat(value).map(value => value + '');\n\n Array.from(el.options).forEach(option => {\n option.selected = arrayWrappedValue.includes(option.value);\n });\n }\n};\n","import Action from '.';\n\nexport default class extends Action {\n constructor(modelAttrVal, params, modelVal, isCustomEvent, el, skipWatcher = false) {\n super(el, skipWatcher);\n\n this.isCustomEvent = isCustomEvent;\n this.type = 'callHandler';\n this.name = modelAttrVal;\n this.payload = {\n modelAttrVal,\n params,\n modelVal\n };\n }\n\n}","import Action from '.';\n\nexport default class extends Action {\n constructor(modelAttrVal, params, modelVal, isCustomEvent, el) {\n super(el);\n\n this.isCustomEvent = isCustomEvent;\n this.type = 'syncInput';\n this.name = modelAttrVal;\n this.payload = {\n // id: this.signature,\n modelAttrVal,\n params,\n modelVal\n };\n }\n}\n","export default class {\n constructor(agent, updateQueue) {\n this.agent = agent;\n this.updateQueue = updateQueue;\n }\n\n payload() {\n return {\n // This ensures only the type & payload properties only get sent over.\n updates: this.updateQueue.map(update => ({\n type: update.type,\n payload: update.payload\n }))\n };\n }\n\n storeResponse(payload) {\n return (this.response = payload);\n }\n\n}\n","import Message from '../Message';\nimport { debounce } from '../util/debounce';\nimport store from '../Store';\nimport DeferredModelAction from '../action/deferred-model';\nimport { walkDOM } from '../util/walkDOM';\nimport MessageBus from '../MessageBus';\n\nexport default class {\n constructor(el, connection, postValue, agentID) {\n this.el = el;\n this.updateQueue = [];\n this.deferredActions = {}; // temp test\n this.messageInTransit = undefined;\n this.connection = connection;\n this.postValue = postValue;\n this.agentID = agentID;\n this.tearDownCallbacks = [];\n this.scopedListeners = new MessageBus();\n this.listeners = [];\n store.callHook('agent.initialized', this);\n }\n\n on(event, callback) {\n this.scopedListeners.register(event, callback);\n }\n\n addAction(action) {\n if (action instanceof DeferredModelAction) {\n this.deferredActions[action.name] = action;\n\n return;\n }\n\n this.updateQueue.push(action);\n\n // This debounce is here in-case two events fire at the \"same\" time:\n // For example: if you are listening for a click on element A,\n // and a \"blur\" on element B. If element B has focus, and then,\n // you click on element A, the blur event will fire before the \"click\"\n // event. This debounce captures them both in the actionsQueue and sends\n // them off at the same time.\n // Note: currently, it's set to 5ms, that might not be the right amount, we'll see.\n debounce(this.fireMessage, 5).apply(this);\n\n // Clear prefetches.\n // this.prefetchManager.clearPrefetches()\n }\n\n fireMessage() {\n Object.entries(this.deferredActions).forEach(([modelName, action]) => {\n this.updateQueue.unshift(action);\n });\n this.deferredActions = {};\n\n this.messageInTransit = new Message(this, this.updateQueue);\n\n let sendMessage = () => {\n this.connection.action(this.messageInTransit);\n\n store.callHook('message.sent', this, this.messageInTransit);\n\n this.updateQueue = [];\n };\n sendMessage();\n }\n\n receiveMessage(message, payload) {\n message.storeResponse(payload);\n\n // This bit of logic ensures that if actions were queued while a request was\n // out to the server, they are sent when the request comes back.\n if (this.updateQueue.length > 0) {\n this.fireMessage();\n }\n }\n \n doReplayResponse(event, agent, response) {\n this.connection.handleResponse(event, agent, response);\n }\n\n walk(callback, callbackWhenNewComponentIsEncountered = el => { }) {\n walkDOM(this.el, el => {\n // Skip the root component element.\n if (el.isSameNode(this.el)) {\n callback(el);\n return;\n }\n\n if (callback(el) === false) {\n return false;\n }\n });\n }\n\n callAfterModelDebounce(callback) {\n // This is to protect against the following scenario:\n // A user is typing into a debounced input, and hits the enter key.\n // If the enter key submits a form or something, the submission\n // will happen BEFORE the model input finishes syncing because\n // of the debounce. This makes sure to clear anything in the debounce queue.\n\n if (this.modelDebounceCallbacks) {\n this.modelDebounceCallbacks.forEach(callbackRegister => {\n callbackRegister.callback();\n callbackRegister.callback = () => { };\n });\n }\n\n callback();\n }\n\n addListenerForTeardown(teardownCallback) {\n this.tearDownCallbacks.push(teardownCallback);\n }\n\n tearDown() {\n this.tearDownCallbacks.forEach(callback => callback());\n }\n}\n","import { debounce } from './util/debounce';\nimport { kebabCase } from './util';\nimport { asynDirectives } from './util/asynDirectives';\nimport Connection from './connection/index';\nimport DOM from './dom/dom';\nimport HandlerAction from './action/handler';\nimport store from './Store';\nimport ModelAction from './action/model';\nimport DeferredModelAction from './action/deferred-model';\nimport Deferred from './action/Deferred';\nimport Agent from './agent/index';\n\nexport default {\n\n initialize(el, url, reregisterEvl) {\n let isAgent = false;\n asynDirectives(el).all().forEach(directive => {\n let test;\n let lcFunc;\n switch (directive.type) {\n\n case 'model': {\n if (!directive.value) {\n console.warn('ASYNergy: [asyn:model] is missing a value.', el);\n\n break;\n }\n\n // CHECK FOR COMPUND MODEL ATTRIBUTE VALUE, USE THE PREFIX\n // TO CHECK IF THERE IS A CORRESPONDING MUTABLE ELEMENT\n let directiveVal = '';\n let compoundAttrVal = directive.value.search(/[.]/);\n if (compoundAttrVal !== -1) {\n const dirValPrefix = /^.+(?=(\\.))/.exec(directive.value);\n directiveVal = dirValPrefix[0];\n } else {\n directiveVal = directive.value;\n }\n\t\t\t\t\t\t\t\n // GET RID OF THE MODEL PARAMETERS\n compoundAttrVal = directive.value.search(/[\\(]/);\n \n if (compoundAttrVal !== -1) {\n const dirVal = /^.+(?=(\\())/.exec(directive.value);\n directiveVal = dirVal[0];\n }\n\n let mutableElem = document.querySelector(`[asyn\\\\:mutable=${directiveVal}]`);\n\n // GET ATTRIBUTE\n let attr = this.modelAttr(directive.fullName, directiveVal); \n let modelElem = document.querySelector(`[${attr}]`);\n\n if (mutableElem !== null) {\n this.attachModelListener(el, directive, mutableElem, url, modelElem, directiveVal);\n } else {\n mutableElem = document.querySelector(`[asyn\\\\:mutable^=${directiveVal}\\\\.]`);\n if (mutableElem !== null) {\n this.attachModelListener(el, directive, mutableElem, url, modelElem, directiveVal);\n } else {\n console.warn(\n 'ASYNergy: [asyn:model] is missing a corresponding [asyn:mutable] element.',\n el);\n }\n }\n\n test = 'model';\n isAgent = true;\n break;\n }\n\n case 'mutable':\n if (reregisterEvl !== true) {\n\n if ((typeof directive.el.innerHTML === \"string\") && (directive.el.innerHTML.search(/asyn:/)) !== -1) {\n store.mutableIncludesModel = true;\n }\n\n lcFunc = directive.lcFunction;\n store.mutables.push(directive);\n\n test = 'mutable';\n isAgent = false;\n }\n break;\n\n case 'transmit':\n // CHECK IF ELEMENT IS A MUTABLE ELEMENT, NEEDED TO\n // REPLACE A MODEL CHECKBOX VALUE WITH THE MODEL INPUT VALUE\n directive.el.mutable = el.getAttributeNames().indexOf('asyn:mutable');\n\n store.transmissionEls.push(directive.el);\n\n test = 'transmit';\n isAgent = true;\n break;\n\n case 'csrf':\n store.csrf.tokenName = directive.value;\n store.csrf.token = el.value;\n\n test = 'csrf';\n isAgent = false;\n break;\n\n default:\n const params = directive.params;\n let handler = directive.value;\n if (params.length !== 0) {\n handler = directive.handler;\n }\n\n let mutableElem = document.querySelector(`[asyn\\\\:mutable=${handler}]`);\n if (mutableElem === null) {\n mutableElem = document.querySelector(`[asyn\\\\:mutable^=${handler}\\\\.]`);\n }\n\n // GET ATTRIBUTE\n let attr = this.modelAttr(directive.fullName, directive.value);\n let modelElem = document.querySelector(`[${attr}]`);\n\n if (store.directives.has(directive.type)) {\n store.directives.call(\n directive.type,\n el,\n directive,\n mutableElem,\n url\n );\n }\n\n this.attachDomListener(el, directive, mutableElem, url, modelElem);\n\n test = 'default';\n isAgent = true;\n break;\n }\n\n });\n\n if (isAgent === true) {\n let index = store.agents.length - 1;\n let theAgent = store.agents[index];\n let eventType = theAgent.connection.event;\n store.callHook('element.initialized', el, theAgent, eventType);\n }\n isAgent = false;\n },\n\n attachModelListener(el, directive, mutableEl, url, modelEl, modelAttrVal) {\n const isLazy = directive.modifiers.includes('lazy');\n\n //---------------------------------------\n // Slider control directive to update the current value in real time\n // without sending requests while the user moves the slider.\n // Request is sent when the user releases the slider.\n // Example: asyn:model.lazy=\"slider('displayValue','%')\"\n if (el.type === \"range\" && isLazy && directive.value.match(/\\bdisplayValue\\b/) !== null) {\n let sliderElement = el;\n let sliderValueElement = mutableEl;\n let valUnit = '';\n\n let directiveValues = directive.value;\n\n // Regular expression pattern to match strings enclosed in single quotes\n let regex = /'(.*?)'/g;\n\n let matches = [];\n let match;\n while (match = regex.exec(directiveValues)) {\n matches.push(match[1]);\n }\n if (matches[1] !== undefined) {\n valUnit = matches[1];\n }\n\n sliderElement.oninput = () => {\n sliderValueElement.innerHTML = sliderElement.value + valUnit;\n }\n };\n //---------------------------------------\n\n const debounceIf = (condition, callback, time) =>\n condition ? directive.modelSyncDebounce(callback, time) : callback;\n\n const hasDebounceModifier = directive.modifiers.includes('debounce');\n\n store.callHook('interceptAsynModelAttachListener', directive, el, mutableEl);\n\n let event = el.tagName.toLowerCase() === 'select'\n || ['checkbox', 'radio'].includes(el.type)\n || directive.modifiers.includes('lazy') ? 'change' : 'input';\n \n if (el.tagName.toLowerCase() === 'input' && directive.modifiers.includes('blur')) {\n event = 'blur';\n }\n\n let model = modelAttrVal;\n\n // TODO check for trailing slashes\n const handlerURL = url + '/' + model;\n const initConnection = new Connection(handlerURL, mutableEl, model, modelEl, event);\n\n const connect = {\n sendMessage: function () {\n // INPUT FIELD VALUE\n let postValue = el.value;\n initConnection.action(postValue);\n }\n };\n\n let agentID = store.agents.length + 1;\n let agent = store.addAgent(new Agent(el, initConnection, el.value, agentID));\n\n if (directive.modifiers.includes('defer')) {\n Deferred.addAction(directive.value, el.value, el);\n }\n\n // If it's a text input and not .lazy, debounce, otherwise fire immediately.\n let handler = debounceIf(hasDebounceModifier || (DOM.isTextInput(el) && !isLazy), e => {\n let model = directive.value;\n let params = directive.params;\n let el = e.target;\n\n const isCustomEvent = e instanceof CustomEvent;\n\n isCustomEvent ? directive.emitEvent = true : directive.emitEvent = false;\n\n let modelVal = e instanceof CustomEvent\n // We have to check for typeof e.detail here for IE 11.\n && typeof e.detail != 'undefined'\n && typeof window.document.documentMode == 'undefined'\n // With autofill in Safari, Safari triggers a custom event and assigns\n // the value to e.target.value, so we need to check for that value as well.\n ? e.detail || e.target.value\n : DOM.valueFromInput(el, agent);\n\n if (directive.modifiers.includes('defer')) {\n agent.addAction(new DeferredModelAction(model, params, modelVal, isCustomEvent, el));\n } else {\n agent.addAction(new ModelAction(model, params, modelVal, isCustomEvent, el));\n }\n }, directive.durationOr(150));\n\n store.addEmitEvent(el, directive.params, directive.handler, handler);\n\n el.addEventListener(event, handler);\n\n agent.addListenerForTeardown(() => {\n el.removeEventListener(event, handler);\n });\n\n // Taken from: https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser\n let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\n // Safari is weird and doesn't properly fire input events when\n // a user \"autofills\" a asyn:model(.lazy) field. So we are\n // firing them manually for assurance.\n isSafari && el.addEventListener('animationstart', e => {\n if (e.animationName !== 'asynergyAutofill') {\n return;\n }\n\n e.target.dispatchEvent(new Event('change', { bubbles: true }));\n e.target.dispatchEvent(new Event('input', { bubbles: true }));\n });\n },\n\n attachDomListener(el, directive, mutableEl, url, modelEl) {\n switch (directive.type) {\n case 'keydown':\n case 'keyup':\n\n this.attachListener(el, directive, e => {\n // Detect system modifier key combinations if specified.\n const systemKeyModifiers = [\n 'ctrl',\n 'shift',\n 'alt',\n 'meta',\n 'cmd',\n 'super'\n ];\n const selectedSystemKeyModifiers = systemKeyModifiers.filter(\n key => directive.modifiers.includes(key)\n );\n\n if (selectedSystemKeyModifiers.length > 0) {\n const selectedButNotPressedKeyModifiers = selectedSystemKeyModifiers.filter(\n key => {\n // Alias \"cmd\" and \"super\" to \"meta\"\n if (key === 'cmd' || key === 'super') {\n key = 'meta';\n }\n return !e[`${key}Key`];\n }\n );\n\n if (selectedButNotPressedKeyModifiers.length > 0) {\n return false;\n }\n }\n\n // Handle spacebar\n if (e.keyCode === 32 || (e.key === ' ' || e.key === 'Spacebar')) {\n return directive.modifiers.includes('space');\n }\n\n // Strip 'debounce' modifier and time modifiers from modifiers list\n let modifiers = directive.modifiers.filter(modifier =>\n (\n !modifier.match(/^debounce$/) &&\n !modifier.match(/^[0-9]+m?s$/)\n )\n );\n\n // Only handle listener if no, or matching key modifiers are passed.\n // It's important to check that e.key exists - OnePassword's extension\n // does weird things.\n return Boolean(modifiers.length ===\n 0 || (e.key && modifiers.includes(kebabCase(e.key))));\n }, mutableEl, url, modelEl);\n\n break;\n case 'click':\n\n this.attachListener(el, directive, e => {\n // We only care about elements that have the .self modifier on them.\n if (!directive.modifiers.includes('self')) {\n return;\n }\n\n // This ensures a listener is only run if the event originated\n // on the elemenet that registered it (not children).\n // This is useful for things like modal back-drop listeners.\n return el.isSameNode(e.target);\n }, mutableEl, url, modelEl);\n\n break;\n default:\n this.attachListener(el, directive, e => el === e.target, mutableEl, url, modelEl);\n break;\n \t\t\t}\n },\n\n attachListener(el, directive, callback, mutableEl, url, modelEl) {\n const event = directive.type;\n const model = directive.handler;\n\n // TODO check for trailing slashes\n const handlerURL = url + '/' + model;\n\n const initConnection = new Connection(handlerURL, mutableEl, model, modelEl, event);\n\n let postValue = () => {\n return el.value !== undefined ? el.value : el.innerText;\n };\n\n let agentID = store.agents.length + 1;\n let agent = store.addAgent(new Agent(el, initConnection, postValue(), agentID));\n\n const handler = e => {\n if (callback && callback(e) === false) {\n return;\n }\n \n const isCustomEvent = e instanceof CustomEvent;\n\n agent.callAfterModelDebounce(() => {\n const el = e.target;\n\n directive.setEventContext(e);\n\n // This is outside the conditional below so \"asyn:click.prevent\"\n // without a value still prevents default.\n this.preventAndStop(e, directive.modifiers);\n const handler = directive.handler;\n let params = directive.params;\n let modelVal = directive.modelValue;\n\n if (isCustomEvent) {\n directive.emitEvent = true;\n if (e.detail !== undefined) {\n modelVal = e.detail;\n }\n } else {\n directive.emitEvent = false;\n }\n\n if (\n params.length === 0 &&\n isCustomEvent &&\n e.detail\n ) {\n params.push(e.detail);\n }\n\n if (directive.value) {\n agent.addAction(new HandlerAction(handler, params, modelVal, isCustomEvent, el));\n }\n\n });\n };\n\n const debounceIf = (condition, callback, time) =>\n condition ? debounce(callback, time) : callback;\n\n const hasDebounceModifier = directive.modifiers.includes('debounce');\n const debouncedHandler = debounceIf(\n hasDebounceModifier,\n handler,\n directive.durationOr(150)\n );\n\n store.addEmitEvent(el, directive.params, directive.handler, handler);\n\n el.addEventListener(event, debouncedHandler);\n\n agent.addListenerForTeardown(() => {\n el.removeEventListener(event, debouncedHandler);\n });\n },\n \n preventAndStop(event, modifiers) {\n modifiers.includes('prevent') && event.preventDefault();\n modifiers.includes('stop') && event.stopPropagation();\n },\n\n modelAttr(fullName, attrVal) {\n let escFullName = this.escapedStr(fullName);\n let escAttrVal = this.escapedStr(attrVal);\n let attr = escFullName + \"=\" + \"\\\"\" + escAttrVal + \"\\\"\";\n return attr;\n },\n \n escapedStr(str) {\n let escapedStr = str;\n escapedStr = escapedStr.replace(/\\:|\\.|\"|'/gi, function (x) {\n return \"\\\\\" + x;\n });\n return escapedStr;\n }\n};\n","\nexport * from './debounce';\nexport * from './asynDirectives';\nexport * from './walkDOM';\n\nexport function kebabCase(subject) {\n return subject.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[_\\s]/, '-').toLowerCase();\n}\n\nexport function tap(output, callback) {\n callback(output);\n\n return output;\n}\n","import store from '../Store';\nimport { asynDirectives } from '../util/asynDirectives';\n\nlet cleanupStackByAgentId = {};\n\nexport default function () {\n store.registerHook('element.initialized', (el, agent) => {\n let directives = asynDirectives(el);\n\n if (directives.missing('submit')) return;\n\n // Set a forms \"disabled\" state on inputs and buttons.\n // ASYNergy will clean it all up automatically submitting the form.\n el.addEventListener('submit', () => {\n cleanupStackByAgentId[agent.agentID] = [];\n\n agent.walk(node => {\n if (!el.contains(node)) return;\n\n if (node.hasAttribute('asyn:ignore')) return false;\n\n if (\n //
- revIgniter User Guide Version 2.4.4
+ revIgniter User Guide Version 2.4.5
@@ -94,6 +94,7 @@
Change Log
+
Version 2.4.5
+
Release Date: 2024-11-13
+
+
+ - Fixed: Percentage signs in JSON data are now preserved and no longer produce zero (“<NUL>”) characters while filtering the data in the _rigSanitizeGlobals function of the Input library.
+ - Changed: The _rigAsynPayload() function of the ASYNergy library now refrains from executing the XSS filter if the global filtering is set to true.
+ - Changed: The stylesheets of the tutorial examples have been revised.
+ - Added: the latest version of the ASYNergy framework.
+
+
+
Version 2.4.4
-
Release Date: 2024-10-013
+
Release Date: 2024-10-13
- Fixed: the mess in the User Guide caused by unintentional execution of 'find and replace' in preparation of the previous version.
diff --git a/userGuide/database/active_record.html b/userGuide/database/active_record.html
index 84bfa6a3..50301a4b 100644
--- a/userGuide/database/active_record.html
+++ b/userGuide/database/active_record.html
@@ -34,7 +34,7 @@
- revIgniter User Guide Version 2.4.4
+ revIgniter User Guide Version 2.4.5
@@ -91,6 +91,7 @@
Downloading revIgniter
diff --git a/userGuide/installation/upgrade_245.html b/userGuide/installation/upgrade_245.html
new file mode 100644
index 00000000..743e48d0
--- /dev/null
+++ b/userGuide/installation/upgrade_245.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+
Upgrading from 2.4.4 to 2.4.5 : revIgniter User Guide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table of Contents
+
Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Upgrading from 2.4.4 to 2.4.5
+
+
+
Before performing an update you should take your site offline by replacing the index.lc file with a static one.
+
+
+
+
Step 1: Update your revIgniter files
+
+
+
Replace these files in your "system/libraries" folder with the new versions:
+
+
+ - ASYNergy.livecodescript
+ - Input.livecodescript
+
+
+
+
Replace these files in your "assets/js/ASYNergy" folder with the new versions:
+
+
+ - asynergy.js
+ - asynergy.js.map
+
+
+
+
Replace this file in your "system/revigniter" folder with the new version:
+
+
+
+
+
Step 2: Update your user guide
+
Please replace your local copy of the user guide with the new version.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/userGuide/installation/upgrading.html b/userGuide/installation/upgrading.html
index 69fc4194..6fbaf915 100644
--- a/userGuide/installation/upgrading.html
+++ b/userGuide/installation/upgrading.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
@@ -93,6 +93,7 @@
Upgrading From a Previous Version
Please read the upgrade notes corresponding to the version you are upgrading from.
diff --git a/userGuide/libraries/authentication/authenticationConfig.html b/userGuide/libraries/authentication/authenticationConfig.html
index fed1da98..6536b41e 100644
--- a/userGuide/libraries/authentication/authenticationConfig.html
+++ b/userGuide/libraries/authentication/authenticationConfig.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationEmail.html b/userGuide/libraries/authentication/authenticationEmail.html
index 3366f326..75068fc2 100644
--- a/userGuide/libraries/authentication/authenticationEmail.html
+++ b/userGuide/libraries/authentication/authenticationEmail.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationExample.html b/userGuide/libraries/authentication/authenticationExample.html
index 9847e1da..8bcbb98c 100644
--- a/userGuide/libraries/authentication/authenticationExample.html
+++ b/userGuide/libraries/authentication/authenticationExample.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationHooks.html b/userGuide/libraries/authentication/authenticationHooks.html
index 78858d8b..232674df 100644
--- a/userGuide/libraries/authentication/authenticationHooks.html
+++ b/userGuide/libraries/authentication/authenticationHooks.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationOTP.html b/userGuide/libraries/authentication/authenticationOTP.html
index a49405b7..a75454d2 100644
--- a/userGuide/libraries/authentication/authenticationOTP.html
+++ b/userGuide/libraries/authentication/authenticationOTP.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationOverview.html b/userGuide/libraries/authentication/authenticationOverview.html
index 244dc69e..29833803 100644
--- a/userGuide/libraries/authentication/authenticationOverview.html
+++ b/userGuide/libraries/authentication/authenticationOverview.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationRef.html b/userGuide/libraries/authentication/authenticationRef.html
index b0956aea..b4eed73d 100644
--- a/userGuide/libraries/authentication/authenticationRef.html
+++ b/userGuide/libraries/authentication/authenticationRef.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
diff --git a/userGuide/libraries/authentication/authenticationTables.html b/userGuide/libraries/authentication/authenticationTables.html
index ef1e362d..0cab3f6b 100644
--- a/userGuide/libraries/authentication/authenticationTables.html
+++ b/userGuide/libraries/authentication/authenticationTables.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
@@ -393,7 +393,7 @@
Setting preferences in a config file
If you prefer not to set preferences using the above method, you can instead put them into a config file.
Simply create a new file called pagination.lc, add an
array in that file, call the rigRunInitialPaginationConfig handler at the bottom of your file with the
-array as parameter. Then save the file in: application/config/pagination.lc and it will be used automatically.
+array as parameter. Then save the file in:
application/config/ and it will be used automatically.
Example:
diff --git a/userGuide/libraries/sessions.html b/userGuide/libraries/sessions.html
index 73c04248..f35ff8c7 100644
--- a/userGuide/libraries/sessions.html
+++ b/userGuide/libraries/sessions.html
@@ -34,7 +34,7 @@
-
revIgniter User Guide Version 2.4.4
+
revIgniter User Guide Version 2.4.5
@@ -293,7 +293,6 @@
The Stylesheet
#filterList {
flex-direction: column;
- width: 400px;
padding: 1em 1em 4em;
}
@@ -325,6 +324,14 @@ The Stylesheet
#noMatch {
color: rgb(109, 189, 0);
}
+
+@media only screen and (min-width: 420px) {
+ #filterList {
+ width: 400px;
+ }
+}
+
+
diff --git a/userGuide/tutorials/asynergy_form_validation.html b/userGuide/tutorials/asynergy_form_validation.html
index dffe9ddf..57d076ad 100644
--- a/userGuide/tutorials/asynergy_form_validation.html
+++ b/userGuide/tutorials/asynergy_form_validation.html
@@ -34,7 +34,7 @@