<commit_before>'use strict';

var backboneUrlResolver = require('backbone-url-resolver');
var log                 = require('loglevel');
var _                   = require('underscore');
var Backbone            = require('backbone');
var defaultContextModel = require('./default-context-model');

function TemplateSubscription(client, options) {
  this.options = options;
  this.client = client;

  if (!options.onMessage) throw new Error('onMessage is required');

  if (options.urlModel) {
    this.urlModel = options.urlModel;
  } else {
    this.urlModel = this._getUrlModel(options);
  }

  this._subscribe();
}

_.extend(TemplateSubscription.prototype, Backbone.Events, {
  _subscribe: function(/*options*/) {
    if (this._subscribed) return;
    this._subscribed = true;

    this._registerForSnapshots();

    var url = this.url();

    this.listenTo(this.urlModel, 'change:url', function(model, newChannel) {  // jshint unused:true
      if (newChannel) {
        this._resubscribe(newChannel);
      } else {
        this._unsubscribe();
      }
    });

    if (!url) return;
    this._resubscribe(url);
  },

  cancel: function() {
    if (!this._subscribed) return;
    this._subscribed = false;

    // Stop listening to model changes....
    this.stopListening(this.contextModel);
    this._unsubscribe();
    this._deregisterForSnapshots();
  },

  _unsubscribe: function() {
    if(!this.subscription) return;
    this.trigger('unsubscribe');
    this.subscription.cancel();
    this.subscription = null;
  },

  _registerForSnapshots: function() {
    if (this._snapshotsRegistered) return;
    // Do we need snapshots?
    var options = this.options;
    if (!options.getSnapshotState && !options.handleSnapshot && !options.getSubscribeOptions) return;
    this._snapshotsRegistered = true;
    // Subscribe to snapshots to the `null` channel so that we
    // can handle snapshots when the channel changes name
    this.client.registerSnapshotHandler(null, this);
  },

  _deregisterForSnapshots: function() {
    this.client.deregisterSnapshotHandler(null, this);
    this._snapshotsRegistered = false;
  },

  _resubscribe: function(channel) {
    this._unsubscribe();

    this.trigger('resubscribe', channel);
    var self = this;
    this.subscription = this.client.subscribe(channel, this.options.onMessage);

    this.subscription.promise.catch(function(err) {
      console.error(err && err.stack || err);
      log.error('template-subscription: Subscription error for ' + channel, err);
      self.trigger('subscriptionError', channel, err);
      self.subscription = null;
    });
  },

  // Note that this may be overridden by child classes
  url: function() {
    return this.urlModel.get('url');
  },

  _getUrlModel: function(options) {
    var url = _.result(options, 'urlTemplate');
    var contextModel = _.result(options, 'contextModel');

    if (!contextModel) {
      contextModel = defaultContextModel(this.client);
    }

    return backboneUrlResolver(url, contextModel);
  },

  getSnapshotStateForChannel: function(snapshotChannel) {
    // Since we subscribed to all snapshots, we need to ensure that
    // the snapshot is for this channel
    if (snapshotChannel !== this.urlModel.get('url')) return;

    if (this.options.getSnapshotState) {
      return this.options.getSnapshotState(snapshotChannel);
    }
  },

  getSubscribeOptions: function(snapshotChannel) {
    if (snapshotChannel !== this.urlModel.get('url')) return;

    if (this.options.getSubscribeOptions) {
      return this.options.getSubscribeOptions(snapshotChannel);
    }
  },

  handleSnapshot: function(snapshot, snapshotChannel) {
    // Since we subscribed to all snapshots, we need to ensure that
    // the snapshot is for this channel
    if (snapshotChannel !== this.urlModel.get('url')) return;

    if (this.options.handleSnapshot) {
      return this.options.handleSnapshot(snapshot, snapshotChannel);
    }
  }
});

module.exports = TemplateSubscription;
<commit_msg>Additional debugging<commit_after>'use strict';

var backboneUrlResolver = require('backbone-url-resolver');
var log                 = require('loglevel');
var _                   = require('underscore');
var Backbone            = require('backbone');
var defaultContextModel = require('./default-context-model');
var debug               = require('debug-proxy')('grc:template-subscription');

function TemplateSubscription(client, options) {
  this.options = options;
  this.client = client;

  if (!options.onMessage) throw new Error('onMessage is required');

  if (options.urlModel) {
    this.urlModel = options.urlModel;
  } else {
    this.urlModel = this._getUrlModel(options);
  }

  this._subscribe();
}

_.extend(TemplateSubscription.prototype, Backbone.Events, {
  _subscribe: function(/*options*/) {
    if (this._subscribed) return;
    this._subscribed = true;

    this._registerForSnapshots();

    var url = this.url();

    this.listenTo(this.urlModel, 'change:url', function(model, newChannel) {  // jshint unused:true
      if (newChannel) {
        this._resubscribe(newChannel);
      } else {
        this._unsubscribe();
      }
    });

    if (!url) return;
    this._resubscribe(url);
  },

  cancel: function() {
    if (!this._subscribed) return;
    this._subscribed = false;

    // Stop listening to model changes....
    this.stopListening(this.contextModel);
    this._unsubscribe();
    this._deregisterForSnapshots();
  },

  _unsubscribe: function() {
    if(!this.subscription) return;
    debug('Unsubscribe');
    this.trigger('unsubscribe');
    this.subscription.cancel();
    this.subscription = null;
  },

  _registerForSnapshots: function() {
    if (this._snapshotsRegistered) return;
    // Do we need snapshots?
    var options = this.options;
    if (!options.getSnapshotState && !options.handleSnapshot && !options.getSubscribeOptions) return;
    this._snapshotsRegistered = true;
    // Subscribe to snapshots to the `null` channel so that we
    // can handle snapshots when the channel changes name
    this.client.registerSnapshotHandler(null, this);
  },

  _deregisterForSnapshots: function() {
    this.client.deregisterSnapshotHandler(null, this);
    this._snapshotsRegistered = false;
  },

  _resubscribe: function(channel) {
    this._unsubscribe();
    debug('Subscribe %s', channel);
    this.trigger('resubscribe', channel);

    var self = this;
    this.subscription = this.client.subscribe(channel, this.options.onMessage);

    this.subscription.promise.catch(function(err) {
      console.error(err && err.stack || err);
      log.error('template-subscription: Subscription error for ' + channel, err);
      self.trigger('subscriptionError', channel, err);
      self.subscription = null;
    });
  },

  // Note that this may be overridden by child classes
  url: function() {
    return this.urlModel.get('url');
  },

  _getUrlModel: function(options) {
    var url = _.result(options, 'urlTemplate');
    var contextModel = _.result(options, 'contextModel');

    if (!contextModel) {
      contextModel = defaultContextModel(this.client);
    }

    return backboneUrlResolver(url, contextModel);
  },

  getSnapshotStateForChannel: function(snapshotChannel) {
    // Since we subscribed to all snapshots, we need to ensure that
    // the snapshot is for this channel
    if (snapshotChannel !== this.urlModel.get('url')) return;

    if (this.options.getSnapshotState) {
      return this.options.getSnapshotState(snapshotChannel);
    }
  },

  getSubscribeOptions: function(snapshotChannel) {
    if (snapshotChannel !== this.urlModel.get('url')) return;

    if (this.options.getSubscribeOptions) {
      return this.options.getSubscribeOptions(snapshotChannel);
    }
  },

  handleSnapshot: function(snapshot, snapshotChannel) {
    // Since we subscribed to all snapshots, we need to ensure that
    // the snapshot is for this channel
    if (snapshotChannel !== this.urlModel.get('url')) return;

    if (this.options.handleSnapshot) {
      return this.options.handleSnapshot(snapshot, snapshotChannel);
    }
  }
});

module.exports = TemplateSubscription;
<|endoftext|>


<commit_before>/*
 * @package jsDAV
 * @subpackage DAV
 * @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org>
 * @author Mike de Boer <info AT mikedeboer DOT nl>
 * @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
 */
"use strict";

var jsDAV              = require("./../../jsdav");
var jsDAV_ServerPlugin = require("./../plugin").jsDAV_ServerPlugin;

var Spawn = require("child_process").spawn;
var Util  = require("./../util");

function jsDAV_Codesearch_Plugin(handler) {
    this.handler = handler;
    this.initialize();
}

jsDAV_Codesearch_Plugin.IGNORE_DIRS = {
    ".bzr"              : "Bazaar",
    ".cdv"              : "Codeville",
    "~.dep"             : "Interface Builder",
    "~.dot"             : "Interface Builder",
    "~.nib"             : "Interface Builder",
    "~.plst"            : "Interface Builder",
    ".git"              : "Git",
    ".hg"               : "Mercurial",
    ".pc"               : "quilt",
    ".svn"              : "Subversion",
    "blib"              : "Perl module building",
    "CVS"               : "CVS",
    "RCS"               : "RCS",
    "SCCS"              : "SCCS",
    "_darcs"            : "darcs",
    "_sgbak"            : "Vault/Fortress",
    "autom4te.cache"    : "autoconf",
    "cover_db"          : "Devel::Cover",
    "_build"            : "Module::Build"
};

