<commit_before>/* -----------------------------------
      Loading
   ----------------------------------- */
var loadProgress = 1;
var loadTotal = $("main img").length + 2;

function animateLoadStart() {
    $("#ajax-loader").remove();
    $("body").append("

");
}

function startLoadProgress(total) {
    loadProgress = 1;
    loadTotal = total + 2;
    doLoadProgress();
}

function updateLoadProgress() {
    loadProgress++;
    animateLoader((loadProgress/loadTotal*100).toString() + '%');
}

function doLoadProgress() {
    $("main img").load(function() {
        updateLoadProgress();
    })
}

function animateLoader(newWidth) {
    $("#ajax-loader").width(newWidth);
    if (loadProgress >= loadTotal) {
        setTimeout(function() {
            $("#ajax-loader").animate({opacity:0});
            if ($("main").length >= 2) {
                var main_old = $($("main")[0]);
                var main_new = $($("main")[1]);
                main_old.fadeOut(500, function() {
                    $(this).remove();
                });
                main_new.fadeIn(500);
            }

        }, 500);
    }
}

function ajaxLinks() {
    $("a").unbind("click");
    $("a").click(function(e) {
        var exclude = ["http://", "https://", "#"];
        var href = $(this).attr("href");
        if ($.inArray(href, exclude) == -1) {
            e.preventDefault();
            animateLoadStart();
            if (window.location.pathname != href) {
                ajaxLoad(href);
            } else {
                startLoadProgress(0);
                $("html, body").animate( {scrollTop: 0}, "slow");
                updateLoadProgress();
            }
        }
    });
}

function loadPage(data) {

    // Get and set <title></title>
    var start = data.indexOf("<title>") + 7;
    var end = data.indexOf("</title>");
    document.title = data.substring(start, end);

    // Get  and add it to the body
    start = data.indexOf("") + 6;
    end = data.lastIndexOf("");
    var mainData = data.substring(start, end);
    $("body").append("");
    var main_new = $($("main")[1]);
    main_new.hide();
    main_new.append(mainData);

    // Start loading
    startLoadProgress(main_new.children("img").length);
    updateLoadProgress();
}

function ajaxLoad(href) {
    $.ajax({
        url: href,
    }).done(function(data) {
        loadPage(data)
        window.history.pushState(data,"", href);
    });
}

function navigateHistory(e) {
    if (e.state) {
        animateLoadStart();
        loadPage(e.state);
    }
}

/* -----------------------------------
      Header
   ----------------------------------- */
// Hamburger menu show/hide
function miniMenu() {
    var nav = $("nav ul");

    $(".menu-icon").click(function(e) {
        e.preventDefault();
        if (nav.hasClass("active")) {
            nav.animate({
                "margin-left": "-151px"
            });

        } else {
            nav.animate({
                "margin-left": "0px"
            });
        }
        $("header").addClass("sticky");
        nav.toggleClass("active");
    });

    $("nav li a").click(function(e) {
        if ($(".menu-icon").is(":visible")) {
            nav.animate({
                "margin-left": "-151px"
            });
            $("header").toggleClass("sticky");
            nav.removeClass("active");
        }
    });

    $(window).on("resize", function() {
        if (window.innerWidth >= 600) {
            nav.css("margin-left", "");
            nav.removeClass("active");
            var position = $(window).scrollTop();
            if (position == 0 && $("header").hasClass("sticky")) {
                $("header").removeClass("sticky");
            }
        }
    });
}

// Show/hide header depending on scroll direction
function scrollHeader() {
    var position = $(window).scrollTop();
    var header = $("header");

    $(window).on("scroll", function() {
        var scroll = $(window).scrollTop();
        header.removeClass();
        if (scroll == 0) {

        } else if (scroll > position) {
            // Scrolling down
            header.addClass("hidden")
        } else {
            header.addClass("sticky");
        }

        position = scroll;
    });
}

/* -----------------------------------
      Pixel Storm
   ----------------------------------- */
// Adapted from http://thecodeplayer.com/walkthrough/html5-canvas-snow-effect
function pixelStorm(){
    //canvas init
    var canvas = document.getElementById("pixel-storm");
    var ctx = canvas.getContext("2d");

    //canvas dimensions
    var W = window.innerWidth;
    var H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;

    // Random Colour function
    function randomColour() {
        return "rgba(" + Math.round(Math.random()*255).toString() + ", " + Math.round(Math.random()*255).toString() + ", " + Math.round(Math.random()*255).toString() + ", 0.8)";
    }

    //snowflake particles
    var mp = 100; //max particles
    var particles = [];
    for(var i = 0; i < mp; i++)
    {
        particles.push({
            x: Math.random()*W, //x-coordinate
            y: Math.random()*H, //y-coordinate
            r: Math.random()*3+1, //radius
            d: Math.random()*mp, //density
            rgba: randomColour() //color
        })
    }

    //Lets draw the flakes
    function draw()
    {
        ctx.clearRect(0, 0, W, H);

        for(var i = 0; i < mp; i++)
        {

            var p = particles[i];
            ctx.fillStyle = p.rgba;
            ctx.beginPath();
            ctx.moveTo(p.x, p.y);
            // ctx.arc(p.x, p.y, p.r, 0, Math.PI*2, true);

            // ctx.fill();
            ctx.fillRect(p.x, p.y, p.r, p.r);
        }
        update();
    }

    //Function to move the snowflakes
    //angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
    var angle = 0;
    function update()
    {
        angle += 0.01;
        for(var i = 0; i < mp; i++)
        {
            var p = particles[i];
            //Updating X and Y coordinates
            //We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards
            //Every particle has its own density which can be used to make the downward movement different for each flake
            //Lets make it more random by adding in the radius
            p.y += 2*(Math.cos(angle+p.d) + 1 + p.r/2);
            p.x += Math.sin(angle)*2;

            //Sending flakes back from the top when it exits
            //Lets make it a bit more organic and let flakes enter from the left and right also.
            if(p.x > W+5*Math.random() || p.x < -5*Math.random() || p.y > H)
            {
                if(i%9 > 0)
                {
                    particles[i] = {x: Math.random()*W, y: -10, r: p.r, d: p.d, rgba: randomColour()};
                }
                else
                {
                    //If the flake is exitting from the right
                    if(Math.sin(angle) > 0)
                    {
                        //Enter from the left
                        particles[i] = {x: -5*Math.random()*p.r, y: Math.random()*H, r: p.r, d: p.d, rgba: randomColour()};
                    }
                    else
                    {
                        //Enter from the right
                        particles[i] = {x: W+5*Math.random()*p.r, y: Math.random()*H, r: p.r, d: p.d, rgba: randomColour()};
                    }
                }
            }
        }
    }

    ctx.translate(0, canvas.height);
    ctx.scale(1, -1);
    //animation loop
    setInterval(draw, 33);

    // Resize canvas on browser resize
    $(window).on("resize", function() {
        var parent = $("#pixel-storm").parent();
        // W = canvas.clientWidth;
        // H = canvas.clientHeight;
        W = parent.width();
        H = parent.height();
        canvas.width = W;
        canvas.height = H;

        ctx.translate(0, canvas.height);
        ctx.scale(1, -1);
    });
}<commit_msg>Ajax load now scrolls to top<commit_after>/* -----------------------------------
      Loading
   ----------------------------------- */
var loadProgress = 1;
var loadTotal = $("main img").length + 2;

function animateLoadStart() {
    $("#ajax-loader").remove();
    $("body").append("

");
}

function startLoadProgress(total) {
    loadProgress = 1;
    loadTotal = total + 2;
    doLoadProgress();
}

function updateLoadProgress() {
    loadProgress++;
    animateLoader((loadProgress/loadTotal*100).toString() + '%');
}

function doLoadProgress() {
    $("main img").load(function() {
        updateLoadProgress();
    })
}

function animateLoader(newWidth) {
    $("#ajax-loader").width(newWidth);
    if (loadProgress >= loadTotal) {
        setTimeout(function() {
            $("#ajax-loader").animate({opacity:0});
            if ($("main").length >= 2) {
                var main_old = $($("main")[0]);
                var main_new = $($("main")[1]);
                main_old.fadeOut(500, function() {
                    $(this).remove();
                });
                main_new.fadeIn(500);
                $("html, body").animate( {scrollTop: 0}, "slow");
            }

        }, 500);
    }
}

function ajaxLinks() {
    $("a").unbind("click");
    $("a").click(function(e) {
        var exclude = ["http://", "https://", "#"];
        var href = $(this).attr("href");
        if ($.inArray(href, exclude) == -1) {
            e.preventDefault();
            animateLoadStart();
            if (window.location.pathname != href) {
                ajaxLoad(href);
            } else {
                startLoadProgress(0);
                $("html, body").animate( {scrollTop: 0}, "slow");
                updateLoadProgress();
            }
        }
    });
}

function loadPage(data) {

    // Get and set <title></title>
    var start = data.indexOf("<title>") + 7;
    var end = data.indexOf("</title>");
    document.title = data.substring(start, end);

    // Get  and add it to the body
    start = data.indexOf("") + 6;
    end = data.lastIndexOf("");
    var mainData = data.substring(start, end);
    $("body").append("");
    var main_new = $($("main")[1]);
    main_new.hide();
    main_new.append(mainData);

    // Start loading
    startLoadProgress(main_new.children("img").length);
    updateLoadProgress();
}

function ajaxLoad(href) {
    $.ajax({
        url: href,
    }).done(function(data) {
        loadPage(data)
        window.history.pushState(data,"", href);
    });
}

function navigateHistory(e) {
    if (e.state) {
        animateLoadStart();
        loadPage(e.state);
    }
}

/* -----------------------------------
      Header
   ----------------------------------- */
// Hamburger menu show/hide
function miniMenu() {
    var nav = $("nav ul");

    $(".menu-icon").click(function(e) {
        e.preventDefault();
        if (nav.hasClass("active")) {
            nav.animate({
                "margin-left": "-151px"
            });

        } else {
            nav.animate({
                "margin-left": "0px"
            });
        }
        $("header").addClass("sticky");
        nav.toggleClass("active");
    });

    $("nav li a").click(function(e) {
        if ($(".menu-icon").is(":visible")) {
            nav.animate({
                "margin-left": "-151px"
            });
            $("header").toggleClass("sticky");
            nav.removeClass("active");
        }
    });

    $(window).on("resize", function() {
        if (window.innerWidth >= 600) {
            nav.css("margin-left", "");
            nav.removeClass("active");
            var position = $(window).scrollTop();
            if (position == 0 && $("header").hasClass("sticky")) {
                $("header").removeClass("sticky");
            }
        }
    });
}

// Show/hide header depending on scroll direction
function scrollHeader() {
    var position = $(window).scrollTop();
    var header = $("header");

    $(window).on("scroll", function() {
        var scroll = $(window).scrollTop();
        header.removeClass();
        if (scroll == 0) {

        } else if (scroll > position) {
            // Scrolling down
            header.addClass("hidden")
        } else {
            header.addClass("sticky");
        }

        position = scroll;
    });
}

/* -----------------------------------
      Pixel Storm
   ----------------------------------- */
// Adapted from http://thecodeplayer.com/walkthrough/html5-canvas-snow-effect
function pixelStorm(){
    //canvas init
    var canvas = document.getElementById("pixel-storm");
    var ctx = canvas.getContext("2d");

    //canvas dimensions
    var W = window.innerWidth;
    var H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;

    // Random Colour function
    function randomColour() {
        return "rgba(" + Math.round(Math.random()*255).toString() + ", " + Math.round(Math.random()*255).toString() + ", " + Math.round(Math.random()*255).toString() + ", 0.8)";
    }

    //snowflake particles
    var mp = 100; //max particles
    var particles = [];
    for(var i = 0; i < mp; i++)
    {
        particles.push({
            x: Math.random()*W, //x-coordinate
            y: Math.random()*H, //y-coordinate
            r: Math.random()*3+1, //radius
            d: Math.random()*mp, //density
            rgba: randomColour() //color
        })
    }

    //Lets draw the flakes
    function draw()
    {
        ctx.clearRect(0, 0, W, H);

        for(var i = 0; i < mp; i++)
        {

            var p = particles[i];
            ctx.fillStyle = p.rgba;
            ctx.beginPath();
            ctx.moveTo(p.x, p.y);
            // ctx.arc(p.x, p.y, p.r, 0, Math.PI*2, true);

            // ctx.fill();
            ctx.fillRect(p.x, p.y, p.r, p.r);
        }
        update();
    }

    //Function to move the snowflakes
    //angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
    var angle = 0;
    function update()
    {
        angle += 0.01;
        for(var i = 0; i < mp; i++)
        {
            var p = particles[i];
            //Updating X and Y coordinates
            //We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards
            //Every particle has its own density which can be used to make the downward movement different for each flake
            //Lets make it more random by adding in the radius
            p.y += 2*(Math.cos(angle+p.d) + 1 + p.r/2);
            p.x += Math.sin(angle)*2;

            //Sending flakes back from the top when it exits
            //Lets make it a bit more organic and let flakes enter from the left and right also.
            if(p.x > W+5*Math.random() || p.x < -5*Math.random() || p.y > H)
            {
                if(i%9 > 0)
                {
                    particles[i] = {x: Math.random()*W, y: -10, r: p.r, d: p.d, rgba: randomColour()};
                }
                else
                {
                    //If the flake is exitting from the right
                    if(Math.sin(angle) > 0)
                    {
                        //Enter from the left
                        particles[i] = {x: -5*Math.random()*p.r, y: Math.random()*H, r: p.r, d: p.d, rgba: randomColour()};
                    }
                    else
                    {
                        //Enter from the right
                        particles[i] = {x: W+5*Math.random()*p.r, y: Math.random()*H, r: p.r, d: p.d, rgba: randomColour()};
                    }
                }
            }
        }
    }

    ctx.translate(0, canvas.height);
    ctx.scale(1, -1);
    //animation loop
    setInterval(draw, 33);

    // Resize canvas on browser resize
    $(window).on("resize", function() {
        var parent = $("#pixel-storm").parent();
        // W = canvas.clientWidth;
        // H = canvas.clientHeight;
        W = parent.width();
        H = parent.height();
        canvas.width = W;
        canvas.height = H;

        ctx.translate(0, canvas.height);
        ctx.scale(1, -1);
    });
}<|endoftext|>


<commit_before>/*
* IX Server code
*
* Copyright (C) Province of British Columbia, 2013
*/

var express = require('express')
  , url = require('url')
  , util = require('util')
  , vault = require('./vault')
  , config = require('../config')
  , request = require('../lib/request')
  , token = require('../lib/token')
  , db = require('../lib/db')
  , api = require('../lib/api')
  , jwt = require('../lib/jwt')
  , mw = require('../lib/middleware')

function diCreate ( req, res, next ) {
    var AS = req.request['request.a2p3.org'].AS
    var rsHosts = req.request['request.a2p3.org'].RS
    var redirects = req.request['request.a2p3.org'].redirects
    db.newUser( AS, rsHosts, redirects, function ( e, dis ) {
      if (e) { e.code = "INTERNAL_ERROR"; return next(e) }
      res.send( {'result': {'dis': dis}} )
    })
}

// parses out hosts from passed in scopes
function getHosts ( scopes ) {
  var results = {}
  if (scopes)
    scopes.forEach( function ( scope ) {
      var o = url.parse( scope )
      results[o.hostname] = results[o.hostname] || []
      results[o.hostname].push( scope )
    })
  return results
}

// exchange IX Token for RS Tokens if all is good
function exchange ( req, res, next ) {

debugger;

  var jwe, jws, ixToken, e
  try {
    jwe = new jwt.Parse( req.request['request.a2p3.org'].token )
    if (jwe.header.typ === 'JWS') {
      e = new Error( 'Received a JWS when expecting a JWE')
      e.code = "INVALID_TOKEN"
      return next( e )
    }
    if (jwe.header.typ !== 'JWE') {
      e = new Error( 'Uknown token type.')
      e.code = "INVALID_TOKEN"
      return next( e )
    }
    if ( !jwe.header.kid || !vault.keys.as[jwe.header.kid] ) {
      e = new Error( "No AS key for 'kid' "+jwe.header.kid)
      e.code = "INVALID_TOKEN"
      return next( e )
    }
    ixToken = jwe.decrypt( vault.keys.as[jwe.header.kid] ) // list of keys for AS
  }
  catch (e) {
    e.code = 'INVALID_TOKEN'
    return next( e )
  }
  try {
    jws = new jwt.Parse( req.request['request.a2p3.org'].request )  // agent Request
    if ( !jwt ) {
      e = new Error( "Invalid Agent Request")
      e.code = "INVALID_REQUEST"
      return next( e )
    }
  }
  catch (e) {
    e.code = 'INVALID_REQUEST'
    return next( e )
  }

  // make sure IX Token 'iss' matches associated 'kid'
  if (!vault.keys[ixToken.iss][jwe.header.kid]) {
      e = new Error("'iss' does not have supplied 'kid'")
      e.code = 'INVALID_TOKEN'
      return next( e )
  }
  // check agent request signature matches 'sar' that AS got
  if (ixToken['token.a2p3.org'].sar != jws.signature) {
      e = new Error("IX Token 'sar' does not match request signature")
      e.code = 'INVALID_IXREQUEST'
      return next( e )
  }
  // check ix request and agent request are from same app
  if (req.request.iss != jws.payload.iss) {
      e = new Error("Agent Request 'iss' does not match IX Request 'iss'")
      e.code = 'INVALID_REQUEST'
      return next( e )
  }
  // check IX & Agent Requests and IX Token have not expired
  if ( jwt.expired( req.request.iat ) ) {
      e = new Error("IX Request has expired")
      e.code = 'EXPIRED_IXREQUEST'
      return next( e )
  }
  if ( jwt.expired( jws.payload.iat ) ) {
      e = new Error("Agent Request has expired")
      e.code = 'EXPIRED_REQUEST'
      return next( e )
  }
  if ( jwt.expired( jwe.payload.iat ) ) {
      e = new Error("IX Token has expired")
      e.code = 'EXPIRED_TOKEN'
      return next( e )
  }

// console.log('\njwe\n', util.inspect( jwe, null, null ) )
// console.log('\njws\n', util.inspect( jws, null, null ) )

  var rsScopes = getHosts( jws.payload['request.a2p3.org'].resources )
  db.getStandardResourceHosts( ixToken.sub, ixToken.iss, Object.keys( rsScopes ), function ( e, redirects ) {
    var hostList = {}
    hostList[jws.payload.iss] = true
    Object.keys(rsScopes).forEach( function (rs) {
      if (redirects && redirects[rs]) {
        redirects[rs].forEach( function (host) {
          hostList[host] = true
          rsScopes[host] = rsScopes[rs] // redirected host has scope of standardized resource
        })
      } else {
        hostList[rs] = true
      }
    })

// console.log('\nhostList\n',hostList)

    // app keys for IX are stored with registrar
    db.getAppKeys( 'registrar', Object.keys(hostList), vault.keys, function ( e, keys ) {
      if (e) return next( e )
        var err
      if ( !keys[jws.payload.iss] || !keys[jws.payload.iss][jws.header.kid] ) {
        err = new Error( "Invalid Agent Request key or kid")
        err.code = "INVALID_REQUEST"
        return next( err )
      }
      if ( !jws.verify( keys[jws.payload.iss][jws.header.kid] ) ) {
        err = new Error( "Invalid Agent Request")
        err.code = "INVALID_REQUEST"
        return next( err )
      }
      db.getRsDIfromAsDI( ixToken.sub, ixToken.iss, Object.keys(hostList), function ( e, dis ) {
        if (e) return next( e )
        // make tokens for all resources, delete caller from list
        var tokens = {}
        delete hostList[jws.payload.iss]
        Object.keys( hostList ).forEach( function (rs) {
          var payload =
            { 'iss': config.host.ix
            , 'aud': rs
            , 'sub': dis[rs]
            , 'token.a2p3.org':
              { 'auth': jwe.payload['token.a2p3.org'].auth
              , 'app': jws.payload.iss
              , 'scopes': rsScopes[rs]
              }
            }
          tokens[rs] = token.create( payload, keys[rs].latest )
        })
        return res.send( { result: {'sub': dis[jws.payload.iss], 'tokens': tokens, 'redirects': redirects} } )
      })
    })
  })
}

// APIs called from AS agent registration web app
function agentList ( req, res, next ) {
    db.listAgents(  req.request['request.a2p3.org'].di
                  , req.request.iss
                  , function( e, handles ) {
                      if (e) return next (e)
                      return res.send({ result: { 'handles': handles } } )
                    } )
}

function agentAdd ( req, res, next ) {
    db.addAgent( req.request['request.a2p3.org'].di
                  , req.request.iss
                  , req.request['request.a2p3.org'].name
                  , function( e, token, handle ) {
                      if (e) return next (e)
                      return res.send({ result: { token: token, handle: handle } } )
                    } )
}

function agentDelete ( req, res, next ) {
  var di = req.request['request.a2p3.org'].di
    , as = req.request.iss
    , handle = req.request['request.a2p3.org'].handle
    db.deleteAgent( di, as, handle, function( e, AS ) {
      var details =
        { host: config.reverseHost[AS]  // hack because of how api.call works currently TBD :(
        , api: '/agent/delete'
        , credentials: vault.keys[AS].latest
        , payload:
          { iss: config.host.ix
          , aud: AS
          , 'request.a2p3.org': { 'handle': handle }
          }
        }
      api.call( details, function ( e ) {
        if (e) return next (e)
        return res.send( { result: { success: true } } )
    })
  })
}

exports.app = function() {
	var app = express()
  app.use(express.limit('10kb'))  // protect against large POST attack
  app.use(express.bodyParser())

  app.post('/di/create'
          , request.check( vault.keys, config.roles.enroll )
          , mw.a2p3Params( ['AS', 'RS', 'redirects'] )
          , diCreate
          )
  app.post('/exchange'
          , request.check( vault.keys, null, 'registrar' )  // registrar holds all IX App keys
          , mw.a2p3Params( ['token', 'request'] )
          , exchange
          )
  app.post('/agent/list'
          , request.check( vault.keys, config.roles.as )
          , agentList
          )
  app.post('/agent/add'
          , request.check( vault.keys, config.roles.as )
          , agentAdd
          )
  app.post('/agent/delete'
          , request.check( vault.keys, config.roles.as )
          , agentDelete
          )

  // show README.md as documentation
  app.get('/documentation', mw.md( __dirname+'/README.md' ) )

  // key integrity checking API
  app.post( '/key/check', mw.keyCheck( vault, config.host.ix ) )

  app.use( mw.errorHandler )

	return app
}
<commit_msg>remove debugger<commit_after>/*
* IX Server code
*
* Copyright (C) Province of British Columbia, 2013
*/

var express = require('express')
  , url = require('url')
  , util = require('util')
  , vault = require('./vault')
  , config = require('../config')
  , request = require('../lib/request')
  , token = require('../lib/token')
  , db = require('../lib/db')
  , api = require('../lib/api')
  , jwt = require('../lib/jwt')
  , mw = require('../lib/middleware')

function diCreate ( req, res, next ) {
    var AS = req.request['request.a2p3.org'].AS
    var rsHosts = req.request['request.a2p3.org'].RS
    var redirects = req.request['request.a2p3.org'].redirects
    db.newUser( AS, rsHosts, redirects, function ( e, dis ) {
      if (e) { e.code = "INTERNAL_ERROR"; return next(e) }
      res.send( {'result': {'dis': dis}} )
    })
}

// parses out hosts from passed in scopes
function getHosts ( scopes ) {
  var results = {}
  if (scopes)
    scopes.forEach( function ( scope ) {
      var o = url.parse( scope )
      results[o.hostname] = results[o.hostname] || []
      results[o.hostname].push( scope )
    })
  return results
}

// exchange IX Token for RS Tokens if all is good
function exchange ( req, res, next ) {
  var jwe, jws, ixToken, e
  try {
    jwe = new jwt.Parse( req.request['request.a2p3.org'].token )
    if (jwe.header.typ === 'JWS') {
      e = new Error( 'Received a JWS when expecting a JWE')
      e.code = "INVALID_TOKEN"
      return next( e )
    }
    if (jwe.header.typ !== 'JWE') {
      e = new Error( 'Uknown token type.')
      e.code = "INVALID_TOKEN"
      return next( e )
    }
    if ( !jwe.header.kid || !vault.keys.as[jwe.header.kid] ) {
      e = new Error( "No AS key for 'kid' "+jwe.header.kid)
      e.code = "INVALID_TOKEN"
      return next( e )
    }
    ixToken = jwe.decrypt( vault.keys.as[jwe.header.kid] ) // list of keys for AS
  }
  catch (e) {
    e.code = 'INVALID_TOKEN'
    return next( e )
  }
  try {
    jws = new jwt.Parse( req.request['request.a2p3.org'].request )  // agent Request
    if ( !jwt ) {
      e = new Error( "Invalid Agent Request")
      e.code = "INVALID_REQUEST"
      return next( e )
    }
  }
  catch (e) {
    e.code = 'INVALID_REQUEST'
    return next( e )
  }

  // make sure IX Token 'iss' matches associated 'kid'
  if (!vault.keys[ixToken.iss][jwe.header.kid]) {
      e = new Error("'iss' does not have supplied 'kid'")
      e.code = 'INVALID_TOKEN'
      return next( e )
  }
  // check agent request signature matches 'sar' that AS got
  if (ixToken['token.a2p3.org'].sar != jws.signature) {
      e = new Error("IX Token 'sar' does not match request signature")
      e.code = 'INVALID_IXREQUEST'
      return next( e )
  }
  // check ix request and agent request are from same app
  if (req.request.iss != jws.payload.iss) {
      e = new Error("Agent Request 'iss' does not match IX Request 'iss'")
      e.code = 'INVALID_REQUEST'
      return next( e )
  }
  // check IX & Agent Requests and IX Token have not expired
  if ( jwt.expired( req.request.iat ) ) {
      e = new Error("IX Request has expired")
      e.code = 'EXPIRED_IXREQUEST'
      return next( e )
  }
  if ( jwt.expired( jws.payload.iat ) ) {
      e = new Error("Agent Request has expired")
      e.code = 'EXPIRED_REQUEST'
      return next( e )
  }
  if ( jwt.expired( jwe.payload.iat ) ) {
      e = new Error("IX Token has expired")
      e.code = 'EXPIRED_TOKEN'
      return next( e )
  }

// console.log('\njwe\n', util.inspect( jwe, null, null ) )
// console.log('\njws\n', util.inspect( jws, null, null ) )

  var rsScopes = getHosts( jws.payload['request.a2p3.org'].resources )
  db.getStandardResourceHosts( ixToken.sub, ixToken.iss, Object.keys( rsScopes ), function ( e, redirects ) {
    var hostList = {}
    hostList[jws.payload.iss] = true
    Object.keys(rsScopes).forEach( function (rs) {
      if (redirects && redirects[rs]) {
        redirects[rs].forEach( function (host) {
          hostList[host] = true
          rsScopes[host] = rsScopes[rs] // redirected host has scope of standardized resource
        })
      } else {
        hostList[rs] = true
      }
    })

// console.log('\nhostList\n',hostList)

    // app keys for IX are stored with registrar
    db.getAppKeys( 'registrar', Object.keys(hostList), vault.keys, function ( e, keys ) {
      if (e) return next( e )
        var err
      if ( !keys[jws.payload.iss] || !keys[jws.payload.iss][jws.header.kid] ) {
        err = new Error( "Invalid Agent Request key or kid")
        err.code = "INVALID_REQUEST"
        return next( err )
      }
      if ( !jws.verify( keys[jws.payload.iss][jws.header.kid] ) ) {
        err = new Error( "Invalid Agent Request")
        err.code = "INVALID_REQUEST"
        return next( err )
      }
      db.getRsDIfromAsDI( ixToken.sub, ixToken.iss, Object.keys(hostList), function ( e, dis ) {
        if (e) return next( e )
        // make tokens for all resources, delete caller from list
        var tokens = {}
        delete hostList[jws.payload.iss]
        Object.keys( hostList ).forEach( function (rs) {
          var payload =
            { 'iss': config.host.ix
            , 'aud': rs
            , 'sub': dis[rs]
            , 'token.a2p3.org':
              { 'auth': jwe.payload['token.a2p3.org'].auth
              , 'app': jws.payload.iss
              , 'scopes': rsScopes[rs]
              }
            }
          tokens[rs] = token.create( payload, keys[rs].latest )
        })
        return res.send( { result: {'sub': dis[jws.payload.iss], 'tokens': tokens, 'redirects': redirects} } )
      })
    })
  })
}

// APIs called from AS agent registration web app
function agentList ( req, res, next ) {
    db.listAgents(  req.request['request.a2p3.org'].di
                  , req.request.iss
                  , function( e, handles ) {
                      if (e) return next (e)
                      return res.send({ result: { 'handles': handles } } )
                    } )
}

function agentAdd ( req, res, next ) {
    db.addAgent( req.request['request.a2p3.org'].di
                  , req.request.iss
                  , req.request['request.a2p3.org'].name
                  , function( e, token, handle ) {
                      if (e) return next (e)
                      return res.send({ result: { token: token, handle: handle } } )
                    } )
}

function agentDelete ( req, res, next ) {
  var di = req.request['request.a2p3.org'].di
    , as = req.request.iss
    , handle = req.request['request.a2p3.org'].handle
    db.deleteAgent( di, as, handle, function( e, AS ) {
      var details =
        { host: config.reverseHost[AS]  // hack because of how api.call works currently TBD :(
        , api: '/agent/delete'
        , credentials: vault.keys[AS].latest
        , payload:
          { iss: config.host.ix
          , aud: AS
          , 'request.a2p3.org': { 'handle': handle }
          }
        }
      api.call( details, function ( e ) {
        if (e) return next (e)
        return res.send( { result: { success: true } } )
    })
  })
}

exports.app = function() {
	var app = express()
  app.use(express.limit('10kb'))  // protect against large POST attack
  app.use(express.bodyParser())

  app.post('/di/create'
          , request.check( vault.keys, config.roles.enroll )
          , mw.a2p3Params( ['AS', 'RS', 'redirects'] )
          , diCreate
          )
  app.post('/exchange'
          , request.check( vault.keys, null, 'registrar' )  // registrar holds all IX App keys
          , mw.a2p3Params( ['token', 'request'] )
          , exchange
          )
  app.post('/agent/list'
          , request.check( vault.keys, config.roles.as )
          , agentList
          )
  app.post('/agent/add'
          , request.check( vault.keys, config.roles.as )
          , agentAdd
          )
  app.post('/agent/delete'
          , request.check( vault.keys, config.roles.as )
          , agentDelete
          )

  // show README.md as documentation
  app.get('/documentation', mw.md( __dirname+'/README.md' ) )

  // key integrity checking API
  app.post( '/key/check', mw.keyCheck( vault, config.host.ix ) )

  app.use( mw.errorHandler )

	return app
}
<|endoftext|>


<commit_before>var UI = {
	Menu: function Menu (type, options) {
		var aside = document.createElement("aside");
		aside.setAttribute("class", "UI-Menu");
		this.element = aside;
		switch (type) {
			case "standard":
				UI.defaults.menu.options.forEach(function (menuItem) {
					// create menu for app
				});
			break;
			case "custom":
				options.menuItems.forEach(function (menuItem) {
					// create menu for app
				});
			break;
		}

	},
	Sidebar: function Sidebar (type, options) {
		var aside = document.createElement("aside");
		aside.setAttribute("class", "UI-Sidebar");
		this.element = aside;
		switch (type) {
			case "standard":

			break;
			case "custom":

			break;
		}
	},
	Toolbar: function Toolbar (type, options) {
		var aside = document.createElement("aside");
		aside.setAttribute("class", "UI-Toolbar");
		this.element = aside;
		switch (type) {
			case "standard":

			break;
			case "custom":

			break;
		}
	},
	Frame: function Frame (type, options) {
		var main = document.createElement("main");
		main.setAttribute("class", "UI-Frame");
		this.element = main;
		switch (type) {
			case "text":

			break;
			case "codemirror":

			break;
			case "canvas":

			break;
			case "webgl":

			break;

		}
		this.animate = function () {

		};
		this.resize = function () {

		};
	},
	ContextMenu: function ContextMenu (options) {
		var div = document.createElement("div"),
			list = document.createElement("ul"),
			item = document.createElement("li");
		this.element = div;
		div.setAttribute("class", "UI-Context-Menu");
		item.innerHTML="Open";
		div.appendChild(list);
		list.appendChild(item);
		if (options.menuItems.length == 0) {
			options.menuItems = UI.defaults.contextMenu.options.menuItems;
		}
		options.menuItems.forEach(function (menuItem) {
			item = document.createElement("li");
			item.innerHTML = menuItem.name;
			item.setAttribute("data-resource", options.resource);
            item.setAttribute("data-name", options.name);
			item.addEventListener("click", function (evt) {
				menuItem.click(evt);
			}, true);
			list.appendChild(item);
		});
	},
	VectorEditor: function VectorEditor (options) {
		var div = document.createElement("div"),
			list = document.createElement("ul"),
			item = document.createElement("li");
		this.element = div;
		div.setAttribute("class", "UI-Vector-Editor");
		item.innerHTML="
"+options.name+"
";
		div.appendChild(list);
		list.appendChild(item);
		if (options.menuItems.length == 0) {
			options.menuItems = UI.defaults.contextMenu.options.menuItems;
		}
		options.menuItems.forEach(function (menuItem) {
			item = document.createElement("li");
			item.innerHTML = menuItem.name;
			item.addEventListener("click", function (evt) {
				menuItem.click(evt);
			}, false);
			list.appendChild(item);
		});
	},
	defaults: {
		menu: {
			options:[
				{"name": "App Icon", "icon":"/app/data/192/text.png", "click": function (e) {

				}},
				{"name": "New", "icon":"/app/data/plus.png", "click": function (e) {

				}},
				{"name": "Open", "icon":"/app/data/hidpi-box.png", "click": function (e) {

				}},
				{"name": "Save As", "icon":"/app/data/plus.png", "click": function (e) {

				}},
				{"name": "Options", "icon":"/app/data/circle-192.png", "click": function (e) {

				}},
			]
		},
		sidebar: {
			loadFiles: function () {

			}
		},
		contextMenu: {
			options: {
				menuItems: [
//					{"name": "Open", "icon":"/app/data/hidpi-box.png", "click": function (e) { }},
					{"name": "Edit", "icon":"/app/data/hidpi-box.png", "click": function (e) {
						var element = e.target,
							resource = element.getAttribute("data-resource"),
                            name = element.getAttribute("data-name");
						e.preventDefault();
						app.request("GET", resource+"?cache="+Date.now(), "", function (response) {
							app.openPane('edit', name, {"resource":app.cwd+"/"+name, "text": response});
						});
						return false;
					}},
					{"name": "Delete", "icon":"/app/data/hidpi-box.png", "click": function (e) {
						var element = e.target,
							resource = element.getAttribute("data-resource");
						deletePath(resource);
						// implement...

					}}
				]
			}
		}
	}

}

function AppIcon (name, index) {
	var element = document.createElement("div"),
		applet = app.applets[name],
		directionClass = "";
	if (!! index) {
		if (index == 1) {
			directionClass = " up";
		} else if (index == 4) {
			directionClass = " down";
		}
	}
    console.log("name", name);
	element.setAttribute("class", "UI-App-Icon" + directionClass);
	element.innerHTML = ""+applet.name+"";
	this.element = element;
	element.addEventListener("click", function () {
		app.openPane("app", applet.name, { applet: applet });
	}, true);
}

function DataPane (type, name, data) {
	console.log(data);
    var container = document.createElement("div"),
        titleString = "",
        title = document.createElement("h2"),
		titleEntry = document.createElement("input"),
        done = document.createElement("input"),
		entry = document.createElement("textarea"),
		showTitle = false,
        showEntry = false,
        showTitleEntry = false,
		close = document.createElement("input"),
        upload = null,
        editWidget = null,
		applet = null;
    container.setAttribute("class", "DataPane");
	titleEntry.setAttribute("type", "text");
	titleEntry.setAttribute("class", "titleEntry");
	done.setAttribute("type", "button");
	done.setAttribute("class", "done");
	done.setAttribute("value", "Done");
	done.addEventListener("click", this.getCloseMethod(true), true);
	close.setAttribute("type", "button");
	close.setAttribute("class", "close");
	close.setAttribute("value", "X");
	close.addEventListener("click", this.getCloseMethod(), true);
    if (!!data.applet) {
        // load applet form data.applet
        applet = new Applet(data.applet.name);
        applet.init();
		this.applet = applet;
    } else {
		switch (type) {
            case "touch":
                showEntry = true;
                showTitleEntry = true;
				titleEntry.setAttribute("style", "background-image: url(/app/data/touch.png)");
				titleEntry.setAttribute("class", "titleEntry icon");
            break;
			case "mkdir":
                showTitleEntry = true;
				titleEntry.setAttribute("style", "background-image: url(/app/data/mkdir.png);");
				titleEntry.setAttribute("class", "titleEntry icon");
            break;
            case "upload":
                upload = document.createElement("input");
                upload.setAttribute("type", "file");
                upload.setAttribute("multiple", "multiple");
				titleString = 'Upload Files';
				showTitle = true;
            break;
            case "view":
                titleString = "View File";
                showEntry = true;
            break;
            case "edit":
                titleString = '';
                showEntry = true;
                showTitleEntry = true;
				if (!! data.icon) {
					titleEntry.setAttribute("style", "background-image: url("+data.icon+");");
				} else {
					titleEntry.setAttribute("style", "background-image: url(/app/data/text2.png);");
				}
				titleEntry.setAttribute("class", "titleEntry icon");

            break;
        }
        title.innerHTML = titleString;
		if (showTitle) {
			container.appendChild(title);
		}
		if (typeof(data)=='undefined' || typeof(data.controls) == 'undefined' || data.controls == true) {
        	container.appendChild(done);
        	container.appendChild(close);
		}
		if (!!data && !! data.class) {
			container.setAttribute("class", "DataPane "+data.class);
		}
        if (!! upload) {
            container.appendChild(upload);
        }
        if (showTitleEntry) {
            container.appendChild(titleEntry);
            titleEntry.value = name;
        }
        if (showEntry) {
            container.appendChild(entry);
            if (!! data.text) {
                entry.value = data.text;
            }
        }
        // used to add "done" button here
    }
    this.type = type;
    this.name = name;
    this.data = data;
	this.done = done;
	this.entry = entry;
	this.paneTitle = title;
	this.titleEntry = titleEntry;
    this.uploadInput = upload;
    this.container = container;
}

DataPane.prototype.snap = function (direction) {
	this.container.setAttribute("class", "DataPane "+direction);
};

DataPane.prototype.close = function () {
    var panes = app.panes,
        pane = this;
    pane.container.parentNode.removeChild(pane.container);
    panes.splice(panes.indexOf(pane));
};

DataPane.prototype.getCloseMethod = function (save) {
    var panes = app.panes,
        pane = this;
    return function (evt) {
		var leaveOpen = false;
		if (save) {
			switch (pane.type) {
                case "touch":
                    saveText(pane);
                break;
                case "mkdir":
                    makeDirectory(pane);
                break;
                case "upload":
                    uploadFiles(pane);
					leaveOpen = true;
                break;
                case "edit":
        			saveText(pane);
                break;
            }
		}
		if (!leaveOpen) {
			pane.container.parentNode.removeChild(pane.container);
			panes.splice(panes.indexOf(pane));
		}
    };
};

function Card (name, resource, options) {
    var e = document.createElement("div"),
        i = null,
		up = false,
		isImage = false,
		contextMenu = true,
		link = document.createElement("span"),
		close = document.createElement("input"),
		edit = document.createElement("input");
	e.setAttribute("class", "Card");
	e.setAttribute("data-resource", resource);
	e.setAttribute("data-name", name);
	link.setAttribute("target", "_blank");
	link.innerHTML = name;
	link.setAttribute("href", resource);
	close.setAttribute("class", "close");
	close.setAttribute("type", "button");
	close.setAttribute("value", "X");
	close.addEventListener("click", function (event) {
		event.preventDefault();
		deletePath(app.cwd+"/"+name);
		return false;
	}, true);
	isImage = /(\.jpg|\.png|\.gif|\.jpeg|\.webp)/i.test(resource);
    if (isImage) {
		var thumb = resource.replace(/\s/g, "%20");
		if (!/(\.webp|\.gif)/i.test(resource)) { // don't thumbnail webp or gif
			var rPath = thumb.split("/");
			var thumb = rPath[rPath.length-1];
			rPath.splice(rPath.length-1, 1);
			rPath.push("DHThumbs");
			if (window.innerWidth < 641) { // temporary hack to use high-dpi on mobile
				rPath.push("1024");
			}
			rPath.push(thumb+".jpg");
			thumb = rPath.join("/");
		} else {
			thumb = resource;
		}
		e.setAttribute("style", "background-image: url('"+thumb+"');"); //resource+"');");
		e.setAttribute("class", "Card Image");
    } else {
		e.appendChild(link);
		if (/^(.*\/){0,1}[^\.]*.{1}$/.test(resource)) { // detect folders
			contextMenu = false; // disable context menu for now...
            link.setAttribute("href", "#");
			link.addEventListener("click", function (event) {
				event.preventDefault();
				openFolder(resource);
				return false;
			}, true);
			e.setAttribute("class", "Card Folder");
		} else {
			if (!contextMenu) {
				edit.setAttribute("class", "close edit");
				edit.setAttribute("type", "button");
				edit.setAttribute("value", "Edit");
				edit.addEventListener("click", function (event) {
					event.preventDefault();
					app.request("GET", resource+"?cache="+Date.now(), "", function (response) {
						app.openPane('edit', name, {"resource":app.cwd+"/"+name, "text": response});
					});
					return false;
				}, true);
				e.appendChild(edit);
			}
		}
    }
	//	Experimental..
	if (contextMenu) {
		var menu = new UI.ContextMenu({
			name: name,
			resource: resource,
			menuItems: [] // dummy menu items
		});
		if (isImage) {
			e.appendChild(menu.element);
		} else {
			link.appendChild(menu.element);
		}
	} else {
		e.appendChild(close);
	}

    for (i in options) {
        e.setAttribute(i, options[i]);
    }
    return e;
}

<commit_msg>Fixed bug preventing applets from launching...<commit_after>var UI = {
	Menu: function Menu (type, options) {
		var aside = document.createElement("aside");
		aside.setAttribute("class", "UI-Menu");
		this.element = aside;
		switch (type) {
			case "standard":
				UI.defaults.menu.options.forEach(function (menuItem) {
					// create menu for app
				});
			break;
			case "custom":
				options.menuItems.forEach(function (menuItem) {
					// create menu for app
				});
			break;
		}

	},
	Sidebar: function Sidebar (type, options) {
		var aside = document.createElement("aside");
		aside.setAttribute("class", "UI-Sidebar");
		this.element = aside;
		switch (type) {
			case "standard":

			break;
			case "custom":

			break;
		}
	},
	Toolbar: function Toolbar (type, options) {
		var aside = document.createElement("aside");
		aside.setAttribute("class", "UI-Toolbar");
		this.element = aside;
		switch (type) {
			case "standard":

			break;
			case "custom":

			break;
		}
	},
	Frame: function Frame (type, options) {
		var main = document.createElement("main");
		main.setAttribute("class", "UI-Frame");
		this.element = main;
		switch (type) {
			case "text":

			break;
			case "codemirror":

			break;
			case "canvas":

			break;
			case "webgl":

			break;

		}
		this.animate = function () {

		};
		this.resize = function () {

		};
	},
	ContextMenu: function ContextMenu (options) {
		var div = document.createElement("div"),
			list = document.createElement("ul"),
			item = document.createElement("li");
		this.element = div;
		div.setAttribute("class", "UI-Context-Menu");
		item.innerHTML="Open";
		div.appendChild(list);
		list.appendChild(item);
		if (options.menuItems.length == 0) {
			options.menuItems = UI.defaults.contextMenu.options.menuItems;
		}
		options.menuItems.forEach(function (menuItem) {
			item = document.createElement("li");
			item.innerHTML = menuItem.name;
			item.setAttribute("data-resource", options.resource);
            item.setAttribute("data-name", options.name);
			item.addEventListener("click", function (evt) {
				menuItem.click(evt);
			}, true);
			list.appendChild(item);
		});
	},
	VectorEditor: function VectorEditor (options) {
		var div = document.createElement("div"),
			list = document.createElement("ul"),
			item = document.createElement("li");
		this.element = div;
		div.setAttribute("class", "UI-Vector-Editor");
		item.innerHTML="
"+options.name+"
";
		div.appendChild(list);
		list.appendChild(item);
		if (options.menuItems.length == 0) {
			options.menuItems = UI.defaults.contextMenu.options.menuItems;
		}
		options.menuItems.forEach(function (menuItem) {
			item = document.createElement("li");
			item.innerHTML = menuItem.name;
			item.addEventListener("click", function (evt) {
				menuItem.click(evt);
			}, false);
			list.appendChild(item);
		});
	},
	defaults: {
		menu: {
			options:[
				{"name": "App Icon", "icon":"/app/data/192/text.png", "click": function (e) {

				}},
				{"name": "New", "icon":"/app/data/plus.png", "click": function (e) {

				}},
				{"name": "Open", "icon":"/app/data/hidpi-box.png", "click": function (e) {

				}},
				{"name": "Save As", "icon":"/app/data/plus.png", "click": function (e) {

				}},
				{"name": "Options", "icon":"/app/data/circle-192.png", "click": function (e) {

				}},
			]
		},
		sidebar: {
			loadFiles: function () {

			}
		},
		contextMenu: {
			options: {
				menuItems: [
//					{"name": "Open", "icon":"/app/data/hidpi-box.png", "click": function (e) { }},
					{"name": "Edit", "icon":"/app/data/hidpi-box.png", "click": function (e) {
						var element = e.target,
							resource = element.getAttribute("data-resource"),
                            name = element.getAttribute("data-name");
						e.preventDefault();
						app.request("GET", resource+"?cache="+Date.now(), "", function (response) {
							app.openPane('edit', name, {"resource":app.cwd+"/"+name, "text": response});
						});
						return false;
					}},
					{"name": "Delete", "icon":"/app/data/hidpi-box.png", "click": function (e) {
						var element = e.target,
							resource = element.getAttribute("data-resource");
						deletePath(resource);
						// implement...

					}}
				]
			}
		}
	}

}

function AppIcon (name, index) {
	var element = document.createElement("div"),
		applet = app.applets[name],
		directionClass = "";
	if (!! index) {
		if (index == 1) {
			directionClass = " up";
		} else if (index == 4) {
			directionClass = " down";
		}
	}
    console.log("name", name);
	element.setAttribute("class", "UI-App-Icon" + directionClass);
	element.innerHTML = ""+applet.name+"";
	this.element = element;
	element.addEventListener("click", function () {
		app.openPane("app", applet.name, { applet: {name: name} });
	}, true);
}

function DataPane (type, name, data) {
	console.log(data);
    var container = document.createElement("div"),
        titleString = "",
        title = document.createElement("h2"),
		titleEntry = document.createElement("input"),
        done = document.createElement("input"),
		entry = document.createElement("textarea"),
		showTitle = false,
        showEntry = false,
        showTitleEntry = false,
		close = document.createElement("input"),
        upload = null,
        editWidget = null,
		applet = null;
    container.setAttribute("class", "DataPane");
	titleEntry.setAttribute("type", "text");
	titleEntry.setAttribute("class", "titleEntry");
	done.setAttribute("type", "button");
	done.setAttribute("class", "done");
	done.setAttribute("value", "Done");
	done.addEventListener("click", this.getCloseMethod(true), true);
	close.setAttribute("type", "button");
	close.setAttribute("class", "close");
	close.setAttribute("value", "X");
	close.addEventListener("click", this.getCloseMethod(), true);
    if (!!data.applet) {
        // load applet form data.applet
        applet = new Applet(data.applet.name);
        applet.init();
		this.applet = applet;
    } else {
		switch (type) {
            case "touch":
                showEntry = true;
                showTitleEntry = true;
				titleEntry.setAttribute("style", "background-image: url(/app/data/touch.png)");
				titleEntry.setAttribute("class", "titleEntry icon");
            break;
			case "mkdir":
                showTitleEntry = true;
				titleEntry.setAttribute("style", "background-image: url(/app/data/mkdir.png);");
				titleEntry.setAttribute("class", "titleEntry icon");
            break;
            case "upload":
                upload = document.createElement("input");
                upload.setAttribute("type", "file");
                upload.setAttribute("multiple", "multiple");
				titleString = 'Upload Files';
				showTitle = true;
            break;
            case "view":
                titleString = "View File";
                showEntry = true;
            break;
            case "edit":
                titleString = '';
                showEntry = true;
                showTitleEntry = true;
				if (!! data.icon) {
					titleEntry.setAttribute("style", "background-image: url("+data.icon+");");
				} else {
					titleEntry.setAttribute("style", "background-image: url(/app/data/text2.png);");
				}
				titleEntry.setAttribute("class", "titleEntry icon");

            break;
        }
        title.innerHTML = titleString;
		if (showTitle) {
			container.appendChild(title);
		}
		if (typeof(data)=='undefined' || typeof(data.controls) == 'undefined' || data.controls == true) {
        	container.appendChild(done);
        	container.appendChild(close);
		}
		if (!!data && !! data.class) {
			container.setAttribute("class", "DataPane "+data.class);
		}
        if (!! upload) {
            container.appendChild(upload);
        }
        if (showTitleEntry) {
            container.appendChild(titleEntry);
            titleEntry.value = name;
        }
        if (showEntry) {
            container.appendChild(entry);
            if (!! data.text) {
                entry.value = data.text;
            }
        }
        // used to add "done" button here
    }
    this.type = type;
    this.name = name;
    this.data = data;
	this.done = done;
	this.entry = entry;
	this.paneTitle = title;
	this.titleEntry = titleEntry;
    this.uploadInput = upload;
    this.container = container;
}

DataPane.prototype.snap = function (direction) {
	this.container.setAttribute("class", "DataPane "+direction);
};

DataPane.prototype.close = function () {
    var panes = app.panes,
        pane = this;
    pane.container.parentNode.removeChild(pane.container);
    panes.splice(panes.indexOf(pane));
};

DataPane.prototype.getCloseMethod = function (save) {
    var panes = app.panes,
        pane = this;
    return function (evt) {
		var leaveOpen = false;
		if (save) {
			switch (pane.type) {
                case "touch":
                    saveText(pane);
                break;
                case "mkdir":
                    makeDirectory(pane);
                break;
                case "upload":
                    uploadFiles(pane);
					leaveOpen = true;
                break;
                case "edit":
        			saveText(pane);
                break;
            }
		}
		if (!leaveOpen) {
			pane.container.parentNode.removeChild(pane.container);
			panes.splice(panes.indexOf(pane));
		}
    };
};

function Card (name, resource, options) {
    var e = document.createElement("div"),
        i = null,
		up = false,
		isImage = false,
		contextMenu = true,
		link = document.createElement("span"),
		close = document.createElement("input"),
		edit = document.createElement("input");
	e.setAttribute("class", "Card");
	e.setAttribute("data-resource", resource);
	e.setAttribute("data-name", name);
	link.setAttribute("target", "_blank");
	link.innerHTML = name;
	link.setAttribute("href", resource);
	close.setAttribute("class", "close");
	close.setAttribute("type", "button");
	close.setAttribute("value", "X");
	close.addEventListener("click", function (event) {
		event.preventDefault();
		deletePath(app.cwd+"/"+name);
		return false;
	}, true);
	isImage = /(\.jpg|\.png|\.gif|\.jpeg|\.webp)/i.test(resource);
    if (isImage) {
		var thumb = resource.replace(/\s/g, "%20");
		if (!/(\.webp|\.gif)/i.test(resource)) { // don't thumbnail webp or gif
			var rPath = thumb.split("/");
			var thumb = rPath[rPath.length-1];
			rPath.splice(rPath.length-1, 1);
			rPath.push("DHThumbs");
			if (window.innerWidth < 641) { // temporary hack to use high-dpi on mobile
				rPath.push("1024");
			}
			rPath.push(thumb+".jpg");
			thumb = rPath.join("/");
		} else {
			thumb = resource;
		}
		e.setAttribute("style", "background-image: url('"+thumb+"');"); //resource+"');");
		e.setAttribute("class", "Card Image");
    } else {
		e.appendChild(link);
		if (/^(.*\/){0,1}[^\.]*.{1}$/.test(resource)) { // detect folders
			contextMenu = false; // disable context menu for now...
            link.setAttribute("href", "#");
			link.addEventListener("click", function (event) {
				event.preventDefault();
				openFolder(resource);
				return false;
			}, true);
			e.setAttribute("class", "Card Folder");
		} else {
			if (!contextMenu) {
				edit.setAttribute("class", "close edit");
				edit.setAttribute("type", "button");
				edit.setAttribute("value", "Edit");
				edit.addEventListener("click", function (event) {
					event.preventDefault();
					app.request("GET", resource+"?cache="+Date.now(), "", function (response) {
						app.openPane('edit', name, {"resource":app.cwd+"/"+name, "text": response});
					});
					return false;
				}, true);
				e.appendChild(edit);
			}
		}
    }
	//	Experimental..
	if (contextMenu) {
		var menu = new UI.ContextMenu({
			name: name,
			resource: resource,
			menuItems: [] // dummy menu items
		});
		if (isImage) {
			e.appendChild(menu.element);
		} else {
			link.appendChild(menu.element);
		}
	} else {
		e.appendChild(close);
	}

    for (i in options) {
        e.setAttribute(i, options[i]);
    }
    return e;
}

<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { View, StyleSheet, TextInput, Text, TouchableHighlight } from 'react-native';

export default class login extends React.Component {
	constructor(props) {
		super(props);
		this.state = { username: 'dummy-user'};
	}

	onPress() {
		console.log(this.state.username);
	}

	render() {
		return (
		<View style={styles.container}>
			<View style={styles.loginContainer}>
				<TextInput
					style={styles.input}
					value={this.state.username}
					onChangeText={(text) => this.setState({username: text})}
					placeholder={'Enter User Nickname'}
					maxLength={12}
					multiline={false}
				/>
				<TouchableHighlight
					style={styles.button}
					underlayColor={'#328FE6'}
					onPress={() => this.onPress()}
				>
					<Text style={styles.label}>LOGIN</Text>
				</TouchableHighlight>
			</View>
		</View>
		)
	}
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		justifyContent: 'center',
		alignItems: 'stretch',
		backgroundColor: '#000000'
	},
	loginContainer : {
		flex: 1,
		justifyContent: 'center',
		alignItems: 'center',
	},
	input: {
		width: 250,
		color: '#555555',
		padding: 10,
		height: 50,
		borderColor: '#32C5E6',
		borderWidth: 1,
		borderRadius: 4,
		alignSelf: 'center',
		backgroundColor: '#ffffff'
	},
	button: {
		justifyContent: 'center',
		alignItems: 'center',
		borderWidth: 1,
		borderRadius: 5,
		borderColor: '#328FE6',
		padding: 10,
		marginTop: 10,
		backgroundColor: '#32c5e6'
	},
	label: {
		width: 230,
		alignSelf: 'center',
		textAlign: 'center',
		fontSize: 20,
		fontWeight: '600',
		color: '#ffffff'
	}
});
<commit_msg>navigate from login to chat page<commit_after>import React, { Component } from 'react';
import { View, StyleSheet, TextInput, Text, TouchableHighlight } from 'react-native';

