1+ /**
2+ * Keyword shifted alphabet is a simple cipher using a translation table created with a help of a keyword.
3+ * Keyword must be a word where each character can occur only once.
4+ * To create the translation table, we write all the alphabet characters to the first.
5+ * Second row start with the keyword, then we continue with the rest of the characters that are missing in alphabetical order.
6+ *
7+ * |A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|
8+ * |K|E|Y|W|O|R|D|A|B|C|F|G|H|I|J|L|M|N|P|Q|S|T|U|V|W|Z|
9+ *
10+ * Encryption is then just a matter of writing the matching (same index) letter from the second row instead of the first row:
11+ * 'Hello world' -> 'Aoggj ujngw'
12+ *
13+ * Decryption is then just the reverse process of writing the matching (same index) letter from the first row instead of the second row
14+ * 'Aogg ujngw' -> 'Hello world'
15+ *
16+ * Non alphabetical characters (space, exclamation mark, ...) are kept as they are
17+ */
18+
19+ const alphabet = [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ] ;
20+
21+ function checkKeywordValidity ( keyword ) {
22+ keyword . split ( '' ) . forEach ( ( char , index ) => {
23+ const rest = keyword . slice ( 0 , index ) + keyword . slice ( index + 1 ) ;
24+ if ( rest . indexOf ( char ) !== - 1 ) {
25+ return false ;
26+ }
27+ } )
28+ return true ;
29+ }
30+
31+ function getEncryptedAlphabet ( keyword ) {
32+ const encryptedAlphabet = keyword . split ( '' ) ;
33+ alphabet . forEach ( ( char ) => {
34+ if ( encryptedAlphabet . indexOf ( char ) === - 1 ) {
35+ encryptedAlphabet . push ( char ) ;
36+ }
37+ } ) ;
38+ return encryptedAlphabet ;
39+ }
40+
41+ function translate ( sourceAlphabet , targetAlphabet , message ) {
42+ return message . split ( '' ) . reduce ( ( encryptedMessage , char ) => {
43+ const isUpperCase = char === char . toUpperCase ( ) ;
44+ const encryptedCharIndex = sourceAlphabet . indexOf ( char . toLowerCase ( ) ) ;
45+ const encryptedChar = encryptedCharIndex !== - 1 ? targetAlphabet [ encryptedCharIndex ] : char
46+ return encryptedMessage += isUpperCase ? encryptedChar . toUpperCase ( ) : encryptedChar ;
47+ } , '' ) ;
48+ }
49+
50+ function checkInputs ( keyword , message ) {
51+ if ( ! keyword || ! message ) {
52+ throw new Error ( 'Both keyword and message must be specified' ) ;
53+ }
54+
55+ if ( ! checkKeywordValidity ( keyword ) ) {
56+ throw new Error ( 'Invalid keyword!' )
57+ }
58+ }
59+
60+ function encrypt ( keyword , message ) {
61+ checkInputs ( keyword , message ) ;
62+ return translate ( alphabet , getEncryptedAlphabet ( keyword . toLowerCase ( ) ) , message ) ;
63+ }
64+
65+ function decrypt ( keyword , message ) {
66+ checkInputs ( keyword , message ) ;
67+ return translate ( getEncryptedAlphabet ( keyword . toLowerCase ( ) ) , alphabet , message ) ;
68+ }
69+
70+ console . log ( encrypt ( 'keyword' , 'Hello world!' ) ) ; // Prints 'Aoggj ujngw!'
71+ console . log ( decrypt ( 'keyword' , 'Aoggj ujngw!' ) ) ; // Prints 'Hello world!
0 commit comments