jsDAV_Codesearch_Plugin.MAPPINGS = {
    "actionscript": ["as", "mxml"],
    "ada"         : ["ada", "adb", "ads"],
    "asm"         : ["asm", "s"],
    "batch"       : ["bat", "cmd"],
    //"binary"      : q{Binary files, as defined by Perl's -B op (default: off)},
    "cc"          : ["c", "h", "xs"],
    "cfmx"        : ["cfc", "cfm", "cfml"],
    "clojure"     : ["clj"],
    "cpp"         : ["cpp", "cc", "cxx", "m", "hpp", "hh", "h", "hxx"],
    "csharp"      : ["cs"],
    "css"         : ["css", "less", "scss", "sass"],
    "coffee"      : ["coffee"],
    "elisp"       : ["el"],
    "erlang"      : ["erl", "hrl"],
    "fortran"     : ["f", "f77", "f90", "f95", "f03", "for", "ftn", "fpp"],
    "haskell"     : ["hs", "lhs"],
    "hh"          : ["h"],
    "html"        : ["htm", "html", "shtml", "xhtml"],
    "jade"        : ["jade"],
    "java"        : ["java", "properties"],
    "groovy"      : ["groovy"],
    "js"          : ["js"],
    "json"        : ["json"],
    "latex"       : ["latex", "ltx"],
    "jsp"         : ["jsp", "jspx", "jhtm", "jhtml"],
    "lisp"        : ["lisp", "lsp"],
    "lua"         : ["lua"],
    "make"        : ["makefile"],
    "mason"       : ["mas", "mhtml", "mpl", "mtxt"],
    "markdown"    : ["md", "markdown"],
    "objc"        : ["m", "h"],
    "objcpp"      : ["mm", "h"],
    "ocaml"       : ["ml", "mli"],
    "parrot"      : ["pir", "pasm", "pmc", "ops", "pod", "pg", "tg"],
    "perl"        : ["pl", "pm", "pod", "t"],
    "php"         : ["php", "phpt", "php3", "php4", "php5", "phtml"],
    "plone"       : ["pt", "cpt", "metadata", "cpy", "py"],
    "powershell"  : ["ps1"],
    "python"      : ["py"],
    "rake"        : ["rakefile"],
    "ruby"        : ["rb", "ru", "rhtml", "rjs", "rxml", "erb", "rake", "gemspec"],
    "scala"       : ["scala"],
    "scheme"      : ["scm", "ss"],
    "shell"       : ["sh", "bash", "csh", "tcsh", "ksh", "zsh"],
    //"skipped"     : "q"{"Files but not directories normally skipped by ack ("default": "off")},
    "smalltalk"   : ["st"],
    "sql"         : ["sql", "ctl"],
    "tcl"         : ["tcl", "itcl", "itk"],
    "tex"         : ["tex", "cls", "sty"],
    "text"        : ["txt"],
    "textile"     : ["textile"],
    "tt"          : ["tt", "tt2", "ttml"],
    "vb"          : ["bas", "cls", "frm", "ctl", "vb", "resx"],
    "vim"         : ["vim"],
    "yaml"        : ["yaml", "yml"],
    "xml"         : ["xml", "dtd", "xslt", "ent", "rdf", "rss", "svg", "wsdl", "atom", "mathml", "mml"]
};
var exts = [];
for (var type in jsDAV_Codesearch_Plugin.MAPPINGS) {
    exts = exts.concat(jsDAV_Codesearch_Plugin.MAPPINGS[type]);
}
// grep pattern matching for extensions
jsDAV_Codesearch_Plugin.PATTERN_EXT = Util.makeUnique(exts).join(",");
var dirs = [];
for (type in jsDAV_Codesearch_Plugin.IGNORE_DIRS) {
    dirs.push(type);
}
dirs = Util.makeUnique(dirs);
jsDAV_Codesearch_Plugin.PATTERN_DIR  = Util.escapeRegExp(dirs.join("|"));
jsDAV_Codesearch_Plugin.PATTERN_EDIR = dirs.join(",");
jsDAV_Codesearch_Plugin.GREP_CMD = "grep";
jsDAV_Codesearch_Plugin.PERL_CMD = "perl";
jsDAV_Codesearch_Plugin.MAXSIZE = 2097152;//68157440; // 65MB

(function() {
    this.initialize = function() {
        this.handler.addEventListener("report", this.httpReportHandler.bind(this));
    };

    this.httpReportHandler = function(e, reportName, dom) {
        if (reportName != "{DAV:}codesearch")
            return e.next();
        e.stop();

        var uri     = this.handler.getRequestUri();
        var options = this.parseOptions(dom);
        var self    = this;
        options.uri = uri;
        this.handler.server.tree.getNodeForPath(uri, function(err, node) {
            if (err)
                return e.next(err);
            if (jsDAV.debugMode)
                Util.log("report" + reportName + ", " + node.path + ", ", options);

            self.doCodesearch(node, options, function(err, sResults) {
                //if (err)
                //    return e.stop(err);
                self.handler.httpResponse.writeHead(207, {"Content-Type":"text/xml; charset=utf-8"});
                self.handler.httpResponse.end(sResults);
                e.stop();
            });
        });
    };

    this.parseOptions = function(dom) {
        var options = {};
        for (var child, i = 0, l = dom.childNodes.length; i < l; ++i) {
            child = dom.childNodes[i];
            if (!child || child.nodeType != 1)
                continue;
            options[child.tagName] = child.nodeValue;
        }
        return options;
    };

    this.doCodesearch = function(node, options, cbsearch) {
        var cmd = jsDAV_Codesearch_Plugin.GREP_CMD + " -P -s -r --color=never --binary-files=without-match -n " + ( !Util.isTrue(options.casesensitive) ? "-i" : "" );

        var self  = this;// Util.empty(options.replacement) ? "" : options.replacement

        var include = "";

        if (!Util.empty(options.pattern)) {
            var patterns = options.pattern.split(",");

            if (patterns.length > 0) {
                patterns.forEach(function (p, idx) {
                    patterns[idx] = p.split('.').pop(); // trim "*.", e.g. *.js -> js, foo.js.cs -> cs
                });

                patterns.push("");
                include = "*.{" + patterns.join(",") + "}";
            }
            else {
                include = "*.{" + jsDAV_Codesearch_Plugin.PATTERN_EXT + "}";
            }
        }
        else {
            include = "*.{" + jsDAV_Codesearch_Plugin.PATTERN_EXT + "}";
        }

        if (options.maxresults) {
            cmd += "-m " + parseInt(options.maxresults, 10);
        }

        var query = options.query;
        // grep has a funny way of handling new lines (that is to say, it's non-existent)
        // if we're not doing a regex search, then we must split everything between the
        // new lines, escape the content, and then smush it back together; due to
        // new lines, this is also why we're  now passing -P as default to grep
        if (!Util.isTrue(options.replaceAll) && !Util.isTrue(options.regexp)) {
            var splitQuery = query.split("\\n");

            for (var q in splitQuery) {
                splitQuery[q] = Util.grepEscapeRegExp(splitQuery[q]);
            }
            query = splitQuery.join("\\n");
        }

        cmd += " --exclude=*{" + jsDAV_Codesearch_Plugin.PATTERN_EDIR + "}*"
            +  " --include=" + include
            + " '" + query + "'"
            + " \"" + Util.escapeShell(node.path) + "\"";

        if (Util.isTrue(options.replaceAll) && !Util.empty(options.replacement)) {
            // pipe the grep results into perl
            cmd += " -l | xargs " + jsDAV_Codesearch_Plugin.PERL_CMD +
            // print the grep result to STDOUT (to arrange in parseSearchResult())
            " -p0777i -e 'print STDOUT \"$ARGV:$.:$_\""     +
            // do the actual replace
            "if s/" + query + "/" + options.replacement + "/mg" + ( !Util.isTrue(options.casesensitive) ? "i" : "" ) + ";'"
        }

        if (jsDAV.debugMode) {
            Util.log("search command: " + cmd);
        }

        var out  = "";
        var err  = "";
        var grep = Spawn("/bin/bash", ["-c", cmd]);

        grep.stdout.setEncoding("utf8");
        grep.stderr.setEncoding("utf8");
        grep.stdout.on("data", function(data) {
            if (!Util.empty(data)) {
                out += data;

                if (out.length >= jsDAV_Codesearch_Plugin.MAXSIZE) {
                    grep.kill();
                }
            }
        });
        grep.stderr.on("data", function(data) {
            if (!Util.empty(data))
                err += data;
        });
        grep.on("exit", function(code, signal) {
            options.killed = (signal == "SIGTERM");

            cbsearch(err, self.parseSearchResult(out || "", node.path, options));
        });
    };

    function truncate(s, options) {
        var len = options.maxexcerptlength || 200;
        s = Util.trim(s);
        if (s.length <= len)
            return s;
        var res,
            pos = s.indexOf(options.query);
        if (pos > -1) {
            if ((pos + options.query.length) < len)
                res = s.substr(0, len - 3) + "...";
            else
                res = "..." + s.substr(pos - (Math.floor(len / 2) - options.query.length), len - 6) + "...";
        }
        else {
            res = s.substr(0, len - 3) + "...";
        }
        return res;
    }

    this.parseSearchResult = function(res, basePath, options) {
        var namespace, prefix, lastFile, parts, file, lineno;
        var aLines      = (typeof res == "string" ? res : "").split(/([\n\r]+)/g);
        var i           = 0;
        var count       = 0;
        var filecount   = 0;
        var l           = aLines.length;
        var aXml        = ['<?xml version="1.0" encoding="utf-8"?><d:multistatus count="'];

        // Adding in default namespaces
        for (namespace in this.handler.xmlNamespaces) {
            prefix = this.handler.xmlNamespaces[namespace];
            aXml.push(' xmlns:' + prefix + '="' + namespace + '"');
        }

        aXml.push('><d:querydetail query="' + Util.escapeXml(options.query) + '" replacement="' + Util.escapeXml(options.replacement));

        if (options.killed)
            aXml.push('<d:maxreached/>');

        for (; i < l; ++i) {
            parts = aLines[i].split(":");

            if (parts.length < 3) continue;
            ++count;
            file = parts.shift();
            lineno = parseInt(parts.shift(), 10);
            if (!lineno)
                continue;
            if (file !== lastFile) {
                filecount++;
                if (lastFile)
                    aXml.push('</d:response>');
                aXml.push('<d:response path="' + encodeURI(options.uri
                    + Util.rtrim(file.replace(basePath, "")), "/") + '" query="',
                    Util.escapeXml(options.query), '">');
                lastFile = file;
            }
            aXml.push('<d:excerpt line="', lineno, '">',
                Util.escapeXml(truncate(parts.join(":"), options)), '</d:excerpt>');
        }
        if (count > 0)
            aXml.push('</d:response>');
        // add the final count to the multistatus XML element
        aXml[0] += count + '"';

        var optionsDesc = [];
        if (Util.isTrue(options.casesensitive)) {
            optionsDesc.push("case sensitive");
        }
        if (Util.isTrue(options.regexp)) {
            optionsDesc.push("regexp");
        }

        if (optionsDesc.length > 0) {
            optionsDesc = "(" + optionsDesc.join(", ") + ")";
        }
        else {
            optionsDesc = "";
        }

        aXml[3] += '" options="' + optionsDesc + '" count="' + count + '" filecount="' + filecount + '" />'; //add query metainfo
        return aXml.join("") + '</d:multistatus>';
    };
}).call(jsDAV_Codesearch_Plugin.prototype = new jsDAV_ServerPlugin());