export default class login extends React.Component {
	constructor(props) {
		super(props);
		this.state = { username: 'dummy-user'};
	}

	onPress(navigation) {
		console.log(this.state.username);
		if(navigation){
			navigation('Chat');
		}
	}

	render() {
	    const { navigate } = this.props.navigation;
		return (
		<View style={styles.container}>
			<View style={styles.loginContainer}>
				<TextInput
					style={styles.input}
					value={this.state.username}
					onChangeText={(text) => this.setState({username: text})}
					placeholder={'Enter User Nickname'}
					maxLength={12}
					multiline={false}
				/>
				<TouchableHighlight
					style={styles.button}
					underlayColor={'#328FE6'}
					onPress={() => this.onPress(navigate)}
				>
					<Text style={styles.label}>LOGIN</Text>
				</TouchableHighlight>
			</View>
		</View>
		)
	}
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		justifyContent: 'center',
		alignItems: 'stretch',
		backgroundColor: '#000000'
	},
	loginContainer : {
		flex: 1,
		justifyContent: 'center',
		alignItems: 'center',
	},
	input: {
		width: 250,
		color: '#555555',
		padding: 10,
		height: 50,
		borderColor: '#32C5E6',
		borderWidth: 1,
		borderRadius: 4,
		alignSelf: 'center',
		backgroundColor: '#ffffff'
	},
	button: {
		justifyContent: 'center',
		alignItems: 'center',
		borderWidth: 1,
		borderRadius: 5,
		borderColor: '#328FE6',
		padding: 10,
		marginTop: 10,
		backgroundColor: '#32c5e6'
	},
	label: {
		width: 230,
		alignSelf: 'center',
		textAlign: 'center',
		fontSize: 20,
		fontWeight: '600',
		color: '#ffffff'
	}
});
<|endoftext|>


