RevolveR CMF front-end coding standart

RevolveR Code style

Introduction in Revolver coding standart for front-end parts

Revolver CMF equipted with front-end library based on Object Literal pseudo class arhcitecture on latest ECMAScript. Main Core library intended to manipulate with Document Object Model, performs dynamic requests and apply CSS animations. Front-end library is a part of the framework related to work with back-end parts. Also this script extends standart browser futures such as flexible events API and UI\UX components.

Core part contains some UI\UX elements with base design and layout placed on CSS.

Connectiong library

To start work with RevolveR link to Core script placed before closing body tag section of document and design part placed in CSS file connected in head section of document.



					<link media="all" rel="preload" as="style" href="./revolver.css" />

					</head>

				

					
					<script data-part="revolver core" src="./revolver.js" ></script>

					</body>

				

Initialization

Work with Core means the programmer use proxyed namespace. Extending of main library means adding futures in RR Object Literal without proxyng in external script before initialization.

Initialization process faster, more symple and welcomes placing your code into external script before closing body tag with defer enabled attribute to allow skip Document Ready State.



					<script data-part="revolver tooling" src="./script.js" defer="defer"></script>

				

Initialization and proxyfication is simple. Define your namespace in argument of constructor.



					if( !self.run ) {

						const R = new R_CMF_i[ 0 ]( 'R' );

						self.run = true;

					}

					...

				

When this code executes constructor create new instance, bind namespace R as proxyed and launch front-end futures.

Extending Core library

To extend RR library you can put the code into external script with defer attribute. Place this scripts after main library.



					<script data-part="revolver core" src="./revolver.js">
					</script>

					<script data-part="revolver interface time futures" src="./timeThemes.js" defer="defer"></script>

					</body>

				

To extend the Core simple add into RR your methods and functions. Also you can use all power of Core API to create new futures.



					RR.timeFutures = ( t = null ) => {

						let dateTime = new Date();

						let hours = dateTime.getHours();

						let timeStyle = RR.sel('.revolver__time-futures');

					};

				

After initialization all of this methods are avalilable with proxyed link R.



					R.timeFutures('12:00');

				

File format notes

Script files must use of UTF-8 encoding without BOM with OS Linux line endings.

To improve programming speed used tabulations instead of spaces to rebound code blocks.

Because all front-end scripts go through compression programmer need to be carefull with semicolon on the end of line and end of function definition.



					R.someFututre(); // semicolon (;) requred

					R.anotherSomeFuture = () => {


					}; // semicolon (;) required

				

Code formating notes

Variables

All variables need to be gruped by sense. In priority using of let instead of const.



					let [ comments, strings, res, safe ] = [ [], [], [], { '<': '<', '>': '>', '&': '&' } ], l;

					let all = { 'C': comments, 'S': strings, 'R': res };

				

Flags and stacks

When create dynamic application sometimes programmer need think about how to free a memory after document parts reloaded. Namespace RR allow to keep data when window reloaded via proxyed link.



					// log event
					RR.events.push([ i, eLock ? m +'::lock' : m, c, eventIdMD5 ]);

				

Line breaks

To avoid creation of a bad programm and simplify code reading for another developers code lines mast be separated by emty lines. It's have no effect to resulting script because back-end parts optimize sources and compress lines.

Functions

When define functions use arrow definition if context this not need. Another way use expanded definition.



					R.event('.basket_handler', 'click', (e) => { // used arrow definition because context this not need

						R.loadURI( '/basket/', e.target.title );

					});			

					R.event('.revolver__search-box form', 'submit', function(e) { // expanded definition used with context this

						e.preventDefault();

						if( e.isTrusted ) {

							// Prevent search box fetching
							self.searchAction = true;

							R.search(this.querySelectorAll('input[type="search"]')[ 0 ].value);

						}

					});

				

Arguments brackets should not be separated from function name when it's not a arrow function. Always place opening curly bracket on the same line where function defined.

Place closing curly bracket always on new line.

Protected

Because of native JavaScript class future have no protected methods recomended to use _ prefix for RR namespace methods. It works better.



					(() => {

						self.RR = { // place RR in window object

							_manageKeys: ( k, _that ) => { // _ - pseudo class protected method prefix

								if( !k ) {

									RR._privacyKeys = [];

								}
								else {

									RR._privacyKeys.push( k );

								}

								...

							},

						};

					})(); // when this IIFE closeure executes possible to create protected methods for Object Literals defined inside

				

Default arguments