module.exports = jsDAV_Codesearch_Plugin;
<commit_msg>Remove erroneous comment<commit_after>/*
 * @package jsDAV
 * @subpackage DAV
 * @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org>
 * @author Mike de Boer <info AT mikedeboer DOT nl>
 * @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
 */
"use strict";

var jsDAV              = require("./../../jsdav");
var jsDAV_ServerPlugin = require("./../plugin").jsDAV_ServerPlugin;

var Spawn = require("child_process").spawn;
var Util  = require("./../util");

function jsDAV_Codesearch_Plugin(handler) {
    this.handler = handler;
    this.initialize();
}

jsDAV_Codesearch_Plugin.IGNORE_DIRS = {
    ".bzr"              : "Bazaar",
    ".cdv"              : "Codeville",
    "~.dep"             : "Interface Builder",
    "~.dot"             : "Interface Builder",
    "~.nib"             : "Interface Builder",
    "~.plst"            : "Interface Builder",
    ".git"              : "Git",
    ".hg"               : "Mercurial",
    ".pc"               : "quilt",
    ".svn"              : "Subversion",
    "blib"              : "Perl module building",
    "CVS"               : "CVS",
    "RCS"               : "RCS",
    "SCCS"              : "SCCS",
    "_darcs"            : "darcs",
    "_sgbak"            : "Vault/Fortress",
    "autom4te.cache"    : "autoconf",
    "cover_db"          : "Devel::Cover",
    "_build"            : "Module::Build"
};

jsDAV_Codesearch_Plugin.MAPPINGS = {
    "actionscript": ["as", "mxml"],
    "ada"         : ["ada", "adb", "ads"],
    "asm"         : ["asm", "s"],
    "batch"       : ["bat", "cmd"],
    //"binary"      : q{Binary files, as defined by Perl's -B op (default: off)},
    "cc"          : ["c", "h", "xs"],
    "cfmx"        : ["cfc", "cfm", "cfml"],
    "clojure"     : ["clj"],
    "cpp"         : ["cpp", "cc", "cxx", "m", "hpp", "hh", "h", "hxx"],
    "csharp"      : ["cs"],
    "css"         : ["css", "less", "scss", "sass"],
    "coffee"      : ["coffee"],
    "elisp"       : ["el"],
    "erlang"      : ["erl", "hrl"],
    "fortran"     : ["f", "f77", "f90", "f95", "f03", "for", "ftn", "fpp"],
    "haskell"     : ["hs", "lhs"],
    "hh"          : ["h"],
    "html"        : ["htm", "html", "shtml", "xhtml"],
    "jade"        : ["jade"],
    "java"        : ["java", "properties"],
    "groovy"      : ["groovy"],
    "js"          : ["js"],
    "json"        : ["json"],
    "latex"       : ["latex", "ltx"],
    "jsp"         : ["jsp", "jspx", "jhtm", "jhtml"],
    "lisp"        : ["lisp", "lsp"],
    "lua"         : ["lua"],
    "make"        : ["makefile"],
    "mason"       : ["mas", "mhtml", "mpl", "mtxt"],
    "markdown"    : ["md", "markdown"],
    "objc"        : ["m", "h"],
    "objcpp"      : ["mm", "h"],
    "ocaml"       : ["ml", "mli"],
    "parrot"      : ["pir", "pasm", "pmc", "ops", "pod", "pg", "tg"],
    "perl"        : ["pl", "pm", "pod", "t"],
    "php"         : ["php", "phpt", "php3", "php4", "php5", "phtml"],
    "plone"       : ["pt", "cpt", "metadata", "cpy", "py"],
    "powershell"  : ["ps1"],
    "python"      : ["py"],
    "rake"        : ["rakefile"],
    "ruby"        : ["rb", "ru", "rhtml", "rjs", "rxml", "erb", "rake", "gemspec"],
    "scala"       : ["scala"],
    "scheme"      : ["scm", "ss"],
    "shell"       : ["sh", "bash", "csh", "tcsh", "ksh", "zsh"],
    //"skipped"     : "q"{"Files but not directories normally skipped by ack ("default": "off")},
    "smalltalk"   : ["st"],
    "sql"         : ["sql", "ctl"],
    "tcl"         : ["tcl", "itcl", "itk"],
    "tex"         : ["tex", "cls", "sty"],
    "text"        : ["txt"],
    "textile"     : ["textile"],
    "tt"          : ["tt", "tt2", "ttml"],
    "vb"          : ["bas", "cls", "frm", "ctl", "vb", "resx"],
    "vim"         : ["vim"],
    "yaml"        : ["yaml", "yml"],
    "xml"         : ["xml", "dtd", "xslt", "ent", "rdf", "rss", "svg", "wsdl", "atom", "mathml", "mml"]
};
var exts = [];
for (var type in jsDAV_Codesearch_Plugin.MAPPINGS) {
    exts = exts.concat(jsDAV_Codesearch_Plugin.MAPPINGS[type]);
}
// grep pattern matching for extensions
jsDAV_Codesearch_Plugin.PATTERN_EXT = Util.makeUnique(exts).join(",");
var dirs = [];
for (type in jsDAV_Codesearch_Plugin.IGNORE_DIRS) {
    dirs.push(type);
}
dirs = Util.makeUnique(dirs);
jsDAV_Codesearch_Plugin.PATTERN_DIR  = Util.escapeRegExp(dirs.join("|"));
jsDAV_Codesearch_Plugin.PATTERN_EDIR = dirs.join(",");
jsDAV_Codesearch_Plugin.GREP_CMD = "grep";
jsDAV_Codesearch_Plugin.PERL_CMD = "perl";
jsDAV_Codesearch_Plugin.MAXSIZE = 68157440; // 65MB

