HR/CryptoSync

View on GitHub
static/settings.html

Summary

Maintainability
Test Coverage
<!doctype html>
<html>

  <head>
    <meta charset="utf-8">
    <title>Settings</title>
    <link rel="stylesheet" href="style/settings.css">
  </head>

  <body>
    <section id="settings">
      <div class="panel-container">
        <div class="menu">
          <a class="item navigationLink active" data-target="accounts">Accounts</a>
          <a class="item navigationLink" data-target="crypto">Crypto</a>
          <a class="item navigationLink" data-target="sync">Sync</a>
          <!-- <a class="item navigationLink" data-target="contribute">
                    <img class="icon" src="images/icons/mark-github.svg" alt="" />
                </a> -->
          <a class="item right">
            <img class="icon" src="images/icons/CryptoSyncVault_sl.svg" alt=""/>
          </a>
        </div>
        <div id="panel-accounts" class="current">
          <section class="accounts">
            <header>
              <img class="icon" src="images/icons/accounts.svg" alt=""/>
              <h3>Cloud accounts</h3>
            </header>
            <div class="list" id="accountsList"></div>
            <!-- <button class="button add">
                        <img src="images/icons/add.svg" alt="" class="icon" />
                    </button> -->
          </section>
        </div>
        <div id="panel-crypto">
          <section class="crypto">
            <header>
              <img class="icon" src="images/icons/crypto.svg" alt="">
              <h3>Crypto</h3>
            </header>
            <section class="category">
              <h4>Encryption</h4>
              <div class="options">
                <div class="option">
                  <span>Algorithm</span>
                  <span class="right">AES-256-GCM</span>
                </div>
                <div class="option">
                  <span>Key length</span>
                  <span class="right">256 bits</span>
                  <!-- <select class="right config" data-config="keyLength">
                                    <option value="256">256-bit</option>
                                </select> -->
                </div>
                <div class="option">
                  <span>IV length</span>
                  <span class="right config">96 bits</span>
                </div>
                <div class="option">
                  <span>Salt length</span>
                  <span class="right ">256 bits</span>
                </div>
                <div class="option">
                  <span>PBKDF2 iterations</span>
                  <span class="right ">5,000</span>
                </div>
                <div class="option">
                  <span>Hash function</span>
                  <span class="right ">MD5</span>
                </div>
              </div>
            </section>
            <section class="category">
              <h4>Vault</h4>
              <div class="options" id="vaultCreds">
                <div class="option">
                  <span>Encryption</span>
                </div>
              </div>
            </section>
            <section class="category">
              <h4>MasterPass</h4>
              <div class="options">
                <div class="option">
                  <span>Encryption</span>
                </div>
                <div class="option">
                  <span>PBKDF2 iterations</span>
                  <span class="right config" data-config="ivLength">100,000</span>
                </div>
                <div class="option">
                  <span>Hash function</span>
                  <span class="right ">SHA256</span>
                </div>
                <div class="option">
                  <span>Shares</span>
                  <select class="right config" data-config="shares">
                    <option value="2#3">s = 2, n = 3</option>
                    <option value="2#4">s = 2, n = 4</option>
                  </select>
                </div>
                <div class="option">
                  <img class="info" src="images/icons/info.svg" alt="">
                  <p class="info">s = Total number of shares <br/> n = Number of shares required to reconstruct MasterPass</p>
                </div>
                <!-- <div class="option">
                  <span>Change</span>
                  <button class="right" id="resetMasterPass">Reset</button>
                </div> -->
              </div>
            </section>
          </section>
        </div>
        <div id="panel-sync">
          <section class="sync">
            <header>
              <img class="icon" src="images/icons/sync.svg" alt=""/>
              <h3>Sync</h3>
            </header>
            <section class="category">
              <h4>General</h4>
              <div class="options">
                <div class="option">
                  <span>Autostart</span>
                  <input type="checkbox" class="right config" data-config="autostart" checked/>
                </div>
                <div class="option">
                  <span>Encrypt while offline</span>
                  <input type="checkbox" class="right config" data-config="offlineEnc" checked/>
                </div>
              </div>
            </section>
          </section>
        </div>
        <div id="panel-contribute">
          <section class="contribute">
            <header>
              <img class="icon" src="images/icons/mark-github.svg" alt=""/>
              <h3>Contribute</h3>
            </header>
            <div class="list">
              <div class="item">
                <a class="navigationLink" data-target="https://github.com/HR/CryptoSync/issues">
                  <img src="images/icons/issue-opened.svg" class="icon" alt=""/>
                  <div class="name">
                    Report an issue / Give feedback
                  </div>
                </a>
              </div>
              <div class="item">
                <a class="navigationLink" data-target="https://github.com/HR/CryptoSync">
                  <img src="images/icons/star.svg" alt="" class="icon"/>
                  <div class="name">
                    Star the repo
                  </div>
                </a>
              </div>
              <div class="item">
                <a class="navigationLink" data-target="https://github.com/HR/CryptoSync/fork">
                  <img src="images/icons/repo-forked.svg" alt="" class="icon"/>
                  <div class="name">
                    Fork the repo
                  </div>
                </a>
              </div>
              <div class="item">
                <a class="navigationLink" data-action="check-updates">
                  <img src="images/icons/package.svg" alt="" class="icon"/>
                  <div class="name">
                    Check for Updates
                  </div>
                </a>
              </div>
            </div>
            <footer>
              <img src="images/icons/code.svg" alt="" class="icon"/>
              with
              <img src="images/icons/heart.svg" alt="" class="icon"/>
              by
              <br>
              <a class="navigationLink" data-target="https://github.com/HR">Habib Rehman</a>
            </footer>
          </section>
        </div>
      </div>
    </section>
    <script id="account-template" type="text/x-handlebars-template">
      <div class="item">
        <div class="type">
          <div class="profile" style="background:url(data:image/gif;base64,{{profileImg}}) no-repeat center center;"></div>
          <div class="cloudType">
            <img src="images/icons/{{type}}.svg"></img>
          </div>
        </div>
        <div class="content">
          <div class="name">{{name}}</div>
          <div class="email">{{email}}</div>
          <div class="qouta">{{usage}}
            /
            {{limit}}</div>
        </div>
        <img src="images/icons/remove.svg" data-action="remove-{{account}}" class="icon remove right"/>
      </div>
    </script>
    <script id="vault-template" type="text/x-handlebars-template">
      <div class="option">
        <span>Initialisation Vector</span>
        <span class="right">{{viv}}</span>
      </div>
      <div class="option">
        <span>Authentication tag</span>
        <span class="right">{{authTag}}</span>
      </div>
    </script>
    <script type="text/javascript">
      'use strict'
      // TODO: Add reset config feature
      window.$ = window.jQuery = require('jquery')
      // load core modules first
      var remote = require('electron').remote
      var accounts = remote.getGlobal("accounts")
      var Buffer = require('buffer').Buffer
      var Handlebars = require('handlebars')
      var ipcRenderer = require('electron').ipcRenderer
      var settings = remote.getGlobal("settings")
      var creds = remote.getGlobal("creds")
      var logger = require('../script/logger.js')
      var shell = require('shell')
      var userSettings = {}

      $(window).on('online', updateOnlineStatus)
      $(window).on('offline', updateOnlineStatus)
      updateOnlineStatus()

      function formatBytes(bytes, precision) {
        if (bytes == 0) return '0 byte'
        if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return '-'
        if (typeof precision === 'undefined') precision = 0
        var units = [
          'bytes',
          'KB',
          'MB',
          'GB',
          'TB',
          'PB'
        ]
        var number = Math.floor(Math.log(bytes) / Math.log(1024))
        return (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision) + ' ' + units[number]
      }

      function buf2hex(buf) {
        return (new Buffer(buf)).toString('hex')
      }

      ipcRenderer.on("resetMasterPassResult", function(event, err, MPKset) {
            logger.verbose("IPCRENDER: resetMasterPassResult emitted")
            logger.verbose(`resetMasterPassResult: MPKset ${MPKset}, err: ${err}`)
        })

      $(window).load(function() {
        $('#resetMasterPass').unbind().click(function(event) {
          event.preventDefault()
          ipcRenderer.send("resetMasterPass")
          return false
        })
        //Compile and render template code
        for (var account in accounts) {
          if (accounts.hasOwnProperty(account)) {
            var template = Handlebars.compile($("#account-template").html())
            var account = template({
              account: account,
              type: accounts[account].type,
              name: accounts[account].name,
              profileImg: accounts[account].profileImg,
              email: accounts[account].email,
              usage: formatBytes(accounts[account].quota.usage, 2),
              limit: formatBytes(accounts[account].quota.limit)
            })
            $('#accountsList').append(account)
          }
        }
        var creds_temp = Handlebars.compile($("#vault-template").html())
        $('#vaultCreds').append(creds_temp({
          viv: buf2hex(creds.viv.data),
          authTag: buf2hex(creds.authTag.data)
        }))

        $(".navigationLink").each(function(index) {
          var $this = $(this)
          $(this).on('click', function(event) {
            var target = $this.data("target")
            var action = $this.data("action")
            if (action) {
              // TODO: Action (i.e. check for updates/open updater)
              console.log(`${this} has data-action attr of ${action}`)
              let r = /^remove-/g
              if (r.test(action)) {
                ipcRenderer.send('removeAccount', action.replace(r, ''))
              } else {
                ipcRenderer.send(action)
              }
            } else {
              $(".active").first().removeClass("active")
              $("a[data-target='" + target + "']").addClass("active")
              navigate(target)
            }
            return false
          })
        })

        $(".config").each(function(index) {
          var $this = $(this)
          if (!($.isEmptyObject(settings.user))) {
            // TODO: Load defaults
            console.log("settings.user is not empty")
            setConfig(settings.user)
          }
          $this.change(function() {
            var config = this.getAttribute("data-config")
            var isCheckbox = $this.is(':checkbox')
            var value = (isCheckbox)
              ? $this.is(':checked')
              : $this.val()
            logger.verbose(`RENDER: ${config} has changed to ${value} of type ${typeof value}`)
            userSettings[config] = value
            logger.verbose(`RENDER: Set userSettings[${config}] to ${userSettings[config]}`)
            settings.user = userSettings
          })
        })
      })

      /* Helper functions */
      function navigate(dest) {
        var URLregex = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi
        if (URLregex.test(dest)) {
          shell.openExternal(dest)
          return
        }
        var oldSel = $('.panel-container > div.current')
        var currSel = $("#panel-" + dest)
        if (oldSel)
          oldSel.removeClass("current")
        $("#panel-" + dest).addClass("current")
      }

      function setConfig(configObj) {
        for (var config in configObj) {
          if (configObj.hasOwnProperty(config)) {
            var value = configObj[config]
            if ($.type(value) === "boolean") {
              $("[data-config='" + config + "']").prop('checked', value)
            } else {
              $("[data-config='" + config + "']").val(value)
            }
          }
        }
      }

      /* Event emitters */
      function updateOnlineStatus() {
        // Emitt network change event
        ipcRenderer.send('online-status-changed', navigator.onLine
          ? 'online'
          : 'offline')
      }
    </script>
  </body>

</html>