Defining default arguments values is good. Use it when possible.



					(( s, v, c = [ 0, 0, 0, 0 ], cnt = 0 ) => { // example of binding default arguments when it's not present

						void requestAnimationFrame(

							function frame( d ) {

								// g - time gone; s - start time
								let g = d - s;

								let colors = ( h, m ) => {

									return parseInt(

										lerp( h, m, g / t )

									);

								};

								let f = RR.effects(r, g / t);

								e.style.setProperty(p, 'rgba('+ colors(c[ 0 ], v[ 0 ]) * f +','+ colors(c[ 1 ], v[ 1 ]) * f +','+ colors(c[ 2 ], v[ 2 ]) * f +','+ parseFloat(lerp( v[ 3 ], c[ 3 ], g / t )) * f +')');

								// Is animation time over? if not do next frame
								if( d < t ) {

									void requestAnimationFrame(frame);

								} 
								else {

									e.style.setProperty(p, 'rgba('+ v[ 0 ] +','+ v[ 1 ] +','+ v[ 2 ] +','+ v[ 3 ] +')');

									if( callback && cnt < 1 )  {

										callback.call(e); 

										cnt++;

									}

								}

							}

						);

					})(performance.now(), RR.getRGB(v), RR.getRGB(RR.styleGet(e, p)));

				

Generators

Good tone is create program with generators when not need to return a result.



					RR = {

						...

						// Filter for HTML objects only in Node Lists and are useful
						filterHTML: function * f(n) {

							for( let i of n ) {

								if(
									i.nodeName !== '#comment' &&
									i.nodeName !== '#text' && 
									!RR.isUseless(i) &&
									!RR.isN(i) && 
									!RR.isU(i) && 
									!RR.isS(i) && 
									!RR.isF(i) &&
									!RR.isA(i)

								) {

									if( !!i ) {

										yield i;

									}

								}

							};

						},

						...

						let useful = [ ...RR.filterHTML(shadows) ];

					};

				

Sensed IIFE closeure

Sometimes not possible to descript a lot of arguments when create a function that need more optimizations. To improve code readability and performance use next technique.



					//s = performance.now();		// time now
					//m = (i[1][0] - i[1][1]) / t;	// speed
					//x = i[1][0];					// destination
					//h = i[1][1];					// duration
					//y = i[1][2];					// units
					//p = i[0];						// propertie

					(( s, p, m, x, y, h ) => {

						void requestAnimationFrame(

							function frame( d ) {

								// g - time gone; s - start time; m - speed;  z - delta
								let g = d - s;
								let z = x - (m * g);

								// Time escape preventing
								if( g > t ) {

									g = t;

								}

								// Apply FX's
								let f = RR.effects(fx, g / t);

								// Test for units are defined and set CSS value correct
								RR.setMatrixCss(e, p, z * f + (y ? y : ''));

								// Animation time is over? if not perform next frame
								if( g < t ) {

									void requestAnimationFrame(frame);

								} 
								else {

									// Fix endpoint to prevent escaping ranges
									RR.setMatrixCss(e, p, h + (y ? y : ''));

									if( c && cnt < 1 )  {

										c.call(e);

										cnt++;
									}

								}

							}

						);

					})(performance.now(), i[ 0 ], (i[ 1 ][ 0 ] - i[ 1 ][ 1 ]) / t, i[ 1 ][ 0 ], i[ 1 ][ 2 ], i[ 1 ][ 1 ]);

				

Code understanding is faster when programmer place some operations with arguments into back of immediately invoked self expression function closure.

Timers and intervals

When not need to stop timer execution please always use void before initialization because it cause to free memory effective immediately.



					void setTimeout(() => {

						if( RR.recording ) {

							RR.recordStop();

							RR.recording = null;

						}

					}, 5000);

				

If-else, ternary and switch statements

Switch

Best practice is using switch statement if possible to improve code readability.



					// Zoom prevention
					RR.event('html', 'keydown:lock', (e) => {

						if( e.ctrlKey ) {

							switch( e.which - 0 ) {

								case 61:
								case 107: 
								case 173:
								case 109:
								case 187:
								case 189:

									console.log('... keyboard zoom prevented when View Port Units interface active');

									e.preventDefault();

									break;

							}

						}

					});

				

Using of strict typing in case is better then work with only strings.

If-else

Opening statement bracket must not be separeted by space. Inside brackets statement criterions must be separeted by space.

Opening curly bracket must be placed on the same line as criterion statement. Closing curly bracket always must be placed on new line.

