
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\crypto-js.d.ts

// Type definitions for crypto-js v4.0.0
// Project: https://github.com/brix/crypto-js
// Definitions by: Michael Zabka <https://github.com/misak113>
//                 Max Lysenko <https://github.com/maximlysenko>
//                 Brendan Early <https://github.com/mymindstorm>
//                 Doma <https://github.com/SevenOutman>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

type WordArray = CryptoJS.lib.WordArray;
type CipherParams = CryptoJS.lib.CipherParams;
type X64Word = CryptoJS.x64.Word;

/**
 * Encoding strategy.
 */
interface Encoder {
  /**
   * Converts a word array to a hex string.
   *
   * @param wordArray The word array.
   *
   * @return The hex string.
   *
   * @example
   *
   *     var hexString = CryptoJS.enc.Hex.stringify(wordArray);
   */
  stringify(wordArray: WordArray): string;
  /**
   * Converts a hex string to a word array.
   *
   * @param hexStr The hex string.
   *
   * @return The word array.
   *
   * @example
   *
   *     var wordArray = CryptoJS.enc.Hex.parse(hexString);
   */
  parse(str: string): WordArray;
}

/**
 * Abstract buffered block algorithm template.
 *
 * The property blockSize must be implemented in a concrete subtype.
 */
interface BufferedBlockAlgorithm {
  /**
   * The number of blocks that should be kept unprocessed in the buffer. Default: 0
   */
  _minBufferSize: number;
  /**
   * Resets this block algorithm's data buffer to its initial state.
   *
   * @example
   *
   *     bufferedBlockAlgorithm.reset();
   */
  reset(): void;
  /**
   * Adds new data to this block algorithm's buffer.
   *
   * @param data The data to append. Strings are converted to a WordArray using UTF-8.
   *
   * @example
   *
   *     bufferedBlockAlgorithm._append('data');
   *     bufferedBlockAlgorithm._append(wordArray);
   */
  _append(data: WordArray | string): void;
  /**
   * Processes available data blocks.
   *
   * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.
   *
   * @param doFlush Whether all blocks and partial blocks should be processed.
   *
   * @return The processed data.
   *
   * @example
   *
   *     var processedData = bufferedBlockAlgorithm._process();
   *     var processedData = bufferedBlockAlgorithm._process(!!'flush');
   */
  _process(doFlush?: boolean): WordArray;
  /**
   * Creates a copy of this object.
   *
   * @return The clone.
   *
   * @example
   *
   *     var clone = bufferedBlockAlgorithm.clone();
   */
  clone(): BufferedBlockAlgorithm;
}

/**
 * Abstract hasher template.
 */
interface Hasher {
  /**
   * The number of 32-bit words this hasher operates on. Default: 16 (512 bits)
   */
  blockSize: number;
  /**
   * Resets this hasher to its initial state.
   *
   * @example
   *
   *     hasher.reset();
   */
  reset(): void;
  /**
   * Updates this hasher with a message.
   *
   * @param messageUpdate The message to append.
   *
   * @return This hasher.
   *
   * @example
   *
   *     hasher.update('message');
   *     hasher.update(wordArray);
   */
  update(messageUpdate: WordArray | string): this;
  /**
   * Finalizes the hash computation.
   * Note that the finalize operation is effectively a destructive, read-once operation.
   *
   * @param messageUpdate (Optional) A final message update.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = hasher.finalize();
   *     var hash = hasher.finalize('message');
   *     var hash = hasher.finalize(wordArray);
   */
  finalize(messaegUpdate?: WordArray | string): WordArray;
}

interface HasherStatic {
  /**
   * Initializes a newly created hasher.
   *
   * @param cfg (Optional) The configuration options to use for this hash computation.
   *
   * @example
   *
   *     var hasher = CryptoJS.algo.SHA256.create();
   */
  create(cfg?: object): Hasher;
}

interface HasherHelper {
  (message: WordArray | string, cfg?: object): WordArray;
}

interface HmacHasherHelper {
  (message: WordArray | string, key: WordArray | string): WordArray;
}

/**
 * Abstract base cipher template.
 */
interface Cipher {
  /**
   * This cipher's key size. Default: 4 (128 bits)
   */
  keySize: number;
  /**
   * This cipher's IV size. Default: 4 (128 bits)
   */
  ivSize: number;
  /**
   * A constant representing encryption mode.
   */
  readonly _ENC_XFORM_MODE: number;
  /**
   * A constant representing decryption mode.
   */
  readonly _DEV_XFORM_MODE: number;

  /**
   * Resets this cipher to its initial state.
   *
   * @example
   *
   *     cipher.reset();
   */
  reset(): void;

  /**
   * Adds data to be encrypted or decrypted.
   *
   * @param dataUpdate The data to encrypt or decrypt.
   *
   * @return The data after processing.
   *
   * @example
   *
   *     var encrypted = cipher.process('data');
   *     var encrypted = cipher.process(wordArray);
   */
  process(dataUpdate: WordArray | string): WordArray;

  /**
   * Finalizes the encryption or decryption process.
   * Note that the finalize operation is effectively a destructive, read-once operation.
   *
   * @param dataUpdate The final data to encrypt or decrypt.
   *
   * @return The data after final processing.
   *
   * @example
   *
   *     var encrypted = cipher.finalize();
   *     var encrypted = cipher.finalize('data');
   *     var encrypted = cipher.finalize(wordArray);
   */
  finalize(dataUpdate?: WordArray | string): WordArray;
}

interface CipherStatic {
  /**
   * Creates this cipher in encryption mode.
   *
   * @param key The key.
   * @param cfg (Optional) The configuration options to use for this operation.
   *
   * @return A cipher instance.
   *
   * @example
   *
   *     var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray });
   */
  createEncryptor(key: WordArray, cfg?: CipherOption): Cipher;

  /**
   * Creates this cipher in decryption mode.
   *
   * @param key The key.
   * @param cfg (Optional) The configuration options to use for this operation.
   *
   * @return A cipher instance.
   *
   * @example
   *
   *     var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray });
   */
  createDecryptor(key: WordArray, cfg?: CipherOption): Cipher;

  /**
   * Initializes a newly created cipher.
   *
   * @param xformMode Either the encryption or decryption transormation mode constant.
   * @param key The key.
   * @param cfg (Optional) The configuration options to use for this operation.
   *
   * @example
   *
   *     var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray });
   */
  create(xformMode: number, key: WordArray, cfg?: CipherOption): Cipher;
}

interface CipherHelper {
  encrypt(
    message: WordArray | string,
    key: WordArray | string,
    cfg?: CipherOption
  ): CipherParams;
  decrypt(
    ciphertext: CipherParams | string,
    key: WordArray | string,
    cfg?: CipherOption
  ): WordArray;
}

/**
 * Configuration options.
 */
interface CipherOption {
  /**
   * The IV to use for this operation.
   */
  iv?: WordArray | undefined;
  format?: Format | undefined;
  [key: string]: any;
}

interface Mode {
  /**
   * Processes the data block at offset.
   *
   * @param words The data words to operate on.
   * @param offset The offset where the block starts.
   *
   * @example
   *
   *     mode.processBlock(data.words, offset);
   */
  processBlock(words: number[], offset: number): void;
}

interface ModeStatic {
  /**
   * Initializes a newly created mode.
   *
   * @param cipher A block cipher instance.
   * @param iv The IV words.
   *
   * @example
   *
   *     var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words);
   */
  create(cipher: Cipher, iv: number[]): Mode;
}

/**
 * Abstract base block cipher mode template.
 */
interface BlockCipherMode {
  Encryptor: ModeStatic;
  Decryptor: ModeStatic;
  /**
   * Creates this mode for encryption.
   *
   * @param cipher A block cipher instance.
   * @param iv The IV words.
   *
   * @example
   *
   *     var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);
   */
  createEncryptor(cipher: Cipher, iv: number[]): Mode;

  /**
   * Creates this mode for decryption.
   *
   * @param cipher A block cipher instance.
   * @param iv The IV words.
   *
   * @example
   *
   *     var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words);
   */
  createDecryptor(cipher: Cipher, iv: number[]): Mode;
}

/**
 * Abstract base block cipher mode template.
 */
interface BlockCipherMode {
  /**
   * Creates this mode for encryption.
   *
   * @param cipher A block cipher instance.
   * @param iv The IV words.
   *
   * @example
   *
   *     var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);
   */
  createEncryptor(cipher: Cipher): Mode;
}

/**
 * Padding strategy.
 */
interface Padding {
  /**
   * Pads data using the algorithm defined in PKCS #5/7.
   *
   * @param data The data to pad.
   * @param blockSize The multiple that the data should be padded to.
   *
   * @example
   *
   *     CryptoJS.pad.Pkcs7.pad(wordArray, 4);
   */
  pad(data: WordArray, blockSize: number): void;

  /**
   * Unpads data that had been padded using the algorithm defined in PKCS #5/7.
   *
   * @param data The data to unpad.
   *
   * @example
   *
   *     CryptoJS.pad.Pkcs7.unpad(wordArray);
   */
  unpad(data: WordArray): void;
}

/**
 * Abstract base block cipher template.
 */
interface BlockCipher {
  /**
   * The number of 32-bit words this cipher operates on. Default: 4 (128 bits)
   */
  blockSize: number;
}

/**
 * Configuration options.
 */
interface BlockCipherOption {
  /**
   * The block mode to use. Default: CBC
   */
  mode: Mode;
  /**
   * The padding strategy to use. Default: Pkcs7
   */
  padding: Padding;
}
/**
 * Formatting strategy.
 */
interface Format {
  /**
   * Converts a cipher params object to an OpenSSL-compatible string.
   *
   * @param cipherParams The cipher params object.
   *
   * @return The OpenSSL-compatible string.
   *
   * @example
   *
   *     var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams);
   */
  stringify(cipherParams: CipherParams): string;

  /**
   * Converts an OpenSSL-compatible string to a cipher params object.
   *
   * @param openSSLStr The OpenSSL-compatible string.
   *
   * @return The cipher params object.
   *
   * @example
   *
   *     var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString);
   */
  parse(str: string): CipherParams;
}

/**
 * An array of 64-bit words.
 */
interface X64WordArray {
  /**
   * The array of CryptoJS.x64.Word objects.
   */
  words: number[];
  /**
   * The number of significant bytes in this word array.
   */
  sigBytes: number;

  /**
   * Converts this 64-bit word array to a 32-bit word array.
   *
   * @return This word array's data as a 32-bit word array.
   *
   * @example
   *
   *     var x32WordArray = x64WordArray.toX32();
   */
  toX32(): WordArray;

  /**
   * Creates a copy of this word array.
   *
   * @return The clone.
   *
   * @example
   *
   *     var clone = x64WordArray.clone();
   */
  clone(): X64WordArray;
}

/**
 * Base object for prototypal inheritance.
 */
interface Base {
  /**
   * Creates a copy of this object.
   *
   * @return The clone.
   *
   * @example
   *
   *     var clone = instance.clone();
   */
  clone(): this;
}

/**
 * Configuration options.
 */
interface KDFOption {
  /**
   * The key size in words to generate.
   */
  keySize?: number | undefined;
  /**
   * The hasher to use.
   */
  hasher?: HasherStatic | undefined;
  /**
   * The number of iterations to perform.
   */
  iterations?: number | undefined;
}

declare namespace CryptoJS {
  /**
   * Library namespace.
   */
  export namespace lib {
    /**
     * Base object for prototypal inheritance.
     */
    const Base: {
      /**
       * Creates a new object that inherits from this object.
       *
       * @param overrides Properties to copy into the new object.
       *
       * @return The new object.
       *
       * @example
       *
       *     var MyType = CryptoJS.lib.Base.extend({
       *         field: 'value',
       *
       *         method: function () {
       *         }
       *     });
       */
      extend(overrides: object): any;

      /**
       * Extends this object and runs the init method.
       * Arguments to create() will be passed to init().
       *
       * @return The new object.
       *
       * @example
       *
       *     var instance = MyType.create();
       */
      create(...args: any[]): any;

      /**
       * Copies properties into this object.
       *
       * @param properties The properties to mix in.
       *
       * @example
       *
       *     MyType.mixIn({
       *         field: 'value'
       *     });
       */
      mixIn(properties: object): any;
    };

    /**
     * An array of 32-bit words.
     */
    interface WordArray {
      /**
       * The array of 32-bit words.
       */
      words: number[];
      /**
       * The number of significant bytes in this word array.
       */
      sigBytes: number;
      /**
       * Converts this word array to a string.
       *
       * @param encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex
       *
       * @return The stringified word array.
       *
       * @example
       *
       *     var string = wordArray + '';
       *     var string = wordArray.toString();
       *     var string = wordArray.toString(CryptoJS.enc.Utf8);
       */
      toString(encoder?: Encoder): string;

      /**
       * Concatenates a word array to this word array.
       *
       * @param wordArray The word array to append.
       *
       * @return This word array.
       *
       * @example
       *
       *     wordArray1.concat(wordArray2);
       */
      concat(wordArray: WordArray): this;

      /**
       * Removes insignificant bits.
       *
       * @example
       *
       *     wordArray.clamp();
       */
      clamp(): void;

      /**
       * Creates a copy of this word array.
       *
       * @return The clone.
       *
       * @example
       *
       *     var clone = wordArray.clone();
       */
      clone(): WordArray;
    }
    const WordArray: {
      /**
       * Initializes a newly created word array.
       *
       * @param words (Optional) An array of 32-bit words.
       * @param sigBytes (Optional) The number of significant bytes in the words.
       *
       * @example
       *
       *     var wordArray = CryptoJS.lib.WordArray.create();
       *     var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
       *     var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
       */
      create(words?: number[], sigBytes?: number): WordArray;
      /**
       * Creates a word array filled with random bytes.
       *
       * @param nBytes The number of random bytes to generate.
       *
       * @return The random word array.
       *
       * @example
       *
       *     var wordArray = CryptoJS.lib.WordArray.random(16);
       */
      random(nBytes: number): WordArray;
    };

    const BufferedBlockAlgorithm: any;

    const Hasher: {
      /**
       * Creates a shortcut function to a hasher's object interface.
       *
       * @param hasher The hasher to create a helper for.
       *
       * @return The shortcut function.
       *
       * @example
       *
       *     var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);
       */
      _createHelper(hasher: HasherStatic): HasherHelper;
      /**
       * Creates a shortcut function to the HMAC's object interface.
       *
       * @param hasher The hasher to use in this HMAC helper.
       *
       * @return The shortcut function.
       *
       * @example
       *
       *     var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);
       */
      _createHmacHelper(hasher: HasherStatic): HmacHasherHelper;
    };

    const Cipher: {
      /**
       * Creates shortcut functions to a cipher's object interface.
       *
       * @param cipher The cipher to create a helper for.
       *
       * @return An object with encrypt and decrypt shortcut functions.
       *
       * @example
       *
       *     var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES);
       */
      _createHelper(cipher: Cipher): CipherHelper;
    };

    interface CipherOption {
      /**
       * The IV to use for this operation.
       */
      iv?: WordArray | undefined;
      format?: Format | undefined;
      [key: string]: any;
    }

    /**
     * A collection of cipher parameters.
     */
    interface CipherParams {
      /**
       * The raw ciphertext.
       */
      ciphertext: WordArray;
      /**
       * The key to this ciphertext.
       */
      key: WordArray;
      /**
       * The IV used in the ciphering operation.
       */
      iv: WordArray;
      /**
       * The salt used with a key derivation function.
       */
      salt: WordArray;
      /**
       * The cipher algorithm.
       */
      algorithm: CipherStatic;
      /**
       * The block mode used in the ciphering operation.
       */
      mode: Mode;
      /**
       * The padding scheme used in the ciphering operation.
       */
      padding: Padding;
      /**
       * The block size of the cipher.
       */
      blockSize: number;
      /**
       * The default formatting strategy to convert this cipher params object to a string.
       */
      formatter: Format;
      /**
       * Converts this cipher params object to a string.
       *
       * @param formatter (Optional) The formatting strategy to use.
       *
       * @return The stringified cipher params.
       *
       * @throws Error If neither the formatter nor the default formatter is set.
       *
       * @example
       *
       *     var string = cipherParams + '';
       *     var string = cipherParams.toString();
       *     var string = cipherParams.toString(CryptoJS.format.OpenSSL);
       */
      toString(formatter?: Format): string;
    }
    const CipherParams: {
      /**
       * Initializes a newly created cipher params object.
       *
       * @param cipherParams An object with any of the possible cipher parameters.
       *
       * @example
       *
       *     var cipherParams = CryptoJS.lib.CipherParams.create({
       *         ciphertext: ciphertextWordArray,
       *         key: keyWordArray,
       *         iv: ivWordArray,
       *         salt: saltWordArray,
       *         algorithm: CryptoJS.algo.AES,
       *         mode: CryptoJS.mode.CBC,
       *         padding: CryptoJS.pad.PKCS7,
       *         blockSize: 4,
       *         formatter: CryptoJS.format.OpenSSL
       *     });
       */
      create(cipherParams: Partial<CipherParams>): CipherParams;
    };

    /**
     * Abstract base stream cipher template.
     */
    interface StreamCipher extends Cipher {
      /**
       * The number of 32-bit words this cipher operates on. Default: 1 (32 bits)
       */
      blockSize: number;
    }

    /**
     * Abstract base block cipher mode template.
     */
    const BlockCipherMode: any;

    /**
     * A cipher wrapper that returns ciphertext as a serializable cipher params object.
     */
    const SerializableCipher: {
      /**
       * Encrypts a message.
       *
       * @param cipher The cipher algorithm to use.
       * @param message The message to encrypt.
       * @param key The key.
       * @param cfg (Optional) The configuration options to use for this operation.
       *
       * @return A cipher params object.
       *
       * @example
       *
       *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key);
       *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv });
       *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL });
       */
      encrypt(
        cipher: CipherStatic,
        message: WordArray | string,
        key: WordArray,
        cfg?: CipherOption
      ): CipherParams;

      /**
       * Decrypts serialized ciphertext.
       *
       * @param cipher The cipher algorithm to use.
       * @param ciphertext The ciphertext to decrypt.
       * @param key The key.
       * @param cfg (Optional) The configuration options to use for this operation.
       *
       * @return The plaintext.
       *
       * @example
       *
       *     var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL });
       *     var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL });
       */
      decrypt(
        cipher: CipherStatic,
        ciphertext: WordArray | string,
        key: WordArray,
        cfg?: CipherOption
      ): CipherParams;

      /**
       * Converts serialized ciphertext to CipherParams,
       * else assumed CipherParams already and returns ciphertext unchanged.
       *
       * @param ciphertext The ciphertext.
       * @param format The formatting strategy to use to parse serialized ciphertext.
       *
       * @return The unserialized ciphertext.
       *
       * @example
       *
       *     var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format);
       */
      _parse(ciphertext: CipherParams | string, format: Format): CipherParams;
    };

    /**
     * A serializable cipher wrapper that derives the key from a password,
     * and returns ciphertext as a serializable cipher params object.
     */
    const PasswordBasedCipher: {
      /**
       * Encrypts a message using a password.
       *
       * @param cipher The cipher algorithm to use.
       * @param message The message to encrypt.
       * @param password The password.
       * @param cfg (Optional) The configuration options to use for this operation.
       *
       * @return A cipher params object.
       *
       * @example
       *
       *     var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password');
       *     var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL });
       */
      encrypt(
        cipher: CipherStatic,
        message: WordArray | string,
        password: string,
        cfg?: CipherOption
      ): CipherParams;

      /**
       * Decrypts serialized ciphertext using a password.
       *
       * @param cipher The cipher algorithm to use.
       * @param ciphertext The ciphertext to decrypt.
       * @param password The password.
       * @param cfg (Optional) The configuration options to use for this operation.
       *
       * @return The plaintext.
       *
       * @example
       *
       *     var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL });
       *     var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL });
       */
      decrypt(
        cipher: CipherStatic,
        ciphertext: CipherParams | string,
        password: string,
        cfg?: CipherOption
      ): WordArray;
    };
  }
  /**
   * Padding namespace.
   */
  export namespace pad {
    /**
     * PKCS #5/7 padding strategy.
     */
    const Pkcs7: Padding;

    /**
     * ANSI X.923 padding strategy.
     */
    const AnsiX923: Padding;

    /**
     * ISO 10126 padding strategy.
     */
    const Iso10126: Padding;

    /**
     * ISO/IEC 9797-1 Padding Method 2.
     */
    const Iso97971: Padding;

    /**
     * Zero padding strategy.
     */
    const ZeroPadding: Padding;

    /**
     * A noop padding strategy.
     */
    const NoPadding: Padding;
  }

  /**
   * Key derivation function namespace.
   */
  export namespace kdf {
    /**
     * OpenSSL key derivation function.
     */
    const OpenSSL: {
      /**
       * Derives a key and IV from a password.
       *
       * @param password The password to derive from.
       * @param keySize The size in words of the key to generate.
       * @param ivSize The size in words of the IV to generate.
       * @param salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.
       *
       * @return A cipher params object with the key, IV, and salt.
       *
       * @example
       *
       *     var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
       *     var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
       */
      execute(
        password: string,
        keySize: number,
        ivSize: number,
        salt?: WordArray | string
      ): CipherParams;
    };
  }

  /**
   * Mode namespace.
   */
  export namespace mode {
    /**
     * Cipher Block Chaining mode.
     */
    const CBC: BlockCipherMode;