<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import Fuse from 'fuse.js';
// import ElasticLunr from 'elasticlunr';
import jmespath from 'jmespath';
import AppConfig from '../config';
import { Pro } from '../pro';

let currentQuery = '';
let nextQuery = '';
let caseSensitiveSearch = false; // TODO implement case sensitive search

// export type FileTypeGroups = 'images' | 'notes' | 'documents' | 'audio' | 'video' | 'archives';

export const FileTypeGroups = {
  any: [''],
  images: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif', 'tiff', 'ico', 'webp', 'psd'],
  notes: ['md', 'mdown', 'txt', 'html'],
  documents: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'odt', 'ods'],
  audio: ['ogg', 'mp3', 'wav', 'wave'],
  video: ['ogv', 'mp4', 'webm', 'm4v', 'mkv', 'avi'],
  archives: ['zip', 'rar', 'gz', 'tgz', 'arc', '7z'],
  bookmarks: ['url', 'lnk', 'sym'],
  ebooks: ['epub', 'mobi', 'azw', 'prc', 'azw1', 'azw3', 'azw4', 'azw8', 'azk'],
  folders: ['folders'],
  files: ['files'],
  untagged: ['untagged'],
};

export type SearchQuery = {
  textQuery?: string,
  fileTypes?: Array<string>,
  tagsAND?: Array<Tag>,
  tagsOR?: Array<Tag>,
  tagsNOT?: Array<Tag>,
  lastModified: string,
  fileSize: string,
  searchBoxing: 'location' | 'folder',
  currentDirectory: string,
  maxSearchResults?: number
};

