<commit_before>'use strict'

var isNode = (typeof window === 'undefined');

var path, slash, child_process;

var requireHack = require;

if (isNode) {
  path = requireHack("path");
  slash = requireHack("slash");
  child_process = requireHack("child_process");
}

function send(receiver, message) {
  if (!receiver) return;

  if (isNode) {
    receiver.send(message);
  } else {
    receiver.postMessage(message);
  }
}

function createProcess(script, cwd) {
  if (isNode) {
    let p = child_process.fork(path.join(process.cwd(), script), {
      cwd: toAbsPath(cwd),
      // pipe stdout/stderr into this process
      silent: true
    });

    // handle stdout of child
    p.stdout.on("data", (data) => {
      process.stdout.write(`[Indexing] (${cwd}) ${data}`);
    });

    // handle stderr of child
    p.stderr.on("data", (data) => {
      process.stderr.write(`[Indexing] (${cwd}) ${data}`);
    });

    return p;
  }

  // in Browser:
  return new Worker(script);
}

// Object that holds workers for server subdirectories,
// e.g. '/lively4-core'
var workers = {};
var indexStatus = {};

var promiseCallbacks = {};
var workerReadyPromise;
// rootFolder is an absolute directory to where the server serves,
// e.g. where the lively4-core folder is found
var rootFolder = null;
var idxFileName = "index.l4idx";

var curMsgId = 0;

// *** Exported methods ***

export function setRootFolder(filepath) {
  // becomes e.g. "C:/felix/bla" on Windows
  if (isNode) {
    rootFolder = slash(path.resolve(filepath));
  } else {
    rootFolder = filepath;
  }
}

export function startWorker(subdir) {
  return new Promise((resolve, reject) => {
    if (!rootFolder) {
      throw new Error("startWorker: no root folder set");
    }

    if (workers[subdir]) {
      console.log("[Indexing] Worker already exists");
      resolve();
      return;
    }

    console.log("[Indexing] Starting new worker for " + subdir);
    try {
      let script = isNode ? "./lively4-search/server/lunr-node-search-worker.js" : "./src/external/lively4-search/client/lunr-es6-search-worker-wrapper.js";
      workers[subdir] = createProcess(script, subdir);
    } catch (err) {
      console.log("[Indexing] Error starting new worker for " + subdir + ": " + err);
      reject(err);
      return;
    }

    let messageHandler = function(m) {
      // web workers hide the sent message behind the data key
      if (!isNode) {
        m = m.data;
      }
      switch (m.type) {
        case "worker-ready":
          resolve();
          break;
        case "search-response":
          handleSearchResponse(m.msgId, m.message);
          break;
        case "init-response":
          handleInitResponse(m.msgId, m.message, subdir);
          break;
        case "index-status-response":
          handleIndexStatusResponse(m.msgId, m.message, subdir);
          break;
        case "error":
          console.log("[Indexing] Error (" + subdir + "): " + m.message);
          break;
        default:
          console.log("[Indexing] Unknown message:", m);
      }
    }

    if (isNode) {
      workers[subdir].on("message", messageHandler);
    } else {
      workers[subdir].onmessage = messageHandler;
    }
  });
}

function stopWorker(subdir) {
  console.log("stop", subdir);
  send(workers[subdir], {
    type: "stop"
  });

  delete workers[subdir];
  delete indexStatus[subdir];
}

export function createIndex(subdir, options) {
  return startWorker(subdir).then(() => {
    return new Promise((resolve, reject) => {
      if (!rootFolder) {
        console.log("[Indexing] Cannot create index, no root folder set");
        reject("Error: no root folder set");
        return;
      }

      if (indexStatus[subdir] === "ready") {
        console.log("[Indexing] Index already exists");
        resolve();
        return;
      }

      if (indexStatus[subdir] === "indexing") {
        reject();
        return;
      }

      var msgId = getNextMsgId();
      promiseCallbacks[msgId] = {
        resolve: resolve,
        reject: reject
      };

      send(workers[subdir], {
        type: "init",
        msgId: msgId,
        options: options
      });

      indexStatus[subdir] = "indexing";
    });
  });
}

export function removeIndex(subdir, options) {
  return startWorker(subdir).then(() => {
    send(workers[subdir], {
      type: "removeIndex"
    });

    stopWorker(subdir);

    delete indexStatus[subdir];
  });
}

export function setup(options) {
  return new Promise((resolve, reject) => {

    let fetchStatus =  () => {
      createIndex(options.path, options).then( () => {
        resolve();
        clearInterval(interval);
      }, () => {
        console.log("[Indexing] Waiting for index: " + options.name)
      });
    };

    fetchStatus();
    let interval = setInterval(fetchStatus, 5000);
  });
}

export function search(subdir, query) {
  if (!workers[subdir]) {
    console.log("[Indexing] Cannot search, no index created for " + subdir);
    return Promise.reject();
  }

  var msgId = getNextMsgId();
  promiseCallbacks[msgId] = {};

  var p = new Promise((resolve, reject) => {
    promiseCallbacks[msgId].resolve = resolve;
    promiseCallbacks[msgId].reject = reject;
  });

  send(workers[subdir], {
    type: "search",
    msgId: msgId,
    query: query
  });

  return p;
}

export function getStatus(subdir, options) {
  return startWorker(subdir).then(() => {
    if (indexStatus[subdir]) {
      return Promise.resolve(indexStatus[subdir]);
    }
    var msgId = getNextMsgId();
    promiseCallbacks[msgId] = {};

    var p = new Promise((resolve, reject) => {
      promiseCallbacks[msgId].resolve = resolve;
      promiseCallbacks[msgId].reject = reject;
    });

    send(workers[subdir], {
      type: "checkIndexFile",
      msgId: msgId,
      options: options
    });

    return p;
  });
}

export function find(pattern) {
  return search(this.path, pattern).then( (result) => {
    return result.map( (res) => {
      res.path = join(this.path, res.ref);
      res.type = "dropbox";
      return res;
    });
  });
}

function handleSearchResponse(msgId, result) {
  if (!promiseCallbacks[msgId]) {
    console.log(`[Indexing] No promise registered for ${msgId}`);
    return;
  }

  var resolve = promiseCallbacks[msgId].resolve;
  delete promiseCallbacks[msgId];

  resolve(result);
}

function handleInitResponse(msgId, result, subdir) {
  // Worker does not send a msgId, when it had to create the
  // requested index, because the msgId was already used to
  // signal that the index did not exist.
  if (!msgId) {
    // index has been created now,
    // so next call to createIndex will resolve immediately
    indexStatus[subdir] = "ready";
    return;
  }

  if (!promiseCallbacks[msgId]) {
    // this should never happen!
    console.log(`[Indexing] No promise registered for ${msgId}`);
    return;
  }

  var resolve = promiseCallbacks[msgId].resolve;
  var reject = promiseCallbacks[msgId].reject;
  delete promiseCallbacks[msgId];

  if (result === "ready") {
    // index was requested and just had to be loaded from disk
    indexStatus[subdir] = "ready";
    resolve();
  } else {
    // index was requested, does not exist and is being built now
    indexStatus[subdir] = "indexing";
    reject();
  }
}

function handleIndexStatusResponse(msgId, result, subdir) {
  if (!promiseCallbacks[msgId]) {
    // this should never happen!
    console.log(`[Indexing] No promise registered for ${msgId}`);
    return;
  }

  var resolve = promiseCallbacks[msgId].resolve;
  delete promiseCallbacks[msgId];

  resolve(result);

  if (result === "unavailable") {
    stopWorker(subdir);
  }
}

export function addFile(serverRelPath) {
  // e.g. serverRelPath == "/lively4-core/src/client/foo.js"
  serverRelPath = slash(serverRelPath);
  // find corresponding index
  var subdir = getIndexSubdir(serverRelPath);

  if (!subdir || !workers[subdir]) {
    console.log("[Indexing] Cannot add file, no index created for " + serverRelPath);
    return;
  }

  var absPath = toAbsPath(serverRelPath);
  send(workers[subdir], {
    type: "addFile",
    idxRelPath: toIdxRelPath(subdir, absPath)
  });
}

export function removeFile(serverRelPath) {
  // e.g. serverRelPath == "/lively4-core/src/client/foo.js"
  serverRelPath = slash(serverRelPath);
  // find corresponding index
  var subdir = getIndexSubdir(serverRelPath);
  if (!subdir) {
    // no index found for the serverRelPath, so dont remove it
    return;
  }
  if (!workers[subdir]) {
    console.log("[Indexing] Cannot remove file, no index created for " + subdir);
    return;
  }

  var absPath = toAbsPath(serverRelPath);
  send(workers[subdir], {
    type: "removeFile",
    idxRelPath: toIdxRelPath(subdir, absPath)
  });
}

// *** Helper methods ***

function getIndexSubdir(filepath) {
  return Object.keys(workers).find(function(subidx) {
    return filepath.indexOf(subidx + "/") == 0;
  });
}

function toAbsPath(serverRelPath) {
  return slash(path.join(rootFolder, serverRelPath));
}

function toIdxRelPath(subdir, absPath) {
  if (!rootFolder) {
    return;
  }

  // remove e.g. <rootFolder>/lively4-core/ (including the last slash, therefore + 1)
  return slash(absPath.slice(rootFolder.length + subdir.length + 1));
}

