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
JavaScript
/**
* 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"]
]
]
};
}());