// id, name, isFile, tags, extension, size, lmdt, path

// index[?size > '9000'  && contains(path, 'Apple')]
// index[?size < '2000' || isFile=='false']
// index[?contains(path, 'testfiles')].[tags[?title=='todo']
// index[].[tags[?type=='sidecar']]
// index[?tags[?type=='sidecar']]
// index[?tags[?title=='todo' || title=='high' ]

/*
  {
    "description": "",
    "extension": "jpg",
    "isFile": true,
    "lmdt": 1479679132839,
    "name": "Apple-Notes-Mac2 (copy).jpg",
    "path":
      "/home/na/TagSpaces/testfiles/sidecarTests/Apple-Notes-Mac2 (copy).jpg",
    "size": 84162,
    "tags": [
      {
        "style":
          "color: #333333 !important; background-color: #ffad46 !important;",
        "title": "todo",
        "type": "sidecar"
      }
    ],
    "thumbPath":
      "/home/na/TagSpaces/testfiles/sidecarTests/.ts/Apple-Notes-Mac2 (copy).jpg.jpg",
    "uuid": "76075fc2-4a2f-4e27-a188-d96fece032aa"
  },
*/

const fuseOptions = {
  shouldSort: true,
  threshold: 0.3,
  location: 0,
  distance: 100,
  tokenize: true,
  maxPatternLength: 32,
  minMatchCharLength: 1,
  keys: [{
    name: 'name',
    weight: 0.4
  }, {
    name: 'description',
    weight: 0.4
  }, {
    name: 'textContent',
    weight: 0.4
  }, {
    name: 'tags',
    weight: 0.3
  }, {
    name: 'path', // TODO ignore .ts folder, should not be in the index
    weight: 0.1
  }]
};