function getNextMsgId() {
  curMsgId++;
  return curMsgId;
}

function join(path1, path2) {
  if (path1[path1.length-1] != "/") {
    path1 += "/";
  }
  if (path2[0] == "/") {
    path2 = path2.slice(1, path2.length);
  }
  return path1 + path2;
}
<commit_msg>adjust worker path<commit_after>'use strict'

var isNode = (typeof window === 'undefined');

var path, slash, child_process;

var requireHack = require;

if (isNode) {
  path = requireHack("path");
  slash = requireHack("slash");
  child_process = requireHack("child_process");
}

function send(receiver, message) {
  if (!receiver) return;

  if (isNode) {
    receiver.send(message);
  } else {
    receiver.postMessage(message);
  }
}

function createProcess(script, cwd) {
  if (isNode) {
    let p = child_process.fork(path.join(process.cwd(), script), {
      cwd: toAbsPath(cwd),
      // pipe stdout/stderr into this process
      silent: true
    });

    // handle stdout of child
    p.stdout.on("data", (data) => {
      process.stdout.write(`[Indexing] (${cwd}) ${data}`);
    });

    // handle stderr of child
    p.stderr.on("data", (data) => {
      process.stderr.write(`[Indexing] (${cwd}) ${data}`);
    });

    return p;
  }

  // in Browser:
  return new Worker(script);
}

// Object that holds workers for server subdirectories,
// e.g. '/lively4-core'
var workers = {};
var indexStatus = {};

var promiseCallbacks = {};
var workerReadyPromise;
// rootFolder is an absolute directory to where the server serves,
// e.g. where the lively4-core folder is found
var rootFolder = null;
var idxFileName = "index.l4idx";

var curMsgId = 0;

// *** Exported methods ***

export function setRootFolder(filepath) {
  // becomes e.g. "C:/felix/bla" on Windows
  if (isNode) {
    rootFolder = slash(path.resolve(filepath));
  } else {
    rootFolder = filepath;
  }
}

export function startWorker(subdir) {
  return new Promise((resolve, reject) => {
    if (!rootFolder) {
      throw new Error("startWorker: no root folder set");
    }

    if (workers[subdir]) {
      console.log("[Indexing] Worker already exists");
      resolve();
      return;
    }

    console.log("[Indexing] Starting new worker for " + subdir);
    try {
      let script = isNode ? "./dist/lively4-search/server/lunr-node-search-worker.js" : "./src/external/lively4-search/client/lunr-es6-search-worker-wrapper.js";
      workers[subdir] = createProcess(script, subdir);
    } catch (err) {
      console.log("[Indexing] Error starting new worker for " + subdir + ": " + err);
      reject(err);
      return;
    }

    let messageHandler = function(m) {
      // web workers hide the sent message behind the data key
      if (!isNode) {
        m = m.data;
      }
      switch (m.type) {
        case "worker-ready":
          resolve();
          break;
        case "search-response":
          handleSearchResponse(m.msgId, m.message);
          break;
        case "init-response":
          handleInitResponse(m.msgId, m.message, subdir);
          break;
        case "index-status-response":
          handleIndexStatusResponse(m.msgId, m.message, subdir);
          break;
        case "error":
          console.log("[Indexing] Error (" + subdir + "): " + m.message);
          break;
        default:
          console.log("[Indexing] Unknown message:", m);
      }
    }

    if (isNode) {
      workers[subdir].on("message", messageHandler);
    } else {
      workers[subdir].onmessage = messageHandler;
    }
  });
}

function stopWorker(subdir) {
  console.log("stop", subdir);
  send(workers[subdir], {
    type: "stop"
  });

  delete workers[subdir];
  delete indexStatus[subdir];
}

export function createIndex(subdir, options) {
  return startWorker(subdir).then(() => {
    return new Promise((resolve, reject) => {
      if (!rootFolder) {
        console.log("[Indexing] Cannot create index, no root folder set");
        reject("Error: no root folder set");
        return;
      }

      if (indexStatus[subdir] === "ready") {
        console.log("[Indexing] Index already exists");
        resolve();
        return;
      }

      if (indexStatus[subdir] === "indexing") {
        reject();
        return;
      }

      var msgId = getNextMsgId();
      promiseCallbacks[msgId] = {
        resolve: resolve,
        reject: reject
      };

      send(workers[subdir], {
        type: "init",
        msgId: msgId,
        options: options
      });

      indexStatus[subdir] = "indexing";
    });
  });
}

export function removeIndex(subdir, options) {
  return startWorker(subdir).then(() => {
    send(workers[subdir], {
      type: "removeIndex"
    });

    stopWorker(subdir);

    delete indexStatus[subdir];
  });
}

export function setup(options) {
  return new Promise((resolve, reject) => {

    let fetchStatus =  () => {
      createIndex(options.path, options).then( () => {
        resolve();
        clearInterval(interval);
      }, () => {
        console.log("[Indexing] Waiting for index: " + options.name)
      });
    };

    fetchStatus();
    let interval = setInterval(fetchStatus, 5000);
  });
}

export function search(subdir, query) {
  if (!workers[subdir]) {
    console.log("[Indexing] Cannot search, no index created for " + subdir);
    return Promise.reject();
  }

  var msgId = getNextMsgId();
  promiseCallbacks[msgId] = {};

  var p = new Promise((resolve, reject) => {
    promiseCallbacks[msgId].resolve = resolve;
    promiseCallbacks[msgId].reject = reject;
  });

  send(workers[subdir], {
    type: "search",
    msgId: msgId,
    query: query
  });

  return p;
}

export function getStatus(subdir, options) {
  return startWorker(subdir).then(() => {
    if (indexStatus[subdir]) {
      return Promise.resolve(indexStatus[subdir]);
    }
    var msgId = getNextMsgId();
    promiseCallbacks[msgId] = {};

    var p = new Promise((resolve, reject) => {
      promiseCallbacks[msgId].resolve = resolve;
      promiseCallbacks[msgId].reject = reject;
    });

    send(workers[subdir], {
      type: "checkIndexFile",
      msgId: msgId,
      options: options
    });

    return p;
  });
}

export function find(pattern) {
  return search(this.path, pattern).then( (result) => {
    return result.map( (res) => {
      res.path = join(this.path, res.ref);
      res.type = "dropbox";
      return res;
    });
  });
}

function handleSearchResponse(msgId, result) {
  if (!promiseCallbacks[msgId]) {
    console.log(`[Indexing] No promise registered for ${msgId}`);
    return;
  }

  var resolve = promiseCallbacks[msgId].resolve;
  delete promiseCallbacks[msgId];

  resolve(result);
}

function handleInitResponse(msgId, result, subdir) {
  // Worker does not send a msgId, when it had to create the
  // requested index, because the msgId was already used to
  // signal that the index did not exist.
  if (!msgId) {
    // index has been created now,
    // so next call to createIndex will resolve immediately
    indexStatus[subdir] = "ready";
    return;
  }

  if (!promiseCallbacks[msgId]) {
    // this should never happen!
    console.log(`[Indexing] No promise registered for ${msgId}`);
    return;
  }

  var resolve = promiseCallbacks[msgId].resolve;
  var reject = promiseCallbacks[msgId].reject;
  delete promiseCallbacks[msgId];

  if (result === "ready") {
    // index was requested and just had to be loaded from disk
    indexStatus[subdir] = "ready";
    resolve();
  } else {
    // index was requested, does not exist and is being built now
    indexStatus[subdir] = "indexing";
    reject();
  }
}

function handleIndexStatusResponse(msgId, result, subdir) {
  if (!promiseCallbacks[msgId]) {
    // this should never happen!
    console.log(`[Indexing] No promise registered for ${msgId}`);
    return;
  }

  var resolve = promiseCallbacks[msgId].resolve;
  delete promiseCallbacks[msgId];

  resolve(result);

  if (result === "unavailable") {
    stopWorker(subdir);
  }
}

export function addFile(serverRelPath) {
  // e.g. serverRelPath == "/lively4-core/src/client/foo.js"
  serverRelPath = slash(serverRelPath);
  // find corresponding index
  var subdir = getIndexSubdir(serverRelPath);

  if (!subdir || !workers[subdir]) {
    console.log("[Indexing] Cannot add file, no index created for " + serverRelPath);
    return;
  }

  var absPath = toAbsPath(serverRelPath);
  send(workers[subdir], {
    type: "addFile",
    idxRelPath: toIdxRelPath(subdir, absPath)
  });
}

export function removeFile(serverRelPath) {
  // e.g. serverRelPath == "/lively4-core/src/client/foo.js"
  serverRelPath = slash(serverRelPath);
  // find corresponding index
  var subdir = getIndexSubdir(serverRelPath);
  if (!subdir) {
    // no index found for the serverRelPath, so dont remove it
    return;
  }
  if (!workers[subdir]) {
    console.log("[Indexing] Cannot remove file, no index created for " + subdir);
    return;
  }

  var absPath = toAbsPath(serverRelPath);
  send(workers[subdir], {
    type: "removeFile",
    idxRelPath: toIdxRelPath(subdir, absPath)
  });
}

// *** Helper methods ***

