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.