// Constructs the string for the JMESPath search query.  We first filter for all ORTags, then continuously pipe the result in to the
// filters for all AND and NOT tags. The Pro version can pipe the result into an additional filter for extension instead of tags.title.
// The final string for the tag search should look like this:
// index[? tags[? title=='ORTag1' || title=='ORTag2']] | [? tags[? title=='ANDTag1']] | [? tags[? title=='ANDTag2']] | [?!(tags[? title=='NOTTag1'])] | [?!(tags[? title=='NOTTag2'])] | extensionFilter
function constructjmespathQuery(searchQuery: SearchQuery): string {
  let jmespathQuery = '';
  const ANDtagsExist = searchQuery.tagsAND && searchQuery.tagsAND.length >= 1;
  const ORtagsExist = searchQuery.tagsOR && searchQuery.tagsOR.length >= 1;
  const NOTtagsExist = searchQuery.tagsNOT && searchQuery.tagsNOT.length >= 1;
  if (ANDtagsExist || ORtagsExist || NOTtagsExist) {
    jmespathQuery = '[?';

    if (ORtagsExist) {
      jmespathQuery += ' tags[? ';
      searchQuery.tagsOR.forEach(tag => {
        const cleanedTagTitle = tag.title.trim().toLowerCase();
        if (cleanedTagTitle.length > 0) {
          jmespathQuery += 'title==\'' + cleanedTagTitle + '\' || ';
        }
        return true;
      });
    }

    if (ANDtagsExist) {
      if (jmespathQuery.endsWith(' || ')) {
        jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 4) + ']] | [? tags[? ';
      } else {
        jmespathQuery += ' tags[? ';
      }
      searchQuery.tagsAND.forEach(tag => {
        const cleanedTagTitle = tag.title.trim().toLowerCase();
        if (cleanedTagTitle.length > 0) {
          jmespathQuery += 'title==\'' + cleanedTagTitle + '\']] | [? tags[? ';
        }
      });
    }

    if (NOTtagsExist) {
      if (jmespathQuery.endsWith(' || ')) {
        jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 4) + ']] | [?!(tags[? ';
      } else if (jmespathQuery.endsWith('[? tags[? ')) {
        jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 10) + '[?!(tags[? ';
      } else {
        jmespathQuery += '!(tags[? ';
      }
      searchQuery.tagsNOT.forEach(tag => {
        const cleanedTagTitle = tag.title.trim().toLowerCase();
        if (cleanedTagTitle.length > 0) {
          jmespathQuery += 'title==\'' + cleanedTagTitle + '\'])] | [?!(tags[? ';
        }
        return true;
      });
    }

    if (jmespathQuery.endsWith(' || ')) {
      jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 4) + ']]';
    } else if (jmespathQuery.endsWith(' | [? tags[? ')) {
      jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 13);
    } else if (jmespathQuery.endsWith(' | [?!(tags[? ')) {
      jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 14);
    }
  }

  const extensionQuery = Pro ? Pro.Search.constructFileTypeQuery(searchQuery) : '';
  if (extensionQuery.length > 0) {
    jmespathQuery = (jmespathQuery.length > 0) ? jmespathQuery + ' | ' + extensionQuery : extensionQuery;
  }

  if (jmespathQuery.length > 0) {
    return 'index' + jmespathQuery;
  }
  return jmespathQuery;
}