function getIndexSubdir(filepath) {
  return Object.keys(workers).find(function(subidx) {
    return filepath.indexOf(subidx + "/") == 0;
  });
}

function toAbsPath(serverRelPath) {
  return slash(path.join(rootFolder, serverRelPath));
}

function toIdxRelPath(subdir, absPath) {
  if (!rootFolder) {
    return;
  }

  // remove e.g. <rootFolder>/lively4-core/ (including the last slash, therefore + 1)
  return slash(absPath.slice(rootFolder.length + subdir.length + 1));
}

function getNextMsgId() {
  curMsgId++;
  return curMsgId;
}

function join(path1, path2) {
  if (path1[path1.length-1] != "/") {
    path1 += "/";
  }
  if (path2[0] == "/") {
    path2 = path2.slice(1, path2.length);
  }
  return path1 + path2;
}
<|endoftext|>


<commit_before>/*global console, window*/

(function($) {

    "use strict";

    /**
     * Microsoft safe helper to spit out our little diagnostics information
     *
     * @ignore
     */
    function log(msg) {
        if(console && console.log) {
            console.log(msg);
        }
    }

    /**
     * jQuery.find() workaround for IE7
     *
     * If your selector is an pure tag id (#foo) IE7 finds nothing
     * if you do jQuery.find() in a specific jQuery context.
     *
     * This workaround makes a (false) assumptions
     * ids are always unique across the page.
     *
     * @ignore
     *
     * @param  {jQuery} context  jQuery context where we look child elements
     * @param  {String} selector selector as a string
     * @return {jQuery}          context.find() result
     */
    function safeFind(context, selector) {

        if(selector[0] == "#") {

            // Pseudo-check that this is a simple id selector
            // and not a complex jQuery selector
            if(selector.indexOf(" ") < 0) {
                return $(selector);
            }
        }

        return context.find(selector);
    }

    /**
     * Sample configuration object which can be passed to {@link jQuery.deps#enable}
     *
     * @class Configuration
     */
    var configExample = {

        /**
         * @cfg show Callback function show(elem) for showing elements
         * @type {Function}
         */
        show : null,

        /**
         * @cfg hide Callback function hide(elem) for hiding elements
         * @type {Function}
         */
        hide : null,

        /**
         * @cfg log Write console.log() output of rule applying
         * @type {Boolean}
         */
        log : false,

        /**
         * @cfg checkTargets When ruleset is enabled, check that all controllers and controls referred by ruleset exist on the page.
         *
         * @default true
         *
         * @type {Boolean}
         */
        checkTargets : true

    };

    /**
     * Define one field inter-dependency rule.
     *
     * When condition is true then this input and all
     * its children rules' inputs are visible.
     *
     * Possible condition strings:
     *
     *  * **==**  Widget value must be equal to given value
     *
     *  * **any** Widget value must be any of the values in the given value array
     *
     *  * **non-any** Widget value must not be any of the values in the given value array
     *
     *  * **!=** Widget value must not be qual to given value
     *
     *  * **()** Call value as a function(context, controller, ourWidgetValue) and if it's true then the condition is true
     *
     *  * **null** This input does not have any sub-conditions
     *
     *
     *
     */
    function Rule(controller, condition, value) {
        this.init(controller, condition, value);
    }

    $.extend(Rule.prototype, {

        /**
         * @method constructor
         *
         * @param {String} controller     jQuery expression to match the `<input>`   source
         *
         * @param {String} condition What input value must be that {@link Rule the rule takes effective}.
         *
         * @param value Matching value of **controller** when widgets become visible
         *
         */
        init : function(controller, condition, value) {
            this.controller = controller;

            this.condition = condition;

            this.value = value;

            // Child rules
            this.rules = [];

            // Controls shown/hidden by this rule
            this.controls = [];
        },

        /**
         * Evaluation engine
         *
         * @param  {String} condition Any of given conditions in Rule class description
         * @param  {Object} val1      The base value we compare against
         * @param  {Object} val2      Something we got out of input
         * @return {Boolean}          true or false
         */
        evalCondition : function(control, context, condition, val1, val2) {

           if(this.condition == "==") {
                return val1 == val2;
            } else if(condition == "!=") {
                return val1 != val2;
            } else if(condition == "()") {
                return val1(context, control, val2);
            } else if(condition == "any") {
                return $.inArray(val2, val1);
            } else if(condition == "not-any") {
                return !$.inArray(val2, val1);
            } else {
                throw new Error("Unknown condition:" + condition);
            }

        },

        /**
         * Evaluate the condition of this rule in given jQuery context.
         *
         * The widget value is extracted using getControlValue()
         *
         * @param {jQuery} context The jQuery selection in which this rule is evaluated.
         *
         */
        checkCondition : function(context, cfg) {

            // We do not have condition set, we are always true
            if(!this.condition) {
                return true;
            }

            var control = context.find(this.controller);
            if(control.size() === 0 && cfg.log) {
                log("Evaling condition: Could not find controller input " + this.controller);
            }

            var val = this.getControlValue(context, control);
            if(cfg.log && val === undefined) {
                log("Evaling condition: Could not exctract value from input " + this.controller);
            }

            if(val === undefined) {
                return false;
            }

            val = this.normalizeValue(control, this.value, val);

            return this.evalCondition(context, this.control, this.condition, this.value, val);
        },

        /**
         * Make sure that what we read from input field is comparable against Javascript primitives
         *
         */
        normalizeValue : function(control, baseValue, val) {

            if(typeof baseValue == "number") {
                // Make sure we compare numbers against numbers
                return parseFloat(val);
            }

            return val;
        },

        /**
         * Read value from a diffent HTML controls.
         *
         * Handle, text, checkbox, select.
         *
         */
        getControlValue : function(context, control) {

            // Handle checkboxes
            if(control.attr("type") == "checkbox") {
                return control.is(":checked");
            }

            return control.val();
        },

        /**
         * Create a sub-rule.
         *
         * Example:
         *
         *      var masterSwitch = ruleset.createRule("#mechanicalThrombectomyDevice")
         *      var twoAttempts = masterSwitch.createRule("#numberOfAttempts", "==", 2);
         *
         * @return Rule instance
         */
        createRule : function(controller, condition, value) {
            var rule = new Rule(controller, condition, value);
            this.rules.push(rule);
            return rule;
        },

        /**
         * Include a control in this rule.
         *
         * @param  {String} input     jQuery expression to match the input within ruleset context
         */
        include : function(input) {

            if(!input) {
                throw new Error("Must give an input selector");
            }

            this.controls.push(input);
        },

        /**
         * Apply this rule to all controls in the given context
         *
         * @param  {jQuery} context  jQuery selection within we operate
         * @param  {Object} cfg      {@link Configuration} object or undefined
         * @param  {Object} enforced Recursive rule enforcer: undefined to evaluate condition, true show always, false hide always
         *
         */
        applyRule : function(context, cfg, enforced) {

            var result;

            if(enforced === undefined) {
                result = this.checkCondition(context, cfg);
            } else {
                result = enforced;
            }

            if(cfg.log) {
                log("Applying rule on " + this.controller + "==" + this.value + " enforced:" + enforced + " result:" + result);
            }

            if(cfg.log && !this.controls.length) {
                log("Zero length controls slipped through");
            }

            // Get show/hide callback functions

            var show = cfg.show || function(control) {
                control.show();
            };

            var hide = cfg.show || function(control) {
                control.hide();
            };

            // Resolve controls from ids to jQuery selections
            // we are controlling in this context
            var controls = $.map(this.controls, function(elem, idx) {
                var control = context.find(elem);
                if(cfg.log && control.size() === 0) {
                    log("Could not find element:" + elem);
                }
                return control;
            });

            if(result) {

                $(controls).each(function() {

                    // Some friendly debug info
                    if(cfg.log && $(this).size() === 0) {
                        log("Control selection is empty when showing");
                        log(this);
                    }

                    show(this);
                });

                // Evaluate all child rules
                $(this.rules).each(function() {
                    this.applyRule(context, cfg);
                });

            } else {

                $(controls).each(function() {

                    // Some friendly debug info
                    if(cfg.log && $(this).size() === 0) {
                        log("Control selection is empty when hiding:");
                        log(this);
                    }

                    hide(this);
                });

                // Supress all child rules
                $(this.rules).each(function() {
                    this.applyRule(context, cfg, false);
                });
            }
        }
    });

    /**
     * A class which manages interdependenice rules.
     */
    function Ruleset() {

        // Hold a tree of rules
        this.rules = [];
    }

    $.extend(Ruleset.prototype, {

        /**
         * Add a new rule into this ruletset.
         *
         * See  {@link Rule} about the contstruction parameters.
         * @return {Rule}
         */
        createRule : function(controller, condition, value) {
            var rule = new Rule(controller, condition, value);
            this.rules.push(rule);
            return rule;
        },

        /**
         * Apply these rules on an element.
         *
         * @param {jQuery} context Selection we are dealing with
         *
         * @param cfg {@link Configuration} object or undefined.
         */
        applyRules: function(context, cfg) {
            var i;

            cfg = cfg || {};

            if(cfg.log) {
                log("Starting evaluation ruleset of " + this.rules.length + " rules");
            }

            for(i=0; i<this.rules.length; i++) {
                this.rules[i].applyRule(context, cfg);
            }
        },

        /**
         * Walk all rules and sub-rules in this ruleset
         * @param  {Function} callback(rule)
         *
         * @return {Array} Rules as depth-first searched
         */
        walk : function() {

            var rules = [];

            function descent(rule) {

                rules.push(rule);

                $(rule.children).each(function() {
                    descent(this);
                });
            }

            $(this.rules).each(function() {
                descent(this);
            });

            return rules;
        },

        /**
         * Check that all controllers and controls referred in ruleset exist.
         *
         * Throws an Error if any of them are missing.
         *
         * @param {jQuery} context jQuery selection of items
         *
         * @param  {Configuration} cfg
         */
        checkTargets : function(context, cfg) {

            var controls = 0;
            var rules = this.walk();

            $(rules).each(function() {

                if(context.find(this.controller).size() === 0) {
                    throw new Error("Rule's controller does not exist:" + this.controller);
                }

                if(this.controls.length === 0) {
                    throw new Error("Rule has no controls:" + this);
                }

                $(this.controls).each(function() {

                    if(safeFind(context, this) === 0) {
                        throw new Error("Rule's target control " + this + " does not exist in context " + context.get(0));
                    }

                    controls++;
                });

            });

            if(cfg.log) {
                log("Controller check ok, rules count " + rules.length + " controls count " + controls);
            }

        },

        /**
         * Make this ruleset effective on the whole page.
         *
         * Set event handler on **window.document** to catch all input events
         * and apply those events to defined rules.
         *
         * @param  {Configuration} cfg {@link Configuration} object or undefined
         *
         */
        install : function(cfg) {
            $.deps.enable($(document.body), this, cfg);
        }

    });

    /**
     * jQuery interdependencie plug-in
     *
     * @class jQuery.deps
     *
     */
    var deps = {

        /**
         * Create a new Ruleset instance.
         *
         * Example:
         *
         *      $(document).ready(function() {
         *           // Start creating a new ruleset
         *           var ruleset = $.deps.createRuleset();
         *
         *
         * @return {Ruleset}
         */
        createRuleset : function() {
            return new Ruleset();
        },

        /**
         * Enable ruleset on a specific jQuery selection.
         *
         * Checks the existince of all ruleset controllers and controls
         * by default (see config).
         *
         * See possible IE event bubbling problems: http://stackoverflow.com/q/265074/315168
         *
         * @param  {Object} selection jQuery selection in where we monitor all change events. All controls and controllers must exist within this selection.
         * @param  {Ruleset} ruleset
         * @param  {Configuration} cfg
         */
        enable : function(selection, ruleset, cfg) {

            cfg = cfg || {};

            if(cfg.checkTargets || cfg.checkTargets === undefined) {
                ruleset.checkTargets(selection, cfg);
            }

            var self = this;

            if(cfg.log) {
                log("Enabling dependency change monitoring on " + selection.get(0));
            }

            // Namespace our handler to avoid conflicts
            //
            var val = selection.live("change.deps", function() {
                ruleset.applyRules(selection, cfg);
            });

            ruleset.applyRules(selection, cfg);

            return val;
        }
    };

    $.deps = deps;

})(jQuery);<commit_msg>Corrected Use of jquery.inArray()<commit_after>/*global console, window*/