(function() {
    this.initialize = function() {
        this.handler.addEventListener("report", this.httpReportHandler.bind(this));
    };

    this.httpReportHandler = function(e, reportName, dom) {
        if (reportName != "{DAV:}codesearch")
            return e.next();
        e.stop();

        var uri     = this.handler.getRequestUri();
        var options = this.parseOptions(dom);
        var self    = this;
        options.uri = uri;
        this.handler.server.tree.getNodeForPath(uri, function(err, node) {
            if (err)
                return e.next(err);
            if (jsDAV.debugMode)
                Util.log("report" + reportName + ", " + node.path + ", ", options);

            self.doCodesearch(node, options, function(err, sResults) {
                //if (err)
                //    return e.stop(err);
                self.handler.httpResponse.writeHead(207, {"Content-Type":"text/xml; charset=utf-8"});
                self.handler.httpResponse.end(sResults);
                e.stop();
            });
        });
    };

    this.parseOptions = function(dom) {
        var options = {};
        for (var child, i = 0, l = dom.childNodes.length; i < l; ++i) {
            child = dom.childNodes[i];
            if (!child || child.nodeType != 1)
                continue;
            options[child.tagName] = child.nodeValue;
        }
        return options;
    };

    this.doCodesearch = function(node, options, cbsearch) {
        var cmd = jsDAV_Codesearch_Plugin.GREP_CMD + " -P -s -r --color=never --binary-files=without-match -n " + ( !Util.isTrue(options.casesensitive) ? "-i" : "" );

        var self  = this;// Util.empty(options.replacement) ? "" : options.replacement

        var include = "";

        if (!Util.empty(options.pattern)) {
            var patterns = options.pattern.split(",");

            if (patterns.length > 0) {
                patterns.forEach(function (p, idx) {
                    patterns[idx] = p.split('.').pop(); // trim "*.", e.g. *.js -> js, foo.js.cs -> cs
                });

                patterns.push("");
                include = "*.{" + patterns.join(",") + "}";
            }
            else {
                include = "*.{" + jsDAV_Codesearch_Plugin.PATTERN_EXT + "}";
            }
        }
        else {
            include = "*.{" + jsDAV_Codesearch_Plugin.PATTERN_EXT + "}";
        }

        if (options.maxresults) {
            cmd += "-m " + parseInt(options.maxresults, 10);
        }

        var query = options.query;
        // grep has a funny way of handling new lines (that is to say, it's non-existent)
        // if we're not doing a regex search, then we must split everything between the
        // new lines, escape the content, and then smush it back together; due to
        // new lines, this is also why we're  now passing -P as default to grep
        if (!Util.isTrue(options.replaceAll) && !Util.isTrue(options.regexp)) {
            var splitQuery = query.split("\\n");

            for (var q in splitQuery) {
                splitQuery[q] = Util.grepEscapeRegExp(splitQuery[q]);
            }
            query = splitQuery.join("\\n");
        }

        cmd += " --exclude=*{" + jsDAV_Codesearch_Plugin.PATTERN_EDIR + "}*"
            +  " --include=" + include
            + " '" + query + "'"
            + " \"" + Util.escapeShell(node.path) + "\"";

        if (Util.isTrue(options.replaceAll) && !Util.empty(options.replacement)) {
            // pipe the grep results into perl
            cmd += " -l | xargs " + jsDAV_Codesearch_Plugin.PERL_CMD +
            // print the grep result to STDOUT (to arrange in parseSearchResult())
            " -p0777i -e 'print STDOUT \"$ARGV:$.:$_\""     +
            // do the actual replace
            "if s/" + query + "/" + options.replacement + "/mg" + ( !Util.isTrue(options.casesensitive) ? "i" : "" ) + ";'"
        }

        if (jsDAV.debugMode) {
            Util.log("search command: " + cmd);
        }

        var out  = "";
        var err  = "";
        var grep = Spawn("/bin/bash", ["-c", cmd]);

        grep.stdout.setEncoding("utf8");
        grep.stderr.setEncoding("utf8");
        grep.stdout.on("data", function(data) {
            if (!Util.empty(data)) {
                out += data;

                if (out.length >= jsDAV_Codesearch_Plugin.MAXSIZE) {
                    grep.kill();
                }
            }
        });
        grep.stderr.on("data", function(data) {
            if (!Util.empty(data))
                err += data;
        });
        grep.on("exit", function(code, signal) {
            options.killed = (signal == "SIGTERM");

            cbsearch(err, self.parseSearchResult(out || "", node.path, options));
        });
    };

    function truncate(s, options) {
        var len = options.maxexcerptlength || 200;
        s = Util.trim(s);
        if (s.length <= len)
            return s;
        var res,
            pos = s.indexOf(options.query);
        if (pos > -1) {
            if ((pos + options.query.length) < len)
                res = s.substr(0, len - 3) + "...";
            else
                res = "..." + s.substr(pos - (Math.floor(len / 2) - options.query.length), len - 6) + "...";
        }
        else {
            res = s.substr(0, len - 3) + "...";
        }
        return res;
    }

    this.parseSearchResult = function(res, basePath, options) {
        var namespace, prefix, lastFile, parts, file, lineno;
        var aLines      = (typeof res == "string" ? res : "").split(/([\n\r]+)/g);
        var i           = 0;
        var count       = 0;
        var filecount   = 0;
        var l           = aLines.length;
        var aXml        = ['<?xml version="1.0" encoding="utf-8"?><d:multistatus count="'];

        // Adding in default namespaces
        for (namespace in this.handler.xmlNamespaces) {
            prefix = this.handler.xmlNamespaces[namespace];
            aXml.push(' xmlns:' + prefix + '="' + namespace + '"');
        }

        aXml.push('><d:querydetail query="' + Util.escapeXml(options.query) + '" replacement="' + Util.escapeXml(options.replacement));

        if (options.killed)
            aXml.push('<d:maxreached/>');

        for (; i < l; ++i) {
            parts = aLines[i].split(":");

            if (parts.length < 3) continue;
            ++count;
            file = parts.shift();
            lineno = parseInt(parts.shift(), 10);
            if (!lineno)
                continue;
            if (file !== lastFile) {
                filecount++;
                if (lastFile)
                    aXml.push('</d:response>');
                aXml.push('<d:response path="' + encodeURI(options.uri
                    + Util.rtrim(file.replace(basePath, "")), "/") + '" query="',
                    Util.escapeXml(options.query), '">');
                lastFile = file;
            }
            aXml.push('<d:excerpt line="', lineno, '">',
                Util.escapeXml(truncate(parts.join(":"), options)), '</d:excerpt>');
        }
        if (count > 0)
            aXml.push('</d:response>');
        // add the final count to the multistatus XML element
        aXml[0] += count + '"';

        var optionsDesc = [];
        if (Util.isTrue(options.casesensitive)) {
            optionsDesc.push("case sensitive");
        }
        if (Util.isTrue(options.regexp)) {
            optionsDesc.push("regexp");
        }

        if (optionsDesc.length > 0) {
            optionsDesc = "(" + optionsDesc.join(", ") + ")";
        }
        else {
            optionsDesc = "";
        }

        aXml[3] += '" options="' + optionsDesc + '" count="' + count + '" filecount="' + filecount + '" />'; //add query metainfo
        return aXml.join("") + '</d:multistatus>';
    };
}).call(jsDAV_Codesearch_Plugin.prototype = new jsDAV_ServerPlugin());

module.exports = jsDAV_Codesearch_Plugin;
<|endoftext|>