function transformTagsToLowercase(index: Array<Object>) {
  console.time('taglowercase');
  const resultIndex = index.map((entry) => {
    const tags = [...entry.tags];
    if (tags && tags.length) {
      tags.map((tag) => {
        tag.title = tag.title.toLowerCase();
        return tag;
      });
    }
    return {
      ...entry,
      tags
    };
  });
  console.timeEnd('taglowercase');
  return resultIndex;
}

export default class Search {
  static searchLocationIndex = (locationContent: Array<Object>, searchQuery: SearchQuery): Promise<Array<Object> | []> => new Promise((resolve) => {
    console.time('searchtime');
    const jmespathQuery = constructjmespathQuery(searchQuery);
    let jmespathResults;
    let results;
    let currentDirectoryEntries = transformTagsToLowercase(locationContent);

    if (searchQuery.searchBoxing === 'folder') {
      currentDirectoryEntries = locationContent.filter(entry => entry.path.startsWith(searchQuery.currentDirectory));
    }

    if (jmespathQuery) {
      console.log('jmespath query: ' + jmespathQuery);
      console.time('jmespath');
      jmespathResults = jmespath.search({ index: currentDirectoryEntries || locationContent }, jmespathQuery);
      console.timeEnd('jmespath');
      console.log('jmespath results: ' + jmespathResults.length);
    }

    // if (Pro && Pro.Search.filterIndex) {
    //   results = Pro.Search.filterIndex(results || locationContent, searchQuery);
    // }

    if (searchQuery.textQuery && searchQuery.textQuery.length > 1) {
      console.log('fuse query: ' + searchQuery.textQuery);
      console.time('fuse');
      const fuse = new Fuse(jmespathResults || currentDirectoryEntries || locationContent, fuseOptions);
      results = fuse.search(searchQuery.textQuery);
      console.timeEnd('fuse');
    } else {
      results = jmespathResults;
    }

    if (results) {
      console.log('Results found: ' + results.length);
      if (searchQuery.maxSearchResults && results.length >= searchQuery.maxSearchResults) {
        results = results.slice(0, searchQuery.maxSearchResults);
      }
      console.log('Results send: ' + results.length);
      console.timeEnd('searchtime');
      resolve(results);
      return true;
    }
    results = [];
    console.timeEnd('searchtime');
    resolve(results);
  });
}
<commit_msg>fixing work with correct index<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import Fuse from 'fuse.js';
// import ElasticLunr from 'elasticlunr';
import jmespath from 'jmespath';
import AppConfig from '../config';
import { Pro } from '../pro';