(function($) {

    "use strict";

    /**
     * Microsoft safe helper to spit out our little diagnostics information
     *
     * @ignore
     */
    function log(msg) {
        if(console && console.log) {
            console.log(msg);
        }
    }

    /**
     * jQuery.find() workaround for IE7
     *
     * If your selector is an pure tag id (#foo) IE7 finds nothing
     * if you do jQuery.find() in a specific jQuery context.
     *
     * This workaround makes a (false) assumptions
     * ids are always unique across the page.
     *
     * @ignore
     *
     * @param  {jQuery} context  jQuery context where we look child elements
     * @param  {String} selector selector as a string
     * @return {jQuery}          context.find() result
     */
    function safeFind(context, selector) {

        if(selector[0] == "#") {

            // Pseudo-check that this is a simple id selector
            // and not a complex jQuery selector
            if(selector.indexOf(" ") < 0) {
                return $(selector);
            }
        }

        return context.find(selector);
    }

    /**
     * Sample configuration object which can be passed to {@link jQuery.deps#enable}
     *
     * @class Configuration
     */
    var configExample = {

        /**
         * @cfg show Callback function show(elem) for showing elements
         * @type {Function}
         */
        show : null,

        /**
         * @cfg hide Callback function hide(elem) for hiding elements
         * @type {Function}
         */
        hide : null,

        /**
         * @cfg log Write console.log() output of rule applying
         * @type {Boolean}
         */
        log : false,

        /**
         * @cfg checkTargets When ruleset is enabled, check that all controllers and controls referred by ruleset exist on the page.
         *
         * @default true
         *
         * @type {Boolean}
         */
        checkTargets : true

    };

    /**
     * Define one field inter-dependency rule.
     *
     * When condition is true then this input and all
     * its children rules' inputs are visible.
     *
     * Possible condition strings:
     *
     *  * **==**  Widget value must be equal to given value
     *
     *  * **any** Widget value must be any of the values in the given value array
     *
     *  * **non-any** Widget value must not be any of the values in the given value array
     *
     *  * **!=** Widget value must not be qual to given value
     *
     *  * **()** Call value as a function(context, controller, ourWidgetValue) and if it's true then the condition is true
     *
     *  * **null** This input does not have any sub-conditions
     *
     *
     *
     */
    function Rule(controller, condition, value) {
        this.init(controller, condition, value);
    }

    $.extend(Rule.prototype, {

        /**
         * @method constructor
         *
         * @param {String} controller     jQuery expression to match the `<input>`   source
         *
         * @param {String} condition What input value must be that {@link Rule the rule takes effective}.
         *
         * @param value Matching value of **controller** when widgets become visible
         *
         */
        init : function(controller, condition, value) {
            this.controller = controller;

            this.condition = condition;

            this.value = value;

            // Child rules
            this.rules = [];

            // Controls shown/hidden by this rule
            this.controls = [];
        },

        /**
         * Evaluation engine
         *
         * @param  {String} condition Any of given conditions in Rule class description
         * @param  {Object} val1      The base value we compare against
         * @param  {Object} val2      Something we got out of input
         * @return {Boolean}          true or false
         */
        evalCondition : function(control, context, condition, val1, val2) {

           if(this.condition == "==") {
                return val1 == val2;
            } else if(condition == "!=") {
                return val1 != val2;
            } else if(condition == "()") {
                return val1(context, control, val2);
            } else if(condition == "any") {
                return $.inArray(val2, val1) > -1;
            } else if(condition == "not-any") {
                return $.inArray(val2, val1) == -1;
            } else {
                throw new Error("Unknown condition:" + condition);
            }

        },

        /**
         * Evaluate the condition of this rule in given jQuery context.
         *
         * The widget value is extracted using getControlValue()
         *
         * @param {jQuery} context The jQuery selection in which this rule is evaluated.
         *
         */
        checkCondition : function(context, cfg) {

            // We do not have condition set, we are always true
            if(!this.condition) {
                return true;
            }

            var control = context.find(this.controller);
            if(control.size() === 0 && cfg.log) {
                log("Evaling condition: Could not find controller input " + this.controller);
            }

            var val = this.getControlValue(context, control);
            if(cfg.log && val === undefined) {
                log("Evaling condition: Could not exctract value from input " + this.controller);
            }

            if(val === undefined) {
                return false;
            }

            val = this.normalizeValue(control, this.value, val);

            return this.evalCondition(context, this.control, this.condition, this.value, val);
        },

        /**
         * Make sure that what we read from input field is comparable against Javascript primitives
         *
         */
        normalizeValue : function(control, baseValue, val) {

            if(typeof baseValue == "number") {
                // Make sure we compare numbers against numbers
                return parseFloat(val);
            }

            return val;
        },

        /**
         * Read value from a diffent HTML controls.
         *
         * Handle, text, checkbox, select.
         *
         */
        getControlValue : function(context, control) {

            // Handle checkboxes
            if(control.attr("type") == "checkbox") {
                return control.is(":checked");
            }

            return control.val();
        },

        /**
         * Create a sub-rule.
         *
         * Example:
         *
         *      var masterSwitch = ruleset.createRule("#mechanicalThrombectomyDevice")
         *      var twoAttempts = masterSwitch.createRule("#numberOfAttempts", "==", 2);
         *
         * @return Rule instance
         */
        createRule : function(controller, condition, value) {
            var rule = new Rule(controller, condition, value);
            this.rules.push(rule);
            return rule;
        },

        /**
         * Include a control in this rule.
         *
         * @param  {String} input     jQuery expression to match the input within ruleset context
         */
        include : function(input) {

            if(!input) {
                throw new Error("Must give an input selector");
            }

            this.controls.push(input);
        },

        /**
         * Apply this rule to all controls in the given context
         *
         * @param  {jQuery} context  jQuery selection within we operate
         * @param  {Object} cfg      {@link Configuration} object or undefined
         * @param  {Object} enforced Recursive rule enforcer: undefined to evaluate condition, true show always, false hide always
         *
         */
        applyRule : function(context, cfg, enforced) {

            var result;

            if(enforced === undefined) {
                result = this.checkCondition(context, cfg);
            } else {
                result = enforced;
            }

            if(cfg.log) {
                log("Applying rule on " + this.controller + "==" + this.value + " enforced:" + enforced + " result:" + result);
            }

            if(cfg.log && !this.controls.length) {
                log("Zero length controls slipped through");
            }

            // Get show/hide callback functions

            var show = cfg.show || function(control) {
                control.show();
            };

            var hide = cfg.show || function(control) {
                control.hide();
            };

            // Resolve controls from ids to jQuery selections
            // we are controlling in this context
            var controls = $.map(this.controls, function(elem, idx) {
                var control = context.find(elem);
                if(cfg.log && control.size() === 0) {
                    log("Could not find element:" + elem);
                }
                return control;
            });

            if(result) {

                $(controls).each(function() {

                    // Some friendly debug info
                    if(cfg.log && $(this).size() === 0) {
                        log("Control selection is empty when showing");
                        log(this);
                    }

                    show(this);
                });

                // Evaluate all child rules
                $(this.rules).each(function() {
                    this.applyRule(context, cfg);
                });

            } else {

                $(controls).each(function() {

                    // Some friendly debug info
                    if(cfg.log && $(this).size() === 0) {
                        log("Control selection is empty when hiding:");
                        log(this);
                    }

                    hide(this);
                });

                // Supress all child rules
                $(this.rules).each(function() {
                    this.applyRule(context, cfg, false);
                });
            }
        }
    });

    /**
     * A class which manages interdependenice rules.
     */
    function Ruleset() {

        // Hold a tree of rules
        this.rules = [];
    }

    $.extend(Ruleset.prototype, {

        /**
         * Add a new rule into this ruletset.
         *
         * See  {@link Rule} about the contstruction parameters.
         * @return {Rule}
         */
        createRule : function(controller, condition, value) {
            var rule = new Rule(controller, condition, value);
            this.rules.push(rule);
            return rule;
        },

        /**
         * Apply these rules on an element.
         *
         * @param {jQuery} context Selection we are dealing with
         *
         * @param cfg {@link Configuration} object or undefined.
         */
        applyRules: function(context, cfg) {
            var i;

            cfg = cfg || {};

            if(cfg.log) {
                log("Starting evaluation ruleset of " + this.rules.length + " rules");
            }

            for(i=0; i<this.rules.length; i++) {
                this.rules[i].applyRule(context, cfg);
            }
        },

        /**
         * Walk all rules and sub-rules in this ruleset
         * @param  {Function} callback(rule)
         *
         * @return {Array} Rules as depth-first searched
         */
        walk : function() {

            var rules = [];

            function descent(rule) {

                rules.push(rule);

                $(rule.children).each(function() {
                    descent(this);
                });
            }

            $(this.rules).each(function() {
                descent(this);
            });

            return rules;
        },

        /**
         * Check that all controllers and controls referred in ruleset exist.
         *
         * Throws an Error if any of them are missing.
         *
         * @param {jQuery} context jQuery selection of items
         *
         * @param  {Configuration} cfg
         */
        checkTargets : function(context, cfg) {

            var controls = 0;
            var rules = this.walk();

            $(rules).each(function() {

                if(context.find(this.controller).size() === 0) {
                    throw new Error("Rule's controller does not exist:" + this.controller);
                }

                if(this.controls.length === 0) {
                    throw new Error("Rule has no controls:" + this);
                }

                $(this.controls).each(function() {

                    if(safeFind(context, this) === 0) {
                        throw new Error("Rule's target control " + this + " does not exist in context " + context.get(0));
                    }

                    controls++;
                });

            });

            if(cfg.log) {
                log("Controller check ok, rules count " + rules.length + " controls count " + controls);
            }

        },

        /**
         * Make this ruleset effective on the whole page.
         *
         * Set event handler on **window.document** to catch all input events
         * and apply those events to defined rules.
         *
         * @param  {Configuration} cfg {@link Configuration} object or undefined
         *
         */
        install : function(cfg) {
            $.deps.enable($(document.body), this, cfg);
        }

    });

    /**
     * jQuery interdependencie plug-in
     *
     * @class jQuery.deps
     *
     */
    var deps = {

        /**
         * Create a new Ruleset instance.
         *
         * Example:
         *
         *      $(document).ready(function() {
         *           // Start creating a new ruleset
         *           var ruleset = $.deps.createRuleset();
         *
         *
         * @return {Ruleset}
         */
        createRuleset : function() {
            return new Ruleset();
        },

        /**
         * Enable ruleset on a specific jQuery selection.
         *
         * Checks the existince of all ruleset controllers and controls
         * by default (see config).
         *
         * See possible IE event bubbling problems: http://stackoverflow.com/q/265074/315168
         *
         * @param  {Object} selection jQuery selection in where we monitor all change events. All controls and controllers must exist within this selection.
         * @param  {Ruleset} ruleset
         * @param  {Configuration} cfg
         */
        enable : function(selection, ruleset, cfg) {

            cfg = cfg || {};

            if(cfg.checkTargets || cfg.checkTargets === undefined) {
                ruleset.checkTargets(selection, cfg);
            }

            var self = this;

            if(cfg.log) {
                log("Enabling dependency change monitoring on " + selection.get(0));
            }

            // Namespace our handler to avoid conflicts
            //
            var val = selection.live("change.deps", function() {
                ruleset.applyRules(selection, cfg);
            });

            ruleset.applyRules(selection, cfg);

            return val;
        }
    };

    $.deps = deps;

})(jQuery);<|endoftext|>