<commit_before>/**
 * Copyright 2015-2019 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "/web_modules/lit-element.js";
import UtilsNew from "../../utilsNew.js";
import "../commons/opencga-browser.js";

export default class OpencgaFileBrowser extends LitElement {

    constructor() {
        super();

        // Set status and init private properties
        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object
            },
            query: {
                type: Object
            },
            facetQuery: {
                type: Object
            },
            selectedFacet: {
                type: Object
            },
            config: {
                type: Object
            }
        };
    }

    _init() {
        this._prefix = "fb" + UtilsNew.randomString(6);

        // These are for making the queries to server
        this.facetFields = [];
        this.facetRanges = [];

        this.facetFieldsName = [];
        this.facetRangeFields = [];

        this.facets = new Set();
        this.facetFilters = [];

        this.facetActive = true;
        this.selectedFacet = {};
        this.selectedFacetFormatted = {};
        this.errorState = false;

        this._config = this.getDefaultConfig();
    }

    connectedCallback() {
        super.connectedCallback();
        this._config = {...this.getDefaultConfig(), ...this.config};
    }

    updated(changedProperties) {
        if (changedProperties.has("config")) {
            this._config = {...this.getDefaultConfig(), ...this.config};
            this.requestUpdate();
        }
    }

    getDefaultConfig() {
        return {
            title: "File Browser",
            //active: false,
            icon: "fab fa-searchengin",
            description: "",
            searchButtonText: "Search",
            views: [
                {
                    id: "table-tab",
                    name: "Table result",
                    icon: "fa fa-table",
                    active: true
                },
                {
                    id: "facet-tab",
                    name: "Aggregation stats",
                    icon: "fas fa-chart-bar"
                }
            ],
            filter: {
                sections: [
                    {
                        title: "Section title",
                        collapsed: false,
                        fields: [
                            {
                                id: "name",
                                name: "Name",
                                type: "string",
                                placeholder: "accepted_hits.bam, phenotypes.vcf...",
                                description: ""
                            },
                            {
                                id: "sampleIds",
                                name: "Sample ID",
                                type: "string",
                                placeholder: "HG01879, HG01880, HG01881...",
                                description: ""
                            },/*
                            {
                                id: "path",
                                name: "Path",
                                type: "string",
                                placeholder: "genomes/resources/files/...",
                                description: ""
                            },*/
                            {
                                id: "directory",
                                name: "Directory",
                                type: "string",
                                placeholder: "genomes/resources/files/...",
                                description: ""
                            },
                            {
                                id: "format",
                                name: "Format",
                                type: "category",
                                allowedValues: ["VCF", "BCF", "GVCF", "TBI", "BIGWIG", "SAM", "BAM", "BAI", "CRAM", "CRAI", "FASTQ", "FASTA", "PED", "TAB_SEPARATED_VALUES", "COMMA_SEPARATED_VALUES", "XML", "PROTOCOL_BUFFER", "JSON", "AVRO", "PARQUET", "IMAGE", "PLAIN", "BINARY", "EXECUTABLE", "GZIP", "NONE", "UNKNOWN"],
                                placeholder: "genomes/resources/files/...",
                                description: ""
                            },
                            {
                                id: "bioformat",
                                name: "Bioformat",
                                type: "category",
                                allowedValues: ["MICROARRAY_EXPRESSION_ONECHANNEL_AGILENT", "MICROARRAY_EXPRESSION_ONECHANNEL_AFFYMETRIX", "MICROARRAY_EXPRESSION_ONECHANNEL_GENEPIX", "MICROARRAY_EXPRESSION_TWOCHANNELS_AGILENT", "MICROARRAY_EXPRESSION_TWOCHANNELS_GENEPIX", "DATAMATRIX_EXPRESSION", "IDLIST", "IDLIST_RANKED", "ANNOTATION_GENEVSANNOTATION", "OTHER_NEWICK", "OTHER_BLAST", "OTHER_INTERACTION", "OTHER_GENOTYPE", "OTHER_PLINK", "OTHER_VCF", "OTHER_PED", "VARIANT", "ALIGNMENT", "COVERAGE", "SEQUENCE", "PEDIGREE", "REFERENCE_GENOME", "NONE", "UNKNOWN"],
                                placeholder: "ALIGNMENT,VARIANT...",
                                description: ""
                            },
                            {
                                id: "internal.index.status.name",
                                name: "Index Status",
                                allowedValues: ["READY", "DELETED", "TRASHED", "STAGE", "MISSING", "PENDING_DELETE", "DELETING", "REMOVED", "NONE"],
                                type: "category"
                            },
                            {
                                id: "date",
                                name: "Date",
                                type: "date",
                                description: ""
                            },
                            {
                                id: "annotations",
                                name: "File Annotations",
                                description: ""
                            }
                        ]
                    }
                ],
                examples: [
                    {
                        id: "Alignment",
                        active: false,
                        query: {
                            format: "BAM,SAM,BIGWIG",
                            bioformat: "ALIGNMENT",
                            creationDate: ">=20200216"
                        }
                    }
                ],
                result: {
                    grid: {}
                },
                detail: {
                    title: "File",
                    showTitle: true,
                    items: [
                        {
                            id: "file-view",
                            name: "Overview",
                            active: true,
                            render: (file, active, opencgaSession) => {
                                return html` <opencga-file-view .opencgaSession="${opencgaSession}" .file="${file}"></opencga-file-view>`;
                            }
                        },
                        {
                            id: "file-preview",
                            name: "Preview",
                            render: (file, active, opencgaSession) => {
                                return html`<opencga-file-preview .opencgaSession=${opencgaSession} .active="${active}" .file="${file}"></opencga-file-preview>`;
                            }
                        },
                        {
                            id: "json-view",
                            name: "JSON Data",
                            mode: "development",
                            render: (file, active, opencgaSession) => {
                                return html`<json-viewer .data="${file}"></json-viewer>`;
                            }
                        }
                    ]
                }
            },
            aggregation: {
                //default: ["creationYear>>creationMonth", "format", "bioformat", "format>>bioformat", "status", "size[0..214748364800]:10737418240", "numSamples[0..10]:1"],
                default: ["type>>size[0..214748364800]:10737418240", "format>>avg(size)", "release"],
                result: {
                    numColumns: 2
                },
                sections: [
                    {
                        name: "File attributes",
                        // collapsed: false,
                        fields: [
                            {
                                id: "studyId",
                                name: "Study id",
                                type: "string",
                                description: "Study [[user@]project:]study where study and project can be either the ID or UUID"
                            },
                            {
                                id: "creationYear",
                                name: "Creation Year",
                                type: "string",
                                description: "Creation year"
                            },
                            {
                                id: "creationMonth",
                                name: "Creation Month",
                                type: "category",
                                multiple: true,
                                allowedValues: ["JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"],
                                description: "Creation month (JANUARY, FEBRUARY...)"
                            },
                            {
                                id: "creationDay",
                                name: "Creation Day",
                                type: "category",
                                allowedValues: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"],
                                description: "Creation day"
                            },
                            {
                                id: "creationDayOfWeek",
                                name: "Creation Day Of Week",
                                type: "category",
                                allowedValues: ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"],
                                description: "Creation day of week (MONDAY, TUESDAY...)"
                            },
                            {
                                id: "status",
                                name: "Status",
                                type: "category",
                                allowedValues: ["READY", "DELETED", "TRASHED", "STAGE", "MISSING", "PENDING_DELETE", "DELETING", "REMOVED"],
                                description: "Status"
                            },
                            {
                                id: "release",
                                name: "Release",
                                type: "string",
                                description: "Release"
                            },
                            {
                                id: "name",
                                name: "Name",
                                type: "string",
                                description: "Name"
                            },
                            {
                                id: "type",
                                name: "Type",
                                type: "category",
                                allowedValues: ["FILE", "DIRECTORY"],
                                description: "Type"
                            },
                            {
                                id: "format",
                                name: "Format",
                                type: "category",
                                allowedValues: ["VCF", "BCF", "GVCF", "TBI", "BIGWIG", "SAM", "BAM", "BAI", "CRAM", "CRAI", "FASTQ", "FASTA", "PED", "TAB_SEPARATED_VALUES", "COMMA_SEPARATED_VALUES", "XML", "PROTOCOL_BUFFER", "JSON", "AVRO", "PARQUET", "IMAGE", "PLAIN", "BINARY", "EXECUTABLE", "GZIP", "NONE", "UNKNOWN"],
                                description: "Format"
                            },
                            {
                                id: "bioformat",
                                name: "Bioformat",
                                type: "category",
                                allowedValues: ["MICROARRAY_EXPRESSION_ONECHANNEL_AGILENT", "MICROARRAY_EXPRESSION_ONECHANNEL_AFFYMETRIX", "MICROARRAY_EXPRESSION_ONECHANNEL_GENEPIX", "MICROARRAY_EXPRESSION_TWOCHANNELS_AGILENT", "MICROARRAY_EXPRESSION_TWOCHANNELS_GENEPIX", "DATAMATRIX_EXPRESSION", "IDLIST", "IDLIST_RANKED", "ANNOTATION_GENEVSANNOTATION", "OTHER_NEWICK", "OTHER_BLAST", "OTHER_INTERACTION", "OTHER_GENOTYPE", "OTHER_PLINK", "OTHER_VCF", "OTHER_PED", "VARIANT", "ALIGNMENT", "COVERAGE", "SEQUENCE", "PEDIGREE", "REFERENCE_GENOME", "NONE", "UNKNOWN"],
                                description: "Bioformat"
                            },
                            {
                                id: "external",
                                name: "External",
                                type: "category",
                                allowedValues: ["true", "false"],
                                defaultValue: "false",
                                description: "External"
                            },
                            {
                                id: "size",
                                name: "Size",
                                type: "integer",
                                defaultValue: "[0..214748364800]:10737418240",
                                description: "Size"
                            },
                            {
                                id: "softwareName",
                                name: "Software Name",
                                type: "string",
                                description: "Software name"
                            },
                            {
                                id: "softwareVersion",
                                name: "Software Version",
                                type: "string",
                                description: "Software version"
                            },
                            {
                                id: "experimentTechnology",
                                name: "Experiment Technology",
                                type: "string",
                                description: "Experiment technology"
                            },
                            {
                                id: "experimentMethod",
                                name: "Experiment Method",
                                type: "string",
                                description: "Experiment method"
                            },
                            {
                                id: "experimentNucleicAcidType",
                                name: "Experiment Nucleic Acid Type",
                                type: "string",
                                description: "Experiment nucleic acid type"
                            },
                            {
                                id: "experimentManufacturer",
                                name: "Experiment Manufacturer",
                                type: "string",
                                description: "Experiment manufacturer"
                            },
                            {
                                id: "experimentPlatform",
                                name: "Experiment Platform",
                                type: "string",
                                description: "Experiment platform"
                            },
                            {
                                id: "experimentLibrary",
                                name: "Experiment Library",
                                type: "string",
                                description: "Experiment library"
                            },
                            {
                                id: "experimentCenter",
                                name: "Experiment Center",
                                type: "string",
                                description: "Experiment center"
                            },
                            {
                                id: "experimentLab",
                                name: "Experiment Lab",
                                type: "string",
                                description: "Experiment lab"
                            },
                            {
                                id: "experimentResponsible",
                                name: "Experiment Responsible",
                                type: "string",
                                description: "Experiment responsible"
                            },
                            {
                                id: "tags",
                                name: "Tags",
                                type: "string",
                                description: "Tags"
                            },
                            {
                                id: "numSamples",
                                name: "Number Of Samples",
                                type: "integer",
                                description: "Number of samples",
                                defaultValue: "[0..10]:1"
                            },
                            {
                                id: "numRelatedFiles",
                                name: "Number Of Related Files",
                                type: "string",
                                description: "Number of related files"
                            },
                            {
                                id: "annotations",
                                name: "Annotations",
                                type: "string",
                                description: "Annotations, e.g: key1=value(,key2=value)"
                            }
                        ]
                    },
                    {
                        name: "Advanced",
                        fields: [
                            {
                                id: "field",
                                name: "Field",
                                type: "string",
                                description: "List of fields separated by semicolons, e.g.: studies;type. For nested fields use >>, e.g.: studies>>biotype;type;numSamples[0..10]:1"
                            }
                        ]
                    }
                ]
            }
        };
    }

    render() {
        return this._config ? html`
            <opencga-browser  resource="FILE"
                            .opencgaSession="${this.opencgaSession}"
                            .query="${this.query}"
                            .config="${this._config}">
            </opencga-browser>` : null;
    }

}

