@gullerya/i18n
Advanced tools
Comparing version 0.0.1 to 0.1.0
@@ -1,1 +0,1 @@ | ||
import{Observable}from"./object-observer.min.js";export const CHANGE_EVENT_NAME_PROVIDER="changeEventName";export const DEFAULT_TIE_TARGET_PROVIDER="defaultTieTarget";export const ties=new Ties;export const addRootDocument=e=>{if(!e||Node.DOCUMENT_NODE!==e.nodeType&&Node.DOCUMENT_FRAGMENT_NODE!==e.nodeType)throw new Error("invalid argument, NULL or not one of: DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE");if(roots.has(e))return console.warn("any root document may be added only once"),!1;initDocumentObserver(e),console.debug("DT: scanning the document for a views...");const t=performance.now();return addTree(e),console.debug("DT: ... scanning the "+e+" for a views DONE (took "+Math.floor(100*(performance.now()-t))/100+"ms"),roots.add(e),!0};export const removeRootDocument=e=>roots.has(e)?(discard(e),roots.delete(e),!0):(console.warn("no root document "+e+" known"),!1);console.info("DT: starting initialization...");const initStartTime=performance.now(),initParams={},PRIVATE_MODEL_SYMBOL=Symbol("private-tie-model-key"),ELEMENT_PROCESSED_SYMBOL=Symbol("element-processed"),roots=new WeakSet,views={},viewsParams=new WeakMap,PARAM_SPLITTER=/\s*=>\s*/,MULTI_PARAMS_SPLITTER=/\s*[,;]\s*/;Array.from(new URL(import.meta.url).searchParams).forEach(e=>{initParams[e[0]]=e[1]}),Object.keys(initParams).length?(console.info("DT: init params are as following"),console.dir(initParams)):console.info("DT: no init params found");class Tie{constructor(e){this[PRIVATE_MODEL_SYMBOL]=null,Object.defineProperty(this,"name",{value:e}),Object.seal(this)}get model(){return this[PRIVATE_MODEL_SYMBOL]}set model(e){const t=this[PRIVATE_MODEL_SYMBOL];if(e!==t){const n=ensureObservable(e);n&&n.observe(this.processDataChanges.bind(this)),this[PRIVATE_MODEL_SYMBOL]=n,this.processDataChanges([{path:[]}]),t&&t.revoke()}}processDataChanges(e){const t=this.name,n=views[t],o=Object.keys(n),r={};let a,i,s,l,c,d,f,h,m,u="";if(o.length)for(a=0,i=e.length;a<i;a++){if(l=(s=e[a]).object,c=s.path,!Array.isArray(l)||"insert"!==s.type&&"delete"!==s.type||isNaN(c[c.length-1])){const e=c.length;if(e>1){for(let t=0;t<e-1;t++)u+=c[t]+".";u+=c[e-1]}else 1===e&&(u=c[0])}else{if(r[u=c.slice(0,-1).join(".")]===l)continue;r[u]=l,s=null}for(d=o.length;d;)if(0===(f=o[--d]).indexOf(u))for(m=(h=n[f]).length;m;)update(h[--m],u,s)}}}function Ties(){const e={},t=/^[a-zA-Z0-9]+$/;function n(e){if(!e||"string"!=typeof e)throw new Error("tie name MUST be a non empty string");if(!t.test(e))throw new Error("tie name MUST contain alphanumeric characters ONLY (use "+t+' to check yourself); "'+e+'" not fits');return!0}this.get=function(t){return n(t),e[t]},this.create=function(t,o){if(e[t])throw new Error("tie ("+t+") is already present and MAY NOT be re-created");n(t),ensureObservable(o);const r=new Tie(t);return e[t]=r,t in views||(views[t]={}),r.model=o,r},this.remove=function(t){let o;if("object"==typeof t&&t.name)o=t.name;else{if("string"!=typeof t||!n(t))throw new Error("tie to remove MUST either be a valid tie name or tie self");o=t}delete views[o];const r=e[o];r&&(r[PRIVATE_MODEL_SYMBOL]&&r[PRIVATE_MODEL_SYMBOL].revoke(),delete e[o])},Object.freeze(this)}function ensureObservable(e){if(void 0===e||null===e)return e;if("object"!=typeof e)throw new Error(e+" is not an object");if(Observable.isObservable(e))return e;if(Observable){if(e.observe||e.unobserve||e.revoke)throw new Error(e+" is not of type Observable and can not be transformed into Observable (some of the properties already occupied?)");return Observable.from(e)}throw new Error(e+" is not of type Observable and no embedded Observable implementation found")}function getPath(e,t){if(!e)return null;const n=t.length;let o,r=0;for(;r<n;r++)if(void 0===(e=e[o=t[r]]))return null;return e}function setPath(e,t,n){if(!e)return;const o=t.length;let r,a=0;for(;a<o-1;a++)e[r=t[a]]&&"object"==typeof e[r]?e=e[r]:(e={})[r]=e;e[t[a]]=n}function changeListener(e){const t=e.currentTarget,n=getDefaultTargetProperty(t),o=viewsParams.get(t);let r,a,i,s;for(i=o.length;i;)(r=o[--i]).targetProperty===n&&((a=ties.get(r.tieName))?(s=t[n],setPath(a[PRIVATE_MODEL_SYMBOL],r.path,s)):console.warn('no Tie identified by "'+r.tieName+'" found'))}function addChangeListenerIfRelevant(e){const t=obtainChangeEventName(e);t&&e.addEventListener(t,changeListener)}function delChangeListener(e){const t=obtainChangeEventName(e);t&&e.removeEventListener(t,changeListener)}function obtainChangeEventName(e){let t=e[CHANGE_EVENT_NAME_PROVIDER];return t||"INPUT"!==e.nodeName&&"SELECT"!==e.nodeName&&"TEXTAREA"!==e.nodeName||(t="change"),t}function add(e){if(e[ELEMENT_PROCESSED_SYMBOL]=!0,e.matches(":defined"))processAddedElement(e);else{let t="";if(e.localName.indexOf("-")>0)t=e.localName;else{const n=/.*is\s*=\s*"([^"]+)"\s*.*/.exec(e.outerHTML);n&&n.length>1&&(t=n[1])}t?customElements.whenDefined(t).then(()=>processAddedElement(e)):(console.warn("failed to determine yet undefined custom element name of "+e+" to wait for definition; processing as usual"),processAddedElement(e))}}function processAddedElement(e){const t=extractTieParams(e);let n,o,r,a,i,s=0;for(i=t.length;s<i;s++)n=t[s].tieName,o=t[s].rawPath,(a=(r=views[n]||(views[n]={}))[o]||(r[o]=[])).indexOf(e)<0&&(a.push(e),viewsParams.set(e,t),update(e),addChangeListenerIfRelevant(e));e.shadowRoot&&addRootDocument(e.shadowRoot)}function extractTieParams(e){let t,n=[];return e&&e.dataset&&(t=e.dataset.tie)&&(n=parseTiePropertiesParam(t,e)),n}function parseTiePropertyParam(e,t){const n=e.split(PARAM_SPLITTER);1===n.length&&n.push(getDefaultTargetProperty(t));const o=n[0].split(":");if(!o.length||o.length>2||!o[0])throw new Error('invalid tie value; found: "'+e+'"; expected (example): "orders:0.address.street => textContent"');const r=o.length>1?o[1]:"";return{tieName:o[0],rawPath:r,path:r.split(".").filter(e=>e),targetProperty:n[1]}}function parseTiePropertiesParam(e,t){if(!e||"string"!=typeof e)throw new Error('invalid tie value; found: "'+e+'"; expected (example): "orders:0.address.street => textContent, orders:0.address.apt => title"');const n=[],o=e.split(MULTI_PARAMS_SPLITTER),r=o.length;let a=0;for(;a<r;a++)if(o[a])try{n.push(parseTiePropertyParam(o[a],t))}catch(e){console.error("failed to parse one of a multi param parts",e)}return n}function getDefaultTargetProperty(e){let t=e[DEFAULT_TIE_TARGET_PROVIDER];if(!t){const n=e.nodeName;t="INPUT"===n&&"checkbox"===e.type?"checked":"INPUT"===n||"SELECT"===n||"TEXTAREA"===n?"value":"IMG"===n||"IFRAME"===n||"SOURCE"===n?"src":"textContent"}return t}function update(e,t,n){const o=viewsParams.get(e);let r,a,i,s,l,c,d=0;for(l=o.length;d<l;d++)r=o[d],void 0!==(a=ties.get(r.tieName))&&(t&&0!==r.rawPath.indexOf(t)||(i=a.model,void 0===(s=n&&t===r.rawPath&&void 0!==n.value?n.value:getPath(i,r.path))&&(s=""),"value"===(c=r.targetProperty)&&"INPUT"===e.nodeName&&"checkbox"===e.type?e.checked=s:"href"===c?e.href.baseVal=s:e[c]=s))}function addTree(e){let t;e.childElementCount?(t=Array.from(e.querySelectorAll("*"))).unshift(e):t=[e];let n,o=t.length;for(;o;)try{n=t[--o],Node.ELEMENT_NODE!==n.nodeType||n[ELEMENT_PROCESSED_SYMBOL]||add(n)}catch(e){console.error("failed to process/add element",e)}}function discard(e){if(e.querySelectorAll){const t=e.querySelectorAll("*"),n=t.length;let o,r,a,i,s,l,c,d=0;for(;d<=n;d++){if(o=d<n?t[d]:e,r=viewsParams.get(o)){for(l=0,c=r.length;l<c;l++)a=r[l],views[a.tieName]&&(s=(i=views[a.tieName][a.rawPath]).indexOf(o))>=0&&(i.splice(s,1),delChangeListener(o));viewsParams.delete(o)}o.shadowRoot&&removeRootDocument(o.shadowRoot)}}}function move(e,t,n){let o,r,a,i,s,l,c;if(t){for(r=0,a=(o=viewsParams.get(e)).length;r<a;r++)i=o[r],views[i.tieName]&&(l=views[i.tieName][i.rawPath])&&(c=l.indexOf(e))>=0&&l.splice(c,1);delChangeListener(e)}if(n){for(o=parseTiePropertiesParam(n,e),viewsParams.set(e,o),r=0,a=o.length;r<a;r++)i=o[r],(l=(s=views[i.tieName]||(views[i.tieName]={}))[i.rawPath]||(s[i.rawPath]=[])).indexOf(e)<0&&(l.push(e),update(e,i.rawPath));addChangeListenerIfRelevant(e)}}function processDomChanges(e){const t=e.length;let n,o,r,a,i,s,l,c,d,f=0;for(;f<t;f++)if("attributes"===(a=(r=e[f]).type))i=r.attributeName,move(n=r.target,r.oldValue,n.getAttribute(i));else if("childList"===a){for(l=(s=r.addedNodes).length;l;)o=(n=s[--l]).nodeType,Node.ELEMENT_NODE===o&&addTree(n);for(d=(c=r.removedNodes).length;d;)o=(n=c[--d]).nodeType,Node.ELEMENT_NODE===o&&discard(n)}}function initDocumentObserver(e){console.debug("DT: initializing DOM observer on document"),new MutationObserver(processDomChanges).observe(e,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0,attributeFilter:["data-tie"],characterData:!1,characterDataOldValue:!1})}addRootDocument(document),console.info("DT: ... initialization DONE (took "+Math.floor(100*(performance.now()-initStartTime))/100+"ms)"); | ||
import{ensureObservable,DEFAULT_TIE_TARGET_PROVIDER,getTargetProperty,extractViewParams,CHANGE_EVENT_NAME_PROVIDER,addChangeListener,delChangeListener,getPath,setPath,setViewProperty,callViewFunction}from"./dt-utils.min.js";export{DEFAULT_TIE_TARGET_PROVIDER,CHANGE_EVENT_NAME_PROVIDER};export const ties=new Ties;export const addRootDocument=e=>{if(!e||Node.DOCUMENT_NODE!==e.nodeType&&Node.DOCUMENT_FRAGMENT_NODE!==e.nodeType)throw new Error("invalid argument, NULL or not one of: DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE");return roots.has(e)?(console.warn("any root document may be added only once"),!1):(initDocumentObserver(e),addTree(e),roots.add(e),!0)};export const removeRootDocument=e=>roots.has(e)?(dropTree(e),roots.delete(e),!0):(console.warn("no root document "+e+" known"),!1);console.info("DT: starting initialization...");const initStartTime=performance.now(),ELEMENT_PROCESSED_SYMBOL=Symbol("element-processed"),VIEW_PARAMS_KEY=Symbol("view.params.key"),roots=new WeakSet,views={};class Tie{constructor(e,t){this.key=e,this.model=ensureObservable(t),this.ownModel=this.model!==t,this.observer=Tie.processDataChanges.bind(this),this.model.observe(this.observer),Object.freeze(this)}static processDataChanges(e){const t=this.key,o=this.model,n=views[t],i=Object.keys(n),r={};let s,a,l,c,d,h,f,E,u,m="";if(i.length)for(s=0,a=e.length;s<a;s++){if(c=(l=e[s]).object,d=l.path,!Array.isArray(c)||"insert"!==l.type&&"delete"!==l.type||isNaN(d[d.length-1])){const e=d.length;if(e>1){for(let t=0;t<e-1;t++)m+=d[t]+".";m+=d[e-1]}else 1===e&&(m=d[0])}else{if(r[m=d.slice(0,-1).join(".")]===c)continue;r[m]=c,l=null}for(h=i.length;h;)if(0===(f=i[--h]).indexOf(m))for(u=(E=n[f]).length;u;)updateFromTie(E[--u],m,l,t,o)}}}function Ties(){const e={},t=/^[a-zA-Z0-9]+$/;this.get=function(t){const o=e[t];return o?o.model:void 0},this.create=function(o,n){if(e[o])throw new Error('tie "'+o+'" already exists');if(function(e){if(!e||"string"!=typeof e)throw new Error("tie key MUST be a non empty string");if(!t.test(e))throw new Error("tie key MUST match "+t+'; "'+e+'" is not')}(o),null===n)throw new Error("initial model, when provided, MUST NOT be null");o in views||(views[o]={});const i=new Tie(o,n);return e[o]=i,e[o].observer([{path:[]}]),i.model},this.remove=function(t){let o;if("object"==typeof t)o=Object.keys(e).find(o=>e[o].model===t);else{if("string"!=typeof t)throw new Error("tie to remove MUST either be a valid tie key or tie self");o=t}delete views[o];const n=e[o];n&&(n.model&&n.ownModel&&n.model.revoke(),delete e[o])},Object.freeze(this)}function changeListener(e){const t=e.currentTarget,o=getTargetProperty(t),n=t[VIEW_PARAMS_KEY];let i,r,s,a=n.length;for(;a;)(i=n[--a]).targetProperty===o&&(r=ties.get(i.tieKey))&&(s=t[o],setPath(r,i.path,s))}function add(e){if(e[ELEMENT_PROCESSED_SYMBOL]=!0,e.matches(":defined")){const t=extractViewParams(e);let o;if(t&&(o=t.length)){for(e[VIEW_PARAMS_KEY]=t;o;){const n=t[--o];if(n.isFunctional)n.fParams.forEach(t=>{const o=t.tieKey,n=t.rawPath,i=views[o]||(views[o]={}),r=i[n]||(i[n]=[]);r.indexOf(e)<0&&r.push(e)});else{const t=n.tieKey,o=n.rawPath,i=views[t]||(views[t]={}),r=i[o]||(i[o]=[]);r.indexOf(e)<0&&r.push(e)}}updateFromView(e),addChangeListener(e,changeListener)}e.shadowRoot&&addRootDocument(e.shadowRoot)}else waitUndefined(e)}function waitUndefined(e){let t="";if(e.localName.indexOf("-")>0)t=e.localName;else{const o=/.*is\s*=\s*"([^"]+)"\s*.*/.exec(e.outerHTML);o&&o.length>1&&(t=o[1])}t?customElements.whenDefined(t).then(()=>add(e)):console.warn("failed to determine tag of yet undefined custom element "+e+", abandoning")}function updateFromTie(e,t,o,n,i){const r=e[VIEW_PARAMS_KEY];let s=r.length;for(;s;){const a=r[--s];if(a.isFunctional){if(a.fParams.some(e=>e.tieKey===n&&0===e.rawPath.indexOf(t))){let t=!1;const n=[];a.fParams.forEach(e=>{let o;const i=ties.get(e.tieKey);i&&(o=getPath(i,e.path),t=!0),n.push(o)}),t&&(n.push([o]),callViewFunction(e,a.targetProperty,n))}}else{if(a.tieKey!==n)continue;if(0!==a.rawPath.indexOf(t))continue;let r;o&&void 0!==o.value&&t===a.rawPath?o&&(r=o.value):r=getPath(i,a.path),void 0===r&&(r=""),setViewProperty(e,a.targetProperty,r)}}}function updateFromView(e,t){const o=e[VIEW_PARAMS_KEY];let n=o.length;for(;n;){const i=o[--n];if(i.isFunctional){if(!t||i.fParams.some(e=>0===e.rawPath.indexOf(t))){let t=!1;const o=[];i.fParams.forEach(e=>{let n;const i=ties.get(e.tieKey);i&&(n=getPath(i,e.path),t=!0),o.push(n)}),t&&(o.push(null),callViewFunction(e,i.targetProperty,o))}}else if(!t||0===i.rawPath.indexOf(t)){const t=ties.get(i.tieKey);if(void 0===t)continue;let o=getPath(t,i.path);void 0===o&&(o=""),setViewProperty(e,i.targetProperty,o)}}}function addTree(e){let t;e.childElementCount?(t=Array.from(e.querySelectorAll("*"))).unshift(e):t=[e];let o=t.length;for(;o;)try{const e=t[--o];Node.ELEMENT_NODE!==e.nodeType||e[ELEMENT_PROCESSED_SYMBOL]||add(e)}catch(e){console.error("failed to process/add element",e)}}function dropTree(e){let t;e.childElementCount?(t=Array.from(e.querySelectorAll("*"))).unshift(e):t=[e];let o=t.length;for(;o;){const e=t[--o],n=e[VIEW_PARAMS_KEY];if(n){let t=n.length;for(;t;){const o=n[--t];if(!views[o.tieKey])continue;const i=views[o.tieKey][o.rawPath],r=i.indexOf(e);r>=0&&(i.splice(r,1),delChangeListener(e,changeListener))}delete e[VIEW_PARAMS_KEY]}e.shadowRoot&&removeRootDocument(e.shadowRoot)}}function move(e,t,o){let n,i,r;if(t){for(i=(n=e[VIEW_PARAMS_KEY]).length;i;){const t=n[--i];if(!views[t.tieKey])continue;const o=views[t.tieKey][t.rawPath];o&&(r=o.indexOf(e))>=0&&o.splice(r,1)}delChangeListener(e,changeListener)}if(o&&(n=extractViewParams(e))&&(i=n.length)){for(e[VIEW_PARAMS_KEY]=n;i;){const t=n[--i],o=views[t.tieKey]||(views[t.tieKey]={}),r=o[t.rawPath]||(o[t.rawPath]=[]);r.indexOf(e)<0&&(r.push(e),updateFromView(e,t.rawPath))}addChangeListener(e,changeListener)}}function processDomChanges(e){const t=e.length;let o,n,i,r,s,a,l,c,d,h=0;for(;h<t;h++)if("attributes"===(r=(i=e[h]).type))s=i.attributeName,move(o=i.target,i.oldValue,o.getAttribute(s));else if("childList"===r){for(l=(a=i.addedNodes).length;l;)n=(o=a[--l]).nodeType,Node.ELEMENT_NODE===n&&addTree(o);for(d=(c=i.removedNodes).length;d;)n=(o=c[--d]).nodeType,Node.ELEMENT_NODE===n&&dropTree(o)}}function initDocumentObserver(e){new MutationObserver(processDomChanges).observe(e,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0,attributeFilter:["data-tie"],characterData:!1,characterDataOldValue:!1})}addRootDocument(document),console.info("DT: ... initialization DONE (took "+Math.floor(100*(performance.now()-initStartTime))/100+"ms)"); |
@@ -1,1 +0,1 @@ | ||
const e="insert",t="update",r="delete",o="reverse",n="shuffle",s=Symbol("system-observer-key"),p={Date:!0,Blob:!0,Number:!0,String:!0,Boolean:!0,Error:!0,SyntaxError:!0,TypeError:!0,URIError:!0,Function:!0,Promise:!0,RegExp:!0},a=["path","pathsFrom"],c={revoke:{value:function(){this[s].revoke()}},observe:{value:function(e,t){const r=this[s].observers;if("function"!=typeof e)throw new Error("observer parameter MUST be a function");if(t){if("path"in t&&("string"!=typeof t.path||!t.path))throw new Error('"path" option, if/when provided, MUST be a non-empty string');if("pathsFrom"in t&&t.path)throw new Error('"pathsFrom" option MAY NOT be specified together with "path" option');if("pathsFrom"in t&&("string"!=typeof t.pathsFrom||!t.pathsFrom))throw new Error('"pathsFrom" option, if/when provided, MUST be a non-empty string');const e=Object.keys(t).find(e=>!a.includes(e));if(e)throw new Error('"'+e+'" is not a one of the valid options ('+a.join(", ")+")")}r.has(e)?console.info("observer may be bound to an observable only once"):r.set(e,Object.assign({},t))}},unobserve:{value:function(){const e=this[s].observers;let t;if(e.size)if(t=arguments.length)for(;t;)e.delete(arguments[--t]);else e.clear()}}},i=function(e,t){let r,o=e.length;const n=new Array(o);for(n[s]=t;o;)(r=e[--o])&&"object"==typeof r&&!Object.prototype.hasOwnProperty.call(p,r.constructor.name)?n[o]=Array.isArray(r)?new b({target:r,ownKey:o,parent:t}).proxy:new u({target:r,ownKey:o,parent:t}).proxy:n[o]=r;return n},h=function(e,t){const r=Object.keys(e),o={[s]:t};let n,a,c=r.length;for(;c;)(a=e[n=r[--c]])&&"object"==typeof a&&!p.hasOwnProperty(a.constructor.name)?o[n]=Array.isArray(a)?new b({target:a,ownKey:n,parent:t}).proxy:new u({target:a,ownKey:n,parent:t}).proxy:o[n]=a;return o},y=function(e,t){let r,o,n,s,p;for(r of e.keys())try{o=e.get(r),n=t,o.path?(s=o.path,n=t.filter(e=>e.path.join(".")===s)):o.pathsFrom&&(p=o.pathsFrom,n=t.filter(e=>e.path.join(".").startsWith(p))),n.length&&r(n)}catch(e){console.error("failed to deliver changes to listener "+r,e)}},l=function(e){const t=[];let r=0,o=0;for(;e.parent;)t[r++]=e.ownKey,e=e.parent;const n=new Array(r);for(;r;)n[o++]=t[--r];return{observers:e.observers,path:n}};class f{constructor(e,t){const r=t(e.target,this);null===e.parent?(this.isRevoked=!1,Object.defineProperty(this,"observers",{value:new Map}),Object.defineProperties(r,c)):(this.parent=e.parent,this.ownKey=e.ownKey),this.revokable=Proxy.revocable(r,this),this.proxy=this.revokable.proxy,this.target=r}set(r,o,n){let a,c,i=r[o];if(n===i)return!0;if(a=n&&"object"==typeof n&&!p.hasOwnProperty(n.constructor.name)?Array.isArray(n)?new b({target:n,ownKey:o,parent:this}).proxy:new u({target:n,ownKey:o,parent:this}).proxy:n,r[o]=a,i&&"object"==typeof i){const e=i[s];e&&(i=e.revoke())}const h=l(this);return h.observers.size&&(h.path.push(o),c=void 0===i?[{type:e,path:h.path,value:a,object:this.proxy}]:[{type:t,path:h.path,value:a,oldValue:i,object:this.proxy}],y(h.observers,c)),!0}deleteProperty(e,t){let o,n=e[t];if(delete e[t],n&&"object"==typeof n){const e=n[s];e&&(n=e.revoke())}const p=l(this);return p.observers.size&&(p.path.push(t),o=[{type:r,path:p.path,oldValue:n,object:this.proxy}],y(p.observers,o)),!0}}class b extends f{constructor(e){super(e,i)}revoke(){this.revokable.revoke();const e=this.target;let t,r,o=e.length;for(;o;)(t=e[--o])&&"object"==typeof t&&(r=t[s])&&(e[o]=r.revoke());return e}get(a,c){const i={pop:function(e,t){const o=e.length-1;let n=e.pop();if(n&&"object"==typeof n){const e=n[s];e&&(n=e.revoke())}const p=l(t);return p.observers.size&&(p.path.push(o),y(p.observers,[{type:r,path:p.path,oldValue:n,object:t.proxy}])),n},push:function(t,r){let o,n,s,a,c=arguments.length-2;const i=new Array(c),h=t.length;for(o=0;o<c;o++)(n=arguments[o+2])&&"object"==typeof n&&!p.hasOwnProperty(n.constructor.name)&&(n=Array.isArray(n)?new b({target:n,ownKey:h+o,parent:r}).proxy:new u({target:n,ownKey:h+o,parent:r}).proxy),i[o]=n;const f=Reflect.apply(t.push,t,i),v=l(r);if(v.observers.size){for(s=[],o=h,c=t.length;o<c;o++)(a=v.path.slice(0)).push(o),s[o-h]={type:e,path:a,value:t[o],object:r.proxy};y(v.observers,s)}return f},shift:function(e,t){let o,n,p,a,c,i;for((o=e.shift())&&"object"==typeof o&&(i=o[s])&&(o=i.revoke()),n=0,p=e.length;n<p;n++)(a=e[n])&&"object"==typeof a&&(i=a[s])&&(i.ownKey=n);const h=l(t);return h.observers.size&&(h.path.push(0),c=[{type:r,path:h.path,oldValue:o,object:t.proxy}],y(h.observers,c)),o},unshift:function(t,r){const o=Array.from(arguments);let n;o.splice(0,2),o.forEach((e,t)=>{e&&"object"==typeof e&&!p.hasOwnProperty(e.constructor.name)&&(o[t]=Array.isArray(e)?new b({target:e,ownKey:t,parent:r}).proxy:new u({target:e,ownKey:t,parent:r}).proxy)});const a=Reflect.apply(t.unshift,t,o);for(let e,r=0,o=t.length;r<o;r++)if((e=t[r])&&"object"==typeof e){const t=e[s];t&&(t.ownKey=r)}const c=l(r);if(c.observers.size){const s=o.length;let p;n=new Array(s);for(let o=0;o<s;o++)(p=c.path.slice(0)).push(o),n[o]={type:e,path:p,value:t[o],object:r.proxy};y(c.observers,n)}return a},reverse:function(e,t){let r,n,p,a;for(e.reverse(),r=0,n=e.length;r<n;r++)if((p=e[r])&&"object"==typeof p){const e=p[s];e&&(e.ownKey=r)}const c=l(t);return c.observers.size&&(a=[{type:o,path:c.path,object:t.proxy}],y(c.observers,a)),t.proxy},sort:function(e,t,r){let o,p,a,c;for(e.sort(r),o=0,p=e.length;o<p;o++)if((a=e[o])&&"object"==typeof a){const e=a[s];e&&(e.ownKey=o)}const i=l(t);return i.observers.size&&(c=[{type:n,path:i.path,object:t.proxy}],y(i.observers,c)),t.proxy},fill:function(r,o){const n=l(o),a=[],c=r.length,i=Array.from(arguments);i.splice(0,2);const h=i.length,f=h<2?0:i[1]<0?c+i[1]:i[1],v=h<3?c:i[2]<0?c+i[2]:i[2],w=r.slice(0);let g,j;Reflect.apply(r.fill,r,i);for(let c,i,h=f;h<v;h++)(c=r[h])&&"object"==typeof c&&!p.hasOwnProperty(c.constructor.name)&&(r[h]=Array.isArray(c)?new b({target:c,ownKey:h,parent:o}).proxy:new u({target:c,ownKey:h,parent:o}).proxy),w.hasOwnProperty(h)?((i=w[h])&&"object"==typeof i&&(g=i[s])&&(i=g.revoke()),(j=n.path.slice(0)).push(h),a.push({type:t,path:j,value:r[h],oldValue:i,object:o.proxy})):((j=n.path.slice(0)).push(h),a.push({type:e,path:j,value:r[h],object:o.proxy}));return n.observers.size&&y(n.observers,a),o.proxy},splice:function(o,n){const a=l(n),c=[],i=Array.from(arguments),h=o.length;i.splice(0,2);const f=i.length;for(let e,t=2;t<f;t++)(e=i[t])&&"object"==typeof e&&!p.hasOwnProperty(e.constructor.name)&&(i[t]=Array.isArray(e)?new b({target:e,ownKey:t,parent:n}).proxy:new u({target:e,ownKey:t,parent:n}).proxy);const v=0===f?0:i[0]<0?h+i[0]:i[0],w=f<2?h-v:i[1],g=Math.max(f-2,0),j=Reflect.apply(o.splice,o,i),x=o.length;let d,m,k,A;for(let e,t=0;t<x;t++)(e=o[t])&&"object"==typeof e&&(d=e[s])&&(d.ownKey=t);for(m=0,k=j.length;m<k;m++)(A=j[m])&&"object"==typeof A&&(d=A[s])&&(j[m]=d.revoke());if(a.observers.size){let s,p;for(s=0;s<w;s++)(p=a.path.slice(0)).push(v+s),s<g?c.push({type:t,path:p,value:o[v+s],oldValue:j[s],object:n.proxy}):c.push({type:r,path:p,oldValue:j[s],object:n.proxy});for(;s<g;s++)(p=a.path.slice(0)).push(v+s),c.push({type:e,path:p,value:o[v+s],object:n.proxy});y(a.observers,c)}return j}};return i.hasOwnProperty(c)?i[c].bind(void 0,a,this):a[c]}}class u extends f{constructor(e){super(e,h)}revoke(){this.revokable.revoke();const e=this.target,t=Object.keys(e);let r,o,n,p=t.length;for(;p;)(o=e[r=t[--p]])&&"object"==typeof o&&(n=o[s])&&(e[r]=n.revoke());return e}}class v{constructor(){throw new Error('Observable MAY NOT be created via constructor, see "Observable.from" API')}static from(e){if(!(!e||"object"!=typeof e||p.hasOwnProperty(e.constructor.name)||"observe"in e||"unobserve"in e||"revoke"in e)){return(Array.isArray(e)?new b({target:e,ownKey:null,parent:null}):new u({target:e,ownKey:null,parent:null})).proxy}if(!e||"object"!=typeof e)throw new Error("observable MAY ONLY be created from non-null object only");if("observe"in e||"unobserve"in e||"revoke"in e)throw new Error('target object MUST NOT have nor own neither inherited properties from the following list: "observe", "unobserve", "revoke"');if(p.hasOwnProperty(e.constructor.name))throw new Error(e+" found to be one of non-observable object types: "+p)}static isObservable(e){return!!(e&&e[s]&&e.observe)}}Object.freeze(v);export{v as Observable}; | ||
const e="insert",t="update",r="delete",o="reverse",n="shuffle",s=Symbol("system-observer-key"),p=["path","pathsFrom"],a={revoke:{value:function(){this[s].revoke()}},observe:{value:function(e,t){const r=this[s].observers;if("function"!=typeof e)throw new Error("observer parameter MUST be a function");if(t){if("path"in t&&("string"!=typeof t.path||!t.path))throw new Error('"path" option, if/when provided, MUST be a non-empty string');if("pathsFrom"in t&&t.path)throw new Error('"pathsFrom" option MAY NOT be specified together with "path" option');if("pathsFrom"in t&&("string"!=typeof t.pathsFrom||!t.pathsFrom))throw new Error('"pathsFrom" option, if/when provided, MUST be a non-empty string');const e=Object.keys(t).find(e=>!p.includes(e));if(e)throw new Error('"'+e+'" is not a one of the valid options ('+p.join(", ")+")")}r.has(e)?console.info("observer may be bound to an observable only once"):r.set(e,Object.assign({},t))}},unobserve:{value:function(){const e=this[s].observers;let t;if(e.size)if(t=arguments.length)for(;t;)e.delete(arguments[--t]);else e.clear()}}},i=function(e,t){let r,o=e.length;const n=new Array(o);for(n[s]=t;o;)(r=e[--o])&&"object"==typeof r&&f(r)?n[o]=Array.isArray(r)?new u({target:r,ownKey:o,parent:t}).proxy:new v({target:r,ownKey:o,parent:t}).proxy:n[o]=r;return n},c=function(e,t){const r=Object.keys(e),o={[s]:t};let n,p,a=r.length;for(;a;)(p=e[n=r[--a]])&&"object"==typeof p&&f(p)?o[n]=Array.isArray(p)?new u({target:p,ownKey:n,parent:t}).proxy:new v({target:p,ownKey:n,parent:t}).proxy:o[n]=p;return o},h=function(e,t){let r,o,n,s,p;for(r of e.keys())try{o=e.get(r),n=t,o.path?(s=o.path,n=t.filter(e=>e.path.join(".")===s)):o.pathsFrom&&(p=o.pathsFrom,n=t.filter(e=>e.path.join(".").startsWith(p))),n.length&&r(n)}catch(e){console.error("failed to deliver changes to listener "+r,e)}},l=function(e){const t=[];let r=0,o=0;for(;e.parent;)t[r++]=e.ownKey,e=e.parent;const n=new Array(r);for(;r;)n[o++]=t[--r];return{observers:e.observers,path:n}},y=[Date,Blob,Number,String,Boolean,Error,Function,Promise,RegExp],f=function(e){return!y.some(t=>e instanceof t)};class b{constructor(e,t){const r=t(e.target,this);null===e.parent?(this.isRevoked=!1,Object.defineProperty(this,"observers",{value:new Map}),Object.defineProperties(r,a)):(this.parent=e.parent,this.ownKey=e.ownKey),this.revokable=Proxy.revocable(r,this),this.proxy=this.revokable.proxy,this.target=r}set(r,o,n){let p,a,i=r[o];if(n===i)return!0;if(p=n&&"object"==typeof n&&f(n)?Array.isArray(n)?new u({target:n,ownKey:o,parent:this}).proxy:new v({target:n,ownKey:o,parent:this}).proxy:n,r[o]=p,i&&"object"==typeof i){const e=i[s];e&&(i=e.revoke())}const c=l(this);return c.observers.size&&(c.path.push(o),a=void 0===i?[{type:e,path:c.path,value:p,object:this.proxy}]:[{type:t,path:c.path,value:p,oldValue:i,object:this.proxy}],h(c.observers,a)),!0}deleteProperty(e,t){let o,n=e[t];if(delete e[t],n&&"object"==typeof n){const e=n[s];e&&(n=e.revoke())}const p=l(this);return p.observers.size&&(p.path.push(t),o=[{type:r,path:p.path,oldValue:n,object:this.proxy}],h(p.observers,o)),!0}}class u extends b{constructor(e){super(e,i)}revoke(){this.revokable.revoke();const e=this.target;let t,r,o=e.length;for(;o;)(t=e[--o])&&"object"==typeof t&&(r=t[s])&&(e[o]=r.revoke());return e}get(p,a){const i={pop:function(e,t){const o=e.length-1;let n=e.pop();if(n&&"object"==typeof n){const e=n[s];e&&(n=e.revoke())}const p=l(t);return p.observers.size&&(p.path.push(o),h(p.observers,[{type:r,path:p.path,oldValue:n,object:t.proxy}])),n},push:function(t,r){let o,n,s,p,a=arguments.length-2;const i=new Array(a),c=t.length;for(o=0;o<a;o++)(n=arguments[o+2])&&"object"==typeof n&&f(n)&&(n=Array.isArray(n)?new u({target:n,ownKey:c+o,parent:r}).proxy:new v({target:n,ownKey:c+o,parent:r}).proxy),i[o]=n;const y=Reflect.apply(t.push,t,i),b=l(r);if(b.observers.size){for(s=[],o=c,a=t.length;o<a;o++)(p=b.path.slice(0)).push(o),s[o-c]={type:e,path:p,value:t[o],object:r.proxy};h(b.observers,s)}return y},shift:function(e,t){let o,n,p,a,i,c;for((o=e.shift())&&"object"==typeof o&&(c=o[s])&&(o=c.revoke()),n=0,p=e.length;n<p;n++)(a=e[n])&&"object"==typeof a&&(c=a[s])&&(c.ownKey=n);const y=l(t);return y.observers.size&&(y.path.push(0),i=[{type:r,path:y.path,oldValue:o,object:t.proxy}],h(y.observers,i)),o},unshift:function(t,r){const o=Array.from(arguments);let n;o.splice(0,2),o.forEach((e,t)=>{e&&"object"==typeof e&&f(e)&&(o[t]=Array.isArray(e)?new u({target:e,ownKey:t,parent:r}).proxy:new v({target:e,ownKey:t,parent:r}).proxy)});const p=Reflect.apply(t.unshift,t,o);for(let e,r=0,o=t.length;r<o;r++)if((e=t[r])&&"object"==typeof e){const t=e[s];t&&(t.ownKey=r)}const a=l(r);if(a.observers.size){const s=o.length;let p;n=new Array(s);for(let o=0;o<s;o++)(p=a.path.slice(0)).push(o),n[o]={type:e,path:p,value:t[o],object:r.proxy};h(a.observers,n)}return p},reverse:function(e,t){let r,n,p,a;for(e.reverse(),r=0,n=e.length;r<n;r++)if((p=e[r])&&"object"==typeof p){const e=p[s];e&&(e.ownKey=r)}const i=l(t);return i.observers.size&&(a=[{type:o,path:i.path,object:t.proxy}],h(i.observers,a)),t.proxy},sort:function(e,t,r){let o,p,a,i;for(e.sort(r),o=0,p=e.length;o<p;o++)if((a=e[o])&&"object"==typeof a){const e=a[s];e&&(e.ownKey=o)}const c=l(t);return c.observers.size&&(i=[{type:n,path:c.path,object:t.proxy}],h(c.observers,i)),t.proxy},fill:function(r,o){const n=l(o),p=[],a=r.length,i=Array.from(arguments);i.splice(0,2);const c=i.length,y=c<2?0:i[1]<0?a+i[1]:i[1],b=c<3?a:i[2]<0?a+i[2]:i[2],w=r.slice(0);let g,j;Reflect.apply(r.fill,r,i);for(let a,i,c=y;c<b;c++)(a=r[c])&&"object"==typeof a&&f(a)&&(r[c]=Array.isArray(a)?new u({target:a,ownKey:c,parent:o}).proxy:new v({target:a,ownKey:c,parent:o}).proxy),w.hasOwnProperty(c)?((i=w[c])&&"object"==typeof i&&(g=i[s])&&(i=g.revoke()),(j=n.path.slice(0)).push(c),p.push({type:t,path:j,value:r[c],oldValue:i,object:o.proxy})):((j=n.path.slice(0)).push(c),p.push({type:e,path:j,value:r[c],object:o.proxy}));return n.observers.size&&h(n.observers,p),o.proxy},splice:function(o,n){const p=l(n),a=[],i=Array.from(arguments),c=o.length;i.splice(0,2);const y=i.length;for(let e,t=2;t<y;t++)(e=i[t])&&"object"==typeof e&&f(e)&&(i[t]=Array.isArray(e)?new u({target:e,ownKey:t,parent:n}).proxy:new v({target:e,ownKey:t,parent:n}).proxy);const b=0===y?0:i[0]<0?c+i[0]:i[0],w=y<2?c-b:i[1],g=Math.max(y-2,0),j=Reflect.apply(o.splice,o,i),x=o.length;let d,k,A,m;for(let e,t=0;t<x;t++)(e=o[t])&&"object"==typeof e&&(d=e[s])&&(d.ownKey=t);for(k=0,A=j.length;k<A;k++)(m=j[k])&&"object"==typeof m&&(d=m[s])&&(j[k]=d.revoke());if(p.observers.size){let s,i;for(s=0;s<w;s++)(i=p.path.slice(0)).push(b+s),s<g?a.push({type:t,path:i,value:o[b+s],oldValue:j[s],object:n.proxy}):a.push({type:r,path:i,oldValue:j[s],object:n.proxy});for(;s<g;s++)(i=p.path.slice(0)).push(b+s),a.push({type:e,path:i,value:o[b+s],object:n.proxy});h(p.observers,a)}return j}};return i.hasOwnProperty(a)?i[a].bind(void 0,p,this):p[a]}}class v extends b{constructor(e){super(e,c)}revoke(){this.revokable.revoke();const e=this.target,t=Object.keys(e);let r,o,n,p=t.length;for(;p;)(o=e[r=t[--p]])&&"object"==typeof o&&(n=o[s])&&(e[r]=n.revoke());return e}}class w{constructor(){throw new Error('Observable MAY NOT be created via constructor, see "Observable.from" API')}static from(e){if(!(!e||"object"!=typeof e||!f(e)||"observe"in e||"unobserve"in e||"revoke"in e)){return(Array.isArray(e)?new u({target:e,ownKey:null,parent:null}):new v({target:e,ownKey:null,parent:null})).proxy}if(!e||"object"!=typeof e)throw new Error("observable MAY ONLY be created from non-null object only");if("observe"in e||"unobserve"in e||"revoke"in e)throw new Error('target object MUST NOT have nor own neither inherited properties from the following list: "observe", "unobserve", "revoke"');if(!f(e))throw new Error(e+" found to be one of non-observable object types: "+y)}static isObservable(e){return!!(e&&e[s]&&e.observe)}}Object.freeze(w);export{w as Observable}; |
177
dist/i18n.js
@@ -1,3 +0,176 @@ | ||
import { DataTier } from './data-tier/data-tier.js'; | ||
import * as DataTier from './data-tier/data-tier.min.js'; | ||
export { } | ||
class Locale { | ||
constructor(key, dir, lang, label) { | ||
if (!key) throw new Error(`invalid key param "${key}"`); | ||
if (dir !== 'ltr' && dir !== 'rtl') throw new Error(`invalid dir param "${dir}" (only "ltr" or "rtl" allowed)`); | ||
if (!lang || typeof lang !== 'string') throw new Error(`invalid lang param "${lang}"`); | ||
if (!label || typeof label !== 'string') throw new Error(`invalid label param "${label}"`); | ||
this.key = key; | ||
this.dir = dir; | ||
this.lang = lang; | ||
this.label = label; | ||
Object.freeze(this); | ||
} | ||
} | ||
const | ||
DEFAULT_NAMESPACE = 'i18n', | ||
locales = [ | ||
new Locale('ar', 'rtl', 'ar', 'عربى'), | ||
new Locale('en', 'ltr', 'en', 'English'), | ||
new Locale('he', 'rtl', 'he', 'עברית'), | ||
new Locale('ru', 'ltr', 'ru', 'Русский') | ||
], | ||
events = new EventTarget(), | ||
LOCALE_SET_EVENT = 'localeSet', | ||
LOCALE_APPLIED_EVENT = 'localeApplied', | ||
packsMetadata = {}, | ||
DEFAULT_PACK_LOCALE = Symbol('default.pack.locale'); | ||
let namespace = DEFAULT_NAMESPACE, | ||
i18nData, | ||
currentLocale; | ||
export { | ||
setNamespace, | ||
locales, | ||
definePack, | ||
getActiveLocale, | ||
setActiveLocale, | ||
events, | ||
LOCALE_SET_EVENT, | ||
LOCALE_APPLIED_EVENT | ||
} | ||
setActiveLocale('en'); | ||
function setNamespace(newNS) { | ||
if (!newNS || typeof newNS !== 'string') { | ||
throw new Error(`invalid namespace "${newNS}"`); | ||
} | ||
if (newNS === namespace) { | ||
return; | ||
} | ||
i18nData = DataTier.ties.create(newNS, i18nData || {}); | ||
namespace = newNS; | ||
} | ||
function getActiveLocale() { | ||
return currentLocale; | ||
} | ||
async function setActiveLocale(locale) { | ||
if (!locale) { | ||
throw new Error(`invalid locale "${locale}"`); | ||
} | ||
const newLocale = typeof locale === 'object' ? locales.find(l => l === locale) : locales.find(l => l.key === locale); | ||
if (!newLocale) { | ||
throw new Error(`"${locale}" unmatchable to any of known locales`); | ||
} | ||
if (!currentLocale || currentLocale.key !== newLocale.key) { | ||
// set current locale | ||
const eventDetails = { | ||
current: newLocale, | ||
previous: currentLocale | ||
}; | ||
currentLocale = newLocale; | ||
// emit localeSet event | ||
events.dispatchEvent(new CustomEvent(LOCALE_SET_EVENT, { detail: eventDetails })); | ||
// apply global styling | ||
document.documentElement.lang = currentLocale.key; | ||
document.body.dir = currentLocale.dir; | ||
// apply new locale to any registered component | ||
const allApplied = []; | ||
Object.keys(packsMetadata).forEach(packKey => allApplied.push(applyLocale(packKey))); | ||
await Promise.all(allApplied); | ||
// emit localeApplied event | ||
events.dispatchEvent(new CustomEvent(LOCALE_APPLIED_EVENT, { detail: eventDetails })); | ||
} | ||
} | ||
async function definePack(key, source, options) { | ||
const opts = Object.assign({ | ||
defaultLocale: 'en' | ||
}, options); | ||
// pack key validation | ||
if (!key || typeof key !== 'string') { | ||
throw new Error(`invalid pack key "${key}"`); | ||
} | ||
if (key in packsMetadata) { | ||
throw new Error(`key "${key}" is already defined`); | ||
} | ||
// source validation | ||
if (!source || typeof source !== 'object') { | ||
throw new Error(`invalid source "${source}"`); | ||
} | ||
if (!Object.keys(source).length) { | ||
throw new Error(`invalid source "${source}" (no locale keys found, at least one expected)`); | ||
} | ||
// set default locale for this pack | ||
source[DEFAULT_PACK_LOCALE] = opts.defaultLocale in source ? opts.defaultLocale : Object.keys(source)[0]; | ||
packsMetadata[key] = source; | ||
await applyLocale(key); | ||
} | ||
async function applyLocale(packKey) { | ||
const localeKey = currentLocale ? currentLocale.key : null; | ||
try { | ||
ensureI18nDataStore(); | ||
i18nData[packKey] = localeKey ? await getI18nData(localeKey, packKey) : null; | ||
} catch (e) { | ||
console.error(`failed to apply i18n data for "${localeKey}" of "${packKey}" pack, error:`, e); | ||
} | ||
} | ||
async function getI18nData(localeKey, packKey) { | ||
let result = null; | ||
const packMeta = packsMetadata[packKey]; | ||
if (!packMeta) { | ||
console.warn(`no i18n data found for "${packKey}" pack`); | ||
} else { | ||
let packLocaleData = packMeta[localeKey]; | ||
if (!packLocaleData) { | ||
const defaultPackLocale = packMeta[DEFAULT_PACK_LOCALE]; | ||
packLocaleData = packMeta[defaultPackLocale]; | ||
console.warn(`no i18n data found for "${localeKey}" of "${packKey}" pack, fallen back to "${defaultPackLocale}"`); | ||
} | ||
if (typeof packLocaleData === 'object') { | ||
result = packMeta[localeKey]; | ||
} else if (typeof packLocaleData === 'function') { | ||
result = await Promise.resolve(packLocaleData()); | ||
} else if (typeof packLocaleData === 'string') { | ||
console.info(`fetching i18n data for "${localeKey}" of "${packKey}" pack from "${packLocaleData}"...`) | ||
try { | ||
const resource = await fetch(packMeta[localeKey]); | ||
if (resource.ok) { | ||
result = await resource.json(); | ||
packMeta[localeKey] = result; | ||
} else { | ||
throw new Error(`unexpected status '${resource.status}`); | ||
} | ||
} catch (e) { | ||
console.error(`failed to fetch i18n data for "${localeKey}" of "${packKey}" from "${packLocaleData}", error:`, e); | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
function ensureI18nDataStore() { | ||
return i18nData || DataTier.ties.create(namespace); | ||
} |
@@ -1,1 +0,1 @@ | ||
import{DataTier as t}from"./data-tier/data-tier.js";export{}; | ||
import*as DataTier from"./data-tier/data-tier.min.js";class e{constructor(e,t,r,o){if(!e)throw new Error(`invalid key param "${e}"`);if("ltr"!==t&&"rtl"!==t)throw new Error(`invalid dir param "${t}" (only "ltr" or "rtl" allowed)`);if(!r||"string"!=typeof r)throw new Error(`invalid lang param "${r}"`);if(!o||"string"!=typeof o)throw new Error(`invalid label param "${o}"`);this.key=e,this.dir=t,this.lang=r,this.label=o,Object.freeze(this)}}const t=[new e("ar","rtl","ar","عربى"),new e("en","ltr","en","English"),new e("he","rtl","he","עברית"),new e("ru","ltr","ru","Русский")],r=new EventTarget,o="localeSet",n="localeApplied",a={},i=Symbol("default.pack.locale");let l,c,f="i18n";export{s as setNamespace,t as locales,u as definePack,d as getActiveLocale,w as setActiveLocale,r as events,o as LOCALE_SET_EVENT,n as LOCALE_APPLIED_EVENT};function s(e){if(!e||"string"!=typeof e)throw new Error(`invalid namespace "${e}"`);e!==f&&(l=DataTier.ties.create(e,l||{}),f=e)}function d(){return c}async function w(e){if(!e)throw new Error(`invalid locale "${e}"`);const i="object"==typeof e?t.find(t=>t===e):t.find(t=>t.key===e);if(!i)throw new Error(`"${e}" unmatchable to any of known locales`);if(!c||c.key!==i.key){const e={current:i,previous:c};c=i,r.dispatchEvent(new CustomEvent(o,{detail:e})),document.documentElement.lang=c.key,document.body.dir=c.dir;const t=[];Object.keys(a).forEach(e=>t.push(y(e))),await Promise.all(t),r.dispatchEvent(new CustomEvent(n,{detail:e}))}}async function u(e,t,r){const o=Object.assign({defaultLocale:"en"},r);if(!e||"string"!=typeof e)throw new Error(`invalid pack key "${e}"`);if(e in a)throw new Error(`key "${e}" is already defined`);if(!t||"object"!=typeof t)throw new Error(`invalid source "${t}"`);if(!Object.keys(t).length)throw new Error(`invalid source "${t}" (no locale keys found, at least one expected)`);t[i]=o.defaultLocale in t?o.defaultLocale:Object.keys(t)[0],a[e]=t,await y(e)}async function y(e){const t=c?c.key:null;try{l||DataTier.ties.create(f),l[e]=t?await async function(e,t){let r=null;const o=a[t];if(o){let n=o[e];if(!n){const r=o[i];n=o[r],console.warn(`no i18n data found for "${e}" of "${t}" pack, fallen back to "${r}"`)}if("object"==typeof n)r=o[e];else if("function"==typeof n)r=await Promise.resolve(n());else if("string"==typeof n){console.info(`fetching i18n data for "${e}" of "${t}" pack from "${n}"...`);try{const a=await fetch(o[e]);if(!a.ok)throw new Error(`unexpected status '${a.status}`);r=await a.json(),o[e]=r}catch(r){console.error(`failed to fetch i18n data for "${e}" of "${t}" from "${n}", error:`,r)}}}else console.warn(`no i18n data found for "${t}" pack`);return r}(t,e):null}catch(r){console.error(`failed to apply i18n data for "${t}" of "${e}" pack, error:`,r)}}w("en"); |
{ | ||
"name": "@gullerya/i18n", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "client (browser) oriented internationalization library", | ||
@@ -37,15 +37,15 @@ "keywords": [ | ||
"dependencies": { | ||
"data-tier": "^1.12.0" | ||
"data-tier": "^2.2.0" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^6.6.0", | ||
"eslint": "^6.8.0", | ||
"eslint-config-standard": "^14.1.0", | ||
"eslint-plugin-import": "^2.18.2", | ||
"eslint-plugin-node": "^10.0.0", | ||
"eslint-plugin-standard": "^4.0.1", | ||
"eslint-plugin-promise": "^4.2.1", | ||
"eslint-plugin-standard": "^4.0.1", | ||
"eslint-plugin-import": "^2.20.0", | ||
"eslint-plugin-node": "^11.0.0", | ||
"fs-extra": "^8.1.0", | ||
"just-test": "^1.6.0", | ||
"just-test": "^2.1.3", | ||
"uglify-es": "^3.3.9" | ||
} | ||
} | ||
} |
@@ -9,7 +9,12 @@ [![GitHub](https://img.shields.io/github/license/gullerya/i18n.svg)](https://github.com/gullerya/i18n) | ||
__`i18n`__ is a client (browser) oriented library providing an easy means to quickly localize web sites; `i18n` is capable of handling complex web-component based sites with the same easiness as a simple 'flat' ones. | ||
__`i18n`__ is a client (browser) oriented library providing an easy means to quickly internationalize web sites. | ||
As of now the library takes care of __translation__ aspect only. Other kinds of formatting / symboling needs might be considered as per future needs. | ||
`i18n` is capable of handling complex web-component based sites with the same easiness as a simple 'flat' ones. | ||
Main aspects: | ||
* `i18n` library implemented as a classic service from consumer perspective - import it and use the JS APIs and correlated HTML syntax | ||
* `i18n` library is component-design oriented: each component can and should take care of its own needs | ||
> Sharing of resources is super easy, to be sure. But me myself almost always consider it to be a best practice to strive to a self contained, isolated, independent components, even if in this case it means sacrificing some network and memory. | ||
* data binding part is powered by [`data-tier`](https://www.npmjs.com/package/data-tier) engine | ||
* the whole work performed client side, while actual localization resources requested from the backend as a static resources | ||
* the whole work performed client side, localization data may be provided in inline fashion or fetched as static resources requested from the backend | ||
* JSON resource format supported | ||
@@ -21,2 +26,5 @@ | ||
* __0.1.0__ | ||
* Next take - mostly finalized APIs | ||
* __0.0.1__ | ||
@@ -26,13 +34,40 @@ * Initial implementation | ||
# API | ||
`i18n` library consists of a ... | ||
Refer to this [documentation](docs/api.md). | ||
#### import: | ||
# Usage | ||
Generally, it is highly advised to read some of the [`data-tier`](https://www.npmjs.com/package/data-tier) documentation. Being an underlying engine, it has everything needed to understand usage and internals of `i18n`. | ||
Import the library as in example below: | ||
```javascript | ||
import { ... } from './dist/i18n.min.js'; | ||
import * as i18n from './dist/i18n.min.js'; | ||
``` | ||
# Typical usage example | ||
The flow below exemplifies typical usage of the library: | ||
Define your language data per component as following: | ||
```javascript | ||
i18n.definePack(packKey, { | ||
en: { ...}, // inlining data | ||
he: '/i18n/about-he.json', // fetched resource | ||
ru: () => { ... } // function returning data | ||
}, options); | ||
``` | ||
Parameters description (`sources` stands for the second parameter): | ||
* `packKey` - unique key per component/pack, required | ||
* `sources` - sources of localized texts, required | ||
* `JSON`, keys of which considered to be a locale keys | ||
* properties values may be either | ||
* `options` - reserved, optional | ||
> Under component I mean any level of segregation up to consumers arbitrary choice. One may define such a pack for each and every micro part in UI, other may throw together all of the language data for the whole site. | ||
> Pro tip: it really makes sense to split/pack i18n data according to the components visibility, so that 'About' page texts, for example, won't be dealt with until actually navigated to. | ||
Use the following syntax in your `HTML` resources: | ||
```html | ||
<span data-tie="i18n:packKey.path.to.text"></span> | ||
``` | ||
Let's break that attribute to the parts to get is clear: | ||
* `i18n` - is the default tying namespace, you can change it via `setNamespace` API; the namespace is __global__ for the whole application | ||
* `packKey` - first token in the path to the localized text is the component / pack key, used when the internatiolization resources was defined | ||
* `path.to.text` - rest of the path is just a path within the localization data graph |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
70
32787
8
149
3
+ Addeddata-tier@2.12.0(transitive)
+ Addedobject-observer@4.8.0(transitive)
- Removeddata-tier@1.14.0(transitive)
- Removedobject-observer@2.9.4(transitive)
Updateddata-tier@^2.2.0