<commit_before>// @flow
import Devices from './devices'
import DumbSheet from './dev/dumb-sheet'
import Folders from './folders'
import Login from './login'
import MetaNavigator from './router/meta-navigator'
import NoTab from './no-tab'
import ProfileContainer from './profile/container'
import React, {Component} from 'react'
import Search from './search'
import Settings from './settings'
import TabBar from './tab-bar/index.render.native'
import globalRoutes from './router/global-routes'
import hello from './util/hello'
import {Text, Box, NativeBackAndroid, NativeDrawerLayoutAndroid, NativeImage, NativeTouchableNativeFeedback} from './common-adapters/index.native'
import {bootstrap} from './actions/config'
import {connect} from 'react-redux'
import {listenForNotifications} from './actions/notifications'
import {mapValues} from 'lodash'
import {navigateBack, switchTab} from './actions/router'
import {profileTab, folderTab, chatTab, peopleTab, devicesTab, settingsTab, loginTab, prettify} from './constants/tabs'
import {setupSource} from './util/forward-logs.native'
import GlobalError from './global-errors/container'

import type {VisibleTab} from './constants/tabs'

const tabs: {[key: VisibleTab]: {module: any}} = {
  [settingsTab]: {module: Settings, name: 'Settings'},
  [profileTab]: {module: ProfileContainer, name: 'Profile'},
  [folderTab]: {module: Folders, name: 'Folders'},
  [chatTab]: {module: Settings, name: 'Chat'},
  [peopleTab]: {module: Search, name: 'People'},
  [devicesTab]: {module: Devices, name: 'Devices'},
}

setupSource()

class AndroidNavigator extends Component {
  push (componentAtTop) {
    return false
  }

  getCurrentRoutes () {
    return []
  }

  popToRoute (targetRoute) {
    return false
  }

  immediatelyResetRouteStack () {
    return false
  }

  render () {
    let componentAtTop = this.props.initialRouteStack[this.props.initialRouteStack.length - 1]
    return this.props.renderScene(componentAtTop, null)
  }
}

AndroidNavigator.propTypes = {
  initialRouteStack: React.PropTypes.array.isRequired,
  renderScene: React.PropTypes.func.isRequired,
}

class Nav extends Component {
  constructor (props) {
    super(props)
    this.props.bootstrap()
    this.props.listenForNotifications()

    // Introduce ourselves to the service
    hello(0, 'Android app', [], '0.0.0') // TODO real version
  }

  _renderContent (activeTab, module) {
    return (
      <Box style={styles.tabContent} collapsable={false}>
        <MetaNavigator
          rootComponent={module || NoTab}
          tab={activeTab}
          globalRoutes={globalRoutes}
          navBarHeight={0}
          Navigator={AndroidNavigator}
          NavBar={<Box />}
        />
        <GlobalError />
      </Box>
    )
  }

  componentWillMount () {
    NativeBackAndroid.addEventListener('hardwareBackPress', () => {
      // TODO Properly handle android back button press
      const currentRoute = this.props.router.getIn(['tabs', this.props.router.get('activeTab'), 'uri'])
      if (currentRoute == null || currentRoute.count() <= 1) {
        return false
      }
      this.props.navigateBack()
      return true
    })
  }