customElements.define("opencga-file-browser", OpencgaFileBrowser);
<commit_msg>opencga-file-browser config fix<commit_after>/**
 * Copyright 2015-2019 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "/web_modules/lit-element.js";
import UtilsNew from "../../utilsNew.js";
import "../commons/opencga-browser.js";

export default class OpencgaFileBrowser extends LitElement {

    constructor() {
        super();

        // Set status and init private properties
        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object
            },
            query: {
                type: Object
            },
            facetQuery: {
                type: Object
            },
            selectedFacet: {
                type: Object
            },
            config: {
                type: Object
            }
        };
    }

    _init() {
        this._prefix = "fb" + UtilsNew.randomString(6);

        // These are for making the queries to server
        this.facetFields = [];
        this.facetRanges = [];

        this.facetFieldsName = [];
        this.facetRangeFields = [];

        this.facets = new Set();
        this.facetFilters = [];

        this.facetActive = true;
        this.selectedFacet = {};
        this.selectedFacetFormatted = {};
        this.errorState = false;

        this._config = this.getDefaultConfig();
    }

    connectedCallback() {
        super.connectedCallback();
        this._config = {...this.getDefaultConfig(), ...this.config};
    }

    updated(changedProperties) {
        if (changedProperties.has("config")) {
            this._config = {...this.getDefaultConfig(), ...this.config};
            this.requestUpdate();
        }
    }

    getDefaultConfig() {
        return {
            title: "File Browser",
            //active: false,
            icon: "fab fa-searchengin",
            description: "",
            searchButtonText: "Search",
            views: [
                {
                    id: "table-tab",
                    name: "Table result",
                    icon: "fa fa-table",
                    active: true
                },
                {
                    id: "facet-tab",
                    name: "Aggregation stats",
                    icon: "fas fa-chart-bar"
                }
            ],
            filter: {
                sections: [
                    {
                        title: "Section title",
                        collapsed: false,
                        fields: [
                            {
                                id: "name",
                                name: "Name",
                                type: "string",
                                placeholder: "accepted_hits.bam, phenotypes.vcf...",
                                description: ""
                            },
                            {
                                id: "sampleIds",
                                name: "Sample ID",
                                type: "string",
                                placeholder: "HG01879, HG01880, HG01881...",
                                description: ""
                            },/*
                            {
                                id: "path",
                                name: "Path",
                                type: "string",
                                placeholder: "genomes/resources/files/...",
                                description: ""
                            },*/
                            {
                                id: "directory",
                                name: "Directory",
                                type: "string",
                                placeholder: "genomes/resources/files/...",
                                description: ""
                            },
                            {
                                id: "format",
                                name: "Format",
                                type: "category",
                                allowedValues: ["VCF", "BCF", "GVCF", "TBI", "BIGWIG", "SAM", "BAM", "BAI", "CRAM", "CRAI", "FASTQ", "FASTA", "PED", "TAB_SEPARATED_VALUES", "COMMA_SEPARATED_VALUES", "XML", "PROTOCOL_BUFFER", "JSON", "AVRO", "PARQUET", "IMAGE", "PLAIN", "BINARY", "EXECUTABLE", "GZIP", "NONE", "UNKNOWN"],
                                placeholder: "genomes/resources/files/...",
                                description: ""
                            },
                            {
                                id: "bioformat",
                                name: "Bioformat",
                                type: "category",
                                allowedValues: ["MICROARRAY_EXPRESSION_ONECHANNEL_AGILENT", "MICROARRAY_EXPRESSION_ONECHANNEL_AFFYMETRIX", "MICROARRAY_EXPRESSION_ONECHANNEL_GENEPIX", "MICROARRAY_EXPRESSION_TWOCHANNELS_AGILENT", "MICROARRAY_EXPRESSION_TWOCHANNELS_GENEPIX", "DATAMATRIX_EXPRESSION", "IDLIST", "IDLIST_RANKED", "ANNOTATION_GENEVSANNOTATION", "OTHER_NEWICK", "OTHER_BLAST", "OTHER_INTERACTION", "OTHER_GENOTYPE", "OTHER_PLINK", "OTHER_VCF", "OTHER_PED", "VARIANT", "ALIGNMENT", "COVERAGE", "SEQUENCE", "PEDIGREE", "REFERENCE_GENOME", "NONE", "UNKNOWN"],
                                placeholder: "ALIGNMENT,VARIANT...",
                                description: ""
                            },
                            {
                                id: "internal.index.status.name",
                                name: "Index Status",
                                allowedValues: ["READY", "DELETED", "TRASHED", "STAGE", "MISSING", "PENDING_DELETE", "DELETING", "REMOVED", "NONE"],
                                type: "category"
                            },
                            {
                                id: "date",
                                name: "Date",
                                type: "date",
                                description: ""
                            },
                            {
                                id: "annotations",
                                name: "File Annotations",
                                description: ""
                            }
                        ]
                    }
                ],
                examples: [
                    {
                        id: "Alignment",
                        active: false,
                        query: {
                            format: "BAM,SAM,BIGWIG",
                            bioformat: "ALIGNMENT",
                            creationDate: ">=20200216"
                        }
                    }
                ],
                result: {
                    grid: {}
                },
                detail: {
                    title: "File",
                    showTitle: true,
                    items: [
                        {
                            id: "file-view",
                            name: "Overview",
                            active: true,
                            render: (file, active, opencgaSession) => {
                                return html` <opencga-file-view .opencgaSession="${opencgaSession}" .file="${file}"></opencga-file-view>`;
                            }
                        },
                        {
                            id: "file-preview",
                            name: "Preview",
                            render: (file, active, opencgaSession) => {
                                return html`<opencga-file-preview .opencgaSession=${opencgaSession} .active="${active}" .file="${file}"></opencga-file-preview>`;
                            }
                        },
                        {
                            id: "json-view",
                            name: "JSON Data",
                            mode: "development",
                            render: (file, active, opencgaSession) => {
                                return html`<json-viewer .data="${file}"></json-viewer>`;
                            }
                        }
                    ]
                }
            },
            aggregation: {
                default: ["creationYear>>creationMonth", "format", "bioformat", "format>>bioformat", "status", "size[0..214748364800]:10737418240", "numSamples[0..10]:1"],
                // default: ["type>>size[0..214748364800]:10737418240", "format>>avg(size)", "release"],
                result: {
                    numColumns: 2
                },
                sections: [
                    {
                        name: "File attributes",
                        // collapsed: false,
                        fields: [
                            {
                                id: "studyId",
                                name: "Study id",
                                type: "string",
                                description: "Study [[user@]project:]study where study and project can be either the ID or UUID"
                            },
                            {
                                id: "creationYear",
                                name: "Creation Year",
                                type: "string",
                                description: "Creation year"
                            },
                            {
                                id: "creationMonth",
                                name: "Creation Month",
                                type: "category",
                                multiple: true,
                                allowedValues: ["JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"],
                                description: "Creation month (JANUARY, FEBRUARY...)"
                            },
                            {
                                id: "creationDay",
                                name: "Creation Day",
                                type: "category",
                                allowedValues: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"],
                                description: "Creation day"
                            },
                            {
                                id: "creationDayOfWeek",
                                name: "Creation Day Of Week",
                                type: "category",
                                allowedValues: ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"],
                                description: "Creation day of week (MONDAY, TUESDAY...)"
                            },
                            {
                                id: "status",
                                name: "Status",
                                type: "category",
                                allowedValues: ["READY", "DELETED", "TRASHED", "STAGE", "MISSING", "PENDING_DELETE", "DELETING", "REMOVED"],
                                description: "Status"
                            },
                            {
                                id: "release",
                                name: "Release",
                                type: "string",
                                description: "Release"
                            },
                            {
                                id: "name",
                                name: "Name",
                                type: "string",
                                description: "Name"
                            },
                            {
                                id: "type",
                                name: "Type",
                                type: "category",
                                allowedValues: ["FILE", "DIRECTORY"],
                                description: "Type"
                            },
                            {
                                id: "format",
                                name: "Format",
                                type: "category",
                                allowedValues: ["VCF", "BCF", "GVCF", "TBI", "BIGWIG", "SAM", "BAM", "BAI", "CRAM", "CRAI", "FASTQ", "FASTA", "PED", "TAB_SEPARATED_VALUES", "COMMA_SEPARATED_VALUES", "XML", "PROTOCOL_BUFFER", "JSON", "AVRO", "PARQUET", "IMAGE", "PLAIN", "BINARY", "EXECUTABLE", "GZIP", "NONE", "UNKNOWN"],
                                description: "Format"
                            },
                            {
                                id: "bioformat",
                                name: "Bioformat",
                                type: "category",
                                allowedValues: ["MICROARRAY_EXPRESSION_ONECHANNEL_AGILENT", "MICROARRAY_EXPRESSION_ONECHANNEL_AFFYMETRIX", "MICROARRAY_EXPRESSION_ONECHANNEL_GENEPIX", "MICROARRAY_EXPRESSION_TWOCHANNELS_AGILENT", "MICROARRAY_EXPRESSION_TWOCHANNELS_GENEPIX", "DATAMATRIX_EXPRESSION", "IDLIST", "IDLIST_RANKED", "ANNOTATION_GENEVSANNOTATION", "OTHER_NEWICK", "OTHER_BLAST", "OTHER_INTERACTION", "OTHER_GENOTYPE", "OTHER_PLINK", "OTHER_VCF", "OTHER_PED", "VARIANT", "ALIGNMENT", "COVERAGE", "SEQUENCE", "PEDIGREE", "REFERENCE_GENOME", "NONE", "UNKNOWN"],
                                description: "Bioformat"
                            },
                            {
                                id: "external",
                                name: "External",
                                type: "category",
                                allowedValues: ["true", "false"],
                                defaultValue: "false",
                                description: "External"
                            },
                            {
                                id: "size",
                                name: "Size",
                                type: "integer",
                                defaultValue: "[0..214748364800]:10737418240",
                                description: "Size"
                            },
                            {
                                id: "softwareName",
                                name: "Software Name",
                                type: "string",
                                description: "Software name"
                            },
                            {
                                id: "softwareVersion",
                                name: "Software Version",
                                type: "string",
                                description: "Software version"
                            },
                            {
                                id: "experimentTechnology",
                                name: "Experiment Technology",
                                type: "string",
                                description: "Experiment technology"
                            },
                            {
                                id: "experimentMethod",
                                name: "Experiment Method",
                                type: "string",
                                description: "Experiment method"
                            },
                            {
                                id: "experimentNucleicAcidType",
                                name: "Experiment Nucleic Acid Type",
                                type: "string",
                                description: "Experiment nucleic acid type"
                            },
                            {
                                id: "experimentManufacturer",
                                name: "Experiment Manufacturer",
                                type: "string",
                                description: "Experiment manufacturer"
                            },
                            {
                                id: "experimentPlatform",
                                name: "Experiment Platform",
                                type: "string",
                                description: "Experiment platform"
                            },
                            {
                                id: "experimentLibrary",
                                name: "Experiment Library",
                                type: "string",
                                description: "Experiment library"
                            },
                            {
                                id: "experimentCenter",
                                name: "Experiment Center",
                                type: "string",
                                description: "Experiment center"
                            },
                            {
                                id: "experimentLab",
                                name: "Experiment Lab",
                                type: "string",
                                description: "Experiment lab"
                            },
                            {
                                id: "experimentResponsible",
                                name: "Experiment Responsible",
                                type: "string",
                                description: "Experiment responsible"
                            },
                            {
                                id: "tags",
                                name: "Tags",
                                type: "string",
                                description: "Tags"
                            },
                            {
                                id: "numSamples",
                                name: "Number Of Samples",
                                type: "integer",
                                description: "Number of samples",
                                defaultValue: "[0..10]:1"
                            },
                            {
                                id: "numRelatedFiles",
                                name: "Number Of Related Files",
                                type: "string",
                                description: "Number of related files"
                            },
                            {
                                id: "annotations",
                                name: "Annotations",
                                type: "string",
                                description: "Annotations, e.g: key1=value(,key2=value)"
                            }
                        ]
                    },
                    {
                        name: "Advanced",
                        fields: [
                            {
                                id: "field",
                                name: "Field",
                                type: "string",
                                description: "List of fields separated by semicolons, e.g.: studies;type. For nested fields use >>, e.g.: studies>>biotype;type;numSamples[0..10]:1"
                            }
                        ]
                    }
                ]
            }
        };
    }

    render() {
        return this._config ? html`
            <opencga-browser  resource="FILE"
                            .opencgaSession="${this.opencgaSession}"
                            .query="${this.query}"
                            .config="${this._config}">
            </opencga-browser>` : null;
    }

}