    /**
     * Cipher Feedback block mode.
     */
    const CFB: BlockCipherMode;
    /**
     * Counter block mode.
     */
    const CTR: BlockCipherMode;
    /**
     * @preserve
     * Counter block mode compatible with  Dr Brian Gladman fileenc.c
     * derived from CryptoJS.mode.CTR
     * Jan Hruby jhruby.web@gmail.com
     */
    const CTRGladman: BlockCipherMode;
    /**
     * Output Feedback block mode.
     */
    const OFB: BlockCipherMode;

    /**
     * Electronic Codebook block mode.
     */
    const ECB: BlockCipherMode;
  }

  /**
   * Format namespace.
   */
  export namespace format {
    /**
     * OpenSSL formatting strategy.
     */
    const OpenSSL: Format;
    const Hex: Format;
  }

  /**
   * Encoder namespace.
   */
  export namespace enc {
    /**
     * Hex encoding strategy.
     */
    const Hex: Encoder;
    /**
     * Latin1 encoding strategy.
     */
    const Latin1: Encoder;
    /**
     * UTF-8 encoding strategy.
     */
    const Utf8: Encoder;
    /**
     * UTF-16 BE encoding strategy.
     */
    const Utf16: Encoder;
    const Utf16BE: Encoder;

    /**
     * UTF-16 LE encoding strategy.
     */
    const Utf16LE: Encoder;
    /**
     * Base64 encoding strategy.
     */
    const Base64: Encoder;
  }

  /**
   * Algorithm namespace.
   */
  export namespace algo {
    /**
     * MD5 hash algorithm.
     */
    const MD5: HasherStatic;

    /**
     * SHA-1 hash algorithm.
     */
    const SHA1: HasherStatic;

    /**
     * SHA-256 hash algorithm.
     */
    const SHA256: HasherStatic;
    /**
     * SHA-224 hash algorithm.
     */
    const SHA224: HasherStatic;
    /**
     * SHA-512 hash algorithm.
     */
    const SHA512: HasherStatic;

    /**
     * SHA-384 hash algorithm.
     */
    const SHA384: HasherStatic;
    /**
     * SHA-3 hash algorithm.
     */
    const SHA3: HasherStatic;
    /**
     * RIPEMD160 hash algorithm.
     */
    const RIPEMD160: HasherStatic;
    /**
     * HMAC algorithm.
     */
    abstract class HMAC {
      /**
       * Initializes a newly created HMAC.
       *
       * @param hasher The hash algorithm to use.
       * @param key The secret key.
       *
       * @example
       *
       *     var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
       */
      static create(hasher: HasherStatic, key: WordArray | string): HMAC;
      /**
       * Resets this HMAC to its initial state.
       *
       * @example
       *
       *     hmacHasher.reset();
       */
      reset(): void;

      /**
       * Updates this HMAC with a message.
       *
       * @param messageUpdate The message to append.
       *
       * @return This HMAC instance.
       *
       * @example
       *
       *     hmacHasher.update('message');
       *     hmacHasher.update(wordArray);
       */
      update(messageUpdate: WordArray | string): this;

      /**
       * Finalizes the HMAC computation.
       * Note that the finalize operation is effectively a destructive, read-once operation.
       *
       * @param messageUpdate (Optional) A final message update.
       *
       * @return The HMAC.
       *
       * @example
       *
       *     var hmac = hmacHasher.finalize();
       *     var hmac = hmacHasher.finalize('message');
       *     var hmac = hmacHasher.finalize(wordArray);
       */
      finalize(messageUpdate?: WordArray | string): WordArray;
    }
    /**
     * Password-Based Key Derivation Function 2 algorithm.
     */
    abstract class PBKDF2 {
      /**
       * Initializes a newly created key derivation function.
       *
       * @param cfg (Optional) The configuration options to use for the derivation.
       *
       * @example
       *
       *     var kdf = CryptoJS.algo.PBKDF2.create();
       *     var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 });
       *     var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 });
       */
      static create(cfg?: KDFOption): PBKDF2;

      /**
       * Computes the Password-Based Key Derivation Function 2.
       *
       * @param password The password.
       * @param salt A salt.
       *
       * @return The derived key.
       *
       * @example
       *
       *     var key = kdf.compute(password, salt);
       */
      compute(password: WordArray | string, salt: WordArray): WordArray;
    }
    /**
     * This key derivation function is meant to conform with EVP_BytesToKey.
     * www.openssl.org/docs/crypto/EVP_BytesToKey.html
     */
    abstract class EvpKDF {
      /**
       * Initializes a newly created key derivation function.
       *
       * @param cfg (Optional) The configuration options to use for the derivation.
       *
       * @example
       *
       *     var kdf = CryptoJS.algo.EvpKDF.create();
       *     var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 });
       *     var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 });
       */
      static create(cfg?: {
        keySize: number;
        hasher?: HasherStatic | undefined;
        iterations: number;
      }): EvpKDF;

      /**
       * Derives a key from a password.
       *
       * @param password The password.
       * @param salt A salt.
       *
       * @return The derived key.
       *
       * @example
       *
       *     var key = kdf.compute(password, salt);
       */
      compute(password: WordArray | string, salt: WordArray): WordArray;
    }

    /**
     * AES block cipher algorithm.
     */
    const AES: CipherStatic;

    /**
     * DES block cipher algorithm.
     */
    const DES: CipherStatic;

    /**
     * Triple-DES block cipher algorithm.
     */
    const TripleDES: CipherStatic;

    /**
     * RC4 stream cipher algorithm.
     */
    const RC4: CipherStatic;

    /**
     * Modified RC4 stream cipher algorithm.
     */
    const RC4Drop: CipherStatic;

    /**
     * Rabbit stream cipher algorithm
     */
    const Rabbit: CipherStatic;

    /**
     * Rabbit stream cipher algorithm.
     *
     * This is a legacy version that neglected to convert the key to little-endian.
     * This error doesn't affect the cipher's security,
     * but it does affect its compatibility with other implementations.
     */
    const RabbitLegacy: CipherStatic;
  }

  /**
   * x64 namespace.
   */
  export namespace x64 {
    /**
     * A 64-bit word.
     */
    interface Word {
      /**
       * Bitwise NOTs this word.
       *
       * @return A new x64-Word object after negating.
       *
       * @example
       *
       *     var negated = x64Word.not();
       */
      not(): X64Word;
      /**
       * Bitwise ANDs this word with the passed word.
       *
       * @param word The x64-Word to AND with this word.
       *
       * @return A new x64-Word object after ANDing.
       *
       * @example
       *
       *     var anded = x64Word.and(anotherX64Word);
       */
      and(word: X64Word): X64Word;

      /**
       * Bitwise ORs this word with the passed word.
       *
       * @param word The x64-Word to OR with this word.
       *
       * @return A new x64-Word object after ORing.
       *
       * @example
       *
       *     var ored = x64Word.or(anotherX64Word);
       */
      or(word: X64Word): X64Word;

      /**
       * Bitwise XORs this word with the passed word.
       *
       * @param word The x64-Word to XOR with this word.
       *
       * @return A new x64-Word object after XORing.
       *
       * @example
       *
       *     var xored = x64Word.xor(anotherX64Word);
       */
      xor(word: X64Word): X64Word;
      /**
       * Shifts this word n bits to the left.
       *
       * @param n The number of bits to shift.
       *
       * @return A new x64-Word object after shifting.
       *
       * @example
       *
       *     var shifted = x64Word.shiftL(25);
       */
      shiftL(n: number): X64Word;
      /**
       * Shifts this word n bits to the right.
       *
       * @param n The number of bits to shift.
       *
       * @return A new x64-Word object after shifting.
       *
       * @example
       *
       *     var shifted = x64Word.shiftR(7);
       */
      shiftR(n: number): X64Word;
      /**
       * Rotates this word n bits to the left.
       *
       * @param n The number of bits to rotate.
       *
       * @return A new x64-Word object after rotating.
       *
       * @example
       *
       *     var rotated = x64Word.rotL(25);
       */
      rotL(n: number): X64Word;

      /**
       * Rotates this word n bits to the right.
       *
       * @param n The number of bits to rotate.
       *
       * @return A new x64-Word object after rotating.
       *
       * @example
       *
       *     var rotated = x64Word.rotR(7);
       */
      rotR(n: number): X64Word;
      /**
       * Adds this word with the passed word.
       *
       * @param word The x64-Word to add with this word.
       *
       * @return A new x64-Word object after adding.
       *
       * @example
       *
       *     var added = x64Word.add(anotherX64Word);
       */
      add(word: X64Word): X64Word;
    }

    const Word: {
      /**
       * Initializes a newly created 64-bit word.
       *
       * @param high The high 32 bits.
       * @param low The low 32 bits.
       *
       * @example
       *
       *     var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607);
       */
      create(high: number, low: number): X64Word;
    };

    /**
     * Initializes a newly created word array.
     *
     * @param words (Optional) An array of CryptoJS.x64.Word objects.
     * @param sigBytes (Optional) The number of significant bytes in the words.
     *
     * @example
     *
     *     var wordArray = CryptoJS.x64.WordArray.create();
     *
     *     var wordArray = CryptoJS.x64.WordArray.create([
     *         CryptoJS.x64.Word.create(0x00010203, 0x04050607),
     *         CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)
     *     ]);
     *
     *     var wordArray = CryptoJS.x64.WordArray.create([
     *         CryptoJS.x64.Word.create(0x00010203, 0x04050607),
     *         CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)
     *     ], 10);
     */
    const WordArray: {
      create(words?: X64WordArray[], sigBytes?: number): X64WordArray;
    };
  }

  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.MD5('message');
   *     var hash = CryptoJS.MD5(wordArray);
   */
  export const MD5: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMA'C.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacMD5(message, key);
   */
  export const HmacMD5: HmacHasherHelper;
  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.SHA1('message');
   *     var hash = CryptoJS.SHA1(wordArray);
   */
  export const SHA1: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacSHA1(message, key);
   */
  export const HmacSHA1: HmacHasherHelper;

  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.SHA256('message');
   *     var hash = CryptoJS.SHA256(wordArray);
   */
  export const SHA256: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacSHA256(message, key);
   */
  export const HmacSHA256: HmacHasherHelper;
  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.SHA224('message');
   *     var hash = CryptoJS.SHA224(wordArray);
   */
  export const SHA224: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacSHA224(message, key);
   */
  export const HmacSHA224: HmacHasherHelper;
  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.SHA512('message');
   *     var hash = CryptoJS.SHA512(wordArray);
   */
  export const SHA512: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacSHA512(message, key);
   */
  export const HmacSHA512: HmacHasherHelper;
  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.SHA384('message');
   *     var hash = CryptoJS.SHA384(wordArray);
   */
  export const SHA384: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacSHA384(message, key);
   */
  export const HmacSHA384: HmacHasherHelper;

  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.SHA3('message');
   *     var hash = CryptoJS.SHA3(wordArray);
   */
  export const SHA3: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacSHA3(message, key);
   */
  export const HmacSHA3: HmacHasherHelper;

  /**
   * Shortcut function to the hasher's object interface.
   *
   * @param message The message to hash.
   *
   * @return The hash.
   *
   * @example
   *
   *     var hash = CryptoJS.RIPEMD160('message');
   *     var hash = CryptoJS.RIPEMD160(wordArray);
   */
  export const RIPEMD160: HasherHelper;
  /**
   * Shortcut function to the HMAC's object interface.
   *
   * @param message The message to hash.
   * @param key The secret key.
   *
   * @return The HMAC.
   *
   * @example
   *
   *     var hmac = CryptoJS.HmacRIPEMD160(message, key);
   */
  export const HmacRIPEMD160: HmacHasherHelper;
  /**
   * Computes the Password-Based Key Derivation Function 2.
   *
   * @param password The password.
   * @param salt A salt.
   * @param cfg (Optional) The configuration options to use for this computation.
   *
   * @return The derived key.
   *
   * @example
   *
   *     var key = CryptoJS.PBKDF2(password, salt);
   *     var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 });
   *     var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 });
   */
  export function PBKDF2(
    password: WordArray | string,
    salt: WordArray | string,
    cfg?: KDFOption
  ): WordArray;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.AES.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.AES.decrypt(ciphertext, key, cfg);
   */
  export const AES: CipherHelper;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.DES.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.DES.decrypt(ciphertext, key, cfg);
   */
  export const DES: CipherHelper;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg);
   */
  export const TripleDES: CipherHelper;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.RC4.decrypt(ciphertext, key, cfg);
   */
  export const RC4: CipherHelper;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg);
   */
  export const RC4Drop: CipherHelper;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg);
   */
  export const Rabbit: CipherHelper;

  /**
   * Shortcut functions to the cipher's object interface.
   *
   * @example
   *
   *     var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg);
   *     var plaintext  = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg);
   */
  export const RabbitLegacy: CipherHelper;

  /**
   * Derives a key from a password.
   *
   * @param password The password.
   * @param salt A salt.
   * @param cfg (Optional) The configuration options to use for this computation.
   *
   * @return The derived key.
   *
   * @example
   *
   *     var key = CryptoJS.EvpKDF(password, salt);
   *     var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 });
   *     var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 });
   */
  export function EvpKDF(
    password: WordArray | string,
    salt: WordArray | string,
    cfg?: {
      keySize: number;
      hasher?: HasherStatic | undefined;
      iterations: number;
    }
  ): WordArray;
}

export = CryptoJS;

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\public-api.ts

/*
 * Public API Surface of x-framework-services
 */

//
//#region Export Services ...
export * from './lib/x-dialog.service';
export * from './lib/x-manager.service';
export * from './lib/x-storage.service';
export * from './lib/x-security.service';
export * from './lib/x-settings.service';
export * from './lib/x-identifier.service';
export * from './lib/x-geolocation.service';
export * from './lib/x-localization.service';
export * from './lib/x-notification.service';
export * from './lib/x-task-manager.service';
export * from './lib/x-theme-manager.service';
export * from './lib/x-secure-storage.service';
export * from './lib/x-http-interceptor.service';
//#endregion

//
//#region Export Directives ...
export * from './lib/directives/x-swipable.directive';
export * from './lib/directives/x-date-presenter.directive';
export * from './lib/directives/x-currency-formatter.directive';
export * from './lib/directives/x-from-input-formatter.directive';
export * from './lib/directives/x-locale-input-formatter.directive';
//#endregion

//
//#region Export Pipes ...
export * from './lib/pipes/x-translate.pipe';
//#endregion

//
//#region Tokens ...
export * from './lib/tokens/x-injectable-tokens';
//#endregion

//
//#region Export Config ...
export * from './lib/config/x-framework-services.config';
//#endregion

//
//#region Export Modules ...
export * from './lib/modules/x-directives.module';
export * from './lib/x-framework-services.module';
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\test.ts

// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

declare const require: {
  context(path: string, deep?: boolean, filter?: RegExp): {
    keys(): string[];
    <T>(id: string): T;
  };
};

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-dialog.service.ts