  render () {
    if (this.props.dumbFullscreen) {
      return <DumbSheet />
    }

    const activeTab = this.props.router.get('activeTab')

    if (activeTab === loginTab) {
      return this._renderContent(loginTab, Login)
    }

    const drawerContent = (
      <Box style={{flex: 1, backgroundColor: '#fff'}}>
        <Text type='Header' style={{margin: 10, fontSize: 15, textAlign: 'left'}}>I'm in the Drawer!</Text>
      </Box>
    )

    const tabContent = mapValues(tabs, ({module}, tab) => (activeTab === tab && this._renderContent(tab, module)))
    const username = this.props.username

    return (
      <NativeDrawerLayoutAndroid
        drawerWidth={300}
        ref='drawer'
        drawerPosition={NativeDrawerLayoutAndroid.positions.Left}
        renderNavigationView={() => drawerContent}>
        <Box collapsable={false} style={{flex: 1}}>
          <Box
            title={''}
            style={styles.toolbar}>
            <Box collapsable={false} style={styles.toolbarContent}>
              <Box style={{flex: 0}}>
                <NativeTouchableNativeFeedback
                  onPress={() => this.refs.drawer && this.refs.drawer.openDrawer()}
                  delayPressIn={0}
                  background={NativeTouchableNativeFeedback.SelectableBackground()} >
                  <Box>
                    <NativeImage style={{...styles.toolbarImage, ...{marginTop: 4}}} resizeMode={'contain'} source={require('./images/nav/ic_menu_black_24dp.png')} />
                  </Box>
                </NativeTouchableNativeFeedback>
              </Box>

              <Box style={{marginLeft: 40}}>
                <Text type='Body' style={styles.toolbarName}>{prettify(activeTab)}</Text>
              </Box>

              <Box style={styles.toolbarSearchWrapper}>
                <NativeTouchableNativeFeedback
                  onPress={() => console.log('todo: show search')}
                  delayPressIn={0}
                  background={NativeTouchableNativeFeedback.SelectableBackground()}>
                  <Box>
                    <NativeImage style={styles.toolbarImage} resizeMode={'contain'} source={require('./images/nav/ic_search_black_24dp.png')} />
                  </Box>
                </NativeTouchableNativeFeedback>
              </Box>
            </Box>
          </Box>
          <Box collapsable={false} style={{flex: 2}}>
            <TabBar onTabClick={this.props.switchTab} selectedTab={activeTab} username={username} badgeNumbers={{[folderTab]: this.props.folderBadge}} tabContent={tabContent} />
          </Box>
        </Box>
      </NativeDrawerLayoutAndroid>

    )
  }
}

const styles = {
  tabContent: {
    flex: 1,
  },
  toolbar: {
    height: 50,
    flex: 0,
  },

  toolbarContent: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    paddingLeft: 16,
    paddingRight: 10,
  },

  toolbarName: {
    fontWeight: 'bold',
    fontSize: 24,
    color: 'black',
  },

  toolbarImage: {
    height: 40,
    width: 30,
  },

  toolbarSearchWrapper: {
    flex: 2,
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
}