customElements.define("opencga-file-browser", OpencgaFileBrowser);
<|endoftext|>


<commit_before>export class SrfSearch {

    constructor($inputField, $submitButton, $menu, options = {maxSuggestionCount: 7, minSearchLength: 2}) {
        this.$inputField = $inputField;
        // inputField and submitButton are distinct html elements for accessibility
        this.$submitButton = $submitButton;
        this.$menu = $menu;
        this.$closeIcon = $menu.parent().find('.close-search');
        this.options = options;

        this.expandable = (options && options.expandable) ? options.expandable : false;
        console.log(this.expandable);
        this.typeaheadUrl = this.$inputField.data("typeahead-url");
        this.typeaheadData = null;
        this.suggestionUrl = '';
        this.currTimeout = null;
        this.initialWidth = 0;
        this.resizeTimout = 0;

        // search field is hidden before document.ready (events firing before document.ready can get lost)
        this.$inputField.show();
        this.registerListeners();
    }

    registerListeners() {
        this.$inputField.on("focus", (e) => {
            this.initTypeahead();
            this.disableArticle();
            this.expandSearch();
        });

        this.$closeIcon.on('click', (e) => {
            this.reset();
        })

        this.$inputField.on("keyup", (e) => {
            this.enhanceAccessibility();
            this.onKeyUp(e);
        });

        this.$inputField.on("keydown", (e) => {
            this.onKeyDown(e);
        });

        this.$inputField.on("blur", (e) => {
            this.enableArticle(); // in case of a click always leave.

            this.currTimeout = setTimeout(() => {
                this.hideMenu();
                this.unexpandSearch();
            }, 150);
        });

        this.$inputField.on("click", (e) => {
            e.stopPropagation();
        });

        $(window).on('resize', (e) => {
            if ($(window).width() < 720) {
                $('.searchbox').css('width', "");
            }

            this.$inputField.blur();
        });

        this.$menu.on('click', (e) => {
            e.stopPropagation();
            // do not prevent default to allow menu links be clicked.
            if (this.currTimeout !== null) {
                if (this.currTimeout) {
                    clearTimeout(this.currTimeout);
                }
                this.currTimeout = setTimeout(() => {
                    this.hideMenu();
                }, 500); // defer menu hiding to allow some time to show active state before closing.
            }
        });
    }

    onKeyUp(e) {
        switch (e.keyCode) {
            case 40: // down arrow or
            case 38: // up arrow
                break;
            case 9: // tab or
                this.hideMenu();
                break;
            case 27: // escape must unexpand the menu but not clear it
                this.clearInput();
                this.$inputField.blur();
                break;
            default:
                this.lookup();
        }
    }

    onKeyDown(e) {
        // if no suggestion is selected fall back to default browser behavior for form submission
        if (e.keyCode === 13) {
            if (this.suggestionUrl != '') {
                e.stopPropagation();
                e.preventDefault();
                location.href = this.suggestionUrl;
            }
        }
        if (e.keyCode === 40 || e.keyCode === 38) {
            let direction = e.keyCode === 40 ? 'down' : 'up';
            this.moveInMenu(direction);
        }
    }

    hideMenu() {
        this.$inputField.removeClass('search--has-results' );
        this.$menu.addClass('h-element--hide');
        this.suggestionUrl = '';
    }

    clearInput() {
        this.$inputField.val("");
        this.$submitButton.attr("tabindex", -1).attr("aria-hidden", true);
        this.hideCloseIcon();
        this.suggestionUrl = '';
    }

    reset() {
        this.clearInput();
        this.hideMenu();
        this.unexpandSearch();
    }

    moveInMenu(direction) {
        if (direction === 'down') {
            this.nextSuggestion();
        }
        else if (direction === 'up') {
            this.prevSuggestion();
        }
    }

    prevSuggestion() {
        let $active = this.$menu.find('.active').removeClass('active');
        let $prev = $active.prev();
        if ($prev.length === 0) {
            $prev = this.$menu.find('li').last();
        }
        this.suggestionUrl = $prev.find("a").attr('href');
        $prev.addClass('active');
    }

    nextSuggestion() {
        let $active = this.$menu.find('.active').removeClass('active');
        let $next = $active.next();
        if ($next.length === 0) {
            $next = $(this.$menu.find('li').first());
        }
        this.suggestionUrl = $next.find("a").attr('href');
        $next.addClass('active');
    }

    enhanceAccessibility() {
        // a search button only makes sense on desktop - when it's actually workin.
        // TODO: What is still needed here?
        if (!(('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0))) {
            // this works mobile as well unlike <enter>-keys an d the likes
            if (this.$inputField.val().length > 2 && this.$submitButton.attr("tabindex") == -1) {
                this.$submitButton.attr("tabindex", 0).attr("aria-hidden", false);
            } else if (this.$inputField.val().length == 0 && this.$submitButton.attr("tabindex") == 0) {
                this.$submitButton.attr("tabindex", -1).attr("aria-hidden", true);
            }
        }
    }

    initTypeahead() {
        if (this.typeaheadData === null) {
            $.getJSON(this.typeaheadUrl, (data) => {
                this.typeaheadData = data;
            })
            this.initialWidth = parseInt($('.searchbox:first').css('width'));
        }
        this.showCloseIconIfNeeded();
        this.expandSearch();

    }

    lookup() {
        // adjust close icon state regardless of search results
        this.showCloseIconIfNeeded();
        let results = [];
        let query = this.$inputField.val().toString().toLowerCase();
        if (this.typeaheadData === null) {
            return true;
        }

        if(query.length < this.options.minSearchLength) {
            this.hideMenu();
            return true;
        }

        this.typeaheadData.forEach((item) => {
            let matchIndex = item.name.toString().toLowerCase().indexOf(query);
            if (matchIndex >= 0) {
                results.push({name: item.name, url: item.url, matchIndex: matchIndex});
            }
        });

        if (results.length > 0) {
            results = results.slice(0, this.options.maxSuggestionCount);
            this.renderResults(results, query);
            this.$inputField.addClass('search--has-results' );
        } else {
            this.hideMenu();
        }
    }

    renderResults(results, query) {
        let html = '';

        results.forEach((result) => {
            let name = this.highlightQuery(query, result.name);
            html += `<li role="option" class="typeahead-suggestion" tabindex="-1"> <a href="${result.url}">${name}</a> </li>`;
        })
        this.$menu.css('width', this.$inputField.outerWidth() + "px");
        this.$menu.html(html).removeClass('h-element--hide');
    }

    highlightQuery(query, name) {
        query = query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
        return name.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
            return '' + match + ''
        })
    }

    showCloseIcon() {
        this.$closeIcon.removeClass('h-element--hide');
        let y = this.$inputField.position().top;
        let x = this.$inputField.position().left;
        x = x + this.$inputField.outerWidth() - this.$closeIcon.outerWidth();
        this.$closeIcon.css({'top': y, 'left': x});
    }

    hideCloseIcon() {
        this.$closeIcon.addClass('h-element--hide');
    }

    showCloseIconIfNeeded(deferred) {
        if (!deferred) {
            if (this.$inputField.val() === '') {
                this.hideCloseIcon();
            } else {
                this.showCloseIcon();
            }
        } else {
            if (this.currTimeout) {
                clearTimeout(this.currTimeout);
            }
            this.currTimeout = setTimeout(() => {this.showCloseIconIfNeeded()}, deferred);
        }
    }

    expandSearch() {
        if ($(window).width() < 720) {
            $('.searchbox').css('width', "");
            return;
        }

        if (!this.expandable) {
            return;
        }
        this.hideCloseIcon();
        this.showCloseIconIfNeeded(500); // currTimeout gets set here
        $('.searchbox').addClass('centered'); // add margin: 50% and animations and calculate the new width (90% of container, adjusted by width).

        // calculate new width (bar must be centered)
        let right = $('.menu-handle__info').offset().left;
        let left = $('.header__logo-img').offset().left;
        let newWidth = right - left - 77; // srf logo width and  margins and paddings :/
        $('.searchbox').css('width', newWidth);
    }

    unexpandSearch() {
        if (!this.expandable) {
            return;
        }
        if ($('.searchbox').hasClass('centered')) {
            this.hideCloseIcon();
            $('.searchbox').removeClass('centered');
            let right = parseInt($('.searchbox').css('right'));
            $('.searchbox').css('width', this.initialWidth);
        }
    }

    disableArticle() {
        if (!this.expandable) {
            return;
        }
        if ($(window).width() > 720 && $('body').hasClass('body--fixed') == false) {
            // add classes once
            $('body').addClass('search--overlay');
        }
    }

    enableArticle() {
        if (!this.expandable) {
            return;
        }
        $('body').removeClass('search--overlay');
    }
}