Use brackets always instead of one line comparing.



					// Night mode
					if( hours > 20 || hours <= 5 ) {

						if( !R.night ) {

							if( timeStyle ) {

								RR.rem(timeStyle);

								RR.morning = null;

								RR.evening = null;

							}

							RR.nightMode();

							console.log('Night come ...');

						}

					}				

				

Compare difficulty else if statements

Sometimes impposible to compare light learning code with switch and else if because criterion is to long. Optimize it simple.



					if( [ 'width', 'height', 'top', 'left', 'bottom', 'right' ].includes(prop) ) {

						if( [ 'top', 'left', 'bottom', 'right' ].includes(prop) ) {

							let pos = x.style.position;

							if( ![ 'absolute', 'relative' ].includes( pos ) ) {

								x.style.position = 'relative';

							}

						}

						var from = RR.numberCSS(RR.styleGet(x, p[ 0 ]), p[ 0 ])[ 0 ];

						// Convert % to px
						if( unit === '%' && from !== 0 ) {

							dest *= from / 100;
							unit = 'px';

						}

					}
					else if( [ 'backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor', 'textDecorationColor', 'columnRuleColor', 'textEmphasisColor', 'caretColor' ].includes(prop) ) {

						if( /color/i.test(prop) ) {

							RR.colorMix(x, p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], callback);

							if( callback ) {

								callback = null;

							}

						}

					}
						
				

Ternary operators

Welcomes to use of ternary operators when it possibble but with prejudice to understanding.



					// Set title fot history
					self.onpopstate = ( e ) => {

						document.title = (self.history.state) ? self.history.state.title : RR.title;

					}

					RR = {

						...
						treeHacks: ( e ) => (e[ 0 ]) ? e[ 0 ] : e,

						// Test for HTML objects are equal
						equality: ( a, b ) => a.offsetLeft === b.offsetLeft && a.offsetTop === b.offsetTop && a.outerHTML === b.outerHTML ? true : null,
						...

					};

				

–°ycles

Best choice is avoid of using forEach futures to prior native for statements.

When possible use of method.



					RR = {

						...

						// Filter for HTML objects only in Node Lists and are useful
						filterHTML: function * f(n) {

							for( let i of n ) {

								if(
									i.nodeName !== '#comment' &&
									i.nodeName !== '#text' && 
									!RR.isUseless(i) &&
									!RR.isN(i) && 
									!RR.isU(i) && 
									!RR.isS(i) && 
									!RR.isF(i) &&
									!RR.isA(i)

								) {

									if( !!i ) {

										yield i;

									}

								}

							};

						},

						...

					};

				

Better practice is use of in method when array index need.



					for( let i in args ) {

						localStorage.setItem(args[ i ][ 0 ].trim(), args[ i ][ 1 ].trim());

					};

				

Good way is using forward while without do.



					while( i.firstChild ) {

						parent.insertBefore(i.firstChild, i);

					}

				

Typing

Make it easer to work with small typing helpers.



					RR = {

						...

						// Is callback
						isC: (c) => c && RR.isF(c) ? true : null,

						// Is object
						isO: (v) => typeof(v) === 'object' ? true : null,

						// Is string
						isS: (v) => typeof(v) === 'string' ? true : null,

						// Is array
						isA: (v) => typeof(v) === 'array' ? true : null,

						// Is function
						isF: (v) => typeof(v) === 'function' ? true : null,

						// Is undefined
						isU: (v) => typeof(v) === 'undefined' ? true : null,

						// Is number
						isN: (v) => typeof(v) === 'number' ? true : null,

						// Is number 2
						isNum: (n) => !isNaN(parseFloat(v)) && isFinite(v),

						...

					};

				

Timeout and intervals

Use modern setInterval and setTimeout API to improve an expirience and avoid bugs.



					// Modern setInterval example
					R.talk = RR.interval(() => {

						...

					}, 8000);

					// Stop interval example
					if( R.talk ) {

						R.talk.stop();

					}

					// Modern setTimeout example
					R.task = RR.interval(() => {

						...

					}, 1500, true);

					// Stop timeout example
					if( R.task ) {

						R.task.stop();

					}


				

False and undefined

Never use false and undefined priority to null. It faster, clever and better in performance meaning.



					RR = {

						// This helper test for class value with given name are defined
						hasClass: ( e, c ) => {

							let f = null;

							for( let i of c.split(' ') ) {

								if( RR.treeHacks(RR.htmlObj(e)).classList.contains(i) ) {

									f = true;

								}

							}

							return f;

						},					

					};

				

Another good practices of programming and formating always are welcome. Full API examples available here.