// $FlowIssue
export default connect(
  ({
    router,
    config: {bootstrapped, extendedConfig, username},
    dev: {debugConfig: {dumbFullscreen}},
    favorite: {publicBadge, privateBadge},
    notifications: {menuBadge}}) => ({
      router,
      bootstrapped,
      provisioned: extendedConfig && !!extendedConfig.defaultDeviceID,
      username,
      dumbFullscreen,
      folderBadge: privateBadge + publicBadge,
    }),
  dispatch => {
    return {
      switchTab: tab => dispatch(switchTab(tab)),
      navigateBack: () => dispatch(navigateBack()),
      bootstrap: () => dispatch(bootstrap()),
      listenForNotifications: () => dispatch(listenForNotifications()),
    }
  }
)(Nav)
<commit_msg>android nav back -> nav up (#4656)<commit_after>// @flow
import Devices from './devices'
import DumbSheet from './dev/dumb-sheet'
import Folders from './folders'
import Login from './login'
import MetaNavigator from './router/meta-navigator'
import NoTab from './no-tab'
import ProfileContainer from './profile/container'
import React, {Component} from 'react'
import Search from './search'
import Settings from './settings'
import TabBar from './tab-bar/index.render.native'
import globalRoutes from './router/global-routes'
import hello from './util/hello'
import {Text, Box, NativeBackAndroid, NativeDrawerLayoutAndroid, NativeImage, NativeTouchableNativeFeedback} from './common-adapters/index.native'
import {bootstrap} from './actions/config'
import {connect} from 'react-redux'
import {listenForNotifications} from './actions/notifications'
import {mapValues} from 'lodash'
import {navigateUp, switchTab} from './actions/router'
import {profileTab, folderTab, chatTab, peopleTab, devicesTab, settingsTab, loginTab, prettify} from './constants/tabs'
import {setupSource} from './util/forward-logs.native'
import GlobalError from './global-errors/container'

import type {VisibleTab} from './constants/tabs'

const tabs: {[key: VisibleTab]: {module: any}} = {
  [settingsTab]: {module: Settings, name: 'Settings'},
  [profileTab]: {module: ProfileContainer, name: 'Profile'},
  [folderTab]: {module: Folders, name: 'Folders'},
  [chatTab]: {module: Settings, name: 'Chat'},
  [peopleTab]: {module: Search, name: 'People'},
  [devicesTab]: {module: Devices, name: 'Devices'},
}

setupSource()

class AndroidNavigator extends Component {
  push (componentAtTop) {
    return false
  }

  getCurrentRoutes () {
    return []
  }

  popToRoute (targetRoute) {
    return false
  }

  immediatelyResetRouteStack () {
    return false
  }

  render () {
    let componentAtTop = this.props.initialRouteStack[this.props.initialRouteStack.length - 1]
    return this.props.renderScene(componentAtTop, null)
  }
}

AndroidNavigator.propTypes = {
  initialRouteStack: React.PropTypes.array.isRequired,
  renderScene: React.PropTypes.func.isRequired,
}

class Nav extends Component {
  constructor (props) {
    super(props)
    this.props.bootstrap()
    this.props.listenForNotifications()

    // Introduce ourselves to the service
    hello(0, 'Android app', [], '0.0.0') // TODO real version
  }

  _renderContent (activeTab, module) {
    return (
      <Box style={styles.tabContent} collapsable={false}>
        <MetaNavigator
          rootComponent={module || NoTab}
          tab={activeTab}
          globalRoutes={globalRoutes}
          navBarHeight={0}
          Navigator={AndroidNavigator}
          NavBar={<Box />}
        />
        <GlobalError />
      </Box>
    )
  }

  componentWillMount () {
    NativeBackAndroid.addEventListener('hardwareBackPress', () => {
      // Just going up vs back for now
      const currentRoute = this.props.router.getIn(['tabs', this.props.router.get('activeTab'), 'uri'])
      if (currentRoute == null || currentRoute.count() <= 1) {
        return false
      }
      this.props.navigateUp()
      return true
    })
  }

  render () {
    if (this.props.dumbFullscreen) {
      return <DumbSheet />
    }

    const activeTab = this.props.router.get('activeTab')

    if (activeTab === loginTab) {
      return this._renderContent(loginTab, Login)
    }

    const drawerContent = (
      <Box style={{flex: 1, backgroundColor: '#fff'}}>
        <Text type='Header' style={{margin: 10, fontSize: 15, textAlign: 'left'}}>I'm in the Drawer!</Text>
      </Box>
    )

    const tabContent = mapValues(tabs, ({module}, tab) => (activeTab === tab && this._renderContent(tab, module)))
    const username = this.props.username

    return (
      <NativeDrawerLayoutAndroid
        drawerWidth={300}
        ref='drawer'
        drawerPosition={NativeDrawerLayoutAndroid.positions.Left}
        renderNavigationView={() => drawerContent}>
        <Box collapsable={false} style={{flex: 1}}>
          <Box
            title={''}
            style={styles.toolbar}>
            <Box collapsable={false} style={styles.toolbarContent}>
              <Box style={{flex: 0}}>
                <NativeTouchableNativeFeedback
                  onPress={() => this.refs.drawer && this.refs.drawer.openDrawer()}
                  delayPressIn={0}
                  background={NativeTouchableNativeFeedback.SelectableBackground()} >
                  <Box>
                    <NativeImage style={{...styles.toolbarImage, ...{marginTop: 4}}} resizeMode={'contain'} source={require('./images/nav/ic_menu_black_24dp.png')} />
                  </Box>
                </NativeTouchableNativeFeedback>
              </Box>

              <Box style={{marginLeft: 40}}>
                <Text type='Body' style={styles.toolbarName}>{prettify(activeTab)}</Text>
              </Box>

              <Box style={styles.toolbarSearchWrapper}>
                <NativeTouchableNativeFeedback
                  onPress={() => console.log('todo: show search')}
                  delayPressIn={0}
                  background={NativeTouchableNativeFeedback.SelectableBackground()}>
                  <Box>
                    <NativeImage style={styles.toolbarImage} resizeMode={'contain'} source={require('./images/nav/ic_search_black_24dp.png')} />
                  </Box>
                </NativeTouchableNativeFeedback>
              </Box>
            </Box>
          </Box>
          <Box collapsable={false} style={{flex: 2}}>
            <TabBar onTabClick={this.props.switchTab} selectedTab={activeTab} username={username} badgeNumbers={{[folderTab]: this.props.folderBadge}} tabContent={tabContent} />
          </Box>
        </Box>
      </NativeDrawerLayoutAndroid>
    )
  }
}

const styles = {
  tabContent: {
    flex: 1,
  },
  toolbar: {
    height: 50,
    flex: 0,
  },

  toolbarContent: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    paddingLeft: 16,
    paddingRight: 10,
  },

  toolbarName: {
    fontWeight: 'bold',
    fontSize: 24,
    color: 'black',
  },

  toolbarImage: {
    height: 40,
    width: 30,
  },

  toolbarSearchWrapper: {
    flex: 2,
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
}

// $FlowIssue
export default connect(
  ({
    router,
    config: {bootstrapped, extendedConfig, username},
    dev: {debugConfig: {dumbFullscreen}},
    favorite: {publicBadge, privateBadge},
    notifications: {menuBadge}}) => ({
      router,
      bootstrapped,
      provisioned: extendedConfig && !!extendedConfig.defaultDeviceID,
      username,
      dumbFullscreen,
      folderBadge: privateBadge + publicBadge,
    }),
  dispatch => ({
    switchTab: tab => dispatch(switchTab(tab)),
    navigateUp: () => dispatch(navigateUp()),
    bootstrap: () => dispatch(bootstrap()),
    listenForNotifications: () => dispatch(listenForNotifications()),
  })
)(Nav)
<|endoftext|>


<commit_before>$(function() {
    $( "#tabs" ).tabs();
    $(".result").click(function(event){
        $(".default").click();
        var index;
        var exist = false;
        for(index = 0; index< chrome.extension.getViews().length;index++){
            if (chrome.extension.getViews()[index].location.href.match(/.*process.html.*/)){
                chrome.extension.getViews()[index].chrome.tabs.getCurrent(function(tab){chrome.tabs.update(tab.id, {"selected": true})});
                exist=true;
                break;
            }
        }
        if(!exist){
            chrome.tabs.create({'url': chrome.extension.getURL('process.html')}, function(tab) {
                // Tab opened.
                chrome.storage.local.set({'result':{id:tab.id}});
            });
        }

    });
    $(".target2").change(function(event){
        chrome.tabs.query({'active': true}, function(tabs) {
            var attackTab = tabs[0].id;
            chrome.tabs.sendMessage(
                tabs[0].id,
                {"type":"highlight","name":$(".target2 option:selected").val()
                },function(response){

                }
            );
        });
    });

    $(".manual").click(function(event){
        chrome.tabs.query({'active': true}, function(tabs) {
            var attackTab = tabs[0].id;
            chrome.tabs.sendMessage(
                tabs[0].id,
                {"type":"getInput"
                },function(response){
                    var inputs = JSON.parse(response.input);
                    $( ".target2" ).children().remove();
                    $.each(inputs,function(ind,name){
                        $( ".target2" ).children().end().append('<option value="'+name+'">'+name+'</option>');
                    });
                    chrome.tabs.query({'active': true}, function(tabs) {
                        var attackTab = tabs[0].id;
                        chrome.tabs.sendMessage(
                            tabs[0].id,
                            {"type":"highlight","name":$(".target2 option:selected").val()
                            },function(response){

                            }
                        );
                    });
                }
            );
        });
    });

    $( "input[type=button]")
    .button()
    .click(function( event ) {
        event.preventDefault();
    });

    $( "input[type=submit]")
    .button()
    .click(function( event ) {
        event.preventDefault();
    });
    $( ".stop" ).click(function() {
        chrome.storage.local.get("scanning", function(obj){
            chrome.tabs.duplicate(obj.scanning.tab);
            chrome.tabs.remove(obj.scanning.tab);
        });

        chrome.storage.local.remove('xsrfstore');
        chrome.storage.local.set({'scanning':{input:"",scanId:0,status:false,url:"",payload:"",payloadId:0,tab:0,index:0}});

    });

    $( ".submit1" ).click(function() {
        //check if options are selected
        if($(".type1 option:selected").val() != "" && $(".payload1 option:selected").val() != ""){
            chrome.tabs.query({'active': true}, function(tabs) {

                //current tab

                //chrome.tabs.update(tabs[0].id, {url: "http://www.google.com"});
                var attackTab = tabs[0].id;
                //send message to content tab to begin attack based on payload
                //start attack

                chrome.storage.local.get("scanIndex", function(res){
                    var scanId=res.scanIndex.scanIndex;
                    res.scanIndex.scanIndex++;
                    chrome.storage.local.set({
                        'scanIndex':{
                            scanIndex: res.scanIndex.scanIndex,
                        }
                    });

                    chrome.storage.local.set({'scanning':{scanId:scanId,status:true,url:tabs[0].url,payload:payload[ $(".payload1 option:selected").val()],payloadId:0,tab:tabs[0].id,index:1}},
                                             function(){
                                                 chrome.tabs.sendMessage(
                                                     tabs[0].id,
                                                     {"type":"start",
                                                      "url":tabs[0].url,
                                                      "payload":payload[$(".payload1 option:selected").val()]
                                                     },function(response){
                                                     }
                                                 );

                                             });

                });

            });
        }
    });

    $( ".submit2" ).click(function() {
        //check if options are selected
        if($(".customPayload2").val() != "" &&$(".target2 option:selected").val() != "" &&$(".type2 option:selected").val() != "" && $(".payload2 option:selected").val() != ""){
            chrome.tabs.query({'active': true}, function(tabs) {

                //current tab
                var attackTab = tabs[0].id;
                //send message to content tab to begin attack based on payload
                //start attack
                // open scan result page
                chrome.storage.local.get("scanIndex", function(res){
                    var scanId=res.scanIndex.scanIndex;
                    res.scanIndex.scanIndex++;
                    chrome.storage.local.set({
                        'scanIndex':{
                            scanIndex: res.scanIndex.scanIndex,
                        }
                    });
                    var pay = payload2[ $(".payload2 option:selected").val()];
                    pay[3][0][0] = $(".target2 option:selected").val();
                    pay[3][0][1] = $(".customPayload2").val();

                    chrome.tabs.sendMessage(
                        tabs[0].id,
                        {"type":"start2",
                         "url":tabs[0].url,
                         "payload":pay
                        },function(response){
                            var index;
                            var exist = false;
                            for(index = 0; index< chrome.extension.getViews().length;index++){
                                if (chrome.extension.getViews()[index].location.href.match(/.*process.html.*/)){
                                    exist=true;
                                }
                            }
                            if(!exist){
                                chrome.tabs.create({'url': chrome.extension.getURL('process.html')}, function(tab) {
                                    // Tab opened.
                                    chrome.storage.local.set({'result':{id:tab.id}});
                                });
                            }
                        }
                    );

                });

            });
        }
    });

    $( ".payload2" ).change(function() {
        $(".customPayload2").val(payload2[this.value][3][0][1]);
    });

    $( ".type2" ).change(function() {
        if(this.value==="sql"){
            $( ".payload2" ).children().remove();
            $( ".payload2" ).children().end().append('<option value=""></option>');
            payload2.forEach(function(payloads,index){
                if ("sql" == payloads[0]) {
                    $( ".payload2" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });

            $(".payloadLabel2").show();
        } else if(this.value==="xss") {
            $( ".payload2" ).children().remove();

            $( ".payload2" ).children().end().append('<option value=""></option>');
            payload2.forEach(function(payloads,index){
                if ("xss" == payloads[0]) {
                    $( ".payload2" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel2").show();
        } else {
            $( ".payload2" ).children().remove();
            $(".payloadLabel2").hide();
        }
    });

    $( ".type1" ).change(function() {
        if(this.value==="sql"){
            $( ".payload1" ).children().remove();
            payload.forEach(function(payloads,index){
                if ("sql" == payloads[0]) {
                    $( ".payload1" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel").show();
        } else if(this.value==="xss") {
            $( ".payload1" ).children().remove();
            payload.forEach(function(payloads,index){
                if ("xss" == payloads[0]) {
                    $( ".payload1" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel").show();
        } else if(this.value==="xsrf") {
            $( ".payload1" ).children().remove();
            payload.forEach(function(payloads,index){
                if ("xsrf" == payloads[0]) {
                    $( ".payload1" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel").show();
        } else {
            $( ".payload1" ).children().remove();
            $(".payloadLabel").hide();
        }
    });
});

//payload[0] = category
//payload[1] = Name of scan
//payload[2] = id of scan
//payload[3] = attack parameter
//  payload[3][i][0] = fields to target ( * = everything )
//  payload[3][i][1] = payload to inject.
//payload[4] = signature to detect ( differential detection : @save[1] and @compare[1] )
//sql - regex
//xss - dom
//xsrf - token/captcha

//2 for custom
var payload2 = [
    ["sql", "Test for vulnerability", "sql1",
     [["*","';--"]],
     ["An error occured: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to","error"]
    ],
    ["sql", "Login Field","sql2",
     [["^.*username.*$","' or '1' = '1"]]
    ],
    ["xss", "Test for vulnerability","xss1",
     [["*","

"]],
     ["@XSS,w1232,href,src"]
    ],
    ["xss", "Insert Image","xss2",
     [["*",""]]
    ],
    ["xss", "Insert Script","xss3",
     [["*",""]]
    ],
    ["xss", "Insert Redirection","xss4",
     [["*",""]]
    ]

];

var payload = [
    ["sql", "Test for vulnerability", "sql1",
     [["*","';--"],["*","'--"]],
     ["An error occured: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to","error"]
    ],
    ["sql", "Login Field","sql2",
     [["^.*username.*$","' or '1' = '1"]]
    ],
    ["xss", "Test for vulnerability","xss1",
     [["*","

"]],
     ["@XSS,w1232,href,src"]
    ],
    ["xss", "Insert Image","xss2",
     [["*",""]]
    ],
    ["xss", "Insert Script","xss3",
     [["*",""]]
    ],
    ["xss", "Insert Redirection","xss4",
     [["*",""]]
    ],
    ["xsrf", "Test for vulnerability", "xsrf1",
     [["z{1000}",""]],
     ["@XSRF,compare","@XSRF,tokencaptcha"]]

];
<commit_msg>Payload updated<commit_after>$(function() {
    $( "#tabs" ).tabs();
    $(".result").click(function(event){
        $(".default").click();
        var index;
        var exist = false;
        for(index = 0; index< chrome.extension.getViews().length;index++){
            if (chrome.extension.getViews()[index].location.href.match(/.*process.html.*/)){
                chrome.extension.getViews()[index].chrome.tabs.getCurrent(function(tab){chrome.tabs.update(tab.id, {"selected": true})});
                exist=true;
                break;
            }
        }
        if(!exist){
            chrome.tabs.create({'url': chrome.extension.getURL('process.html')}, function(tab) {
                // Tab opened.
                chrome.storage.local.set({'result':{id:tab.id}});
            });
        }

    });
    $(".target2").change(function(event){
        chrome.tabs.query({'active': true}, function(tabs) {
            var attackTab = tabs[0].id;
            chrome.tabs.sendMessage(
                tabs[0].id,
                {"type":"highlight","name":$(".target2 option:selected").val()
                },function(response){

                }
            );
        });
    });

    $(".manual").click(function(event){
        chrome.tabs.query({'active': true}, function(tabs) {
            var attackTab = tabs[0].id;
            chrome.tabs.sendMessage(
                tabs[0].id,
                {"type":"getInput"
                },function(response){
                    var inputs = JSON.parse(response.input);
                    $( ".target2" ).children().remove();
                    $.each(inputs,function(ind,name){
                        $( ".target2" ).children().end().append('<option value="'+name+'">'+name+'</option>');
                    });
                    chrome.tabs.query({'active': true}, function(tabs) {
                        var attackTab = tabs[0].id;
                        chrome.tabs.sendMessage(
                            tabs[0].id,
                            {"type":"highlight","name":$(".target2 option:selected").val()
                            },function(response){

                            }
                        );
                    });
                }
            );
        });
    });

    $( "input[type=button]")
    .button()
    .click(function( event ) {
        event.preventDefault();
    });

    $( "input[type=submit]")
    .button()
    .click(function( event ) {
        event.preventDefault();
    });
    $( ".stop" ).click(function() {
        chrome.storage.local.get("scanning", function(obj){
            chrome.tabs.duplicate(obj.scanning.tab);
            chrome.tabs.remove(obj.scanning.tab);
        });

        chrome.storage.local.remove('xsrfstore');
        chrome.storage.local.set({'scanning':{input:"",scanId:0,status:false,url:"",payload:"",payloadId:0,tab:0,index:0}});

    });

    $( ".submit1" ).click(function() {
        //check if options are selected
        if($(".type1 option:selected").val() != "" && $(".payload1 option:selected").val() != ""){
            chrome.tabs.query({'active': true}, function(tabs) {

                //current tab

                //chrome.tabs.update(tabs[0].id, {url: "http://www.google.com"});
                var attackTab = tabs[0].id;
                //send message to content tab to begin attack based on payload
                //start attack

                chrome.storage.local.get("scanIndex", function(res){
                    var scanId=res.scanIndex.scanIndex;
                    res.scanIndex.scanIndex++;
                    chrome.storage.local.set({
                        'scanIndex':{
                            scanIndex: res.scanIndex.scanIndex,
                        }
                    });

                    chrome.storage.local.set({'scanning':{scanId:scanId,status:true,url:tabs[0].url,payload:payload[ $(".payload1 option:selected").val()],payloadId:0,tab:tabs[0].id,index:1}},
                                             function(){
                                                 chrome.tabs.sendMessage(
                                                     tabs[0].id,
                                                     {"type":"start",
                                                      "url":tabs[0].url,
                                                      "payload":payload[$(".payload1 option:selected").val()]
                                                     },function(response){
                                                     }
                                                 );

                                             });

                });

            });
        }
    });

    $( ".submit2" ).click(function() {
        //check if options are selected
        if($(".customPayload2").val() != "" &&$(".target2 option:selected").val() != "" &&$(".type2 option:selected").val() != "" && $(".payload2 option:selected").val() != ""){
            chrome.tabs.query({'active': true}, function(tabs) {

                //current tab
                var attackTab = tabs[0].id;
                //send message to content tab to begin attack based on payload
                //start attack
                // open scan result page
                chrome.storage.local.get("scanIndex", function(res){
                    var scanId=res.scanIndex.scanIndex;
                    res.scanIndex.scanIndex++;
                    chrome.storage.local.set({
                        'scanIndex':{
                            scanIndex: res.scanIndex.scanIndex,
                        }
                    });
                    var pay = payload2[ $(".payload2 option:selected").val()];
                    pay[3][0][0] = $(".target2 option:selected").val();
                    pay[3][0][1] = $(".customPayload2").val();

                    chrome.tabs.sendMessage(
                        tabs[0].id,
                        {"type":"start2",
                         "url":tabs[0].url,
                         "payload":pay
                        },function(response){
                            var index;
                            var exist = false;
                            for(index = 0; index< chrome.extension.getViews().length;index++){
                                if (chrome.extension.getViews()[index].location.href.match(/.*process.html.*/)){
                                    exist=true;
                                }
                            }
                            if(!exist){
                                chrome.tabs.create({'url': chrome.extension.getURL('process.html')}, function(tab) {
                                    // Tab opened.
                                    chrome.storage.local.set({'result':{id:tab.id}});
                                });
                            }
                        }
                    );

                });

            });
        }
    });

    $( ".payload2" ).change(function() {
        $(".customPayload2").val(payload2[this.value][3][0][1]);
    });

    $( ".type2" ).change(function() {
        if(this.value==="sql"){
            $( ".payload2" ).children().remove();
            $( ".payload2" ).children().end().append('<option value=""></option>');
            payload2.forEach(function(payloads,index){
                if ("sql" == payloads[0]) {
                    $( ".payload2" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });

            $(".payloadLabel2").show();
        } else if(this.value==="xss") {
            $( ".payload2" ).children().remove();

            $( ".payload2" ).children().end().append('<option value=""></option>');
            payload2.forEach(function(payloads,index){
                if ("xss" == payloads[0]) {
                    $( ".payload2" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel2").show();
        } else {
            $( ".payload2" ).children().remove();
            $(".payloadLabel2").hide();
        }
    });

    $( ".type1" ).change(function() {
        if(this.value==="sql"){
            $( ".payload1" ).children().remove();
            payload.forEach(function(payloads,index){
                if ("sql" == payloads[0]) {
                    $( ".payload1" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel").show();
        } else if(this.value==="xss") {
            $( ".payload1" ).children().remove();
            payload.forEach(function(payloads,index){
                if ("xss" == payloads[0]) {
                    $( ".payload1" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel").show();
        } else if(this.value==="xsrf") {
            $( ".payload1" ).children().remove();
            payload.forEach(function(payloads,index){
                if ("xsrf" == payloads[0]) {
                    $( ".payload1" ).children().end().append('<option value="'+index+'">'+payloads[1]+'</option>');
                }
            });
            $(".payloadLabel").show();
        } else {
            $( ".payload1" ).children().remove();
            $(".payloadLabel").hide();
        }
    });
});

//payload[0] = category
//payload[1] = Name of scan
//payload[2] = id of scan
//payload[3] = attack parameter
//  payload[3][i][0] = fields to target ( * = everything )
//  payload[3][i][1] = payload to inject.
//payload[4] = signature to detect ( differential detection : @save[1] and @compare[1] )
//sql - regex
//xss - dom
//xsrf - token/captcha

//2 for custom
var payload2 = [
    ["sql", "Test for vulnerability", "sql1",
     [["*","';--"]],
     ["An error occured: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to"
     ,"You have an error in your SQL syntax;"
     ,"You have an error in your SQL syntax near"
     ,"MySQL server version for the right syntax to use"
     ,"Unknown MySQL error","An error occured: "
     ,"valid MySQL result"
     ,"on MySQL result index","Column count doesn't match value count at row"
     ,"Column count doesn't match"
     ,"supplied argument is not a valid MySQL"
     ,"Warning.*mysql_.*"]
    ],
    ["sql", "Login Field","sql2",
     [["^.*username.*$","' or '1' = '1"]]
    ],
    ["xss", "Test for vulnerability","xss1",
     [["*","

"]],
     ["@XSS,w1231,href,src"]
    ],
    ["xss", "Insert Image","xss2",
     [["*"," "]],
     ["@XSS,w1232,src"]
    ],
    ["xss", "Insert Script","xss3",
     [["*","