let currentQuery = '';
let nextQuery = '';
let caseSensitiveSearch = false; // TODO implement case sensitive search

// export type FileTypeGroups = 'images' | 'notes' | 'documents' | 'audio' | 'video' | 'archives';

export const FileTypeGroups = {
  any: [''],
  images: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif', 'tiff', 'ico', 'webp', 'psd'],
  notes: ['md', 'mdown', 'txt', 'html'],
  documents: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'odt', 'ods'],
  audio: ['ogg', 'mp3', 'wav', 'wave'],
  video: ['ogv', 'mp4', 'webm', 'm4v', 'mkv', 'avi'],
  archives: ['zip', 'rar', 'gz', 'tgz', 'arc', '7z'],
  bookmarks: ['url', 'lnk', 'sym'],
  ebooks: ['epub', 'mobi', 'azw', 'prc', 'azw1', 'azw3', 'azw4', 'azw8', 'azk'],
  folders: ['folders'],
  files: ['files'],
  untagged: ['untagged'],
};

export type SearchQuery = {
  textQuery?: string,
  fileTypes?: Array<string>,
  tagsAND?: Array<Tag>,
  tagsOR?: Array<Tag>,
  tagsNOT?: Array<Tag>,
  lastModified: string,
  fileSize: string,
  searchBoxing: 'location' | 'folder',
  currentDirectory: string,
  maxSearchResults?: number
};