<commit_msg>Only adjust width of header searchbox, not footer SRFCMS-7416<commit_after>export class SrfSearch {

    constructor($inputField, $submitButton, $menu, options = {maxSuggestionCount: 7, minSearchLength: 2}) {
        this.$inputField = $inputField;
        // inputField and submitButton are distinct html elements for accessibility
        this.$submitButton = $submitButton;
        this.$menu = $menu;
        this.$closeIcon = $menu.parent().find('.close-search');
        this.options = options;

        this.expandable = (options && options.expandable) ? options.expandable : false;
        console.log(this.expandable);
        this.typeaheadUrl = this.$inputField.data("typeahead-url");
        this.typeaheadData = null;
        this.suggestionUrl = '';
        this.currTimeout = null;
        this.initialWidth = 0;
        this.resizeTimout = 0;

        // search field is hidden before document.ready (events firing before document.ready can get lost)
        this.$inputField.show();
        this.registerListeners();
    }

    registerListeners() {
        this.$inputField.on("focus", (e) => {
            this.initTypeahead();
            this.disableArticle();
            this.expandSearch();
        });

        this.$closeIcon.on('click', (e) => {
            this.reset();
        })

        this.$inputField.on("keyup", (e) => {
            this.enhanceAccessibility();
            this.onKeyUp(e);
        });

        this.$inputField.on("keydown", (e) => {
            this.onKeyDown(e);
        });

        this.$inputField.on("blur", (e) => {
            this.enableArticle(); // in case of a click always leave.

            this.currTimeout = setTimeout(() => {
                this.hideMenu();
                this.unexpandSearch();
            }, 150);
        });

        this.$inputField.on("click", (e) => {
            e.stopPropagation();
        });

        $(window).on('resize', (e) => {
            if ($(window).width() < 720) {
                $('.searchbox').css('width', "");
            }

            this.$inputField.blur();
        });

        this.$menu.on('click', (e) => {
            e.stopPropagation();
            // do not prevent default to allow menu links be clicked.
            if (this.currTimeout !== null) {
                if (this.currTimeout) {
                    clearTimeout(this.currTimeout);
                }
                this.currTimeout = setTimeout(() => {
                    this.hideMenu();
                }, 500); // defer menu hiding to allow some time to show active state before closing.
            }
        });
    }

    onKeyUp(e) {
        switch (e.keyCode) {
            case 40: // down arrow or
            case 38: // up arrow
                break;
            case 9: // tab or
                this.hideMenu();
                break;
            case 27: // escape must unexpand the menu but not clear it
                this.clearInput();
                this.$inputField.blur();
                break;
            default:
                this.lookup();
        }
    }

    onKeyDown(e) {
        // if no suggestion is selected fall back to default browser behavior for form submission
        if (e.keyCode === 13) {
            if (this.suggestionUrl != '') {
                e.stopPropagation();
                e.preventDefault();
                location.href = this.suggestionUrl;
            }
        }
        if (e.keyCode === 40 || e.keyCode === 38) {
            let direction = e.keyCode === 40 ? 'down' : 'up';
            this.moveInMenu(direction);
        }
    }

    hideMenu() {
        this.$inputField.removeClass('search--has-results' );
        this.$menu.addClass('h-element--hide');
        this.suggestionUrl = '';
    }

    clearInput() {
        this.$inputField.val("");
        this.$submitButton.attr("tabindex", -1).attr("aria-hidden", true);
        this.hideCloseIcon();
        this.suggestionUrl = '';
    }

    reset() {
        this.clearInput();
        this.hideMenu();
        this.unexpandSearch();
    }

    moveInMenu(direction) {
        if (direction === 'down') {
            this.nextSuggestion();
        }
        else if (direction === 'up') {
            this.prevSuggestion();
        }
    }

    prevSuggestion() {
        let $active = this.$menu.find('.active').removeClass('active');
        let $prev = $active.prev();
        if ($prev.length === 0) {
            $prev = this.$menu.find('li').last();
        }
        this.suggestionUrl = $prev.find("a").attr('href');
        $prev.addClass('active');
    }

    nextSuggestion() {
        let $active = this.$menu.find('.active').removeClass('active');
        let $next = $active.next();
        if ($next.length === 0) {
            $next = $(this.$menu.find('li').first());
        }
        this.suggestionUrl = $next.find("a").attr('href');
        $next.addClass('active');
    }

    enhanceAccessibility() {
        // a search button only makes sense on desktop - when it's actually workin.
        // TODO: What is still needed here?
        if (!(('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0))) {
            // this works mobile as well unlike <enter>-keys an d the likes
            if (this.$inputField.val().length > 2 && this.$submitButton.attr("tabindex") == -1) {
                this.$submitButton.attr("tabindex", 0).attr("aria-hidden", false);
            } else if (this.$inputField.val().length == 0 && this.$submitButton.attr("tabindex") == 0) {
                this.$submitButton.attr("tabindex", -1).attr("aria-hidden", true);
            }
        }
    }

    initTypeahead() {
        if (this.typeaheadData === null) {
            $.getJSON(this.typeaheadUrl, (data) => {
                this.typeaheadData = data;
            })
            this.initialWidth = parseInt($('.searchbox:first').css('width'));
        }
        this.showCloseIconIfNeeded();
        this.expandSearch();

    }

    lookup() {
        // adjust close icon state regardless of search results
        this.showCloseIconIfNeeded();
        let results = [];
        let query = this.$inputField.val().toString().toLowerCase();
        if (this.typeaheadData === null) {
            return true;
        }

        if(query.length < this.options.minSearchLength) {
            this.hideMenu();
            return true;
        }

        this.typeaheadData.forEach((item) => {
            let matchIndex = item.name.toString().toLowerCase().indexOf(query);
            if (matchIndex >= 0) {
                results.push({name: item.name, url: item.url, matchIndex: matchIndex});
            }
        });

        if (results.length > 0) {
            results = results.slice(0, this.options.maxSuggestionCount);
            this.renderResults(results, query);
            this.$inputField.addClass('search--has-results' );
        } else {
            this.hideMenu();
        }
    }

    renderResults(results, query) {
        let html = '';

        results.forEach((result) => {
            let name = this.highlightQuery(query, result.name);
            html += `<li role="option" class="typeahead-suggestion" tabindex="-1"> <a href="${result.url}">${name}</a> </li>`;
        })
        this.$menu.css('width', this.$inputField.outerWidth() + "px");
        this.$menu.html(html).removeClass('h-element--hide');
    }

    highlightQuery(query, name) {
        query = query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
        return name.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
            return '' + match + ''
        })
    }

    showCloseIcon() {
        this.$closeIcon.removeClass('h-element--hide');
        let y = this.$inputField.position().top;
        let x = this.$inputField.position().left;
        x = x + this.$inputField.outerWidth() - this.$closeIcon.outerWidth();
        this.$closeIcon.css({'top': y, 'left': x});
    }

    hideCloseIcon() {
        this.$closeIcon.addClass('h-element--hide');
    }

    showCloseIconIfNeeded(deferred) {
        if (!deferred) {
            if (this.$inputField.val() === '') {
                this.hideCloseIcon();
            } else {
                this.showCloseIcon();
            }
        } else {
            if (this.currTimeout) {
                clearTimeout(this.currTimeout);
            }
            this.currTimeout = setTimeout(() => {this.showCloseIconIfNeeded()}, deferred);
        }
    }

    expandSearch() {
        if ($(window).width() < 720) {
            $('.searchbox').css('width', "");
            return;
        }

        if (!this.expandable) {
            return;
        }
        this.hideCloseIcon();
        this.showCloseIconIfNeeded(500); // currTimeout gets set here
        $('.searchbox.searchbox--header').addClass('centered'); // add margin: 50% and animations and calculate the new width (90% of container, adjusted by width).

        // calculate new width (bar must be centered)
        let right = $('.menu-handle__info').offset().left;
        let left = $('.header__logo-img').offset().left;
        let newWidth = right - left - 77; // srf logo width and  margins and paddings :/
        $('.searchbox.searchbox--header').css('width', newWidth);
    }

    unexpandSearch() {
        if (!this.expandable) {
            return;
        }
        if ($('.searchbox.searchbox--header').hasClass('centered')) {
            this.hideCloseIcon();
            $('.searchbox.searchbox--header').removeClass('centered');
            let right = parseInt($('.searchbox').css('right'));
            $('.searchbox.searchbox--header').css('width', this.initialWidth);
        }
    }

    disableArticle() {
        if (!this.expandable) {
            return;
        }
        if ($(window).width() > 720 && $('body').hasClass('body--fixed') == false) {
            // add classes once
            $('body').addClass('search--overlay');
        }
    }

    enableArticle() {
        if (!this.expandable) {
            return;
        }
        $('body').removeClass('search--overlay');
    }
}

<|endoftext|>