import {
  NavController,
  AlertController,
  ModalController,
  PickerController,
  PopoverController,
  LoadingController,
  ActionSheetController,
} from '@ionic/angular';
import {
  toArray,
  hasChild,
  XActionType,
  XModalInput,
  XBaseService,
  XResourceIDs,
  XModalButton,
  XPickerColumn,
  XAlertOptions,
  XModalOptions,
  XPickerOptions,
  XPopoverOptions,
  XLoadingOptions,
  XModalButtonRole,
  XActionModalOptions,
  XPickerColumnOption,
  XModalButtonRoleIdentifier,
} from 'x-framework-core';
import { Observable, of } from 'rxjs';
import { Injectable, Inject } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { XLocalizationService } from './x-localization.service';
import { XNotificationService } from './x-notification.service';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XDialogService extends XBaseService {
  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig,
    public matDialogService: MatDialog,
    public modalController: ModalController,
    public alertController: AlertController,
    public pickerController: PickerController,
    public navigationController: NavController,
    public popoverController: PopoverController,
    public loadingController: LoadingController,
    private notificationService: XNotificationService,
    private localizationService: XLocalizationService,
    public actionSheetController: ActionSheetController
  ) {
    super(config);
  }

  //
  //#region Service Actions ...
  nativeAlert(message?: string | XResourceIDs): Observable<void> {
    //
    message = !message ? XResourceIDs.are_you_sure : message;

    //
    const mMessage = this.localizationService.getValue(message);
    const confirmation = window.alert(mMessage);

    //
    return of(confirmation);
  }

  nativeConfirm(message?: string | XResourceIDs): Observable<boolean> {
    //
    message = !message ? XResourceIDs.are_you_sure : message;

    //
    const mMessage = this.localizationService.getValue(message);
    const confirmation = window.confirm(mMessage);

    //
    return of(confirmation);
  }

  async presentActionModal(props: XActionModalOptions) {
    //
    const options = await this.toActionSheetOptions(props);
    const actionModal = await this.actionSheetController.create(options);

    //
    if (props.playSound) {
      this.notificationService.playSound(props.type || XActionType.System);
    }

    //
    await actionModal.present();
    return actionModal;
  }

  async presentModal(props: XModalOptions) {
    //
    if (props.fullScreen) {
      //
      const mFullscreen = 'fullscreen';
      if (hasChild(props.cssClass)) {
        props.cssClass = [...toArray(props.cssClass), mFullscreen];
      } else {
        props.cssClass = [mFullscreen];
      }
    } else if (props.autoHeight) {
      //
      const mAutoHeight = 'autoHeight';
      if (hasChild(props.cssClass)) {
        props.cssClass = [...toArray(props.cssClass), mAutoHeight];
      } else {
        props.cssClass = [mAutoHeight];
      }
    }

    //
    const modal = await this.modalController.create(props);
    await modal.present();
    return modal;
  }

  async presentPopover(props: XPopoverOptions) {
    //
    const popover = await this.popoverController.create(props);
    await popover.present();
    return popover;
  }

  async presentLoading(props: XLoadingOptions) {
    //
    const loading = await this.loadingController.create(props);
    await loading.present();
    return loading;
  }

  async presentAlert(props: XAlertOptions) {
    //
    const options = await this.prepareAlertOptions(props);
    const alert = await this.alertController.create(options);

    //
    await alert.present();
    return alert;
  }

  async presentPicker(props: XPickerOptions) {
    //
    const options = await this.preparePickerOptions(props);
    const picker = await this.pickerController.create(options);

    //
    await picker.present();
    return picker;
  }
  //#endregion

  //
  //#region Commonly Used dialogs ...
  async presentAreYouSureOkCancleDialog(
    okHandler: () => void,
    okRole?: XModalButtonRoleIdentifier,
    cancelHandler?: () => void,
    cancelRole?: XModalButtonRoleIdentifier
  ) {
    await this.presentActionModal({
      header: XResourceIDs.are_you_sure,
      buttons: [],
      keyboardClose: true,
      backdropDismiss: true,
      hasOk: true,
      okRole,
      okHandler,
      hasCancel: true,
      cancelRole,
      cancelHandler,
    });
  }

  async presentAreYouSureYesNoDialog(
    yesHandler: () => void,
    yesRole?: XModalButtonRoleIdentifier,
    noHandler?: () => void,
    noRole?: XModalButtonRoleIdentifier
  ) {
    await this.presentActionModal({
      header: XResourceIDs.are_you_sure,
      buttons: [],
      keyboardClose: true,
      backdropDismiss: true,
      hasNo: true,
      noRole,
      noHandler,
      hasYes: true,
      yesRole,
      yesHandler,
    });
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Button Preparer ...
  getButton(
    text?: XResourceIDs | string,
    icon?: string,
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(text);
    return {
      text: mText,
      icon,
      role,
      handler,
    };
  }

  getYesButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.yes);
    const mIcon = 'checkmark';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }

  getNoButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.no);
    const mIcon = 'close';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }

  getOkButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.ok);
    const mIcon = 'checkmark';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }

  getCancelButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.cancel);
    const mIcon = 'close';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }

  getAcceptButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.accept);
    const mIcon = 'checkmark';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }

  getRetryButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Selected;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.retry);
    const mIcon = 'refresh';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }

  getDismissButton(
    role?: XModalButtonRoleIdentifier,
    handler?: () => boolean | void | Promise<boolean>
  ) {
    //
    if (!role) {
      role = XModalButtonRole.Destructive;
    }

    //
    const mText = this.getPreparedMessage(XResourceIDs.dismiss);
    const mIcon = 'remove-circle-outline';

    return {
      text: mText,
      icon: mIcon,
      role,
      handler,
    };
  }
  //#endregion

  private prepareModalButton(button?: XModalButton) {
    //
    const alertButton = {
      ...button,
      text: button.text ? this.getPreparedMessage(button.text) : '',
    };

    //
    return alertButton;
  }

  private async prepareModalButtons(buttons?: XModalButton[]) {
    //
    if (!hasChild(buttons)) {
      return [];
    }

    //
    return buttons.map((b) => this.prepareModalButton(b));
  }

  private prepareModalInput(input?: XModalInput) {
    //
    const alertInput = {
      ...input,
      label: input.label ? this.getPreparedMessage(input.label) : '',
      placeholder: input.placeholder
        ? this.getPreparedMessage(input.placeholder)
        : '',
    };

    //
    return alertInput;
  }

  private async prepareModalInputs(inputs?: XModalInput[]) {
    //
    if (!hasChild(inputs)) {
      return [];
    }

    //
    return inputs.map((i) => this.prepareModalInput(i));
  }

  private async prepareAlertOptions(options?: XAlertOptions) {
    //
    const alertOptions = {
      ...options,
      header: options.header ? this.getPreparedMessage(options.header) : '',
      subHeader: options.subHeader
        ? this.getPreparedMessage(options.subHeader)
        : '',
      message: options.message ? this.getPreparedMessage(options.message) : '',
      inputs: hasChild(options.inputs)
        ? await this.prepareModalInputs(options.inputs)
        : [],
      buttons: hasChild(options.buttons)
        ? await this.prepareModalButtons(options.buttons)
        : [],
    };

    //
    return alertOptions;
  }

  private preparePickerColumnOption(columnOption?: XPickerColumnOption) {
    //
    const pickerColumnOption = {
      ...columnOption,
      text: this.getPreparedMessage(columnOption.text),
    };

    //
    return pickerColumnOption;
  }

  private async preparePickerColumnOptions(
    columnOptions?: XPickerColumnOption[]
  ) {
    //
    if (!hasChild(columnOptions)) {
      return [];
    }

    //
    return columnOptions.map((co) => this.preparePickerColumnOption(co));
  }

  private async preparePickerColumn(column?: XPickerColumn) {
    //
    const pickerColumn = {
      ...column,
      options: await this.preparePickerColumnOptions(column.options),
    };

    //
    return pickerColumn;
  }

  private async preparePickerColumns(columns: XPickerColumn[]) {
    //
    if (!hasChild(columns)) {
      return [];
    }

    //
    return columns.map((c) => this.preparePickerColumn(c));
  }

  private async preparePickerOptions(options?: XPickerOptions) {
    //
    const pickerOptions = {
      ...options,
      buttons: (await this.prepareModalButtons(options.buttons)) || [],
      columns:
        (await Promise.all(await this.preparePickerColumns(options.columns))) ||
        [],
    };

    //
    return pickerOptions;
  }

  private async toActionSheetOptions(options?: XActionModalOptions) {
    //
    let buttons: any[] = [];

    //
    const optButtons = (await this.prepareModalButtons(options.buttons)) || [];
    buttons = [...buttons, ...optButtons];

    //
    //#region Handle Preserved Buttons ...
    if (options.hasRetry) {
      buttons.push(
        this.getRetryButton(options.retryRole, options.retryHandler)
      );
    }

    //
    if (options.hasAccept) {
      buttons.push(
        this.getAcceptButton(options.acceptRole, options.acceptHandler)
      );
    }

    //
    if (options.hasOk) {
      buttons.push(this.getOkButton(options.okRole, options.okHandler));
    }

    //
    if (options.hasCancel) {
      buttons.push(
        this.getCancelButton(options.cancelRole, options.cancelHandler)
      );
    }

    //
    if (options.hasYes) {
      buttons.push(this.getYesButton(options.yesRole, options.yesHandler));
    }

    //
    if (options.hasNo) {
      buttons.push(this.getNoButton(options.noRole, options.noHandler));
    }

    //
    if (options.hasDismiss) {
      buttons.push(
        this.getDismissButton(options.dismissRole, options.dismissHandler)
      );
    }
    //#endregion

    //
    const actionModalOptions = {
      ...options,
      header: options.header ? this.getPreparedMessage(options.header) : '',
      subHeader: options.subHeader
        ? this.getPreparedMessage(options.subHeader)
        : '',
      backdropDismiss: options.backdropDismiss || true,
      buttons,
    };

    //
    return actionModalOptions;
  }

  private getPreparedMessage(key: string | number) {
    return this.localizationService.isExistsId(key)
      ? this.localizationService.getValue(key)
      : key
      ? key.toString()
      : '';
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-framework-services.module.ts

import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { XDialogService } from './x-dialog.service';
import { XHistoryService } from './x-history.service';
import { XManagerService } from './x-manager.service';
import { XStorageService } from './x-storage.service';
import { HttpClientModule } from '@angular/common/http';
import { XSecurityService } from './x-security.service';
import { XSettingsService } from './x-settings.service';
import { XGeolocationService } from './x-geolocation.service';
import { XTaskManagerService } from './x-task-manager.service';
import { XLocalizationService } from './x-localization.service';
import { XNotificationService } from './x-notification.service';
import { XThemeManagerService } from './x-theme-manager.service';
import { XDirectivesModule } from './modules/x-directives.module';
import { XSecureStorageService } from './x-secure-storage.service';

@NgModule({
  declarations: [],
  imports: [
    IonicModule,
    CommonModule,
    RouterModule,
    HttpClientModule,
    XDirectivesModule,
  ],
  exports: [
    CommonModule,
    HttpClientModule,
    XDirectivesModule,
  ],
  providers: [
    //
    XDialogService,
    XStorageService,
    XManagerService,
    XHistoryService,
    XSettingsService,
    XSecurityService,
    XGeolocationService,
    XTaskManagerService,
    XLocalizationService,
    XNotificationService,
    XThemeManagerService,
    XSecureStorageService,
  ]
})
export class XFrameworkServicesModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-geolocation.service.ts

import {
  getError,
  Exceptions,
  XException,
  XBaseService,
} from 'x-framework-core';
import { Observable } from 'rxjs';
import { Injectable, Output, EventEmitter, Inject } from '@angular/core';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XGeolocationService extends XBaseService {
  //
  private IS_SUPPORT = false;
  private NAVIGATION_WATCHER: any;

  //
  options: PositionOptions;

  //
  @Output()
  changeLoading: EventEmitter<boolean> = new EventEmitter<boolean>();

  //
  isLoading = false;

  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig
  ) {
    super(config);

    //
    // Check Service is Supported or not ...
    if (navigator.geolocation) {
      this.IS_SUPPORT = true;
    }

    //
    this.options = {
      ...config.geoLocationConfig,
    };
  }

  /**
   * determines service is Supported or not
   */
  get isSupport() {
    return this.IS_SUPPORT;
  }

  /**
   * get given position link for open on Google Maps ...
   */
  getGoogleMapLink(position: GeolocationPosition) {
    return `https://maps.google.com/?q=${position.coords.latitude},${position.coords.longitude}`;
  }

  /**
   * get given position Lat,Long string ...
   */
  getLatLong(position: GeolocationPosition) {
    return `${position.coords.latitude},${position.coords.longitude}`;
  }

  /**
   * get current location ...
   */
  getCurrentLocation(): Observable<GeolocationPosition> {
    //
    this.startLoading();

    return new Observable((observer) => {
      //
      if (!this.IS_SUPPORT) {
        //
        this.stopLoading();
        observer.error(getError(Exceptions.UnsupportedBrowser));
      }

      //
      navigator.geolocation.getCurrentPosition(
        (position) => {
          //
          this.stopLoading();

          //
          observer.next(position);
          observer.complete();
        },
        (error) => {
          //
          this.stopLoading();

          //
          let exception: XException;
          switch (error.code) {
            case 1:
              exception = Exceptions.PermissionDenied;
              break;

            case 2:
              exception = Exceptions.PositionUnavailable;
              break;

            case 3:
            default:
              exception = Exceptions.Timeout;
              break;
          }

          //
          observer.error(getError(exception));
        },
        {
          ...this.options,
        }
      );
    });
  }

  /**
   * start Watching Location Changes ...
   */
  watchLocation(): Observable<GeolocationPosition> {
    //
    this.startLoading();

    //
    return new Observable((observer) => {
      this.NAVIGATION_WATCHER = navigator.geolocation.watchPosition(
        (position) => {
          //
          if (!this.IS_SUPPORT) {
            //
            this.stopLoading();
            observer.error(getError(Exceptions.UnsupportedBrowser));
            observer.complete();
          }

          //
          observer.next(position);
        },
        (error) => {
          //
          this.stopLoading();
          let exception: XException;
          switch (error.code) {
            case 1:
              exception = Exceptions.PermissionDenied;
              break;

            case 2:
              exception = Exceptions.PositionUnavailable;
              break;

            case 3:
            default:
              exception = Exceptions.Timeout;
              break;
          }

          //
          observer.error(getError(exception));
        }
      );
    });
  }

  /**
   * Stop Watching Location Changes ...
   */
  stopWatchingLocation() {
    //
    this.stopLoading();
    navigator.geolocation.clearWatch(this.NAVIGATION_WATCHER);
  }

  private startLoading() {
    //
    this.isLoading = true;
    this.changeLoading.emit(true);
  }

  private stopLoading() {
    //
    this.isLoading = false;
    this.changeLoading.emit(false);
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-history.service.ts

import { Subject } from 'rxjs';
import { Injectable } from '@angular/core';
import { Location, PlatformLocation } from '@angular/common';

@Injectable({
  providedIn: 'root',
})
export class XHistoryService {
  //
  //#region Props ...
  //
  public readonly backHandler = new Subject();

  //
  //#region Getters ...
  //
  get href() {
    return this.platformLocation.href;
  }

  get protocol() {
    return this.platformLocation.protocol;
  }

  get hostname() {
    return this.platformLocation.hostname;
  }

  get port() {
    return this.platformLocation.port;
  }

  get pathname() {
    return this.platformLocation.pathname;
  }

  get search() {
    return this.platformLocation.search;
  }

  get hash() {
    return this.platformLocation.hash;
  }
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    public location: Location,
    public platformLocation: PlatformLocation
  ) {
    //
    // Prevent from Back Button ...
    this.platformLocation.onPopState(() => {
      this.backHandler.next();
    });
  }
  //#endregion

  //
  //#region Actions ...
  pushEmptyState() {
    this.pushState(null, null, this.platformLocation.href);
  }

  replaceEmptyState() {
    this.replaceState(null, null, this.platformLocation.href);
  }

  getBaseHrefFromDOM() {
    return this.platformLocation.getBaseHrefFromDOM();
  }

  getState() {
    return this.platformLocation.getState();
  }

  replaceState(state: any, title: string, url: string) {
    this.platformLocation.replaceState(state, title, url);
  }

  pushState(state: any, title: string, url: string) {
    this.platformLocation.pushState(state, title, url);
  }

  forward() {
    this.location.forward();
  }

  back() {
    this.location.back();
  }
  //#endregion

  //
  //#region Private ...
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-http-interceptor.service.ts

import {
  HttpEvent,
  HttpRequest,
  HttpHandler,
  HttpInterceptor,
  HttpErrorResponse,
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { Injectable, Inject } from '@angular/core';
import { finalize, catchError } from 'rxjs/operators';
import { XManagerService } from './x-manager.service';
import { XBaseService, getErrorException } from 'x-framework-core';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XHttpInterceptorService
  extends XBaseService
  implements HttpInterceptor {
  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig,
    private managerService: XManagerService
  ) {
    super(config);
  }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return this.handleInterceptRequest(req, next);
  }

  handleInterceptRequest(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    //
    // Apply Error Handler ...
    const handledErrors = this.applyErrorHandler(next.handle(req));

    //
    // Apply Finally Handler ...
    const handledFinally = this.applyFinally(handledErrors);

    //
    // Final Level ...
    const result = handledFinally;

    //
    return result;
  }

  //
  //#region Actions / Handlers ...
  applyFinally(current: Observable<HttpEvent<any>>) {
    //
    return current.pipe(
      finalize(() => {
        //
        // Collection of instructions after finish or error ...
        this.managerService.loading = false;
      })
    );
  }

  applyErrorHandler(current: Observable<HttpEvent<any>>) {
    return current.pipe(
      catchError((error: HttpErrorResponse) => {
        //
        this.managerService.loading = true;
        const mException = getErrorException(error.error);

        //
        this.managerService.error = mException;

        //
        return throwError(error);
      })
    );
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-identifier.service.ts

import { Injectable } from '@angular/core';
import { XBaseService } from 'x-framework-core';

@Injectable({
  providedIn: 'root',
})
export class XIdentifierService extends XBaseService {
  //
  //#region Props ...
  private readonly idStartKey = '_[';
  private readonly idEndKey = ']';
  //#endregion

  /**
   * check if task name contains id  ...
   *
   * @param key the task name ...
   */
  hasIdentifier(key: string | any, forceAppName = false) {
    //
    if (!key) {
      return false;
    }

    //
    const mKey = key.toString() as string;
    const result =
      mKey.includes(this.idStartKey) && mKey.includes(this.idEndKey);

    //
    return forceAppName
      ? result && mKey.includes(this.config.appInstanceName)
      : result;
  }

  /**
   * retrieve and extract the raw task name from a task name ...
   *
   * @param key the task name ...
   */
  getName(key: string | any) {
    //
    if (!key) {
      return '';
    }

    //
    const mKey = key.toString() as string;

    //
    if (!this.hasIdentifier(mKey)) {
      return mKey;
    }

    //
    const startIndex = mKey.indexOf(this.idStartKey);
    let result = mKey.substr(0, startIndex);
    result = result.replace(this.idStartKey, '');

    //
    return result;
  }

  /**
   * retrieve and extract the id inside a task name ...
   *
   * @param key the task name ...
   */
  getIdentifier(key: string | any, cleanAppName = true) {
    //
    // Validate Args ...
    if (!key || !this.hasIdentifier(key)) {
      return '';
    }

    //
    // Task Id ...
    let result = key.toString() as string;

    //
    // Validating Info ...
    let index = result.indexOf(this.idStartKey);

    //
    result = result.substr(index, result.length);
    result = result.replace(this.idStartKey, '');

    //
    index = result.indexOf(this.idEndKey);
    result = result.substring(0, index);

    //
    if (cleanAppName) {
      result = result.replace(this.config.appInstanceName, '');
    }

    //
    // Return Result ...
    return result;
  }

  /**
   * prepare a Task With ID and return ...
   *
   * @param key the Task Name ...
   * @param id th ID ...
   */
  setIdentifier(key: string | any, id: string | any, containsAppName = false) {
    return `${key.toString()}${this.idStartKey}${
      containsAppName ? this.config.appInstanceName : ''
    }${id.toString()}${this.idEndKey}`;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-localization.service.ts

import {
  XParam,
  getVal,
  XLocale,
  isNumber,
  formatDate,
  XException,
  Exceptions,
  fromUtcDate,
  XBaseService,
  XResourceIDs,
  GetDateProps,
  XExceptionIDs,
  throwException,
  isValidException,
  XTranslationItem,
  isNullOrUndefined,
  getErrorException,
  XLanguageDirection,
  XResourceIdentifier,
  XLanguageDescriptor,
  getErrorDateFormatString,
  getDateOfBirthDateFormatString,
} from 'x-framework-core';
import { Injectable, Inject } from '@angular/core';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XLocalizationService extends XBaseService {
  //
  private LANGUAGEDESCRIPTOR: XLanguageDescriptor;
  private AVAILABLELANGUAGES: XLanguageDescriptor[];

  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig
  ) {
    super(config);

    //
    // Inject Loacle and set Application Locale and Direction based on it ...
    this.AVAILABLELANGUAGES = config.availableLanguages;
    this.LANGUAGEDESCRIPTOR = config.availableLanguages.find(
      (al) => al.locale === config.defaultLanguage
    );
    window[XParam.Language] = this.LANGUAGEDESCRIPTOR.locale;
  }

  //
  //#region Properties ...
  /**
   * Current System Locale ...
   */
  get locale() {
    return this.languageDescriptor.locale;
  }

  /**
   * Current Locale UI Direction ...
   */
  get direction(): XLanguageDirection {
    return this.languageDescriptor.direction;
  }

  /**
   * current available language
   */
  get language() {
    return this.languageDescriptor.language;
  }

  /**
   * get Current Locale Name ...
   */
  get name() {
    return this.languageDescriptor.name;
  }

  /**
   * get Current Locale Language Descriptor Object ...
   */
  get languageDescriptor() {
    return this.LANGUAGEDESCRIPTOR;
  }

  /**
   * get Available Languages ...
   */
  get availableLanguages() {
    return this.AVAILABLELANGUAGES;
  }
  //#endregion

  /**
   * change current language ...
   *
   * @param locale name of available language
   */
  changeLocale(locale: XLocale) {
    //
    const languageDescriptor = this.languageDescriptorOfLocale(locale);
    if (!languageDescriptor) {
      throwException(XExceptionIDs.InvalidConfiguration);
    }

    //
    window[XParam.Language] = locale;
    this.LANGUAGEDESCRIPTOR = languageDescriptor;
  }

  /**
   * Find specific Language Descriptor ...
   */
  languageDescriptorOfLocale(locale: XLocale) {
    //
    return this.availableLanguages.find((al) => al.locale === locale);
  }

  /**
   * check an id exists in resources or not
   */
  isExistsId(id: XResourceIdentifier) {
    //
    const key = getVal(XResourceIDs, id) || id;

    //
    return (
      this.languageDescriptor.resources.findIndex((tr) => tr.id === key) > -1
    );
  }

  /**
   * check an id exists in resources of specific locale or not
   */
  isExistsIdOfLocale(locale: XLocale, id: XResourceIdentifier) {
    //
    const key = getVal(XResourceIDs, id) || id;

    //
    return (
      this.languageDescriptorOfLocale(locale).resources.findIndex(
        (tr) => tr.id === key
      ) > -1
    );
  }

  /**
   * get translation item of current language
   */
  getTranslationItem(id: XResourceIdentifier): XTranslationItem {
    //
    const key = getVal(XResourceIDs, id) || id;

    //
    return this.languageDescriptor.resources.find((tr) => tr.id === key);
  }

  /**
   * get translation item of specific language
   */
  getTranslationItemOfLocale(locale: XLocale, id: XResourceIdentifier) {
    //
    const key = getVal(XResourceIDs, id) || id;

    //
    const selectedLocale = this.languageDescriptorOfLocale(locale);

    //
    if (!selectedLocale || !this.isExistsIdOfLocale(locale, key)) {
      throwException(XExceptionIDs.NotFound);
    }

    //
    return selectedLocale.resources.find((tr) => tr.id === key);
  }

  /**
   * get Value of specific id in Current Locale
   */
  getValue(id: XResourceIdentifier) {
    //
    if (!this.isExistsId(id)) {
      return null;
    }

    //
    return this.getTranslationItem(id).value;
  }

  /**
   * get Value of specific id in specific Locale
   */
  getValueOfLocale(locale: XLocale, id: XResourceIdentifier) {
    //
    if (!this.isExistsIdOfLocale(locale, id)) {
      return null;
    }

    //
    return this.getTranslationItemOfLocale(locale, id).value;
  }

  /**
   * get coresponding translated value of errors
   */
  getErrorMessage(err: any, locale?: XLocale) {
    //
    if (!locale) {
      locale = this.locale;
    }

    //
    let exception: XException;

    //
    if (isValidException(err)) {
      exception = err as XException;
    } else {
      exception = getErrorException(err);
    }

    //
    if (exception.id && isNumber(exception.id)) {
      //
      const exceptionID = `ex_${exception.id}`;
      return this.getValueOfLocale(locale, exceptionID);
    }

    //
    return this.getValueOfLocale(locale, 'general_error');
  }

  /**
   * get and format date
   */
  getDate(props?: GetDateProps): string {
    //
    if (!props) {
      props = {};
    }

    //
    if (!props.locale) {
      props.locale = this.locale;
    }
    //
    if (!props.date) {
      props.date = new Date();
    }
    //
    if (!props.dateFormat) {
      props.dateFormat = getDateOfBirthDateFormatString(props.locale);
    }
    //
    if (!props.toUtc) {
      props.toUtc = false;
    }
    //
    return formatDate(props.date, props.locale, props.dateFormat, props.toUtc);
  }

  /**
   * combine some resources and represent single joined string ...
   *
   * @param locale translated dest locale ...
   * @param joinString the string which used to join translated parts ...
   * @param resources an array of translation resources ...
   */
  combine(
    locale?: XLocale,
    joinString: string = ' ',
    ...resources: XResourceIdentifier[]
  ) {
    //
    if (isNullOrUndefined(locale)) {
      locale = this.locale;
    }

    //
    const result = resources
      .map((r) => this.getValueOfLocale(locale, r))
      .join(joinString);

    //
    return result;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-manager.service.ts

import { Location } from '@angular/common';
import { Platform } from '@angular/cdk/platform';
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { XDialogService } from './x-dialog.service';
import { XStorageService } from './x-storage.service';
import { XHistoryService } from './x-history.service';
import { XSecurityService } from './x-security.service';
import { XSettingsService } from './x-settings.service';
import { Router, ActivatedRoute } from '@angular/router';
import { XIdentifierService } from './x-identifier.service';
import { XGeolocationService } from './x-geolocation.service';
import { XTaskManagerService } from './x-task-manager.service';
import { MenuController, NavController } from '@ionic/angular';
import { XNotificationService } from './x-notification.service';
import { XLocalizationService } from './x-localization.service';
import { XThemeManagerService } from './x-theme-manager.service';
import { XSecureStorageService } from './x-secure-storage.service';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';
import { XManagerFinalService } from './x-manager-service-partial/x-manager-final.service';

@Injectable({
  providedIn: 'root',
})
export class XManagerService extends XManagerFinalService {
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG)
    public config: XFrameworkServicesConfig,
    public router: Router,
    public platform: Platform,
    public location: Location,
    public httpClient: HttpClient,
    public dialogService: XDialogService,
    public menuController: MenuController,
    public activatedRoute: ActivatedRoute,
    public historyService: XHistoryService,
    public storageService: XStorageService,
    public securityService: XSecurityService,
    public settingsService: XSettingsService,
    public navigationController: NavController,
    public identifierService: XIdentifierService,
    public geoLocationService: XGeolocationService,
    public taskManagerService: XTaskManagerService,
    public themeManagerService: XThemeManagerService,
    public notificationService: XNotificationService,
    public localizationService: XLocalizationService,
    public secureStorageService: XSecureStorageService
  ) {
    super(
      config,
      router,
      platform,
      location,
      httpClient,
      dialogService,
      menuController,
      activatedRoute,
      historyService,
      storageService,
      securityService,
      settingsService,
      navigationController,
      geoLocationService,
      taskManagerService,
      themeManagerService,
      notificationService,
      localizationService,
      secureStorageService
    );
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-notification.service.ts

import {
  XColor,
  isNumber,
  XActionType,
  XValidators,
  XResourceIDs,
  XBaseService,
  XNotificationParams,
  XTypedNotificationParams,
  XErrorNotificationParams,
} from 'x-framework-core';
import { Howl } from 'howler';
import { ToastOptions } from '@ionic/core';
import { ToastController } from '@ionic/angular';
import { Injectable, Inject } from '@angular/core';
import { XLocalizationService } from './x-localization.service';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XNotificationService extends XBaseService {
  //
  private NOTIFICATION_IDS: string[] = [];

  //
  private userNotifiCationSound: Howl;
  private captureNotifiCationSound: Howl;
  private systemNotifiCationSound: Howl;

  //
  public notificationDelay: number;

  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig,
    private localizationService: XLocalizationService,
    private toastController: ToastController
  ) {
    //
    super(config);

    //
    const userActionConfig = config.notificationAudioSources.find(
      (as) => as.type === XActionType.User
    );
    const systemActionConfig = config.notificationAudioSources.find(
      (as) => as.type === XActionType.System
    );
    const captureActionConfig = config.notificationAudioSources.find(
      (as) => as.type === XActionType.Capture
    );

    //
    this.notificationDelay = config.defaultNotificationDelay;

    //
    this.userNotifiCationSound = new Howl({
      src: [userActionConfig.resourcePath],
    });
    this.captureNotifiCationSound = new Howl({
      src: [captureActionConfig.resourcePath],
    });
    this.systemNotifiCationSound = new Howl({
      src: [systemActionConfig.resourcePath],
    });
  }

  /**
   * play sound based on type
   */
  playSound(type: XActionType = XActionType.System) {
    //
    switch (type) {
      //
      case XActionType.Capture:
        this.playCaptureNotification();
        break;

      //
      case XActionType.User:
        this.playUserNotification();
        break;

      //
      default:
      case XActionType.System:
        this.playSystemNotification();
        break;
    }
  }

  /**
   * play system sound
   */
  playSystemNotification() {
    //
    if (this.systemNotifiCationSound.playing()) {
      return;
    }

    //
    this.systemNotifiCationSound.play();
  }

  /**
   * play user sound
   */
  playUserNotification() {
    //
    if (this.userNotifiCationSound.playing()) {
      return;
    }

    //
    this.userNotifiCationSound.play();
  }

  /**
   * play Capture sound
   */
  playCaptureNotification() {
    //
    if (this.captureNotifiCationSound.playing()) {
      return;
    }

    //
    this.captureNotifiCationSound.play();
  }

  //
  //#region Toas Related ...
  /**
   * create a notification object ...
   *
   * @param opt notification options
   */
  createNotification(props?: XNotificationParams) {
    const toastOpt = this.prepareToastOptions(props);
    return this.toastController.create(toastOpt);
  }

  /**
   * create and present notification ...
   *
   * @param opt notification options
   */
  async presentNotification(props?: XNotificationParams) {
    //
    const mProps = props ? props : this.getDefaultNotificationOptions();

    //
    const notification = await this.createNotification(mProps);
    this.NOTIFICATION_IDS.push(notification.id);

    //
    notification.onDidDismiss().then(() => {
      if (mProps.onDidDismiss) {
        mProps.onDidDismiss();
        this.NOTIFICATION_IDS = this.NOTIFICATION_IDS.filter(
          (id) => id === notification.id
        );
      }
    });

    //
    notification.onWillDismiss().then(() => {
      if (mProps.onWillDismiss) {
        mProps.onWillDismiss();
      }
    });

    //
    notification.present();

    //
    if (props && props.playSound) {
      this.playSound(props.type);
    }
  }

  async presentXNotification(props: XTypedNotificationParams) {
    //
    // Validate Args ...
    XValidators.validateNotNull(props);

    //
    const isResourceID = isNumber(props.message);
    const mMessage = isResourceID
      ? this.localizationService.getValue(props.message as XResourceIDs)
      : (props.message as string);
    const defOpts = this.getDefaultNotificationOptions();
    const mProps = {
      ...defOpts,
      ...props,
      opt: {
        ...defOpts.opt,
        ...props.opt,
        message: mMessage,
      },
    };

    //
    await this.presentNotification(mProps);
  }

  /**
   * present Info notification
   *
   * @param props Notification Properties
   */
  async presentInfoNotification(
    props: XTypedNotificationParams
  ): Promise<void> {
    await this.presentXNotification({
      ...props,
      opt: {
        ...props.opt,
        color: XColor.Tertiary,
      },
    });
  }

  /**
   * present Success Notification
   *
   * @param props Notification Properties
   */
  async presentSuccessNotification(
    props: XTypedNotificationParams
  ): Promise<void> {
    await this.presentXNotification({
      ...props,
      opt: {
        ...props.opt,
        color: XColor.Success,
      },
    });
  }

  /**
   * present Default Success Notification
   */
  async presentDefaultSuccessNotification(
    dissmissHandler?: () => void
  ): Promise<void> {
    //
    const mProps: XTypedNotificationParams = {
      message: XResourceIDs.success_msg,
      dissmissable: true,
      onWillDismiss: dissmissHandler,
    };

    await this.presentSuccessNotification(mProps);
  }

  /**
   * present warning notifications
   *
   * @param props Notification Properties
   */
  async presentWarningNotification(
    props: XTypedNotificationParams
  ): Promise<void> {
    await this.presentXNotification({
      ...props,
      opt: {
        ...props.opt,
        color: XColor.Warning,
      },
    });
  }

  /**
   * present error notification
   *
   * @param props Notification Properties
   */
  async presentDangerNotification(
    props: XTypedNotificationParams
  ): Promise<void> {
    await this.presentXNotification({
      ...props,
      opt: {
        ...props.opt,
        color: XColor.Danger,
      },
    });
  }

  /**
   * present an Exception or Error Message ...
   *
   * @param props Notification Properties
   */
  async presentErrorNotification(
    props: XErrorNotificationParams
  ): Promise<void> {
    //
    const mMessage = this.localizationService.getErrorMessage(props.exception);
    await this.presentXNotification({
      ...props,
      message: mMessage,
      dissmissable: props.dissmissable,
      opt: {
        ...props.opt,
        color: XColor.Danger,
      },
    });
  }

  async closeAllNotifications() {
    this.NOTIFICATION_IDS.forEach(async (id) => {
      try {
        await this.toastController.dismiss(null, null, id);
      } catch {}
    });
  }

  /**
   * combines default notification options by
   * given notification options ...
   *
   * @param opt notification options
   */
  private prepareToastOptions(props?: XNotificationParams): ToastOptions {
    //
    if (!props) {
      props = this.getDefaultNotificationOptions();
    }

    //
    // Handle Dissmissable
    const mDuration = props.dissmissable ? props.opt.duration : 0;

    //
    const result = {
      ...props.opt,
      duration: mDuration,
    };

    //
    return result;
  }

  /**
   * generate default notification options ...
   */
  private getDefaultNotificationOptions(): XNotificationParams {
    return {
      playSound: false,
      type: XActionType.System,
      dissmissable: true,
      opt: {
        color: XColor.Tertiary,
        position: 'bottom',
        duration: this.notificationDelay,
        // ,
        // showCloseButton: true,
        // closeButtonText: this.localizationService.getValue(XResourceIDs.dismiss)
      },
    };
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-secure-storage.service.ts

import {
  fromJson,
  XStorageItem,
  XExceptionIDs,
  throwException,
  isNullOrUndefined
} from 'x-framework-core';
import { Injectable, Inject } from '@angular/core';
import { XStorageService } from './x-storage.service';
import { XSecurityService } from './x-security.service';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root'
})
export class XSecureStorageService extends XStorageService {
  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig,
    private securitySerive: XSecurityService
  ) {
    super(config);
  }

  /**
   * writing a new item in Storage ...
   *
   * @param key the item key
   * @param data the data object which needs to save
   * @param forceRewrite overwrite item if exists, the default is false,
   * means if key exists throws an error ...
   */
  write(key: string, data: any, forceRewrite: boolean = false) {
    //
    if (!forceRewrite && this.isExists(key)) {
      throwException(XExceptionIDs.Duplicate);
    }

    //
    const theKey = this.getPreparedKey(key);
    const encryptedValue = this.securitySerive.encryptWithKey(
      JSON.stringify(data)
    );

    //
    localStorage.setItem(theKey, encryptedValue);
  }

  /**
   * read an item from Storage ...
   * @param key the item key
   * @param forceExists determine the given key must be exists or not, true is default,
   * means if key not exists throws an error ...
   */
  read(key: string, forceExists: boolean = true): any | null {
    //
    if (forceExists && !this.isExists(key)) {
      throwException(XExceptionIDs.NotFound);
    }

    //
    const theKey = this.getPreparedKey(key);
    const item = localStorage.getItem(theKey);
    if (!isNullOrUndefined(item)) {
      try {
        const decryptedValue = this.securitySerive.decryptWithKey(item);
        return JSON.parse(decryptedValue);
      } catch {}
    }

    //
    return null;
  }
  readOfType<T>(key: string, forceExists: boolean = true): T | null {
    //
    if (!this.isExists(key)) {
      //
      if (forceExists) {
        throwException(XExceptionIDs.NotFound);
      }

      //
      return null;
    }

    //
    const valStr = this.read(key, forceExists);
    return fromJson<T>(valStr);
  }

  /**
   * get a List of Stored items as an Array of StorageItem ...
   * @param force true is defualt, means only return list of items
   * from Storage which Stored by Service ...
   */
  list(force: boolean = true, decrypt: boolean = false): XStorageItem[] | null {
    //
    if (localStorage.length === 0) {
      return null;
    }

    //
    const items: XStorageItem[] = [];
    for (let i = 0; i < localStorage.length; i++) {
      //
      const mKey = localStorage.key(i);

      //
      const mValue = localStorage.getItem(mKey);
      const mValueRes = decrypt
        ? this.securitySerive.decryptWithKey(mValue)
        : mValue;

      //
      if (force) {
        const theRawKey = this.getRawKey(mKey);
        if (!isNullOrUndefined(theRawKey)) {
          //
          items.push({
            key: theRawKey,
            value: mValueRes
          });
        }
      } else {
        //
        items.push({
          key: mKey,
          value: mValueRes
        });
      }
    }

    //
    return items;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-security.service.ts

import * as CryptoJS from 'crypto-js';
import { DomSanitizer } from '@angular/platform-browser';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { toMd5, XBaseService, isNullOrEmptyString } from 'x-framework-core';
import { Injectable, Inject, Sanitizer, SecurityContext } from '@angular/core';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XSecurityService extends XBaseService {
  //
  private readonly SECRET_KEY: string;
  private readonly CIPHER_KEY: string;
  private readonly CIPHER_IV: string;
  private readonly CIPHER_OPTION: CryptoJS.lib.CipherOption;

  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig,
    private sanitizer: Sanitizer,
    private domSanitizer: DomSanitizer
  ) {
    super(config);

    //
    this.SECRET_KEY = toMd5(config.secretKey);

    //
    const forIv = this.SECRET_KEY.substring(0, 16);
    this.CIPHER_IV = CryptoJS.enc.Utf8.parse(forIv).toString();

    //
    const forKey = this.SECRET_KEY.substring(0, 32);
    this.CIPHER_KEY = CryptoJS.enc.Utf8.parse(forKey).toString();

    //
    this.CIPHER_OPTION = {
      iv: CryptoJS.enc.Utf8.parse(this.CIPHER_IV),
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7,
    };
  }

  encryptWithKey(
    value: string,
    key?: string,
    options?: CryptoJS.lib.CipherOption
  ) {
    //
    if (isNullOrEmptyString(value)) {
      return null;
    }

    //
    if (!key) {
      key = this.CIPHER_KEY;
    }

    //
    if (!options) {
      options = this.CIPHER_OPTION;
    }

    //
    return CryptoJS.AES.encrypt(value, key, options).toString();
  }

  encryptWithSecret(
    value: string,
    secret?: string,
    options?: CryptoJS.lib.CipherOption
  ) {
    //
    if (isNullOrEmptyString(value)) {
      return null;
    }

    //
    if (!secret) {
      secret = this.SECRET_KEY;
    }

    //
    if (!options) {
      options = this.CIPHER_OPTION;
    }

    //
    return CryptoJS.AES.encrypt(value, secret, options);
  }

  decryptWithKey(
    cipherValue: string,
    key?: string,
    options?: CryptoJS.lib.CipherOption
  ) {
    //
    if (isNullOrEmptyString(cipherValue)) {
      return null;
    }

    //
    if (!key) {
      key = this.CIPHER_KEY;
    }

    //
    if (!options) {
      options = this.CIPHER_OPTION;
    }

    //
    const result = CryptoJS.AES.decrypt(cipherValue, key, options).toString(
      CryptoJS.enc.Utf8
    );

    //
    return result;
  }

  decryptWithSecret(
    cipherValue: string,
    secret?: string,
    options?: CryptoJS.lib.CipherOption
  ) {
    //
    if (isNullOrEmptyString(cipherValue)) {
      return null;
    }

    //
    if (!secret) {
      secret = this.SECRET_KEY;
    }

    //
    if (!options) {
      options = this.CIPHER_OPTION;
    }

    //
    return CryptoJS.AES.decrypt(cipherValue, secret, options).toString();
  }

  toMd5(value: string) {
    return toMd5(value);
  }

  toSafeString(value: string, context: SecurityContext = SecurityContext.NONE) {
    return this.sanitizer.sanitize(context, value);
  }

  toSafeHTML(value: string) {
    return this.domSanitizer.bypassSecurityTrustHtml(value);
  }

  toSafeResourceURL(value: string) {
    return this.domSanitizer.bypassSecurityTrustResourceUrl(value);
  }

  toSafeURL(value: string) {
    return this.domSanitizer.bypassSecurityTrustUrl(value);
  }

  toSafeScript(value: string) {
    return this.domSanitizer.bypassSecurityTrustScript(value);
  }

  toSafeStyle(value: string) {
    return this.domSanitizer.bypassSecurityTrustStyle(value);
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-settings.service.ts

import {
  isArray,
  XLocale,
  XDeviceInfo,
  XValidators,
  XOrientation,
  XBaseService,
  XDeviceTypes,
  XOneOrManyType,
  XCssProperties,
  XLanguageDirection,
  XSettingsStorageKey,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  Breakpoints,
  BreakpointState,
  BreakpointObserver,
} from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { Md5 } from 'md5-typescript';
import { map } from 'rxjs/operators';
import { Platform } from '@ionic/angular';
import { Injectable, Inject } from '@angular/core';
import { XStorageService } from './x-storage.service';
import { DeviceDetectorService } from 'ngx-device-detector';
import { XLocalizationService } from './x-localization.service';
import { Meta, Title, MetaDefinition } from '@angular/platform-browser';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XSettingsService extends XBaseService {
  //
  private PREFIX = '--';
  private DEVICE_INFO: XDeviceInfo;
  private SIDE_MENU_OPENED: boolean;

  //
  //#region Breakpoint Observers ...
  isMobile$: Observable<boolean>;
  isXSmall$: Observable<boolean>;
  isSmall$: Observable<boolean>;
  isMedium$: Observable<boolean>;
  isLarge$: Observable<boolean>;
  isXLarge$: Observable<boolean>;
  isTablet$: Observable<boolean>;
  isWeb$: Observable<boolean>;
  isPortrait$: Observable<boolean>;
  isLandscape$: Observable<boolean>;
  orientation$: Observable<XOrientation>;
  //#endregion

  //
  //#region Properties ...
  //
  get uiDirection(): XLanguageDirection {
    return this.localizationService.direction;
  }
  //#endregion

  constructor(
    public platform: Platform,
    private meta: Meta,
    private titleService: Title,
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig,
    private storageService: XStorageService,
    private localizationService: XLocalizationService,
    private deviceService: DeviceDetectorService,
    private breakPointObserver: BreakpointObserver
  ) {
    //
    super(config);

    //
    // Handle Ui Directions based on Current Locale ...
    this.applyCurrentLocale();

    //
    // Generate Device Info ...
    this.prepareDeviceInfo();

    //
    // define objservable ...
    this.defineUiBreakPointObservers();

    //
    // Handle Metas for Theme ...
    this.handleThemeMetaTags();

    //
    // Prepare Toolbar Height ...
  }

  /**
   * restart application ...
   */
  restart() {
    //
    if (!window || !window.location) {
      return;
    }

    //
    const href = window.location.href;
    setTimeout(() => {
      window.location.href = href;
      window.location.reload();
    }, 100)
  }

  /**
   * change system locale ...
   *
   * @param locale available locale
   */
  async changeLocale(locale: XLocale, refresh: boolean = false) {
    //
    XValidators.validateNotNull(locale);
    if (locale === this.localizationService.locale) {
      return;
    }

    //
    this.storageService.write(XSettingsStorageKey.Locale, locale, true);
    this.localizationService.changeLocale(locale);
    this.applyCurrentLocale();

    //
    if (refresh) {
      this.restart();
    }
  }

  //
  //#region Met Seo Service ...
  addTag(tag: MetaDefinition, forceCreation?: boolean) {
    this.meta.addTag(tag, forceCreation);
  }

  addTags(tags: MetaDefinition[], forceCreation?: boolean) {
    this.meta.addTags(tags, forceCreation);
  }

  getTag(attrSelector: string) {
    this.meta.getTag(attrSelector);
  }

  getTags(attrSelector: string) {
    this.meta.getTags(attrSelector);
  }

  updateTag(tag: MetaDefinition, attrSelector?: string) {
    this.meta.updateTag(tag, attrSelector);
  }

  removeTag(attrSelector: string) {
    this.meta.removeTag(attrSelector);
  }

  removeTagElement(meta: HTMLMetaElement) {
    this.meta.removeTagElement(meta);
  }
  //#endregion

  //
  //#region Title Service ...
  /**
   * set Page ( Current Document ) Title
   * in Browser ...
   */
  setTitle(title?: string) {
    //
    const appNameKey = this.config.appNameResource;
    const appName = this.localizationService.getValue(appNameKey);

    //
    const pageTitle = !isNullOrEmptyString(title)
      ? `${appName} > ${title}`
      : appName;

    //
    this.titleService.setTitle(pageTitle);
  }

  /**
   * get the Current Document Title ...
   */
  getTitle() {
    return this.titleService.getTitle();
  }
  //#endregion

  //
  //#region Device Service ...
  /**
   * get Current Device Model Info
   */
  get device() {
    return this.DEVICE_INFO;
  }

  /**
   * get Current Device token
   */
  get deviceToken() {
    return this.DEVICE_INFO.token;
  }
  //#endregion

  //
  //#region Css Property Handler ...
  /**
   * reading the CSS Property ...
   *
   * @param key the key of property
   */
  readCssProperty(key: XCssProperties): string {
    //
    const bodyStyles = window.getComputedStyle(document.body);
    return bodyStyles.getPropertyValue(this.prepareCssKey(key));
  }

  getCssVar(name: XCssProperties, element?: HTMLElement) {
    //
    const docElement = element
      ? getComputedStyle(element)
      : getComputedStyle(document.documentElement);
    return !name || !docElement || isNullOrEmptyString(name)
      ? ''
      : docElement.getPropertyValue(this.prepareCssKey(name));
  }

  setCssVar(
    name: XCssProperties,
    value: string,
    elements?: XOneOrManyType<HTMLElement>
  ) {
    //
    if (isNullOrEmptyString(name)) {
      return;
    }

    //
    if (!elements || !isArray(elements)) {
      //
      const docElement = elements
        ? (elements as HTMLElement).style
        : document.documentElement.style;

      //
      docElement.setProperty(this.prepareCssKey(name), value);
    } else {
      (elements as HTMLElement[]).forEach((e) => {
        e.style.setProperty(this.prepareCssKey(name), value);
      });
    }
  }
  //#endregion

  //
  //#region Theme Manager ...

  //#endregion

  //
  //#region Private
  private applyCurrentLocale() {
    //
    let storedLocale = this.localizationService.locale;
    const isExistsLocale = this.storageService.isExists(
      XSettingsStorageKey.Locale
    );
    if (isExistsLocale) {
      storedLocale = this.storageService.readOfType<XLocale>(
        XSettingsStorageKey.Locale
      );
      this.localizationService.changeLocale(storedLocale);
    }

    //
    this.storageService.write(XSettingsStorageKey.Locale, storedLocale, true);

    //
    //#region Handle Ui Directions based on Current Locale ...
    document.documentElement.lang = this.localizationService.language;
    document.documentElement.dir = this.localizationService.direction;

    //
    this.setTitle();

    //
    // Document CSS ...
    document.body.style.setProperty(
      this.prepareCssKey('ui-direction'),
      this.localizationService.direction
    );

    //
    const isRtl = this.localizationService.direction === 'rtl';
    document.body.style.setProperty(
      this.prepareCssKey('is-rtl'),
      isRtl.toString()
    );
    //#endregion
  }

  private prepareDeviceInfo() {
    //
    //#region Generate Device Info ...
    //
    // Reading Device Specifications ...
    const deviceOs = this.deviceService.os;
    const deviceBrowser = this.deviceService.browser;
    const deviceOsVersion = this.deviceService.os_version;
    const deviceUserAgent = this.deviceService.userAgent;

    //
    // Generating Device Type ...
    let mDeviceType: XDeviceTypes;
    if (this.deviceService.isDesktop()) {
      mDeviceType = XDeviceTypes.Desktop;
    } else if (this.deviceService.isTablet()) {
      mDeviceType = XDeviceTypes.Tablet;
    } else if (this.deviceService.isMobile()) {
      mDeviceType = XDeviceTypes.Mobile;
    } else {
      mDeviceType = XDeviceTypes.Unknown;
    }

    //
    // Generating Device Identifier and it's token ...
    const deviceIdentifier = `[${deviceOs}]-[${deviceOsVersion}]-[${mDeviceType}]-[${deviceBrowser}]-[${deviceUserAgent}]`;
    const deviceToken = Md5.init(deviceIdentifier);

    //
    this.DEVICE_INFO = {
      os: deviceOs,
      browser: deviceBrowser,
      osVersion: deviceOsVersion,
      userAgent: deviceUserAgent,
      deviceType: mDeviceType,
      identifier: deviceIdentifier,
      token: deviceToken,
    };
    //#endregion
  }

  private defineUiBreakPointObservers() {
    //
    //#region define objservable ...
    this.isPortrait$ = this.breakPointObserver
      .observe([
        Breakpoints.WebPortrait,
        Breakpoints.TabletPortrait,
        Breakpoints.HandsetPortrait,
      ])
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isLandscape$ = this.breakPointObserver
      .observe([
        Breakpoints.WebLandscape,
        Breakpoints.TabletLandscape,
        Breakpoints.HandsetLandscape,
      ])
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.orientation$ = this.breakPointObserver
      .observe([
        Breakpoints.WebPortrait,
        Breakpoints.TabletPortrait,
        Breakpoints.HandsetPortrait,
        Breakpoints.WebLandscape,
        Breakpoints.TabletLandscape,
        Breakpoints.HandsetLandscape,
      ])
      .pipe(
        map(() => {
          if (
            this.breakPointObserver.isMatched([
              Breakpoints.WebPortrait,
              Breakpoints.TabletPortrait,
              Breakpoints.HandsetPortrait,
            ])
          ) {
            return 'portrait';
          } else {
            return 'landscape';
          }
        })
      );

    //
    this.isXSmall$ = this.breakPointObserver
      .observe(Breakpoints.XSmall)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isSmall$ = this.breakPointObserver
      .observe(Breakpoints.Small)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isMedium$ = this.breakPointObserver
      .observe(Breakpoints.Medium)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isLarge$ = this.breakPointObserver
      .observe(Breakpoints.Large)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isXLarge$ = this.breakPointObserver
      .observe(Breakpoints.XLarge)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isTablet$ = this.breakPointObserver
      .observe(Breakpoints.Tablet)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isWeb$ = this.breakPointObserver
      .observe(Breakpoints.Web)
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));

    //
    this.isMobile$ = this.breakPointObserver
      .observe('(max-width: 768px)')
      .pipe(map((breakPointState: BreakpointState) => breakPointState.matches));
    //#endregion
  }

  private handleThemeMetaTags() {
    //
    //#region Handle Metas for Theme ...
    const darkPrimaryColor = this.readCssProperty('x-app-bar-color');
    this.addTag(
      {
        name: 'theme-color',
        content: darkPrimaryColor,
      },
      true
    );
    this.addTag(
      {
        name: 'msapplication-navbutton-color',
        content: darkPrimaryColor,
      },
      true
    );
    this.addTag(
      {
        name: 'apple-mobile-web-app-capable',
        content: 'yes',
      },
      true
    );
    this.addTag(
      {
        name: 'apple-mobile-web-app-status-bar-style',
        content: darkPrimaryColor,
      },
      true
    );
    //#endregion
  }

  private prepareCssKey(key: XCssProperties) {
    return `${this.PREFIX}${key}`;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-storage.service.ts

import {
  fromJson,
  XStorageItem,
  XBaseService,
  XExceptionIDs,
  throwException,
  isNullOrUndefined
} from 'x-framework-core';
import { Injectable, Inject } from '@angular/core';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root'
})
export class XStorageService extends XBaseService {
  //
  private keyPrefix: string;

  //
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig
  ) {
    super(config);

    //
    if (isNullOrUndefined(typeof Storage)) {
      throwException(XExceptionIDs.StorageNotSupported);
    }

    //
    this.keyPrefix = `${config.appInstanceName}`;
  }

  /**
   * determine a key exists in storage or not ...
   * @param key the item key
   */
  isExists(key: string) {
    //
    const theKey = this.getPreparedKey(key);
    const result = localStorage.getItem(theKey) !== null;

    //
    return result;
  }

  /**
   * writing a new item in Storage ...
   *
   * @param key the item key
   * @param data the data object which needs to save
   * @param forceRewrite overwrite item if exists, the default is false,
   * means if key exists throws an error ...
   */
  write(key: string, data: any, forceRewrite: boolean = false) {
    //
    if (!forceRewrite && this.isExists(key)) {
      throwException(XExceptionIDs.Duplicate);
    }

    //
    const theKey = this.getPreparedKey(key);
    localStorage.setItem(theKey, JSON.stringify(data));
  }

  /**
   * read an item from Storage ...
   * @param key the item key
   * @param forceExists determine the given key must be exists or not, true is default,
   * means if key not exists throws an error ...
   */
  read(key: string, forceExists: boolean = true): any | null {
    //
    if (forceExists && !this.isExists(key)) {
      throwException(XExceptionIDs.NotFound);
    }

    //
    const theKey = this.getPreparedKey(key);
    const item = localStorage.getItem(theKey);
    if (!isNullOrUndefined(item)) {
      try {
        return JSON.parse(item);
      } catch {}
    }

    //
    return null;
  }
  readOfType<T>(key: string, forceExists: boolean = true): T | null {
    //
    if (!this.isExists(key)) {
      //
      if (forceExists) {
        throwException(XExceptionIDs.NotFound);
      }

      //
      return null;
    }

    //
    const valStr = this.read(key, forceExists);
    return fromJson<T>(valStr);
  }

  /**
   * remove an stored item from Storage ...
   * @param key the item key
   * @param forceExists determine the given key must be exists or not, true is default,
   * means if key not exists throws an error ...
   */
  remove(key: string, forceExists: boolean = true) {
    //
    if (forceExists && !this.isExists(key)) {
      throwException(XExceptionIDs.NotFound);
    }

    //
    const theKey = this.getPreparedKey(key);
    localStorage.removeItem(theKey);
  }

  /**
   * get a List of Stored items as an Array of StorageItem ...
   * @param force true is defualt, means only return list of items
   * from Storage which Stored by Service ...
   */
  list(force: boolean = true): XStorageItem[] | null {
    //
    if (localStorage.length === 0) {
      return null;
    }

    //
    const items: XStorageItem[] = [];
    for (let i = 0; i < localStorage.length; i++) {
      //
      const mKey = localStorage.key(i);

      //
      if (force) {
        const theRawKey = this.getRawKey(mKey);
        if (!isNullOrUndefined(theRawKey)) {
          //
          items.push({
            key: theRawKey,
            value: localStorage.getItem(mKey)
          });
        }
      } else {
        //
        items.push({
          key: mKey,
          value: localStorage.getItem(mKey)
        });
      }
    }

    //
    return items;
  }

  /**
   * get the number of items which stored in Storage ...
   * @param force true is defualt, means only return number of items
   * from Storage which Stored by Service ...
   */
  length(force: boolean = true) {
    //
    if (force) {
      return this.list().length;
    }

    //
    return localStorage.length;
  }

  /**
   * clear all items from Storage ...
   * @param force true is defualt, means only clear items
   * from Storage which Stored by Service ...
   */
  clear(force: boolean = true) {
    localStorage.clear();
  }

  protected getPreparedKey(key: string) {
    return `${this.keyPrefix}_${key}`;
  }

  protected getRawKey(key: string): string | null {
    //
    const baseKeySubstr = `${this.keyPrefix}_`;
    if (key.indexOf(baseKeySubstr) === -1) {
      return null;
    }

    //
    return key.replace(baseKeySubstr, '');
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-task-manager.service.ts

import {
  XTask,
  XException,
  XBaseService,
  XTaskPriority,
  isValidException,
  getErrorException,
} from 'x-framework-core';
import { Injectable, Inject } from '@angular/core';
import { concatMap, catchError, tap } from 'rxjs/operators';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { Subscription, Subject, BehaviorSubject, of, throwError } from 'rxjs';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

@Injectable({
  providedIn: 'root',
})
export class XTaskManagerService extends XBaseService {
  //
  //#region Props ...
  //
  subscription: Subscription;

  //
  // Notify loading ...
  public allTasksFinished = new Subject();
  public loading = new BehaviorSubject<boolean>(false);

  //
  // notify Specific Task Start or Finished ...
  public taskStart = new Subject<XTask<any>>();
  public taskError = new Subject<XTask<any>>();
  public taskFinished = new Subject<XTask<any>>();

  //
  private isPaused = false;
  private isStarted = false;
  private tasks = new Map<string, XTask<any>>();
  private taskQueue = new Subject<XTask<any>>();

  /**
   * retrieve a Collection of Tasks which Pending to run ...
   */
  get taskList() {
    return this.getSortedTasksLists();
  }

  /**
   * retrieve Running State of Tasks ...
   */
  get paused() {
    return this.isPaused;
  }
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG) config: XFrameworkServicesConfig
  ) {
    super(config);

    //
    // Register Task Queue Runner ...
    this.taskQueue.asObservable().subscribe((t) => {
      this.handleTaskRun(t);
    });

    //
    this.allTasksFinished.asObservable().subscribe(() => {
      this.loading.next(false);
    });
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * determines a Tasks is in Pending List or not ...
   */
  has(taskName: string) {
    return this.tasks.has(taskName);
  }

  /**
   * delete a task from Pending List ...
   */
  delete(taskName: string) {
    return this.tasks.delete(taskName);
  }

  /**
   * add a Task to Pending List ...
   */
  add<T>(task: XTask<T>) {
    //
    // Prevent Duplicate ...
    if (this.has(task.name)) {
      return false;
    }

    //
    // add Task to Pending List ...
    this.tasks.set(task.name, task);

    //
    // Start Running if it's not Started Before ...
    if (!this.isStarted) {
      this.requestNext();
    }

    //
    return true;
  }

  /**
   * Change Specific Tasks Priority ...
   */
  changePriority(taskName: string, priority: XTaskPriority) {
    //
    if (!this.has(taskName)) {
      return;
    }

    //
    this.tasks.get(taskName).priority = priority;
  }

  /**
   * Count Pending List Tasks ...
   */
  count() {
    return this.tasks.size;
  }

  /**
   * Remove All Pending List Task ...
   */
  clear() {
    this.isStarted = false;
    this.tasks.clear();
  }

  /**
   * Pause Running of Tasks ...
   */
  pause() {
    this.isPaused = true;
  }

  /**
   * Continue Running Pending Tasks ...
   */
  continue() {
    //
    if (!this.isPaused) {
      return;
    }

    //
    this.isPaused = false;
    this.requestNext();
  }

  /**
   * Toggle Pause/Continue Tasks Running ...
   */
  toggle() {
    //
    if (this.isPaused) {
      this.continue();
    } else {
      this.pause();
    }
  }
  //#endregion

  //
  //#region Private ...
  private requestNext() {
    //
    if (this.isPaused) {
      return;
    }

    //
    const task = this.popTask();

    //
    if (task) {
      //
      this.isStarted = true;
      this.taskQueue.next(task);
    } else {
      //
      this.isStarted = false;
      if (this.tasks.size === 0) {
        //
        this.loading.next(false);
        this.allTasksFinished.next();
      }
    }
  }

  private popTask() {
    //
    const result = this.getSortedTasksLists().pop() || null;

    //
    if (result) {
      this.tasks.delete(result.name);
    }
    return result;
  }

  private getSortedTasksLists() {
    //
    return Array.from(this.tasks.values())
      .sort((a, b) => a.priority - b.priority)
      .reverse();
  }

  private applyWrapperToTask(task: XTask<any>) {
    //
    return of(task).pipe(
      concatMap((res) => {
        //
        if (res.affectLoading) {
          this.loading.next(true);
        }

        //
        this.taskStart.next(res);

        //
        return res.task.pipe(
          catchError((err) => {
            //
            this.isStarted = false;
            this.loading.next(false);

            //
            res.error = getErrorException(err);

            //
            if (isValidException(err.error)) {
              res.error = err.error as XException;
            }

            //
            this.taskError.next(res);

            //
            this.handleUnSubscribe();
            return throwError(res.error);
          }),
          tap((tRes) => {
            //
            this.isStarted = false;
            this.loading.next(false);

            //
            res.result = tRes;
            this.taskFinished.next(res);

            //
            this.handleUnSubscribe();
            this.requestNext();
          })
        );
      })
    );
  }

  private handleTaskRun(task: XTask<any>) {
    //
    const mXTObs = this.applyWrapperToTask(task);

    //
    this.subscription = mXTObs.subscribe(
      (_) => {},
      (_) => {}
    );
  }

  private handleUnSubscribe() {
    //
    if (this.subscription) {
      //
      this.subscription.unsubscribe();
      this.subscription = null;
    }
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-theme-manager.service.ts

import {
  keys,
  hasChild,
  XThemeType,
  XThemePack,
  XThemeColors,
  downloadFile,
  XBaseService,
  XExceptionIDs,
  XThemeCssVars,
  toNormalString,
  isNullOrUndefined,
  isNullOrEmptyString,
  XExceptionIdentifier,
  XThemeTypeIdentifier,
  XThemeCssVarsIdentifier,
} from 'x-framework-core';
import { XStorageService } from './x-storage.service';
import { XSettingsService } from './x-settings.service';
import { XNotificationService } from './x-notification.service';
import { X_FRAMEWORK_SERVICES_CONFIG } from './tokens/x-injectable-tokens';
import { Injectable, Inject, EventEmitter, ElementRef } from '@angular/core';
import { XFrameworkServicesConfig } from './config/x-framework-services.config';

const XThemeManagerStorageKeys = {
  CurrentTheme: 'current_theme',
  CurrentThemeType: 'current_theme_type',
  Themes: 'themes',
};

@Injectable({
  providedIn: 'root',
})
export class XThemeManagerService extends XBaseService {
  //
  //#region Props ...
  //
  private PREFIX = '--';
  readonly ColorCssVars = Object.assign({}, XThemeCssVars);

  //
  notifyChanges = new EventEmitter<void>();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG)
    public config: XFrameworkServicesConfig,
    private storageService: XStorageService,
    private settingsService: XSettingsService,
    private notificationService: XNotificationService
  ) {
    //
    super(config);

    //
    this.initializeService();
  }
  //#endregion

  //
  //#region Actions ...
  async applyCurrentTheme(element?: ElementRef) {
    //
    const currentTheme = await this.getCurrentTheme();
    const isDefaultThemeExists = !isNullOrUndefined(currentTheme);
    if (isDefaultThemeExists) {
      //
      const type = await this.getCurrentThemeType();
      await this.setTheme(currentTheme, type, false, false, element);

      //
      this.notifyChanges.emit();
    }
  }

  async getCurrentThemeName() {
    //
    const mKey = XThemeManagerStorageKeys.CurrentTheme;

    //
    const isExists = this.storageService.isExists(mKey);
    if (!isExists) {
      return '';
    }

    //
    const result = this.storageService.readOfType<string>(mKey);

    //
    return result;
  }

  async setCurrentThemeName(themeName: string, notifyChanges: boolean = true) {
    //
    const isExists = await this.isThemeExists(themeName);
    if (!isExists) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    const mKey = XThemeManagerStorageKeys.CurrentTheme;

    //
    this.storageService.write(mKey, themeName, true);

    //
    if (notifyChanges) {
      //
      this.notifyChanges.emit();
      await this.issueSuccessNotification();
    }

    //
    return true;
  }

  async getCurrentThemeType() {
    //
    const mKey = XThemeManagerStorageKeys.CurrentThemeType;

    //
    const isExists = this.storageService.isExists(mKey);
    if (!isExists) {
      return XThemeType.Light;
    }

    //
    const result = this.storageService.readOfType<XThemeTypeIdentifier>(mKey);

    //
    return result;
  }

  async setCurrentThemeType(
    themeType: XThemeTypeIdentifier,
    notifyChanges: boolean = true
  ) {
    //
    if (isNullOrEmptyString(themeType)) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.InvalidArgs);
      }

      //
      return false;
    }

    //
    const mKey = XThemeManagerStorageKeys.CurrentThemeType;

    //
    this.storageService.write(mKey, themeType, true);

    //
    if (notifyChanges) {
      //
      this.notifyChanges.emit();
      await this.issueSuccessNotification();
    }

    //
    return true;
  }

  async setTheme(
    theme: XThemePack,
    whichType: XThemeTypeIdentifier = XThemeType.Light,
    notifyChanges: boolean = true,
    restart: boolean = true,
    element?: ElementRef
  ) {
    //
    if (isNullOrUndefined(theme)) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.InvalidArgs);
      }

      //
      return false;
    }

    //
    const isExists = await this.isThemeExists(theme.name);
    if (!isExists) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    let result = false;
    switch (whichType) {
      //
      case XThemeType.Light:
        result = await this.setThemeColors(theme.light.colors, element);
        break;

      //
      case XThemeType.Dark:
        result = await this.setThemeColors(theme.dark.colors, element);
        break;
    }

    //
    if (result) {
      //
      await this.setCurrentThemeType(whichType, false);
      await this.setCurrentThemeName(theme.name, false);
    }

    //
    if (result && notifyChanges) {
      //
      this.notifyChanges.emit();
      await this.issueSuccessNotification();
    }

    //
    if (result && restart) {
      this.settingsService.restart();
    }

    //
    return result;
  }

  async cloneTheme(
    themeName: string,
    clonedThemeName: string,
    notifyChanges: boolean = true
  ) {
    //
    const isExists = await this.isThemeExists(themeName);
    if (!isExists) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    const isClonedThemeNameReserved = await this.isThemeExists(clonedThemeName);
    if (isClonedThemeNameReserved) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.Duplicate);
      }

      //
      return false;
    }

    //
    const theme = await this.getTheme(themeName);
    if (!theme) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    theme.name = clonedThemeName;
    const result = await this.saveTheme(theme, false);

    //
    if (result && notifyChanges) {
      this.notifyChanges.emit();
    }

    //
    return result;
  }

  async exportTheme(themeName: string) {
    //
    if (isNullOrEmptyString(themeName)) {
      return;
    }

    //
    const theme = await this.getTheme(themeName);
    if (isNullOrUndefined(theme)) {
      return;
    }

    //
    const theJSON = JSON.stringify(theme);

    //
    downloadFile(theJSON, 'text/json', `x-framework-${theme.name}-theme.json`);
  }

  async removeTheme(themeName: string, notifyChanges: boolean = true) {
    //
    const isExists = await this.isThemeExists(themeName);
    if (!isExists) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    const currentThemeName = await this.getCurrentThemeName();
    if (!currentThemeName) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    const themes = await this.getThemes();
    if (!hasChild(themes) || themes.length === 1) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    const filteredThemes = themes.filter(
      (th) => toNormalString(th.name) !== toNormalString(themeName)
    );

    //
    let result = false;
    try {
      //
      result = true;
      this.writeThemes(filteredThemes);
    } catch {
      result = false;
    }

    //
    const isCurrentRemoved =
      toNormalString(currentThemeName) === toNormalString(themeName);
    if (isCurrentRemoved) {
      //
      const newCurrentTheme = filteredThemes[0];
      const themeType = await this.getCurrentThemeType();
      result = await this.setTheme(newCurrentTheme, themeType, false, false);
    }

    //
    if (result && notifyChanges) {
      //
      this.notifyChanges.emit();
      await this.issueSuccessNotification();
    }

    //
    if (result && isCurrentRemoved) {
      this.settingsService.restart();
    }

    //
    return result;
  }

  async saveTheme(theme: XThemePack, notifyChanges: boolean = true) {
    //
    if (isNullOrUndefined(theme)) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.InvalidArgs);
      }

      //
      return false;
    }

    //
    const isExists = await this.isThemeExists(theme.name);
    if (isExists) {
      //
      if (notifyChanges) {
        await this.issueFailedNotification(XExceptionIDs.Duplicate);
      }

      //
      return false;
    }

    //
    const themeType = await this.getCurrentThemeType();

    //
    const themes = await this.getThemes();
    if (!hasChild(themes)) {
      //
      const newThemes = [theme];

      //
      this.writeThemes(newThemes);

      //
      const result = await this.setTheme(theme, themeType, true);
      return result;
    } else {
      //
      const themeIndex = themes.findIndex((th) => th.name === theme.name);
      if (themeIndex === -1) {
        themes.push(theme);
      } else {
        themes[themeIndex] = theme;
      }

      //
      this.writeThemes(themes);

      //
      if (notifyChanges) {
        //
        this.notifyChanges.emit();
        await this.issueSuccessNotification();
      }

      //
      return true;
    }
  }

  async updateTheme(
    themeName: string,
    updatedTheme: XThemePack,
    notifiChanges: boolean = true,
    restartIfCurrent: boolean = true
  ) {
    //
    if (isNullOrEmptyString(themeName) || isNullOrUndefined(updatedTheme)) {
      //
      if (notifiChanges) {
        await this.issueFailedNotification(XExceptionIDs.InvalidArgs);
      }

      //
      return false;
    }

    //
    const isExists = await this.isThemeExists(themeName);
    if (!isExists) {
      //
      if (notifiChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return false;
    }

    //
    const currentThemeName = await this.getCurrentThemeName();
    const isCurrent =
      toNormalString(themeName) === toNormalString(currentThemeName);

    //
    let result = false;
    const themes = this.readThemes();
    const themeIndex = themes.findIndex(
      (th) => toNormalString(th.name) === toNormalString(themeName)
    );
    if (themeIndex < 0) {
      //
      if (notifiChanges) {
        await this.issueFailedNotification(XExceptionIDs.NotFound);
      }

      //
      return result;
    }

    //
    themes[themeIndex] = updatedTheme;
    try {
      result = true;
      this.writeThemes(themes);
    } catch {
      //
      if (notifiChanges) {
        await this.issueFailedNotification(XExceptionIDs.ActionFailed);
      }

      //
      return false;
    }

    //
    if (result && isCurrent) {
      //
      const mode = await this.getCurrentThemeType();
      const isSetted = await this.setTheme(updatedTheme, mode, false, false);
      if (!isSetted) {
        //
        if (notifiChanges) {
          await this.issueFailedNotification(XExceptionIDs.ActionFailed);
        }

        //
        return false;
      }
    }

    //
    if (result && notifiChanges) {
      await this.issueSuccessNotification();
    }

    //
    if (isCurrent && restartIfCurrent) {
      this.settingsService.restart();
    }

    //
    return result;
  }

  async getCurrentTheme() {
    //
    const currentThemeName = await this.getCurrentThemeName();
    const isExists = await this.isThemeExists(currentThemeName);
    if (!isExists) {
      return null;
    }

    //
    const result = await this.getTheme(currentThemeName);
    return result;
  }

  async getThemeNames() {
    //
    const themes = await this.getThemes();
    if (!hasChild(themes)) {
      return [];
    }

    //
    return themes.map((th) => th.name);
  }

  async getThemes() {
    //
    const result = this.readThemes();

    //
    return result;
  }

  async getTheme(themeName: string) {
    //
    const isExists = await this.isThemeExists(themeName);
    if (!isExists) {
      return null;
    }

    //
    const themes = await this.getThemes();
    return themes.find(
      (th) => toNormalString(th.name) === toNormalString(themeName)
    );
  }

  async isThemeExists(themeName: string) {
    //
    if (isNullOrEmptyString(themeName)) {
      return false;
    }

    //
    const themes = await this.getThemeNames();
    const result =
      themes.findIndex(
        (th) => toNormalString(th) === toNormalString(themeName)
      ) > -1;

    //
    return result;
  }

  async setLightMode() {
    //
    const currentTheme = await this.getCurrentTheme();
    if (isNullOrUndefined(currentTheme)) {
      return false;
    }

    //
    return await this.setTheme(currentTheme, XThemeType.Light, false, true);
  }

  async setDarkMode() {
    //
    const currentTheme = await this.getCurrentTheme();
    if (isNullOrUndefined(currentTheme)) {
      return false;
    }

    //
    return await this.setTheme(currentTheme, XThemeType.Dark, false, true);
  }

  async toggleMode() {
    //
    const currentTheme = await this.getCurrentTheme();
    const currentThemeMode = await this.getCurrentThemeType();
    if (isNullOrUndefined(currentTheme)) {
      return false;
    }

    //
    const mode: XThemeType =
      currentThemeMode === XThemeType.Light
        ? XThemeType.Dark
        : XThemeType.Light;

    //
    return await this.setTheme(currentTheme, mode, false, true);
  }
  //#endregion

  //
  //#region Private ...
  private async initializeService() {
    //
    const currentTheme = await this.getCurrentTheme();
    const isDefaultThemeExists = !isNullOrUndefined(currentTheme);
    if (!isDefaultThemeExists) {
      //
      const defaultTheme: XThemePack = {
        name: 'default',
        light: {
          type: XThemeType.Light,
          colors: {
            //
            //#region Primary ...
            //
            XColorPrimary: this.getCssVar(XThemeCssVars.XColorPrimary),
            XColorPrimaryContrast: this.getCssVar(
              XThemeCssVars.XColorPrimaryContrast
            ),

            //
            XColorPrimaryShade: this.getCssVar(
              XThemeCssVars.XColorPrimaryShade
            ),
            XColorPrimaryShadeContrast: this.getCssVar(
              XThemeCssVars.XColorPrimaryShadeContrast
            ),

            //
            XColorPrimaryTint: this.getCssVar(XThemeCssVars.XColorPrimaryTint),
            XColorPrimaryTintContrast: this.getCssVar(
              XThemeCssVars.XColorPrimaryTintContrast
            ),
            //#endregion

            //
            //#region Secondary ...
            //
            XColorSecondary: this.getCssVar(XThemeCssVars.XColorSecondary),
            XColorSecondaryContrast: this.getCssVar(
              XThemeCssVars.XColorSecondaryContrast
            ),

            //
            XColorSecondaryShade: this.getCssVar(
              XThemeCssVars.XColorSecondaryShade
            ),
            XColorSecondaryShadeContrast: this.getCssVar(
              XThemeCssVars.XColorSecondaryShadeContrast
            ),

            //
            XColorSecondaryTint: this.getCssVar(
              XThemeCssVars.XColorSecondaryTint
            ),
            XColorSecondaryTintContrast: this.getCssVar(
              XThemeCssVars.XColorSecondaryTintContrast
            ),
            //#endregion

            //
            //#region Tertiary ...
            //
            XColorTertiary: this.getCssVar(XThemeCssVars.XColorTertiary),
            XColorTertiaryContrast: this.getCssVar(
              XThemeCssVars.XColorTertiaryContrast
            ),

            //
            XColorTertiaryShade: this.getCssVar(
              XThemeCssVars.XColorTertiaryShade
            ),
            XColorTertiaryShadeContrast: this.getCssVar(
              XThemeCssVars.XColorTertiaryShadeContrast
            ),

            //
            XColorTertiaryTint: this.getCssVar(
              XThemeCssVars.XColorTertiaryTint
            ),
            XColorTertiaryTintContrast: this.getCssVar(
              XThemeCssVars.XColorTertiaryTintContrast
            ),
            //#endregion

            //
            //#region Success ...
            //
            XColorSuccess: this.getCssVar(XThemeCssVars.XColorSuccess),
            XColorSuccessContrast: this.getCssVar(
              XThemeCssVars.XColorSuccessContrast
            ),

            //
            XColorSuccessShade: this.getCssVar(
              XThemeCssVars.XColorSuccessShade
            ),
            XColorSuccessShadeContrast: this.getCssVar(
              XThemeCssVars.XColorSuccessShadeContrast
            ),

            //
            XColorSuccessTint: this.getCssVar(XThemeCssVars.XColorSuccessTint),
            XColorSuccessTintContrast: this.getCssVar(
              XThemeCssVars.XColorSuccessTintContrast
            ),
            //#endregion

            //
            //#region Warning ...
            //
            XColorWarning: this.getCssVar(XThemeCssVars.XColorWarning),
            XColorWarningContrast: this.getCssVar(
              XThemeCssVars.XColorWarningContrast
            ),

            //
            XColorWarningShade: this.getCssVar(
              XThemeCssVars.XColorWarningShade
            ),
            XColorWarningShadeContrast: this.getCssVar(
              XThemeCssVars.XColorWarningShadeContrast
            ),

            //
            XColorWarningTint: this.getCssVar(XThemeCssVars.XColorWarningTint),
            XColorWarningTintContrast: this.getCssVar(
              XThemeCssVars.XColorWarningTintContrast
            ),
            //#endregion

            //
            //#region Danger ...
            //
            XColorDanger: this.getCssVar(XThemeCssVars.XColorDanger),
            XColorDangerContrast: this.getCssVar(
              XThemeCssVars.XColorDangerContrast
            ),

            //
            XColorDangerShade: this.getCssVar(XThemeCssVars.XColorDangerShade),
            XColorDangerShadeContrast: this.getCssVar(
              XThemeCssVars.XColorDangerShadeContrast
            ),

            //
            XColorDangerTint: this.getCssVar(XThemeCssVars.XColorDangerTint),
            XColorDangerTintContrast: this.getCssVar(
              XThemeCssVars.XColorDangerTintContrast
            ),
            //#endregion

            //
            //#region Dark ...
            //
            XColorDark: this.getCssVar(XThemeCssVars.XColorDark),
            XColorDarkContrast: this.getCssVar(
              XThemeCssVars.XColorDarkContrast
            ),

            //
            XColorDarkShade: this.getCssVar(XThemeCssVars.XColorDarkShade),
            XColorDarkShadeContrast: this.getCssVar(
              XThemeCssVars.XColorDarkShadeContrast
            ),

            //
            XColorDarkTint: this.getCssVar(XThemeCssVars.XColorDarkTint),
            XColorDarkTintContrast: this.getCssVar(
              XThemeCssVars.XColorDarkTintContrast
            ),
            //#endregion

            //
            //#region Medium ...
            //
            XColorMedium: this.getCssVar(XThemeCssVars.XColorMedium),
            XColorMediumContrast: this.getCssVar(
              XThemeCssVars.XColorMediumContrast
            ),

            //
            XColorMediumShade: this.getCssVar(XThemeCssVars.XColorMediumShade),
            XColorMediumShadeContrast: this.getCssVar(
              XThemeCssVars.XColorMediumShadeContrast
            ),

            //
            XColorMediumTint: this.getCssVar(XThemeCssVars.XColorMediumTint),
            XColorMediumTintContrast: this.getCssVar(
              XThemeCssVars.XColorMediumTintContrast
            ),
            //#endregion

            //
            //#region Light ...
            //
            XColorLight: this.getCssVar(XThemeCssVars.XColorLight),
            XColorLightContrast: this.getCssVar(
              XThemeCssVars.XColorLightContrast
            ),

            //
            XColorLightShade: this.getCssVar(XThemeCssVars.XColorLightShade),
            XColorLightShadeContrast: this.getCssVar(
              XThemeCssVars.XColorLightShadeContrast
            ),

            //
            XColorLightTint: this.getCssVar(XThemeCssVars.XColorLightTint),
            XColorLightTintContrast: this.getCssVar(
              XThemeCssVars.XColorLightTintContrast
            ),
            //#endregion
          },
        },
        dark: {
          type: XThemeType.Dark,
          colors: {
            //
            //#region Primary ...
            //
            XColorPrimary: this.getCssVar(XThemeCssVars.XColorDark),
            XColorPrimaryContrast: this.getCssVar(
              XThemeCssVars.XColorDarkContrast
            ),

            //
            XColorPrimaryShade: this.getCssVar(XThemeCssVars.XColorDarkShade),
            XColorPrimaryShadeContrast: this.getCssVar(
              XThemeCssVars.XColorDarkShadeContrast
            ),

            //
            XColorPrimaryTint: this.getCssVar(XThemeCssVars.XColorDarkTint),
            XColorPrimaryTintContrast: this.getCssVar(
              XThemeCssVars.XColorDarkTintContrast
            ),
            //#endregion

            //
            //#region Secondary ...
            //
            XColorSecondary: this.getCssVar(XThemeCssVars.XColorDarkTint),
            XColorSecondaryContrast: this.getCssVar(
              XThemeCssVars.XColorDarkTintContrast
            ),

            //
            XColorSecondaryShade: this.getCssVar(XThemeCssVars.XColorDarkTint),
            XColorSecondaryShadeContrast: this.getCssVar(
              XThemeCssVars.XColorDarkTintContrast
            ),

            //
            XColorSecondaryTint: this.getCssVar(XThemeCssVars.XColorDarkTint),
            XColorSecondaryTintContrast: this.getCssVar(
              XThemeCssVars.XColorDarkTintContrast
            ),
            //#endregion

            //
            //#region Tertiary ...
            //
            XColorTertiary: this.getCssVar(XThemeCssVars.XColorTertiary),
            XColorTertiaryContrast: this.getCssVar(
              XThemeCssVars.XColorTertiaryContrast
            ),

            //
            XColorTertiaryShade: this.getCssVar(
              XThemeCssVars.XColorTertiaryShade
            ),
            XColorTertiaryShadeContrast: this.getCssVar(
              XThemeCssVars.XColorTertiaryShadeContrast
            ),

            //
            XColorTertiaryTint: this.getCssVar(
              XThemeCssVars.XColorTertiaryTint
            ),
            XColorTertiaryTintContrast: this.getCssVar(
              XThemeCssVars.XColorTertiaryTintContrast
            ),
            //#endregion

            //
            //#region Success ...
            //
            XColorSuccess: this.getCssVar(XThemeCssVars.XColorSuccess),
            XColorSuccessContrast: this.getCssVar(
              XThemeCssVars.XColorSuccessContrast
            ),

            //
            XColorSuccessShade: this.getCssVar(
              XThemeCssVars.XColorSuccessShade
            ),
            XColorSuccessShadeContrast: this.getCssVar(
              XThemeCssVars.XColorSuccessShadeContrast
            ),

            //
            XColorSuccessTint: this.getCssVar(XThemeCssVars.XColorSuccessTint),
            XColorSuccessTintContrast: this.getCssVar(
              XThemeCssVars.XColorSuccessTintContrast
            ),
            //#endregion

            //
            //#region Warning ...
            //
            XColorWarning: this.getCssVar(XThemeCssVars.XColorWarning),
            XColorWarningContrast: this.getCssVar(
              XThemeCssVars.XColorWarningContrast
            ),

            //
            XColorWarningShade: this.getCssVar(
              XThemeCssVars.XColorWarningShade
            ),
            XColorWarningShadeContrast: this.getCssVar(
              XThemeCssVars.XColorWarningShadeContrast
            ),

            //
            XColorWarningTint: this.getCssVar(XThemeCssVars.XColorWarningTint),
            XColorWarningTintContrast: this.getCssVar(
              XThemeCssVars.XColorWarningTintContrast
            ),
            //#endregion

            //
            //#region Danger ...
            //
            XColorDanger: this.getCssVar(XThemeCssVars.XColorDanger),
            XColorDangerContrast: this.getCssVar(
              XThemeCssVars.XColorDangerContrast
            ),

            //
            XColorDangerShade: this.getCssVar(XThemeCssVars.XColorDangerShade),
            XColorDangerShadeContrast: this.getCssVar(
              XThemeCssVars.XColorDangerShadeContrast
            ),

            //
            XColorDangerTint: this.getCssVar(XThemeCssVars.XColorDangerTint),
            XColorDangerTintContrast: this.getCssVar(
              XThemeCssVars.XColorDangerTintContrast
            ),
            //#endregion

            //
            //#region Dark ...
            //
            XColorDark: this.getCssVar(XThemeCssVars.XColorDark),
            XColorDarkContrast: this.getCssVar(
              XThemeCssVars.XColorDarkContrast
            ),

            //
            XColorDarkShade: this.getCssVar(XThemeCssVars.XColorDarkShade),
            XColorDarkShadeContrast: this.getCssVar(
              XThemeCssVars.XColorDarkShadeContrast
            ),

            //
            XColorDarkTint: this.getCssVar(XThemeCssVars.XColorDarkTint),
            XColorDarkTintContrast: this.getCssVar(
              XThemeCssVars.XColorDarkTintContrast
            ),
            //#endregion

            //
            //#region Medium ...
            //
            XColorMedium: this.getCssVar(XThemeCssVars.XColorMedium),
            XColorMediumContrast: this.getCssVar(
              XThemeCssVars.XColorMediumContrast
            ),

            //
            XColorMediumShade: this.getCssVar(XThemeCssVars.XColorMediumShade),
            XColorMediumShadeContrast: this.getCssVar(
              XThemeCssVars.XColorMediumShadeContrast
            ),

            //
            XColorMediumTint: this.getCssVar(XThemeCssVars.XColorMediumTint),
            XColorMediumTintContrast: this.getCssVar(
              XThemeCssVars.XColorMediumTintContrast
            ),
            //#endregion

            //
            //#region Light ...
            //
            XColorLight: this.getCssVar(XThemeCssVars.XColorLight),
            XColorLightContrast: this.getCssVar(
              XThemeCssVars.XColorLightContrast
            ),

            //
            XColorLightShade: this.getCssVar(XThemeCssVars.XColorLightShade),
            XColorLightShadeContrast: this.getCssVar(
              XThemeCssVars.XColorLightShadeContrast
            ),

            //
            XColorLightTint: this.getCssVar(XThemeCssVars.XColorLightTint),
            XColorLightTintContrast: this.getCssVar(
              XThemeCssVars.XColorLightTintContrast
            ),
            //#endregion
          },
        },
      };

      //
      await this.saveTheme(defaultTheme, false);
    } else {
      //
      const type = await this.getCurrentThemeType();
      await this.setTheme(currentTheme, type, false, false);
    }

    //
    this.notifyChanges.emit();
  }

  private async issueFailedNotification(exception?: XExceptionIdentifier) {
    //
    if (isNullOrUndefined(exception)) {
      exception = XExceptionIDs.ActionFailed;
    }

    //
    await this.notificationService.presentErrorNotification({
      exception,
      dissmissable: false,
    });
  }

  private async issueSuccessNotification() {
    //
    await this.notificationService.presentDefaultSuccessNotification();
  }

  //
  //#region Read and Write Storage ...
  private writeThemes(themes: XThemePack[]) {
    //
    const mKey = XThemeManagerStorageKeys.Themes;

    //
    this.storageService.write(mKey, themes, true);
  }

  private readThemes() {
    //
    const mKey = XThemeManagerStorageKeys.Themes;

    //
    const isExists = this.storageService.isExists(mKey);
    if (!isExists) {
      return [];
    }

    //
    return this.storageService.readOfType<XThemePack[]>(mKey, false);
  }
  //#endregion

  //
  //#region CssVar Manipulators ...
  /**
   * prepare a css var name
   *
   * @param key css variable name
   */
  private prepareCssKey(key: XThemeCssVarsIdentifier) {
    return `${this.PREFIX}${key}`;
  }

  /**
   * remove css key prefix ...
   *
   * @param key a prepared css var ...
   */
  private removeCssKeyPrefix(key: XThemeCssVarsIdentifier) {
    return key.replace(this.PREFIX, '');
  }

  /**
   * read specific css var value of an element
   * @param name css variable name
   * @param element destination element
   */
  private getCssVar(name: XThemeCssVarsIdentifier) {
    //
    return getComputedStyle(document.body).getPropertyValue(
      this.prepareCssKey(name)
    );
  }

  /**
   * set css var into element ...
   *
   * @param name css var name
   * @param value css var value
   * @param elements destination element of array of elements
   */
  private setCssVar(
    name: XThemeCssVarsIdentifier,
    value: string,
    element?: ElementRef
  ) {
    //
    if (isNullOrEmptyString(name)) {
      return;
    }

    //
    // Setting Variable on Element if exists ...
    if (element && element.nativeElement) {
      (element.nativeElement as HTMLElement).style.setProperty(
        this.prepareCssKey(name),
        value
      );
    }

    //
    // Setting Variable on root ...
    document.documentElement.style.setProperty(this.prepareCssKey(name), value);
  }

  private async setThemeColors(theme: XThemeColors, element?: ElementRef) {
    //
    if (isNullOrUndefined(theme)) {
      return false;
    }

    //
    const colorKeys = keys(theme);
    colorKeys.forEach((cKey) => {
      //
      const cssKey = this.ColorCssVars[cKey] as string;
      const ionCssKey = cssKey.replace('x-', 'ion-');
      const cssValue = toNormalString(theme[cKey]);

      //
      this.setCssVar(cssKey, cssValue, element);
      this.setCssVar(ionCssKey, cssValue, element);
    });

    //
    return true;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\config\x-framework-services.config.ts

import { XFrameworkCoreConfig } from 'x-framework-core';

// tslint:disable-next-line:no-empty-interface
export interface XFrameworkServicesConfig extends XFrameworkCoreConfig {
    geoLocationConfig?: PositionOptions
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\directives\x-currency-formatter.directive.ts

import {
  Input,
  Inject,
  Renderer2,
  Directive,
  ElementRef,
  forwardRef,
  HostListener
} from '@angular/core';
import {
  XLocale,
  removeAll,
  applyLocale,
  isNullOrEmptyString
} from 'x-framework-core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { X_FRAMEWORK_SERVICES_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from '../config/x-framework-services.config';

export const X_CURRENCY_FORMATTER: any = {
  useExisting: forwardRef(() => XCurrencyFormatterDirective),
  provide: NG_VALUE_ACCESSOR,
  multi: true
};

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[xCurrencyFormatter]',
  providers: [X_CURRENCY_FORMATTER]
})
export class XCurrencyFormatterDirective implements ControlValueAccessor {
  //
  //#region Props ...
  @Input()
  locale: XLocale;

  @Input()
  applyLocaleToCurrency: boolean;

  @Input()
  currencySeperator: string;

  @Input()
  enabledCurrencyFormatter: boolean;

  onChange = (_: any) => {};

  onTouched = () => {};
  //#endregion

  //
  //#region Constructor ...
  constructor(
    private renderer: Renderer2,
    private elementRef: ElementRef,
    @Inject(X_FRAMEWORK_SERVICES_CONFIG)
    private config: XFrameworkServicesConfig
  ) {}
  //#endregion

  //
  //#region Host Bindings ...
  @HostListener('change', ['$event.target.value'])
  change(value: string) {
    //
    const res = this.getUnformattedValue(value);
    this.onChange(res);
  }

  @HostListener('input', ['$event.target.value'])
  input(value: string) {
    //
    const res = this.getUnformattedValue(value);
    this.onChange(res);
  }

  @HostListener('blur')
  onBlur() {
    this.onTouched();
  }

  @HostListener('keydown', ['$event'])
  onKeyDown(event: any) {
    if (this.enabledCurrencyFormatter) {
      this.setOnlyNumberType(event);
    }
  }
  //#endregion

  //
  //#region Implemenntation of Interface ...
  writeValue(value: string): void {
    this.setValue(value);
  }

  registerOnChange(fn: (_: string | any | null) => void): void {
    this.onChange = value => {
      //
      fn(this.enabledCurrencyFormatter ? +value : value);
      this.setValue(value);
    };
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.renderer.setProperty(
      this.elementRef.nativeElement,
      'disabled',
      isDisabled
    );
  }
  //#endregion

  //
  //#region Private ...
  private isElementValid() {
    return this.elementRef && this.elementRef.nativeElement;
  }

  private getFormattedValue(value: string) {
    //
    if (!this.enabledCurrencyFormatter || !value) {
      return value || '';
    }

    //
    if (this.applyLocaleToCurrency) {
      value = applyLocale(value, this.locale || this.config.defaultLanguage);
    }

    //
    const result = value
      .toString()
      .replace(/\B(?=(\d{3})+(?!\d))/g, this.currencySeperator);

    //
    return result;
  }

  private getUnformattedValue(value: string) {
    //
    const result = removeAll(value, this.currencySeperator, true);
    return result;
  }

  private getNormalizedValue(value: string) {
    //
    if (!this.isElementValid()) {
      return value;
    }

    //
    value = isNullOrEmptyString(value) ? '' : this.getUnformattedValue(value);

    //
    if (this.enabledCurrencyFormatter) {
      value = this.getFormattedValue(value);
    } else {
      value = this.getUnformattedValue(value);
    }

    //
    return value;
  }

  private setValue(value: string) {
    //
    const normalizedValue = this.getNormalizedValue(value);
    this.renderer.setProperty(
      this.elementRef.nativeElement,
      'value',
      normalizedValue
    );
  }

  private setOnlyNumberType(e: any) {
    //
    const keyCode = e.code as string;
    const isValidKey =
      keyCode.includes('Digit') || keyCode.includes('Backspace');

    //
    if (!isValidKey) {
      e.preventDefault();
    }
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\directives\x-date-presenter.directive.ts

import {
  XLocale,
  formatDate,
  getDateOfBirthDateFormatString,
} from 'x-framework-core';
import { XLocalizationService } from '../x-localization.service';
import {
  Directive,
  ElementRef,
  OnInit,
  Input,
  OnChanges,
  SimpleChanges,
} from '@angular/core';

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[xDatePresenter]',
})
export class XDatePresenterDirective implements OnInit, OnChanges {
  //
  @Input()
  date?: Date | string;

  @Input()
  locale?: XLocale;

  @Input()
  format?: string;

  @Input()
  isUtc?: boolean;

  @Input()
  toUtc?: boolean;

  constructor(
    private element: ElementRef,
    private localizationService: XLocalizationService
  ) {}

  ngOnInit() {
    this.presentDate();
  }

  ngOnChanges(changes: SimpleChanges): void {
    this.presentDate();
  }

  private presentDate() {
    //
    if (!this.locale) {
      this.locale = this.localizationService.locale;
    }

    //
    if (!this.date) {
      this.date = new Date();
    }

    //
    if (!this.format) {
      this.format = getDateOfBirthDateFormatString(this.locale);
    }

    //
    if (!this.toUtc) {
      this.toUtc = false;
    }

    //
    let dateStr: string;
    try {
      dateStr = formatDate(
        this.date,
        this.locale,
        this.format,
        this.isUtc,
        this.toUtc
      );
    } catch {
      dateStr = '---';
    }

    //
    this.element.nativeElement.innerHTML = dateStr;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\directives\x-from-input-formatter.directive.ts

import {
  Input,
  Inject,
  Directive,
  Renderer2,
  ElementRef,
  forwardRef,
  HostListener,
} from '@angular/core';
import {
  XLocale,
  hasChild,
  removeAll,
  getDigits,
  toXString,
  applyLocale,
  getNonDigits,
  removeSpaces,
  isNullOrUndefined,
  extractUniqueChars,
  isNullOrEmptyString,
} from 'x-framework-core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { X_FRAMEWORK_SERVICES_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from '../config/x-framework-services.config';

export const X_FORM_INPUT_FORMATTER: any = {
  useExisting: forwardRef(() => XFromInputFormatterDirective),
  provide: NG_VALUE_ACCESSOR,
  multi: true,
};

export enum XFormInputFormatter {
  ApplyLocale = 'apply_locale',
  FormatCurrency = 'format_currency',
  StringFormatter = 'string_formatter',
  StringInputFormatter = 'string_input_formatter',
  FormatCurrencyAndApplyLocale = 'format_currency_apply_locale',
  StringFormatterAndApplyLocale = 'string_formatter_apply_locale',
  StringInputFormatterAndApplyLocale = 'string_input_formatter_apply_locale',
}

export type XFormInputFormatterIdentifier = XFormInputFormatter | string;

export enum XStringInputFormatType {
  None = 'none',
  LowerCase = 'lowercase',
  UpperCase = 'uppercase',
  NonSpace = 'non_space',
  NonDigits = 'none_digits',
  OnlyDigits = 'only_digits',
  UniqueChars = 'unique_chars',
}

export type XStringInputFormatTypeIdentifier = XStringInputFormatType | string;

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[xFromInputFormatter]',
  providers: [X_FORM_INPUT_FORMATTER],
})
export class XFromInputFormatterDirective implements ControlValueAccessor {
  //
  //#region Props ...
  @Input()
  locale: XLocale;

  @Input()
  inputFormatterType: XFormInputFormatterIdentifier;

  @Input()
  stringInputFormatterType: XStringInputFormatTypeIdentifier;

  @Input()
  seperator: string;

  @Input()
  currencyLable: string;

  @Input()
  stringFormatter: number[];

  @Input()
  enabledInputFormatter: boolean;

  //
  onChange = (_: any) => {};

  onTouched = () => {};

  onFocused = () => {};
  //#endregion

  //
  //#region Constructor ...
  constructor(
    private renderer: Renderer2,
    private elementRef: ElementRef,
    @Inject(X_FRAMEWORK_SERVICES_CONFIG)
    private config: XFrameworkServicesConfig
  ) {}
  //#endregion

  //
  //#region Type Detectors ...
  isCurrencyType() {
    return (
      this.inputFormatterType === XFormInputFormatter.FormatCurrency ||
      this.inputFormatterType ===
        XFormInputFormatter.FormatCurrencyAndApplyLocale
    );
  }

  isStringFormatterType() {
    return (
      this.inputFormatterType === XFormInputFormatter.StringFormatter ||
      this.inputFormatterType ===
        XFormInputFormatter.StringFormatterAndApplyLocale ||
      this.inputFormatterType === XFormInputFormatter.StringInputFormatter ||
      this.inputFormatterType ===
        XFormInputFormatter.StringInputFormatterAndApplyLocale
    );
  }

  isLocaleType() {
    return (
      this.inputFormatterType === XFormInputFormatter.ApplyLocale ||
      this.inputFormatterType ===
        XFormInputFormatter.FormatCurrencyAndApplyLocale ||
      this.inputFormatterType ===
        XFormInputFormatter.StringFormatterAndApplyLocale ||
      this.inputFormatterType ===
        XFormInputFormatter.StringInputFormatterAndApplyLocale
    );
  }
  //#endregion

  //
  //#region Host Bindings ...
  @HostListener('change', ['$event.target.value'])
  change(value: string) {
    //
    const res = this.getUnformattedValue(value);
    this.onChange(res);
  }

  @HostListener('input', ['$event.target.value'])
  input(value: string) {
    //
    const res = this.getUnformattedValue(value);
    this.onChange(res);
  }

  @HostListener('focus')
  onFocus() {
    this.onFocused();
    if (this.isCurrencyType()) {
      this.clearCurrencyLabel();
    }
  }

  @HostListener('blur')
  onBlur() {
    this.onTouched();
    if (this.isCurrencyType()) {
      this.applyCurrencyLabel();
    }
  }

  @HostListener('keydown', ['$event'])
  onKeyDown(event: any) {
    // if (this.isCurrencyType()) {
    //   this.setOnlyNumberType(event);
    // }
  }
  //#endregion

  //
  //#region Implemenntation of Interface ...
  writeValue(value: string): void {
    this.setValue(value);
  }

  registerOnChange(fn: (_: string | any | null) => void): void {
    this.onChange = (value) => {
      //
      fn(this.isCurrencyType() ? +value : value);

      //
      this.setValue(value);
    };
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    //
    if (isDisabled && this.isCurrencyType()) {
      this.applyCurrencyLabel();
    }

    //
    this.renderer.setProperty(
      this.elementRef.nativeElement,
      'disabled',
      isDisabled
    );
  }
  //#endregion

  //
  //#region Private ...
  private isElementValid() {
    return this.elementRef && this.elementRef.nativeElement;
  }

  private getFormattedValue(value: string) {
    //
    // Normallize Value ...
    if (isNullOrEmptyString(value) || !this.enabledInputFormatter) {
      return '';
    }

    //
    // Prepared Formatted Value ...
    let result = this.isCurrencyType()
      ? this.getCurrencyFormattedValue(value)
      : this.isStringFormatterType()
      ? this.getStringFormattedValue(value)
      : value || '';

    //
    // Apply Locale if Localeable Input Type ...
    if (this.isLocaleType()) {
      result = this.getLocaleFormattedValue(result);
    }

    //
    return result;
  }

  private getUnformattedValue(value: string) {
    //
    let result = value.toString() || '';

    //
    const seperator = isNullOrEmptyString(this.seperator) ? '' : this.seperator;

    //
    // Remove All occurence of seperator ...
    if (this.enabledInputFormatter) {
      result = removeAll(value, seperator, true);
    }

    //
    // Currency Lable ...
    if (this.isCurrencyType() && !isNullOrEmptyString(this.currencyLable)) {
      result = result.replace(this.currencyLable, '').trim();
    }

    //
    // Change Locale to Default ...
    if (this.isLocaleType()) {
      result = !isNullOrEmptyString(result) ? applyLocale(result, 'en-US') : '';
    }

    //
    return result;
  }

  private getNormalizedValue(value: string) {
    //
    if (!this.isElementValid()) {
      return value;
    }

    //
    // Normallize Value and prevent from null ...
    let result = value ? value.toString() || '' : '';
    result = isNullOrEmptyString(value) ? '' : this.getUnformattedValue(value);
    if (!this.enabledInputFormatter) {
      return result;
    }

    //
    // Handle Apply Locale ...
    if (this.inputFormatterType === XFormInputFormatter.ApplyLocale) {
      //
      result = applyLocale(value, this.locale || this.config.defaultLanguage);
      return result;
    }

    //
    // Handle Formatter ...
    if (this.isCurrencyType() || this.isStringFormatterType()) {
      return this.getFormattedValue(result);
    }

    //
    return value;
  }

  private setValue(value: string, normalizeValue = true) {
    //
    const normalizedValue = normalizeValue
      ? this.getNormalizedValue(value)
      : value || '';
    this.renderer.setProperty(
      this.elementRef.nativeElement,
      'value',
      normalizedValue
    );
  }

  private getValue() {
    return this.elementRef.nativeElement.value
      ? (this.elementRef.nativeElement.value as string)
      : '';
  }

  private setOnlyNumberType(e: any) {
    //
    const keyCode = e.code as string;
    const numPadKeys = [...new Array(10).keys()].map((c, i) => `Numpad${i}`);
    const isValidKey =
      keyCode.includes('Tab') ||
      keyCode.includes('Digit') ||
      keyCode.includes('Backspace') ||
      numPadKeys.includes(keyCode);

    //
    if (!isValidKey) {
      e.preventDefault();
    }
  }

  //
  //#region Formatter Actions ...
  private getLocaleFormattedValue(value: any) {
    //
    const mValue = value ? (value.toString() as string) : '';
    return applyLocale(mValue, this.locale || this.config.defaultLanguage);
  }

  private getStringFormattedValue(value: any) {
    //
    const mValue = !isNullOrUndefined(value) ? toXString(value) : '';

    //
    let mResultValue = '';

    //
    if (
      this.inputFormatterType === XFormInputFormatter.StringFormatter ||
      this.inputFormatterType ===
        XFormInputFormatter.StringFormatterAndApplyLocale
    ) {
      //
      if (!hasChild(this.stringFormatter)) {
        return mValue;
      }

      //
      let mSource = mValue;
      this.stringFormatter.forEach((l, i) => {
        if (mSource.length >= l) {
          //
          const selected = mSource.substring(0, l);

          //
          mResultValue +=
            i === this.stringFormatter.length
              ? selected
              : `${selected}${this.seperator}`;

          //
          mSource = mSource.substr(l, mValue.length);
        }
      });

      //
      if (mSource.length > 0) {
        mResultValue += `${mSource}`;
      }
    } else if (
      this.inputFormatterType === XFormInputFormatter.StringInputFormatter ||
      this.inputFormatterType ===
        XFormInputFormatter.StringInputFormatterAndApplyLocale
    ) {
      //
      switch (this.stringInputFormatterType) {
        //
        case XStringInputFormatType.LowerCase:
          mResultValue = mValue.toLowerCase();
          break;

        //
        case XStringInputFormatType.UpperCase:
          mResultValue = mValue.toUpperCase();
          break;

        //
        case XStringInputFormatType.NonDigits:
          //
          mResultValue = getNonDigits(mValue);
          break;

        //
        case XStringInputFormatType.UniqueChars:
          //
          mResultValue = extractUniqueChars(mValue);
          break;

        //
        case XStringInputFormatType.NonSpace:
          mResultValue = removeSpaces(mValue.trim());
          break;

        //
        case XStringInputFormatType.OnlyDigits:
          mResultValue = getDigits(mValue);
          break;

        //
        default:
          mResultValue = mValue;
          break;
      }
    }

    //
    return mResultValue;
  }

  private getCurrencyFormattedValue(value: any) {
    //
    const mValue = value ? getDigits(value.toString() as string) : '';
    const seperator = isNullOrEmptyString(this.seperator) ? '' : this.seperator;
    const formattedValue = mValue.replace(/\B(?=(\d{3})+(?!\d))/g, seperator);

    //
    const result = !isNullOrEmptyString(formattedValue) ? formattedValue : '';

    //
    return result;
  }

  private applyCurrencyLabel() {
    //
    if (!this.isCurrencyType()) {
      return;
    }

    //
    let value = this.getValue();

    //
    if (
      !isNullOrEmptyString(value) &&
      !isNullOrEmptyString(this.currencyLable)
    ) {
      value = `${value} ${this.currencyLable}`;
    }

    //
    this.setValue(value, false);
  }

  private clearCurrencyLabel() {
    //
    if (!this.isCurrencyType()) {
      return;
    }

    //
    let value = this.getValue();

    //
    if (
      !isNullOrEmptyString(value) &&
      !isNullOrEmptyString(this.currencyLable)
    ) {
      value = value.replace(this.currencyLable, '');
    }

    //
    this.setValue(value);
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\directives\x-locale-input-formatter.directive.ts

import {
  Input,
  Inject,
  Directive,
  Renderer2,
  ElementRef,
  forwardRef,
  HostListener,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { X_FRAMEWORK_SERVICES_CONFIG } from '../tokens/x-injectable-tokens';
import { XLocale, applyLocale, isNullOrEmptyString } from 'x-framework-core';
import { XFrameworkServicesConfig } from '../config/x-framework-services.config';

export const X_LOCALE_FORMATTER: any = {
  useExisting: forwardRef(() => XLocaleInputFormatterDirective),
  provide: NG_VALUE_ACCESSOR,
  multi: true,
};

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[xLocaleInputFormatter]',
  providers: [X_LOCALE_FORMATTER],
})
export class XLocaleInputFormatterDirective implements ControlValueAccessor {
  //
  //#region Props ...
  @Input()
  locale: XLocale;

  @Input()
  enabledLocaleFormatter: boolean;

  onChange = (_: any) => {};

  onTouched = () => {};
  //#endregion

  //
  //#region Constructor ...
  constructor(
    private renderer: Renderer2,
    private elementRef: ElementRef,
    @Inject(X_FRAMEWORK_SERVICES_CONFIG)
    private config: XFrameworkServicesConfig
  ) {}
  //#endregion

  //
  //#region Host Bindings ...
  @HostListener('change', ['$event.target.value'])
  change(value: string) {
    this.onChange(value);
  }

  @HostListener('input', ['$event.target.value'])
  input(value: string) {
    this.onChange(value);
  }

  @HostListener('blur')
  onBlur() {
    this.onTouched();
  }
  //#endregion

  //
  //#region Implemenntation of Interface ...
  writeValue(value: string): void {
    this.setValue(value);
  }

  registerOnChange(fn: (_: string | any | null) => void): void {
    this.onChange = (value) => {
      //
      fn(value);
      this.setValue(value);
    };
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.renderer.setProperty(
      this.elementRef.nativeElement,
      'disabled',
      isDisabled
    );
  }
  //#endregion

  //
  //#region Private ...
  private isElementValid() {
    return this.elementRef && this.elementRef.nativeElement;
  }

  private getFormattedValue(value: string) {
    //
    if (!this.enabledLocaleFormatter || !value) {
      return value || '';
    }

    //
    value = applyLocale(value, this.locale || this.config.defaultLanguage);

    //
    return value;
  }

  private getNormalizedValue(value: string) {
    //
    if (!this.isElementValid()) {
      return value;
    }

    //
    value = isNullOrEmptyString(value) ? '' : this.getFormattedValue(value);

    //
    return value;
  }

  private setValue(value: string) {
    //
    const normalizedValue = this.getNormalizedValue(value);
    this.renderer.setProperty(
      this.elementRef.nativeElement,
      'value',
      normalizedValue
    );
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\directives\x-swipable.directive.ts

import {
  Input,
  OnInit,
  Output,
  OnChanges,
  Directive,
  OnDestroy,
  ElementRef,
  EventEmitter,
  SimpleChanges,
  AfterViewInit,
} from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { keys, nameof } from 'x-framework-core';
import { GestureController } from '@ionic/angular';
import { Gesture, GestureDetail } from '@ionic/core';
import { takeUntil, debounceTime } from 'rxjs/operators';
import { XSettingsService } from '../x-settings.service';

export enum XSwipeDirection {
  UP = 'up',
  DOWN = 'down',
  LEFT = 'left',
  START = 'start',
  RIGHT = 'right',
  END = 'end',
}

export type XSwipeDirectionIdentifier = XSwipeDirection | string;

export enum XSwipeSupportedDirections {
  VERTICALL,
  HORIZONTAL_LEFT_RIGHT,
  HORIZONTAL_START_END,
  ALL,
}

export interface XSwipeEvent {
  deltaX: number;
  deltaY: number;
  direction: XSwipeDirectionIdentifier;
}

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[xSwipable]',
})
export class XSwipableDirective
  implements OnInit, AfterViewInit, OnDestroy, OnChanges {
  //
  //#region Props ...
  private gesture: Gesture;
  private unsubscriber = new Subject();
  private issueSwipeEvents$ = new Subject<GestureDetail>();
  private uiDirection = this.settingsService.uiDirection;
  //#endregion

  //
  //#region Inputs ...
  @Input()
  threshold = 60;

  @Input()
  swipeDisabled = false;

  @Input()
  debounceTime = 300;

  @Input()
  supportedDirections: XSwipeSupportedDirections =
    XSwipeSupportedDirections.HORIZONTAL_START_END;
  //#endregion

  //
  //#region Event Emitters ...
  @Output()
  swipe = new EventEmitter<XSwipeEvent>();

  @Output()
  swipeUp = new EventEmitter();

  @Output()
  swipeDown = new EventEmitter();

  @Output()
  swipeLeft = new EventEmitter();

  @Output()
  swipeStart = new EventEmitter();

  @Output()
  swipeRight = new EventEmitter();

  @Output()
  swipeEnd = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    private elementRef: ElementRef,
    private gestureCtrl: GestureController,
    private settingsService: XSettingsService
  ) {}
  //#endregion

  //
  //#region LifeCycles ...
  ngOnInit() {
    this.prepareGesture();
  }

  ngAfterViewInit() {
    this.registerIssueSwipeHandler();
  }

  ngOnDestroy() {
    this.destroyGesture();
    this.issueSwipeEvents$.complete();
    this.releaseUnsubscriber();
  }

  ngOnChanges(changes: SimpleChanges) {
    //
    const changeKesy = keys(changes);

    //
    const isThresholdChanged = changeKesy.includes(
      nameof<XSwipableDirective>('threshold')
    );
    if (isThresholdChanged) {
      this.prepareGesture();
    }

    //
    const isDebounceTimeChanged = changeKesy.includes(
      nameof<XSwipableDirective>('debounceTime')
    );
    if (isDebounceTimeChanged) {
      this.registerIssueSwipeHandler();
    }
  }
  //#endregion

  //
  //#region Private ...
  private prepareGesture() {
    //
    this.destroyGesture();

    //
    this.gesture = this.gestureCtrl.create({
      el: this.elementRef.nativeElement,
      threshold: this.threshold,
      gestureName: 'x-gesture',
      onMove: (detail) => {
        this.onMoveHandler(detail);
      },
    });

    //
    this.gesture.enable();
  }

  private destroyGesture() {
    //
    if (this.gesture) {
      //
      this.gesture.destroy();
      this.gesture = null;
    }
  }

  private releaseUnsubscriber() {
    //
    if (!this.unsubscriber.closed) {
      this.unsubscriber.next();
      this.unsubscriber.complete();
    }
  }

  private applyTakeUntilWrapper<T>(task: Observable<T>): Observable<T> {
    return task.pipe(takeUntil(this.unsubscriber));
  }

  private registerIssueSwipeHandler() {
    //
    this.applyTakeUntilWrapper(
      this.issueSwipeEvents$
        .asObservable()
        .pipe(debounceTime(this.debounceTime))
    ).subscribe((gDetail) => {
      //
      if (this.swipeDisabled) {
        return;
      }

      //
      const mSwipeEvent: Partial<XSwipeEvent> = {
        deltaX: gDetail.deltaX,
        deltaY: gDetail.deltaY,
      };

      //
      // Handle Vertical ...
      if (
        this.supportedDirections === XSwipeSupportedDirections.ALL ||
        this.supportedDirections === XSwipeSupportedDirections.VERTICALL
      ) {
        //
        let swipeDir: XSwipeDirection;

        //
        if (gDetail.deltaY >= 0) {
          //
          swipeDir = XSwipeDirection.DOWN;

          //
          this.swipeDown.emit();
        } else {
          //
          swipeDir = XSwipeDirection.UP;

          //
          this.swipeUp.emit();
        }

        //
        // Handle Global Event ...
        mSwipeEvent.direction = swipeDir;
        this.swipe.emit(mSwipeEvent as XSwipeEvent);
      }

      //
      // Handle Horizontal Left Right ...
      if (
        this.supportedDirections === XSwipeSupportedDirections.ALL ||
        this.supportedDirections ===
          XSwipeSupportedDirections.HORIZONTAL_LEFT_RIGHT
      ) {
        //
        let swipeDir: XSwipeDirection;

        //
        if (gDetail.deltaX >= 0) {
          //
          swipeDir = XSwipeDirection.RIGHT;

          //
          this.swipeRight.emit();
        } else {
          //
          swipeDir = XSwipeDirection.LEFT;

          //
          this.swipeLeft.emit();
        }

        //
        // Handle Global Event ...
        mSwipeEvent.direction = swipeDir;
        this.swipe.emit(mSwipeEvent as XSwipeEvent);
      }

      //
      // Handle Horizontal Start End ...
      if (
        this.supportedDirections === XSwipeSupportedDirections.ALL ||
        this.supportedDirections ===
          XSwipeSupportedDirections.HORIZONTAL_START_END
      ) {
        //
        let swipeDir: XSwipeDirection;

        //
        if (gDetail.deltaX >= 0) {
          //
          if (this.uiDirection === 'ltr') {
            //
            swipeDir = XSwipeDirection.END;

            //
            this.swipeEnd.emit();
          } else {
            //
            swipeDir = XSwipeDirection.START;

            //
            this.swipeStart.emit();
          }
        } else {
          //
          if (this.uiDirection === 'ltr') {
            //
            swipeDir = XSwipeDirection.START;

            //
            this.swipeStart.emit();
          } else {
            //
            swipeDir = XSwipeDirection.END;

            //
            this.swipeEnd.emit();
          }
        }

        //
        // Handle Global Event ...
        mSwipeEvent.direction = swipeDir;
        this.swipe.emit(mSwipeEvent as XSwipeEvent);
      }
    });
  }

  private onMoveHandler(ev: GestureDetail) {
    //
    if (this.swipeDisabled) {
      return;
    }

    //
    this.issueSwipeEvents$.next(ev);
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\modules\x-directives.module.ts

import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { CommonModule } from '@angular/common';
import { XTranslatePipe } from '../pipes/x-translate.pipe';
import { XSwipableDirective } from '../directives/x-swipable.directive';
import { XDatePresenterDirective } from '../directives/x-date-presenter.directive';
import { XCurrencyFormatterDirective } from '../directives/x-currency-formatter.directive';
import { XFromInputFormatterDirective } from '../directives/x-from-input-formatter.directive';
import { XLocaleInputFormatterDirective } from '../directives/x-locale-input-formatter.directive';

@NgModule({
  declarations: [
    //
    // Pipes ...
    XTranslatePipe,

    //
    // Directives
    XSwipableDirective,
    XDatePresenterDirective,
    XCurrencyFormatterDirective,
    XFromInputFormatterDirective,
    XLocaleInputFormatterDirective
  ],
  imports: [CommonModule, IonicModule],
  exports: [
    //
    CommonModule,
    IonicModule,

    //
    // Pipes ...
    XTranslatePipe,

    //
    // Directives
    XSwipableDirective,
    XDatePresenterDirective,
    XCurrencyFormatterDirective,
    XFromInputFormatterDirective,
    XLocaleInputFormatterDirective
  ]
})
export class XDirectivesModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\pipes\x-translate.pipe.ts

import {
  XLocale,
  toNumber,
  isString,
  XResourceIDs,
  XTranslationItem
} from 'x-framework-core';
import { Pipe, PipeTransform } from '@angular/core';
import { XLocalizationService } from '../x-localization.service';

@Pipe({
  name: 'xTranslate'
})
export class XTranslatePipe implements PipeTransform {
  //
  constructor(private localizationService: XLocalizationService) {}

  /**
   * translate a resource to specific language in a pipe
   *
   * @param key the resource identifier
   * @param locale the selected or default locale
   */
  transform(
    key: XResourceIDs | XTranslationItem | string,
    locale?: XLocale
  ): string {
    //
    if (!locale) {
      locale = this.localizationService.locale;
    }

    //
    const isStringKey = isString(key);
    if (!isStringKey) {
      key = toNumber(key) as XResourceIDs;
    }

    //
    const isExist = isStringKey
      ? this.localizationService.isExistsIdOfLocale(locale, key as string)
      : this.localizationService.isExistsIdOfLocale(
          locale,
          key as XResourceIDs
        );
    if (!isExist) {
      return '';
    }

    //
    return isStringKey
      ? this.localizationService.getValueOfLocale(locale, key as string)
      : this.localizationService.getValueOfLocale(locale, key as XResourceIDs);
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\tokens\x-injectable-tokens.ts

import { InjectionToken } from '@angular/core';
import { XFrameworkServicesConfig } from '../config/x-framework-services.config';

export const X_FRAMEWORK_SERVICES_CONFIG = new InjectionToken<
  XFrameworkServicesConfig
>('x_config');

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-manager-service-partial\x-manager-base.service.ts

import { Location } from '@angular/common';
import { XBaseService } from 'x-framework-core';
import { Platform } from '@angular/cdk/platform';
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { XDialogService } from '../x-dialog.service';
import { XHistoryService } from '../x-history.service';
import { XStorageService } from '../x-storage.service';
import { XSecurityService } from '../x-security.service';
import { Router, ActivatedRoute } from '@angular/router';
import { XSettingsService } from '../x-settings.service';
import { MenuController, NavController } from '@ionic/angular';
import { XGeolocationService } from '../x-geolocation.service';
import { XTaskManagerService } from '../x-task-manager.service';
import { XNotificationService } from '../x-notification.service';
import { XLocalizationService } from '../x-localization.service';
import { XThemeManagerService } from '../x-theme-manager.service';
import { XSecureStorageService } from '../x-secure-storage.service';
import { X_FRAMEWORK_SERVICES_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkServicesConfig } from '../config/x-framework-services.config';

@Injectable()
export abstract class XManagerBaseService extends XBaseService {
  constructor(
    @Inject(X_FRAMEWORK_SERVICES_CONFIG)
    public config: XFrameworkServicesConfig,
    public router: Router,
    public platform: Platform,
    public location: Location,
    public httpClient: HttpClient,
    public dialogService: XDialogService,
    public menuController: MenuController,
    public activatedRoute: ActivatedRoute,
    public historyService: XHistoryService,
    public storageService: XStorageService,
    public securityService: XSecurityService,
    public settingsService: XSettingsService,
    public navigationController: NavController,
    public geoLocationService: XGeolocationService,
    public taskManagerService: XTaskManagerService,
    public themeManagerService: XThemeManagerService,
    public notificationService: XNotificationService,
    public localizationService: XLocalizationService,
    public secureStorageService: XSecureStorageService
  ) {
    super(config);
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-manager-service-partial\x-manager-final.service.ts

import { XManagerRouteService } from './x-manager-route.service';

export abstract class XManagerFinalService extends XManagerRouteService {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-manager-service-partial\x-manager-route.service.ts

import {
  keys,
  XPage,
  toArray,
  fromJson,
  hasChild,
  XRouterTarget,
  XOneOrManyType,
  XParamIdentifier,
  isNullOrEmptyString,
} from 'x-framework-core';
import { XManagerUIStateService } from './x-manager-ui-state.service';
import { Params, UrlTree, ParamMap, NavigationExtras } from '@angular/router';

export abstract class XManagerRouteService extends XManagerUIStateService {
  //
  //#region Props ...
  //
  public routeDataChanged$ = this.router.routerState.root.data;
  public routeParmChanged$ = this.router.routerState.root.params;
  public routeQueryParmChanged$ = this.router.routerState.root.queryParams;
  //#endregion

  //
  //#region Route Related Actions ...
  /**
   * some times we had to force change location ...
   *
   * @param route the specified route
   */
  changeLocation(route?: string) {
    window.location.href = this.getFullUrl(route);
  }

  /**
   * retrieve base url (Origin) address
   */
  getBaseUrl() {
    return window.location.origin;
  }

  /**
   * retrieve current route
   */
  getRoute() {
    return this.router && this.router.url ? this.router.url : '/';
  }

  /**
   * merge several parts of a url ...
   *
   * @param route an instance of url tree array ...
   */
  mergeRoutes(route: XOneOrManyType<string>) {
    //
    if (!hasChild(route)) {
      return '/';
    }

    //
    try {
      return toArray(route).join('/').replace('//', '/');
    } catch {}

    //
    return '/';
  }

  /**
   * retrieve page route ...
   *
   * @param page an instance of XPage ...
   */
  getPageRoute(page: XPage) {
    //
    if (!page || !hasChild(page.route)) {
      return null;
    }

    //
    return this.mergeRoutes(page.route);
  }

  /**
   * generate and retrieve the fully qualified URL address of
   * specified route
   *
   * @param route the selected app route
   */
  getFullUrl(route?: string) {
    //
    const mBase = this.getBaseUrl().endsWith('/')
      ? this.getBaseUrl()
      : `${this.getBaseUrl()}/`;
    const mRoute = !route
      ? ''
      : !route.startsWith('/')
      ? route
      : route.substring(1);

    //
    return `${mBase}${mRoute}`;
  }

  /**
   * navigate to specific location ...
   *
   * @param commands url or url tree parts which required to navigate
   * @param extras the navigation params or options
   */
  navigate(commands: any[], extras?: NavigationExtras) {
    return this.router.navigate(commands, extras);
  }

  /**
   * navigate to specific location ...
   *
   * @param commands url or url tree parts which required to navigate
   * @param extras the navigation params or options
   * @param replaceUrl determines the url replaced or not
   */
  navigateReplace(
    commands: any[],
    extras?: NavigationExtras,
    replaceUrl: boolean = true
  ) {
    return this.router.navigate(commands, { ...extras, replaceUrl });
  }

  /**
   * navigate to specific location by it's url ...
   *
   * @param url specific url address for navigating
   * @param extras the navigation params or options
   */
  navigateByUrl(url: string | UrlTree, extras?: NavigationExtras) {
    return this.router.navigate([url], extras);
  }

  /**
   * navigate to specific location by it's url ...
   *
   * @param url specific url address for navigating
   * @param extras the navigation params or options
   * @param replaceUrl determines the url replaced or not
   */
  navigateByUrlReplace(
    url: string | UrlTree,
    extras?: NavigationExtras,
    replaceUrl: boolean = true
  ) {
    return this.router.navigate([url], { ...extras, replaceUrl });
  }

  /**
   * navigate to Specific Page ...
   *
   * @param page an instance of XPage
   * @param extras the navigation params or options
   */
  navigateByPage(page: XPage, extras?: NavigationExtras) {
    //
    const route = this.getPageRoute(page);
    if (isNullOrEmptyString(route)) {
      return false;
    }

    //
    return this.navigateByUrl(route, extras);
  }

  /**
   * navigate to Specific Page ...
   *
   * @param page an instance of XPage
   * @param extras the navigation params or options
   * @param replaceUrl determines the url replaced or not
   */
  navigateByPageReplace(
    page: XPage,
    extras?: NavigationExtras,
    replaceUrl: boolean = true
  ) {
    //
    const route = this.getPageRoute(page);
    if (isNullOrEmptyString(route)) {
      return false;
    }

    //
    return this.navigateByUrl(route, { ...extras, replaceUrl });
  }

  /**
   * Applies an array of commands to the current URL tree and creates a new URL tree.
   *
   * When given an activated route, applies the given commands starting from the route.
   * Otherwise, applies the given command starting from the root.
   *
   * @param commands An array of commands to apply.
   * @param navigationExtras Options that control the navigation strategy.
   * @returns The new URL tree.
   */
  createUrlTree(commands: any[], navigationExtras?: NavigationExtras): UrlTree {
    return this.router.createUrlTree(commands, navigationExtras);
  }

  /** Serializes a `UrlTree` into a string */
  serializeUrl(url: UrlTree): string {
    return this.router.serializeUrl(url);
  }

  /** Parses a string into a `UrlTree` */
  parseUrl(url: string): UrlTree {
    return this.router.parseUrl(url);
  }

  /** Returns whether the url is activated */
  isActive(url: string | UrlTree, exact: boolean): boolean {
    return this.router.isActive(url, exact);
  }

  /**
   * open specifc url ...
   *
   * @param url URL address
   * @param target link taget
   */
  openUrl(url: string, target: XRouterTarget = XRouterTarget.BLANK) {
    //
    if (isNullOrEmptyString(url)) {
      return;
    }

    //
    const link = document.createElement('a');
    link.target = target;
    link.href = url;

    //
    link.click();
    link.remove();
  }
  //#endregion

  //
  //#region Handling Query Param actions ...
  /**
   * change page Query Params ...
   * @param params Query Parameters
   */
  changeQueryParams(params: Params) {
    //
    return this.router.navigate([], {
      relativeTo: this.activatedRoute,
      queryParams: params,
      queryParamsHandling: 'merge',
    });
  }

  clearQueryParams() {
    //
    return this.router.navigate([], {
      relativeTo: this.activatedRoute,
      queryParams: {},
    });
  }

  /**
   * Get CurrentPages Query Params ...
   */
  getQueryParams(): Params {
    return this.router.routerState.snapshot.root.queryParams;
  }

  getQueryParamMap(): ParamMap {
    return this.router.routerState.snapshot.root.queryParamMap;
  }

  isContainQueryParam(param: XParamIdentifier) {
    return this.getQueryParamMap().has(param);
  }

  getQueryParamVal(param: XParamIdentifier) {
    //
    if (!this.isContainQueryParam(param)) {
      return null;
    }

    //
    return this.getQueryParamMap().get(param);
  }

  getQueryParamValOfType<T>(param: XParamIdentifier) {
    //
    if (!this.isContainQueryParam(param)) {
      return null;
    }

    //
    return fromJson<T>(this.getQueryParamMap().get(param));
  }
  //#endregion

  //
  //#region Handling Param actions ...
  /**
   * Get CurrentPages Query Params ...
   */
  getParams(): Params {
    return this.router.routerState.snapshot.root.params;
  }

  getParamMap(): Params {
    return this.router.routerState.snapshot.root.paramMap;
  }

  isContainParam(param: XParamIdentifier) {
    return this.getParamMap().has(param);
  }

  changeParam(params: Params) {
    //
    return this.router.navigate(['', params], {
      relativeTo: this.activatedRoute,
      replaceUrl: true,
    });
  }

  getParamVal(param: XParamIdentifier) {
    //
    if (!this.isContainParam(param)) {
      return null;
    }

    //
    return this.getParamMap().get(param);
  }

  getParamValOfType<T>(param: XParamIdentifier) {
    //
    if (!this.isContainParam(param)) {
      return null;
    }

    //
    return fromJson<T>(this.getParamMap().get(param));
  }
  //#endregion

  //
  //#region State Handling ...
  /**
   * change page state ...
   * @param params State Parameters
   */
  changeState(params: Params) {
    //
    return this.router.navigate([], {
      relativeTo: this.activatedRoute,
      replaceUrl: true,
      state: params,
    });
  }

  getStates(): Params {
    return this.router.getCurrentNavigation().extras.state || {};
  }

  isContainState(param: XParamIdentifier) {
    //
    const paramKeys = keys(this.getStates());
    const result = paramKeys.includes(param);

    //
    return result;
  }

  getStateVal(param: XParamIdentifier) {
    //
    if (!this.isContainState(param)) {
      return null;
    }

    //
    return this.getStates()[param] || null;
  }

  getStateValOfType<T>(param: XParamIdentifier) {
    //
    if (!this.isContainState(param)) {
      return null;
    }

    //
    return fromJson<T>(this.getStateVal(param)) || null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkServicesHolder\projects\x-framework-services\src\lib\x-manager-service-partial\x-manager-ui-state.service.ts

import {
  XLocale,
  XUiEvent,
  notValue,
  XException,
  XLanguageDirection,
  isNullOrEmptyString,
} from 'x-framework-core';
import { Subject, of } from 'rxjs';
import { filter, concatMap } from 'rxjs/operators';
import { XManagerBaseService } from './x-manager-base.service';

export abstract class XManagerUIStateService extends XManagerBaseService {
  //
  //#region props ...
  private IS_LOADING: boolean;
  private UI_DISABLED: boolean;
  private ERROR: XException = null;

  //
  public uiStateChanged = new Subject<XUiEvent>();

  //
  public isMobileUi$ = this.settingsService.isMobile$;
  public isNotMobileUi$ = notValue(this.settingsService.isMobile$);

  //
  public isLoading$ = this.uiStateChanged.asObservable().pipe(
    filter((uiState) =>
      [XUiEvent.StartLoading, XUiEvent.StopLoading].includes(uiState)
    ),
    concatMap((state) => {
      return of(state === XUiEvent.StartLoading ? true : false);
    })
  );
  //#endregion

  //
  //#region Getter(s) ...
  set uiDisabled(v: boolean) {
    //
    if (v === this.UI_DISABLED) {
      return;
    }

    this.UI_DISABLED = v;
    this.uiStateChanged.next(v ? XUiEvent.Disabled : XUiEvent.Enabled);
  }

  //
  get uiDisabled() {
    return this.UI_DISABLED;
  }

  //
  set loading(v: boolean) {
    //
    if (v === this.IS_LOADING) {
      return;
    }

    //
    this.IS_LOADING = v;
    this.uiStateChanged.next(v ? XUiEvent.StartLoading : XUiEvent.StopLoading);
  }

  //
  get isLoading() {
    return this.IS_LOADING;
  }

  //
  set error(exception: XException | null) {
    //
    if (exception === this.ERROR) {
      return;
    }

    //
    this.ERROR = exception;

    //
    if (exception) {
      //
      if (this.loading) {
        this.loading = false;
      }

      //
      this.uiDisabled = true;
    } else {
      //
      this.uiDisabled = false;
    }
  }

  //
  get error() {
    return this.ERROR;
  }

  //
  get hasError() {
    return !!this.ERROR;
  }

  //
  get uiDirection(): XLanguageDirection {
    return this.settingsService.uiDirection;
  }

  //
  get currentLanguage() {
    return this.localizationService.language;
  }

  //
  get currentLocale(): XLocale {
    return this.localizationService.locale;
  }

  //
  get isRtl() {
    return this.settingsService.uiDirection === 'rtl';
  }

  //
  get isLtr() {
    return this.settingsService.uiDirection === 'ltr';
  }
  //#endregion

  //
  //#region UI Actions ...
  //
  public dispatchResizeEvent() {
    //
    window.dispatchEvent(new Event('resize'));
    setTimeout(() => window.dispatchEvent(new Event('resize')), 500);
  }

  /**
   * save a text value to clipboard ...
   *
   * @param value the content which reuired to save to clipboard ...
   */
  public saveToClipboard(value: string) {
    //
    if (isNullOrEmptyString(value)) {
      return false;
    }

    //
    if (
      document.queryCommandSupported &&
      document.queryCommandSupported('copy')
    ) {
      //
      const textarea = document.createElement('textarea');
      textarea.textContent = value;
      // Prevent scrolling to bottom of page in Microsoft Edge.
      textarea.style.position = 'fixed';
      document.body.appendChild(textarea);
      textarea.select();

      //
      try {
        // Security exception may be thrown by some browsers.
        return document.execCommand('copy');
      } catch (ex) {
        return false;
      } finally {
        document.body.removeChild(textarea);
      }
    }

    //
    return false;
  }
  //#endregion

  //
  //#region Private ...
  //#endregion
}