// id, name, isFile, tags, extension, size, lmdt, path

// index[?size > '9000'  && contains(path, 'Apple')]
// index[?size < '2000' || isFile=='false']
// index[?contains(path, 'testfiles')].[tags[?title=='todo']
// index[].[tags[?type=='sidecar']]
// index[?tags[?type=='sidecar']]
// index[?tags[?title=='todo' || title=='high' ]

/*
  {
    "description": "",
    "extension": "jpg",
    "isFile": true,
    "lmdt": 1479679132839,
    "name": "Apple-Notes-Mac2 (copy).jpg",
    "path":
      "/home/na/TagSpaces/testfiles/sidecarTests/Apple-Notes-Mac2 (copy).jpg",
    "size": 84162,
    "tags": [
      {
        "style":
          "color: #333333 !important; background-color: #ffad46 !important;",
        "title": "todo",
        "type": "sidecar"
      }
    ],
    "thumbPath":
      "/home/na/TagSpaces/testfiles/sidecarTests/.ts/Apple-Notes-Mac2 (copy).jpg.jpg",
    "uuid": "76075fc2-4a2f-4e27-a188-d96fece032aa"
  },
*/

const fuseOptions = {
  shouldSort: true,
  threshold: 0.3,
  location: 0,
  distance: 100,
  tokenize: true,
  maxPatternLength: 32,
  minMatchCharLength: 1,
  keys: [{
    name: 'name',
    weight: 0.4
  }, {
    name: 'description',
    weight: 0.4
  }, {
    name: 'textContent',
    weight: 0.4
  }, {
    name: 'tags',
    weight: 0.3
  }, {
    name: 'path', // TODO ignore .ts folder, should not be in the index
    weight: 0.1
  }]
};

// Constructs the string for the JMESPath search query.  We first filter for all ORTags, then continuously pipe the result in to the
// filters for all AND and NOT tags. The Pro version can pipe the result into an additional filter for extension instead of tags.title.
// The final string for the tag search should look like this:
// index[? tags[? title=='ORTag1' || title=='ORTag2']] | [? tags[? title=='ANDTag1']] | [? tags[? title=='ANDTag2']] | [?!(tags[? title=='NOTTag1'])] | [?!(tags[? title=='NOTTag2'])] | extensionFilter
function constructjmespathQuery(searchQuery: SearchQuery): string {
  let jmespathQuery = '';
  const ANDtagsExist = searchQuery.tagsAND && searchQuery.tagsAND.length >= 1;
  const ORtagsExist = searchQuery.tagsOR && searchQuery.tagsOR.length >= 1;
  const NOTtagsExist = searchQuery.tagsNOT && searchQuery.tagsNOT.length >= 1;
  if (ANDtagsExist || ORtagsExist || NOTtagsExist) {
    jmespathQuery = '[?';

    if (ORtagsExist) {
      jmespathQuery += ' tags[? ';
      searchQuery.tagsOR.forEach(tag => {
        const cleanedTagTitle = tag.title.trim().toLowerCase();
        if (cleanedTagTitle.length > 0) {
          jmespathQuery += 'title==\'' + cleanedTagTitle + '\' || ';
        }
        return true;
      });
    }

    if (ANDtagsExist) {
      if (jmespathQuery.endsWith(' || ')) {
        jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 4) + ']] | [? tags[? ';
      } else {
        jmespathQuery += ' tags[? ';
      }
      searchQuery.tagsAND.forEach(tag => {
        const cleanedTagTitle = tag.title.trim().toLowerCase();
        if (cleanedTagTitle.length > 0) {
          jmespathQuery += 'title==\'' + cleanedTagTitle + '\']] | [? tags[? ';
        }
      });
    }

    if (NOTtagsExist) {
      if (jmespathQuery.endsWith(' || ')) {
        jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 4) + ']] | [?!(tags[? ';
      } else if (jmespathQuery.endsWith('[? tags[? ')) {
        jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 10) + '[?!(tags[? ';
      } else {
        jmespathQuery += '!(tags[? ';
      }
      searchQuery.tagsNOT.forEach(tag => {
        const cleanedTagTitle = tag.title.trim().toLowerCase();
        if (cleanedTagTitle.length > 0) {
          jmespathQuery += 'title==\'' + cleanedTagTitle + '\'])] | [?!(tags[? ';
        }
        return true;
      });
    }

    if (jmespathQuery.endsWith(' || ')) {
      jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 4) + ']]';
    } else if (jmespathQuery.endsWith(' | [? tags[? ')) {
      jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 13);
    } else if (jmespathQuery.endsWith(' | [?!(tags[? ')) {
      jmespathQuery = jmespathQuery.substring(0, jmespathQuery.length - 14);
    }
  }

  const extensionQuery = Pro ? Pro.Search.constructFileTypeQuery(searchQuery) : '';
  if (extensionQuery.length > 0) {
    jmespathQuery = (jmespathQuery.length > 0) ? jmespathQuery + ' | ' + extensionQuery : extensionQuery;
  }

  if (jmespathQuery.length > 0) {
    return 'index' + jmespathQuery;
  }
  return jmespathQuery;
}

function transformTagsToLowercase(index: Array<Object>) {
  console.time('taglowercase');
  const resultIndex = index.map((entry) => {
    const tags = [...entry.tags];
    if (tags && tags.length) {
      tags.map((tag) => {
        tag.title = tag.title.toLowerCase();
        return tag;
      });
    }
    return {
      ...entry,
      tags
    };
  });
  console.timeEnd('taglowercase');
  return resultIndex;
}

export default class Search {
  static searchLocationIndex = (locationContent: Array<Object>, searchQuery: SearchQuery): Promise<Array<Object> | []> => new Promise((resolve) => {
    console.time('searchtime');
    const jmespathQuery = constructjmespathQuery(searchQuery);
    let jmespathResults;
    let results;
    let currentDirectoryEntries = transformTagsToLowercase(locationContent);

    if (searchQuery.searchBoxing === 'folder') {
      currentDirectoryEntries = currentDirectoryEntries.filter(entry => entry.path.startsWith(searchQuery.currentDirectory));
    }

    if (jmespathQuery) {
      console.log('jmespath query: ' + jmespathQuery);
      console.time('jmespath');
      jmespathResults = jmespath.search({ index: currentDirectoryEntries }, jmespathQuery);
      console.timeEnd('jmespath');
      console.log('jmespath results: ' + jmespathResults.length);
    }

    // if (Pro && Pro.Search.filterIndex) {
    //   results = Pro.Search.filterIndex(currentDirectoryEntries);
    // }

    if (searchQuery.textQuery && searchQuery.textQuery.length > 1) {
      console.log('fuse query: ' + searchQuery.textQuery);
      console.time('fuse');
      const fuse = new Fuse(jmespathResults || currentDirectoryEntries, fuseOptions);
      results = fuse.search(searchQuery.textQuery);
      console.timeEnd('fuse');
    } else {
      results = jmespathResults;
    }

    if (results) {
      console.log('Results found: ' + results.length);
      if (searchQuery.maxSearchResults && results.length >= searchQuery.maxSearchResults) {
        results = results.slice(0, searchQuery.maxSearchResults);
      }
      console.log('Results send: ' + results.length);
      console.timeEnd('searchtime');
      resolve(results);
      return true;
    }
    results = [];
    console.timeEnd('searchtime');
    resolve(results);
  });
}
<|endoftext|>