UNPKG

regstr

Version:

JSON.stringify objects with RegExp properties and then JSON.parse json string resulted back into original objects. Converts RegExp object to be serializable - into pair of strings (key,value). Could be used for RegExp being bilaterally stringified and ge

1,269 lines (1,264 loc) 49.4 kB
/** * Package <regStr> * @fileoverview Serializes and back parses of objects and arrays with RegExp * properties or elements. JSON.stringify() JSON.parse() compatible. * Converts single RegExp object or some object with RegExp properties * to the form adequate to stringify and back parse them using JSON object * by means of JSON.stringify and then JSON.parse back. * @author v.url.node@gmail.com (Vladimir Uralov) */ /* * To get the text bellow in command prompt * go to the root directory of the package and * type the command * ...\regstr> npm run explain_ciph * * * Package <regStr> * (serialization of objects and arrays containing regular expression objects. * JSON.stringify - JSON.parse - compatible) * * Converts single RegExp object or some object with RegExp properties * to the form adequate to stringify and back parse them using JSON * by means of JSON.stringify and then JSON.parse back.) * * ----- Ciphering or conversion ------ * * The main concept is to cipher RegExp object into 'cipher' pair of strings * or cipher object - single property object, easily stringifiable, * and automatically recognizable after back parsing to get "initial" * object contained RegExp-s. * The easiest way to get acquaintance with module is to look at examples: * * Loading handler from working directory ( using variable - h presumes * that module is a handler. Identifier regstr or any yourVar would be OK * as well): * * var h=require('./regstr').regStr; * * * ciphering is provided by method h.streger() , like this: * * var v=/asdf/gi; * * /asdf/gi * * h.streger( v ) or h.streger(/asdf/gi) * gives * * { RE74: 'asdfRE74gi' } * * or an object * * var o={a:/asdf/gim}; * * h.streger(o) or h.streger( {a:/asdf/gim} ) * * gives * * { aRE04: 'asdfRE04gmi' } * * or an object inside an array * * h.streger( [ {a:/asdf/gi},/^[+]+cc.*$/gim ] ); * * returns * * [ { aRE13: 'asdfRE13gi' }, { '1RE18': '^[+]+cc.*$RE18gmi' } ] * * or array with RegExp objects elements * * h.streger( [ * /asdf/gi, * /^[+]+cc.*$/gim, * /sdf/ * ] * ); * * * [ * { RE60: 'asdfRE60gi' }, * { '1RE10': '^[+]+cc.*$RE10gmi' }, * { '2RE94': 'sdfRE94' } * ] * or object with RegExp objects properties * * h.streger( * { a: /asdf/gi, * b: /^[+]+cc.*$/gim, * ab:[{ bb:/sdfg/mg},/sdf/] * } * ); * * * { * ab: [ { bbRE38: 'sdfgRE38gm' }, { '1RE52': 'sdfRE52' } ], * aRE01: 'asdfRE01gi', * bRE03: '^[+]+cc.*$RE03gmi' * } * all these results are easily stringified and back parsed by * JSON.stringify and JSON.parse * * Applying h.reger() method after parsing will bring RegEpx: * keeping in mind the process flow * * ciphering deciphering * RegExp > h.streger > cipherObject > h.reger > RegExp * * h.reger( h.streger({ a: /asdf/gi, * b: /^[+]+cc.*$/gim, * ab:[{ bb:/sdfg/mg},/sdf/] } ) * ); * gives: * * { ab: [ { bb: /sdfg/gm }, /sdf/ ], a: /asdf/gi, b: /^[+]+cc.*$/gim } * You already could have observed the letters 'RE' and few (two) digits after * it in strings representing keys and values ciphered. This is a mark * using in ciphering and identification ciphered regExp later. Digits are * random and their number could be changed by property h.nMin. For example * let's set it to 5. * h.nMin=5; * * and the lay out of key and value strings will have changed. For ex.: * h.streger( { a: /asdf/gi, * b: /^[+]+cc.*$/gim, * ab:[{ bb:/sdfg/mg},/sdf/], * c:{re:/nnn.+$/gim,ar:[/aaa/,/bbb/g]} * } * ); * returns: * * { * ab: [ { bbRE90430: 'sdfgRE90430gm' }, { '1RE45658': 'sdfRE45658' } ], * c: { ar: [ [Object], [Object] ], reRE68302: 'nnn.+$RE68302gmi' }, * aRE30267: 'asdfRE30267gi', * bRE11628: '^[+]+cc.*$RE11628gmi' * } * * The explanation regarding REn follows bellow. Here we just mention * that during deciphering different key-value pairs ciphering different * regexp-s could have different number of that random digits after 'RE' * * like this: * * var b=[ { aRE28: 'asdfRE28gi' }, { '1RE62555': '^[+]+cc.*$RE62555gmi' } ] * * h.reger( b); will return * * [ { a: /asdf/gi }, /^[+]+cc.*$/gim ] * * Handler h (regstr) has h.replacer and h.reviver * methods that could be used to serialize and parse back objects and * arrays with RegExp-s without * direct intermediary of h.streger(...) and h.reger(...). * functions could be used * var replacer = h.replacer.bind(h); * var reviver = h.reviver.bind(h); * and * o -> o1 = JSON.stringify(o,replacer) -> * o = JSON.parse(o1,reviver) * compare o * * [ { a: /asdf/gi }, /^[+]+cc.*$/gim ] * * with * * o=JSON.parse ( JSON.stringify(o,h.replacer.bind(h)), h.reviver.bind(h)) * or * o=JSON.parse ( JSON.stringify(o,replacer), reviver) * * * [ { a: /asdf/gi }, /^[+]+cc.*$/gim ] * * and with * h.reger( h.streger([{a:/asdf/gi},/^[+]+cc.*$/gim]) ); * * * [ { a: /asdf/gi }, /^[+]+cc.*$/gim ] * * * Explanation: * Let's pO is some parent object with a property described by * (key,value) pair, where * * value=pO[key]; // and value is regular expression object * (value instanceof RegExp===true)?true:false; // is true and * * value=new RegExp(body,mig); // where body and mig are strings * * {string}body - is string of regular expression and * {string}mig - RegExp flags - ( multi-line, insensitive, global) * * Conversion algorithm fitures: * 1. Additional property key1 is added to object pO - * * pO[key1]=value1; * key1=key+RE; // value=new RegExp(body,mig) are converted into string * value1=body+RE+mig; // where * RE='RE'+rndm10; * * and {string}rndm10 is random string consisted of 10 random digits * from 0-9 in each place, for ex. * rndm10='0987654321'; // * * The number of random digit depends and could be any of your choice * rndm4, ..rndm2 ... * Obviously pO[key1] is serializable by JSON. * 2. Or * delete pO[key]; * or if you would not have done it yourself the JSON.stringify will do * during serialization * * Inclusion of RE string into key1 and value1 is used as a mark * marking the property and permits to identify it during reverse * parse procedure and to transform * value1 string body+RE+mig into new RegExp(body,mig) : * Back Parsing features: * After JSON.parse( regExpJsoned ) * 1. Selects object string property containing in key and value * marking string RE==='RE'+rndmN , * where N is number of digital positions in random rndmN N-digital string. * RE populates the ending positions of key( property name string) and * separates body-part from mig-part in property string value===body+RE+mig, so * * var vs=pO[oid+RE].split(RE); // (5) * var newRegExpValue= new RegExp( vs[0],vs[1]); // (6) * * when you would be needed to get RE you should look for a property * with string value, in (key,value) pair : * and * 1. property name and property string value contain the same pattern RE * 2. the property name has it located at the end of string * 3. the value string consists of three parts: body,RE and mig * splitting the valueString by RE pattern body and mig -parts could be got * and therefor RegExp value be invoked * * Package Usage for pO object: * * var regstr=require('regstr).regStr; // handler object * * variant A. * RegExp property conversions into string: * For each property being RegExp new converted string property * is created. Original ones keep safe.( are not deleted from object * if it's necessary do it yourself or use optional parameter opt_ciphMode * to manage this option) * * pO=regstr.streger(pO); // Remark: changes of pO properties are accessible * // for external scope of stregger. Possible call is * // regstr.streger(pO); * * variant B. * per single RegExp property transformation * * var o; * // preJson step * for(var k in pO){ * if( pO[k] instanceof RegExp){ * o=regstr.reStr(pO[k],k); * // creates new property with new key and value {string} * pO[o.ky]=o.v; * } * } * // json.stringify * var jsonPo=JSON.stringify(pO) * * // parse Back * * detailed explanation see in deciphering_explain.txt of deciphering_explain.js-object * in docs directory. * to get it in console type: npm run explain * or inside node by require('./docs.deciphering_explain.js'); command * * var oP=JSON.parse(jsonPo); * * postParse properties' conversion into RegExp-s if any * variant 1. * All chyphered propertiesif are available are selected * and are converted to RegExp with new keys (by excluding RE from key): * * oP=regstr.reger(oP); * * // or Variant 2 * // if 'property by property' handling is necessary * var ob; * for( var ip in oP){ * if( typeof oP[ip]==='string'){ * ob=regstr.regUp(oP[ip]); * if(ob.key!==ip){ * // new RegExp property of oP * ob[ob.key]=ob.value; * delete oP[ip]; * } * } * } * Remark 1. * RegExp object variable itself could be converted into * object with one property {RE:regString} * var regE=someRegExp; // someRegExp=new RegExp(body,mig); * var oStrReg=regstr.reStr(regE); * // var o=oStrReg.o(); where o={ RE: regString } where * // RE='RE'+rndmN , regString = body+RE+mig, * // at the same time RE=oStrReg.re; * // regString=oStrReg.c; * // body=oStrReg.b; * // mig=oStrReg.mig * // * Remark 2. * uncycle package extension. (miss it now, go further up to Decipher * method concept...) * * This RegExp handling algorithm could be included in preStringify methods * of <uncycle> package -targeting to stringify objects with circular references * to provide extending functionality, or when json is used for cloning objects. * to switch it on set * unCycle.uiDirect.regOn=true; * * ................................. * * Following comments are based on notion of uncycle package. * suppose some ith-property with (oid,uid,value) being RegExp * oid='oid'; // property's object id or key * uid='##x#x#...#oid'; // appropriate universal identifier of this property * * If pO is this property's parent object, * property's value is RegExp object * * pO[oid]=new RegExp(body,mig); // (1) {RegExp} * unCycle.uiDirect[uid]=new RegExp(body,mig); // (2) {RegExp} * * to create new property for parent object with string value: * pO[oid+RE]=body+RE+mig; // (3) {string} * where RE='RE'+rndm10 * and * rndm10='0987654321'; // -random string of 10 digits from 0-9 in each place * The number of random digit depends and could be any of your choise * rndm4, ..rendm2 ... * * * 2. delete pO[oid]; // if you would no have done it yourself * // JSON.stringify will not do it but only * // would set empty appropriate object * 3. appropriately, the uid of this new property will be * uidNewPr=uid+RE; // {string} and uiDirect object will contain property * unCycle.uiDirect[uid+RE]=body+RE+mig; // (4) {string} * * Inclusion of RE string into oid and string value of newly created property * simplifies it's identification and following transformation of * body+RE+mig string into new RegExp(body,mig) : * * var vs=pO[oid+RE].split(RE); // (5) * var newRegExpValue= new RegExp( vs[0],vs[1]); // (6) * * when you would be need to get RE you should look for a property * with string value, i.e. for (key,value) : * * (typeof value === 'string') && three remarkable points: * 1. For newly created property - property name and * property string ciphered value contain the same pattern RE * 2. the property name has it at the end of string * 3. the value's body and mig string could be obtained splitting the * valueString by RE pattern * ........................................... */ exports.regStr = (function () { return { nMax: 20, nMin: 2, /** * check if parameter is ordinary object * @param {}par parameter * @return {Boolean} true in positive check, false otherwise */ isOb: function (par) { if ( typeof par === 'object' && par !== null && !(Array.isArray(par)) && !(par instanceof Boolean) && !(par instanceof Date) && !(par instanceof Number) && !(par instanceof RegExp) && !(par instanceof String)) { return true; } return false; }, /** * check if parameter is an array * @param {}par parameter * @return {Boolean} true in positive check, false otherwise */ isAr: function (par) { if (Array.isArray(par)) { return true; } return false; }, /** * Returns n-digital random string preceded by 'RE' prefix. * @param {number}opt_n number of random integer digits in string * obtained by means of MatH.rndm cutting current time in * milliseconds. Optional.Default =2 * @param {boolean}opt_t if true permits to use current time cutting * algorithm: last opt_n digits are cut from actual time value * expressed in milliseconds for obtaining random RE+digital string. * Default===false * @return {string} random string precede by 'RE' followed by opt_n * random digits; for ex.: 'RE0123456789' */ reN: function (opt_n, opt_t) { var n = opt_n || this.nMin; var t = opt_t || false; var v = ''; if (t) { v = new Date().getTime().toString().slice(-n); } for (var i = 0; i < n; i++) { v += Math.floor(Math.random() * 10); } return 'RE' + v; }, /** * converts RegExp into string. * @param {RegExp}rE regular expression object * @param {string}opt_ky - key (possible property name if (opt_ky,rE) pair * is some (key,value) pair of some property and it's value of some object * @return {boolean|Object} - false if rE is not a RegExp object or * {Object} - o with properties: * {string}o.c - cipher * {string}o.b body * {string}o.mig - reg exp flags m,g,i * {string}o.v - reg exp ciphered as string value * {string}o.ky - key value modified * {function}o method returning object ob with one property * obj[o.ky]=o.v */ reStr: function (rE, opt_ky) { if (rE instanceof RegExp) { var ky = opt_ky || ''; var b = rE.source; var mig = (rE.global ? 'g' : '') + (rE.multiline ? 'm' : '') + (rE.ignoreCase ? 'i' : ''); // mig var ciph = this.reN(); return { c: ciph, b: b, mig: mig, v: b + ciph + mig, ky: ky + ciph, /** * @return {Object} -two properties object * {key:keyString,value:valueString} */ o: function () { var ob = {}; var key = this.ky; ob[key] = this.v; return ob; } }; } else if (opt_ky && rE[opt_ky] && rE[opt_ky] instanceof RegExp) { rE = rE[opt_ky]; return this.reStr(rE, opt_ky); } else { return false; } }, /** * for RegExp or RegExp properties of input object(first parameter) returns * or cipher object, or array of cipher objects(or conversion object) for * each RegExp property adding that ciphers objects obtained as new * properties with ciphered keys who could be used in JSON.stringify and * reverse parse for those RegExps if o is not RegExp or object containing * RegExp properties method returns null * @param {RegExp|Object|Array}o RegExp or compound object or array whose * properties or elements could be RegExp * @param {string|number}opt_i Optional. Property's name or index * of o array's element in consideration * @return {Object|Array Object[]|null} returns Null if input object is not * RegExp and does not contain any RegExp among properties values. * Otherwise return conversion(or cipher) object (for single input * RegExp) or array of objects appropriate to each RegExp property. * The definition of conversion object see in reStr method description. */ streger_: function (o, opt_i, opt_obAr) { var obAr = (opt_obAr !== undefined) ? opt_obAr : []; var ob; if (o instanceof RegExp) { ob = this.reStr(o, opt_i); return (function (a, b) { var o = {}; o[a] = b; return o; }(ob.ky, ob.v)); } else if (opt_i && o[opt_i] instanceof RegExp) { return this.streger_(o); } else if (this.isAr(o)) { // array of elements some of which could be RegExp for (var j = 0; j < o.length; j++) { if (this.isOb(o[j]) || this.isAr(o[j])) { this.streger_(o[j], undefined, obAr); continue; } else if (o[j] instanceof RegExp) { ob = this.reStr(o[j], j); o.push(ob.o()); obAr.push(ob); } } return obAr; // obAr.push(ob); } else if (this.isOb(o)) { // compound js-object for (var ij in o) { if (this.isOb(o[ij]) || this.isAr(o[ij])) { this.streger_(o[ij], undefined, obAr); continue; } else if (o[ij] instanceof RegExp) { ob = this.reStr(o[ij], ij); o[ob.ky] = ob.v; obAr.push(ob); } } return obAr; // obAr.push(ob); } else { return null; } }, /** * for RegExp or RegExp properties of input object(first parameter) returns * or cipher object, or array of cipher objects(or conversion object) for * each RegExp property adding that ciphers objects obtained as new * properties with ciphered keys who could be used in JSON.stringify and * reverse parse for those RegExps if o is not RegExp or object containing * RegExp properties method returns null * @param {RegExp|Object|Array}o RegExp or compound object or array whose * properties or elements could be RegExp * @param {string|number}opt_i Optional. Property's name or index of * o array's element in consideration * @param {boolean}opt_cipherMode Selects output objects' format * conversion object (while false) or cipher object(when is true) * Default=true; * @return {Object|Array Object[]|null} returns Null if input object is not * RegExp and does not contain any RegExp among properties values. * Otherwise return conversion(or cipher) object (for single input * RegExp) or array of objects appropriate to each RegExp property. * The definition of conversion object see in reStr method description. */ streger: function (o, opt_i, opt_obAr, opt_ciphMode) { var ciphMode = opt_ciphMode === false ? false : true; var obAr = (opt_obAr !== undefined) ? opt_obAr : []; var ob; if (o instanceof RegExp) { ob = this.reStr(o, opt_i); return (function (a, b) { var o = {}; o[a] = b; return o; }(ob.ky, ob.v)); } else if (opt_i && o[opt_i] instanceof RegExp) { return this.streger(o, '', '', ciphMode); } else if (this.isAr(o)) { // array of elements some of which could be RegExp for (var j = 0; j < o.length; j++) { if (this.isOb(o[j]) || this.isAr(o[j])) { this.streger(o[j], undefined, obAr, ciphMode); continue; } else if (o[j] instanceof RegExp) { ob = this.reStr(o[j], j); if (ciphMode) { o.splice(j, 1, ob.o()); } else { o.push(ob.o()); } obAr.push(ob); } } return ciphMode ? o : obAr; } else if (this.isOb(o)) { // compound js-object for (var ij in o) { if (this.isOb(o[ij]) || this.isAr(o[ij])) { this.streger(o[ij], undefined, obAr, ciphMode); continue; } else if (o[ij] instanceof RegExp) { ob = this.reStr(o[ij], ij); o[ob.ky] = ob.v; obAr.push(ob); if (ciphMode) { delete o[ij]; } } } return (ciphMode) ? o : obAr; } else { return o; } }, /** * reconverts 'stringified' regStr into RegExp object, presuming * that regStr has been got using reStr algorithm (see above) * Suppose some parent object pO has property keyRe=key+RE * pO[keyRe]=v1ReV2=v1+RE+v2; where RE determined by pattern * /RE\d{N}/ N - some integer, for ex. RE='RE3455' if N=4 * then pO[key+'RE3455']=v1+'RE3455'+v2; * the following single property object will be returned with * property name key and value new RegExp(v1,v2) - * {'"'+key+'"':new RegExp(v1,v2) } * where v1 - correct literal of regular expression without external * slashes. and v2 - correct regular expression flags ( containing * some combination of single 'm' or 'i' or 'g') * @param {string}keyRe - ciphered property name * @param {string}v1ReV2 - ciphered value string * @param {nubmer}opt_n - number of digit in RE string of random digits * used for ciphering RE parameter RE='RE'+rndm(opt_n) * if RE='RE12345' opt_n = 5. Optional. Default value is 2; * return {Object} {key:..value:*} */ regUpN: function (keyRe, v1ReV2, opt_n) { var n = opt_n || this.nMin; var ptt1 = new RegExp('RE\\d{' + n + '}$'); var notGMI = /[^gmi]+/g; var re, k1, vs; if (typeof v1ReV2 === 'string') { if (ptt1.test(keyRe)) { k1 = keyRe.replace(ptt1, ''); re = keyRe.replace(keyRe.replace(ptt1, ''), ''); if (v1ReV2.search(re) < 0) { return { key: keyRe, value: v1ReV2 }; } vs = v1ReV2.split(re); if (notGMI.test(vs[1])) { throw 'incorrect values of flags'; } else if (!this.gim(vs[1])) { throw 'incorrect regExp flags'; } return { key: k1, value: new RegExp(vs[0], vs[1]) }; } else { return { key: keyRe, value: v1ReV2 }; } } else { throw 'value should be a string'; } }, /** the same as regUp but opt_n is determined automatically * possible values from 1 to 20, where 20 is set in * regstr.nMax property * * reconverts 'stringified' reStr into RegExp object, presuming * that reStr has been got using reStr(ing) algorithm (see above). * Suppose some parent object pO has property named keyRe=key+RE * pO[keyRe]=v1ReV2=v1+RE+v2; where RE determined by pattern * /RE\d{N}/ N - integer, for ex. RE='RE3455' if N=4 * then pO[key+'RE3455']=v1+'RE3455'+v2; * The following single property object will be returned with * property name key and value new RegExp(v1,v2) - * {'"'+key+'"':new RegExp(v1,v2) } * where v1 - correct literal of regular expression without external slashes * and v2 - correct regular expression flags ( containing * some combination of single 'm' or 'i' or 'g') * @param {string}keyRe - ciphered property name * @param {string}v1ReV2 - ciphered value string * @param {nubmer}opt_n - number of digit in RE string of random digits * used for ciphering RE parameter RE='RE'+rndm(opt_n) * if RE='RE12345' opt_n = 5. Optional. Default value is 2; * @return {Object} {key:value} */ regUp: function (keyRe, v1ReV2) { var notGMI = /[^gmi]+/g; var re, k1, vs, nrek; if (typeof v1ReV2 === 'string') { // auto detects n and return obj with two properties (k1:{string},re:{string}} // or 0 if detection failed nrek = this.nDigs(keyRe); if (nrek) { k1 = nrek.k1; re = nrek.re; if (v1ReV2.search(re) < 0) { // no ciphering took place return { key: keyRe, value: v1ReV2 }; } vs = v1ReV2.split(re); if (notGMI.test(vs[1])) { throw 'incorrect values of flags'; } else if (!this.gim(vs[1])) { throw 'incorrect regExp flags'; } else { return { key: k1, value: new RegExp(vs[0], vs[1]) }; } } else { return { key: keyRe, value: v1ReV2 }; } } else { throw 'value should be a string'; } }, /** * returns constant describing RegExp modifieyers * @param {RegExp}reO - RegExp object * @return {string} depending of regexp modifyers' values possible values; 'mig','ig',mi','mg','m','i','g','' */ migV: function (reO) { if (reO instanceof RegExp) { return (reO.multiline ? 'm' : '') + (reO.ignoreCase ? 'i' : '') + (reO.global ? 'g' : ''); // mig } else { return undefined; } }, /** descript regger_old * identifies ciphered 'regexpSting' properties of the object * if any and * converts them into RegExp properties changing keys into deciphered state. * @param {Object}o object in consideration * return {Object} converte or original object */ /** * for input object o identifies properties with string values * ciphered to convert-reconvert RegExp * (for compound object: that are pairs propertyName:propertyStringValue, * conforming ciphering algorithm: keyRE:v1ReV2, where * kyeRE - string,property name, v1ReV2 - string value. The details of cipher * see description of method regUp; * for array's case the elements are identifed being simple one property * object like {kyeRE:bodyREmig} which is appropriate to regular expression * object new RegExp(body,mig) ) * string and reconvert them back after JSON.parse ) * so called 'regexpSting' properties of the object or elements of array * Identification of such properties or elements is carried out * automatically. * * converts them into RegExp propperties changing keys into deciphered state. * For array having among elements some object containg 'cypered' (key,value) * pairs among propreties determins deciphered index of the property * If deciphered state of key for object already * @param {Object|Array}o object in consideration * @param {Object|Array}pO parent object. Optional. * @param {number|string}ind index or property name of object inside * parent object * @param {Object|Array}pPO preParent of object. Optional. * used in the case when o is subobject of pO object of * who has preparent being an array * @return {Object} converte or original object * * RegExp is coded in another object - cipher * cipher carries info regarding RegExp value and * who is itself presuming three states: * 1. separate single RegExp * 2. RegExp as an element of array with indexi k * 3. RegExp as a value of some object's property with key(property name) * * So, cipher is single object independently where it is * located - in object as a property * or in array as an element * or is separate variable * cipher={keyRE:bodyREmig}. * So, after decode * 1. key is or '' - for the case of single RegExp. In this case * it's value is assigned to the variable carring cipher or occupies * the same index is cipher was array element or becomes the property * value of the property carring cipher * @example * var ciph={"REddd":body+"REddd"+mig} -> key='' -> after conversion * ciph=new RegExp(body,mig); * 2. key sutisfies the equation * /^\d+$/g.test(key)===true all charachters of key are digits * in this case * a) If the parent of cipher was an Array than the digital key * is iqual to index of element who should contain this RegExp value * If parent array already has the element with the same index and value * no changes are done for parent and cipher is removed. * b) If the parent of cipher was Object than property is created * with digital name and property equal to RegExp value. If parent object * already has property with the same name and value nothing is going on, * but cipher is removed * 3. key does not cosist only of digits (typical string). A) if the parent of cipher was Object than key should be the name * of the property whose value will be RegExp, cipher is removed. * If parent object already has property with the same name and values * nothing is going on, but cipher is removed * B) If parent of cipher was an array. The cipher is exchanged by onother * object {key:new RegExp(body,mig)} * * test string: * @example * r.reger( JSON.parse( * JSON.stringify( * r.streger([/aaa/mig, /ccc/mi, {re:/asdf/g}, /fff/gmi])))); */ reger: function (o, pO, ind) { if (this.isAr(o)) { return this.regerAr(o, pO, ind); } else if (this.isOb(o)) { return this.regerOb(o, pO, ind); } return o; }, /** * reger part for Array object handling */ regerAr: function (o, pO, ind) { // array of elements some of which could be RegExp for (var j = 0; j < o.length; j++) { if (this.isOb(o[j]) || this.isAr(o[j])) { this.reger(o[j], o, j); continue; } } // trail handling var depo = []; this.fillDepo(o, depo); // depo array sorting and incerts it's elements into pO array if (depo.length > 0) { this.fromDepoToArr(o, depo); } return o; }, /** * fills depo for o array and for removing properties or objects forming * pairs inserted in depo * Creates intermediary object <deletable> determining for each element * of depo being object which properties should be deleted and * array <spliceable> determining which element of parent array should * by mense of splice(ind,1) be removed * @param {Array}o * @param {Object Array[]}depo array of two elements arrays */ fillDepo: function (o, depo) { var deletable = {}; // {j:,dels:[]}; var spliceable = []; var k; for (var j = 0; j < o.length; j++) { if (!this.isOb(o[j])) { continue; } // only of elements being objects for (var ij in o[j]) { // searchs, stores in depo, and works out keys being digitals only // and having RegExp as value if (o[j][ij] instanceof RegExp) { if (/^\d+$/.test(ij)) { // key consists of digits only k = parseInt(ij, 10); depo.push([k, o[j][ij]]); if (Object.keys(o[j]).length === 1) { spliceable.push(j); } else { if (!deletable.hasOwnProperty(j)) { deletable[j] = {}; } if (!deletable[j].hasOwnProperty("dels")) { deletable[j].dels = [ij]; } else { deletable[j].dels.push(ij); } // delete o[j][ij]; } } } // if all properties of object being j-index element of paretn array // are prescribed to be deleted (on the bases of deletable[j].dels // property) // element as a whole is to be removed and deletable[j] object is not // out of // consideration. it's not neccessary. if (deletable[j] && Object.keys(o[j]).length === deletable[j].dels.length) { spliceable.push(j); delete deletable[j]; } } } this.cleaner(o, deletable, spliceable); }, /** * removes arrays elements determined by spliceable array * and delete elements objects properties determined by deletable * @param {Array}o * @param {Object}deletable - this object property deletaba[j] is arrays of * o[j] object properties to be deleted */ cleaner: function (o, deletable, spliceable) { var jj; for (var j = o.length - 1; j >= 0; j--) { if (!this.isOb(o[j])) { continue; } // only of elements being objects jj = spliceable.indexOf(j); if (jj > -1) { o.splice(spliceable[jj], 1); // o.splice(jj,1); continue; } if (deletable[j]) { if (deletable[j].dels.length > 0) { for (var jd = 0; jd < deletable[j].dels.length; jd++) { delete o[j][deletable[j].dels[jd]]; } } } } }, /** * "Digital keys practice": * The notion "Digital key" means property name consisting only of digital * characters. For ex.: '1','12', '234' ... something conforms regular * explression /^\d+$/. * Let's use short variable form <n> as notaion of a Digital key. * Here is an example of digital key in single property object - * {n:new RegExp(v1,v2)} with value as RegExp object. * Decipher converts cipher object into RegExp. * Decipher procedure uses single property digital key object as Intermediary * in conversion. Cipher object like {nRe:v1ReV2} transforms into * {n:new RegExp(v1,v2}, is stored in Depo and than converts into * RegExp object. Depo keeps key value pairs ordered by digital keys values. * to fulfill final conversion. * * Decipher having found digital key single property object as an array's * element will transfere it into an element with value new RegExp(v1,v2) * and index n. * i index n index * pO=[...,{nRe:v1Rev1},... ] ==> [ ..., new RegExp,...] * n could not be equal to i, but typically is when deciphering is reverse * procedure of previous ciphering: * [...,rE,...] ->cipher-> [...,{iRe:v1ReV2},..] ->decipher -> [...,rE,..] * Due to some reasons parent array in ciphered form could have few * cipher objects with * - equal digital keys and equal/not equal regExp values * - or has one object with kye,value pairs as separate properties * Decipher follows convention: * 1.index number got from digital key prevailes indices of existed elements, * but if parent array alredy has element with RegExp value and index * coinsiding that determined by digital key new element is not added. * 2.few cipher objects having equal digital keys combined in commom array * who becomes an element of parent array with index equal to digital key * vlaue. I.e. [..,{k1:rE1},{k1:rE2,...,{k1:rE3}] converts into -> * [..., [rE1,rE2,rE3],...] subarray with rE-s has index k1. The same result * will be when cipher object are cipher pairs propertis of parent object: * [...{k1:re1,k2:re2,k3:re3,...] will be converted into * [...,re1,...,re2,re3], where elements re1,re2,re3 have indices k1,k2,k3 * appropriately. */ /** * transfer depo pairs into array inserting them into appropriate * places (indices). When index locations of regexp values coincide * RegExp-s are input in array as element of parent array with that index. * If some pair of (key,value) are repeated no new element is added to * parent array. * @param {Array}o array * @param {Object Array[]}depo - depo 2d-array * @return {} */ fromDepoToArr: function (o, depo) { this.sortDepo(depo); var iin; for (var id = 0; id < depo.length; id++) { iin = depo[id][0]; if (iin < o.length) { if (!Array.isArray(o[iin])) { // element with index iin is not array if (o[iin] instanceof RegExp !== true) { // actual element with insertion index is not RegExp o.splice(depo[id][0], 0, depo[id][1]); } else if (o[iin].source !== depo[id][1].source || this.migV(o[iin]) !== this.migV(depo[id][1])) { // inserting place(index) is occupied be array // RegExp populating location index is not equivalent to new one // creates new array with two rE elements var arrToInsert = [new RegExp(o[iin].source, this.migV(o[iin])), depo[id][1]]; o.splice(iin, 1, arrToInsert); } } else if (this.allARE(o[iin])) { // parent array's element is array itself and has only regExp-s // as members o[iin].push(depo[id][1]); } } else { o.push(depo[id][1]); } } }, /** Checks if all elements of array are RegExp? * @param {Array}arr - array being checked * @return {boolean} true if answer is yes. */ allARE: function (arr) { for (var ich = 0; ich < arr.length; ich++) { if (arr[ich] instanceof RegExp !== true) { return false; } } return true; }, /** * sorts elements of array being arrays of pairs by their first element value * return modified array as well as modifies input array as well * (but not instanciate it) * @param {Object Array[]}listNRe - array of arrays. Each element is * an array of two elements first of which has integer value, ascending * order of which is the rule for sorting * @retrun {Array} array sorted by value of first element of arrays-elements */ sortDepo: function (depo) { var a = []; a = depo.sort(function (a, b) { return parseInt(a[0]) - parseInt(b[0]); }); return a; }, /** * clones array. Creates new instance of array * @param {Array}ar - array instanciating * @return {Array} new instance of array */ cloneArr: function (ar) { var a = []; for (var i = 0; i < ar.length; i++) { a.push(ar[i]); } return a; }, /** * reger part for Object handling * @param {Object}o */ regerOb: function (o, pO, ind) { // compound js-object var testO; for (var ij in o) { if (this.isOb(o[ij]) || this.isAr(o[ij])) { this.reger(o[ij], o, ij); continue; } else if (typeof o[ij] === 'string') { testO = this.decipher(o, ij, pO, ind); if (testO instanceof RegExp) { return testO; } } } return (pO) ? pO : o; }, /** description of decipher_actual_0_4_0:function(o,j,pO,opt_ind){ * @param {Object}o - cipher object value typeof o[j] ==='string' * @param {number|string}j - cipher object keyRe property name, * o[j]=cipher {string} value * @param {Object|Array|variable}pO - cipher parent object or variable * @param {number|string}opt_ind pO[ind]=o * @return * !important: pO[ind]=o={j:bREmig} || pO=o={j:bREmig}, pO - external variable */ decipher: function (o, j, pO, opt_ind) { if (!o[j] || typeof o[j] !== 'string') { // cipher value should be string throw 'Problem ! - value of cipher object is not a string' + ' (should be string in the form vReV2)'; } var ob = this.regUp(j, o[j]); if (ob.value instanceof RegExp !== true) { return (pO) ? pO : o; } if (!pO) { var oTst = this.deciph(o, j, ob); return (oTst !== o) ? oTst : o; } else { var oTstPO = this.deciphPO(o, j, pO, opt_ind, ob); return (oTstPO !== pO) ? oTstPO : pO; } }, /** * part of decipher handling cases when parent object pO is not set * @param {Object}ob {key:'somekey',value: RegExp} ob.value is RegExp object */ deciph: function (o, j, ob) { if (!ob.key) { // unnamed or unindexed decipher if (Object.keys(o).length > 1) { throw 'number of properties mast not exceed 1'; } return ob.value; } else { if (!o[ob.key] || o[ob.key] instanceof RegExp !== true || o[ob.key].source !== ob.value.source || this.migV(o[ob.key]) !== this.migV(ob.value)) { o[ob.key] = ob.value; } delete o[j]; } return o; }, /** * part of decipher handling cases whith determined parent object pO * and possible opt_ind * All parameters see in decipher method but ob * @param {Object}ob {key:'somekey',value: RegExp} ob.value is RegExp object */ /** * part of decipher handling cases whith determined parent object pO * and possible opt_ind * All parameters see in decipher method but ob * @param {Object}ob {key:'somekey',value: RegExp} ob.value is RegExp object */ deciphPO: function (o, j, pO, opt_ind, ob) { var ind = opt_ind; if (this.isOb(pO)) { // pO is object if (!ob.key) { // unnamed or unindexed decipher if (Object.keys(o).length > 1) { throw 'Logical error. ' + 'o should be single property object of type ' + 'pO={..,o:{RE:vREv2},..}'; } o = ob.value; } else { // parent object is object o[ob.key] = ob.value; delete o[j]; } } else if (Array.isArray(pO)) { // pO is Array if (!ob.key) { // unnamed or unindexed decipher if (ind !== undefined) { pO[ind] = ob.value; } else { throw 'impossible abcense of index of pO element'; // pO=ob.value; } } else { // ob.key is tipical string consists not only of digits o[ob.key] = ob.value; delete o[j]; } } else { throw 'strange type of parent object. Varification is neccessary.'; } return pO; }, /** * checks if or 'i' or 'g' or 'm' encounter * in checking str more than one time * @param {string}str string * @return {boolean} false if repeattance is too large * true is repeatance does not exceed 1 */ gim: function (str) { var g = 0, i = 0, m = 0; for (var j = 0; j < str.length; j++) { g += (str[j] === 'g') ? 1 : 0; m += (str[j] === 'm') ? 1 : 0; i += (str[j] === 'i') ? 1 : 0; } if (g > 1 || i > 1 || m > 1) { return false; } else { return true; } }, /** * tests key string on have RE pattern at the end * at success return n,k1,re property in context of * regUp method * @param {string}key - property name * @retrun {number|Object} or integer 0 if testing failed * or {Object}obj * @property {number}obj.n - number of random digits in RE detected * @property {string}obj.k1 - refined key (with RE subtracted) * key=k1+RE * @property {number}obj.re - RE string value RE='RE'+rndmN, * were rndmN is a string of length n where each character is a digit */ nDigs: function (key) { var n = 0, ptt; var nMax = this.nMax; for (var ig = n; ig < nMax; ig++) { ptt = new RegExp('RE\\d{' + (ig + 1) + '}$'); if (ptt.test(key)) { return { n: ig + 1, k1: key.replace(ptt, ''), re: key.replace(key.replace(ptt, ''), '') }; } } return n; }, /** * replacer to use as seconde parameter of * JSON.stringify(o,h.replacer.bind(h)) to stringify * objects with circular references * parameters key and value is set by definition of JSON.stringify(...) */ replacer: function (key, value) { if (key === '' || key === undefined || !key && (key !== 0)) { this.streger(value); } return value; }, /** * reviver function to use as second parameter of * JSON.parse(o1,h.reviver.bind(h)) to parse * json string o1 resulted from * JSON.stringify(o,h.replacer.bind(h)) * parameters key and value are set in accordance with definitions * of JSON.parse(...). Is used for RegExp as properties of object * or elements of an array * Attention!! See comment regarding h.re... in h.replacer method * description. */ reviver: function (key, value) { if (!key) { this.reger(value); } return value; }, /** some regString for testing */ testRE: [ [ 'asdfRE1234', 'asdfRE1234mi', 'asdfRE1234gmi', 'asdfRE1234m', "^.+[\d{4}]RE7456gm" ], [ ['k1RE1234', 'asdfRE1234'], ['k2RE12345', 'asdfRE12345mi'], ['k3RE123456', 'asdfRE123456gmi'], ['k4RE1234567', 'asdfRE1234567m'], ['k5RE745', "^.+[\d{4}]RE745gm"] ], [ ['1RE1234', 'asdfRE1234'], ['2RE12345', 'asdfRE12345mi'], ['8RE123456', 'asdfRE123456gmi'], ['4RE1234567', 'asdfRE1234567m'], ['5RE745', "^.+[\d{4}]RE745gm"], ['0RE745', "^.+[\d{4}]RE745gm"] ] ] }; }());